
Api Docs Quality Report
- 78 installs
- 93 repo stars
- Updated June 28, 2026
- infrasity-labs/dev-gtm-claude-skills
Crawl an entire API reference site, score every endpoint across five quality checks, and deliver a shareable HTML audit with ranked fixes before launch or a developer-experience review.
About
API Docs Quality Report is a Dev GTM Claude skill that automates a full-reference audit of API documentation. Instead of spot-checking a few endpoints, it crawls the site, applies five structured quality checks—commonly gaps like missing response schemas, thin descriptions, and absent non-200 error codes—and aggregates results into an interactive HTML report suitable for DevRel, technical writing, and engineering review. Site-wide pattern detection highlights systemic doc generator or template failures affecting most routes. Solo API founders use it before public launch or partner integrations to avoid support churn from incomplete references. The workflow assumes a reachable docs base URL and is oriented to Claude Code activation from the dev-gtm-claude-skills repository. It complements manual editorial review rather than replacing OpenAPI source-of-truth fixes; deliverables emphasize prioritization and fix text teams can execute in their doc stack.
- Crawls every endpoint page on an API documentation site and scores each across 5 quality checks
- Produces a dark-theme interactive HTML report with summary scorecard and ranked top issues
- Detects site-wide patterns such as problems affecting 60%+ of endpoints
- Per-endpoint findings include specific fix guidance for writers and engineers
- Triggered via Claude Code with /dev-gtm api-audit or natural-language audit requests
Api Docs Quality Report by the numbers
- 78 all-time installs (skills.sh)
- +4 installs in the week ending Jul 25, 2026 (Skillselion tracking)
- Ranked #692 of 1,901 Documentation skills by installs in the Skillselion catalog
- Data as of Jul 26, 2026 (Skillselion catalog sync)
npx skills add https://github.com/infrasity-labs/dev-gtm-claude-skills --skill api-docs-quality-reportAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 78 |
|---|---|
| repo stars | ★ 93 |
| Last updated | June 28, 2026 |
| Repository | infrasity-labs/dev-gtm-claude-skills ↗ |
What it does
Crawl an entire API reference site, score every endpoint across five quality checks, and deliver a shareable HTML audit with ranked fixes before launch or a developer-experience review.
Files
API Docs Audit Skill
Crawls every API reference endpoint page on a documentation site, scores each against 5 quality checks, detects site-wide patterns, and outputs an interactive HTML report matching the style of previously-generated audits in this session.
---
STEP 0 — COLLECT INPUT
You need exactly one input: the docs URL.
If the user hasn't provided it, ask:
Which API docs URL should I audit?
If the URL is provided, strip trailing slashes and confirm it before proceeding.
Special case — "do the same for X": If the user says "do the same" or "same audit for X", they want the identical workflow and HTML report format applied to the new URL. Proceed directly to Step 1.
---
STEP 1 — DISCOVER ALL ENDPOINT PAGES
1a. Try llms.txt first
Fetch <docs_url>/llms.txt.
If found:
- Extract every line matching the pattern
- [Page Title](URL): description - Filter to API reference pages only — look for paths containing any of:
api-reference, api-reference/, /api/, /endpoints/, /rest/, /docs/, /reference/ or versioned paths (e.g., /v1/)
- Exclude: overview/index pages (no HTTP method implied), SDK docs, CLI docs,
Terraform docs, guides, quickstart pages
- Store as
ENDPOINT_PAGES[]— list of{title, url, description}
If not found or returns 404:
- Fetch the docs homepage
- Look for a nav sidebar or sitemap link referencing API/endpoints
- Extract all API reference page URLs from the nav structure
- Note in the report that llms.txt was unavailable
1b. Identify categories
Group discovered pages by their URL path prefix or nav section:
- e.g.
/api-reference/agents/→ Agents category - e.g.
/api-reference/executions/→ Executions category - Pages at the root level of api-reference → Uncategorized or infer from title
Store CATEGORIES[] — list of {name, page_count, pages[]}.
Report discovery summary:
Found N endpoint pages across K categories. Fetching now...
---
STEP 2 — CHECK OPENAPI SPEC AVAILABILITY
Before fetching individual pages, check if a standalone OpenAPI spec exists.
Try these discovery methods in order (stop at first success): 1. Inspect the Link HTTP header (RFC 8288) on the homepage or docs root for rel="openapi" or rel="describedby" — this is the standard discovery mechanism for API specifications 2. <docs_url>/openapi.json 3. <docs_url>/openapi.yaml 4. <docs_url>/api/openapi.json 5. Any URL referenced in llms.txt under "## OpenAPI Specs"
Result:
- If accessible:
SPEC_AVAILABLE = true, store the URL - If 404 on all attempts and no Link header found:
SPEC_AVAILABLE = false
A missing spec is itself a top-priority finding — record it as Issue #1.
---
STEP 3 — FETCH AND PARSE ALL ENDPOINT PAGES
Fetch each URL in ENDPOINT_PAGES[]. For each page, extract:
3a. Page-level prose description
The text that appears above the OpenAPI block (or between the # title and the first ## OpenAPI heading). This is the human-written description.
3b. OpenAPI block
Look for a fenced code block with language hint yaml or json that contains an OpenAPI spec. Extract:
HTTP_METHOD— e.g.GET,POST,PUT,DELETE,PATCHHTTP_PATH— e.g./api/v1/agents/{agent_id}/executeREQUEST_BODY_SCHEMA— the requestBody schema section (if any)PARAMETERS— path + query params (if any)RESPONSES— the full responses objectRESPONSE_200_SCHEMA— the schema under responses.200 or 201
If no OpenAPI block found, mark SPEC_INLINE = false.
---
STEP 4 — SCORE EACH ENDPOINT (5 CHECKS)
For every endpoint, evaluate all 5 checks. Store results as {check_name: "pass"|"warn"|"fail", current_state: string, fix_guidance: string}.
Read references/scoring-rules.md for the exact scoring criteria per check.
Check 1 — Endpoint Description Check 2 — OpenAPI Spec Check 3 — Body Param Descriptions Check 4 — Response Codes Check 5 — Response Schema
---
STEP 5 — DETECT SITE-WIDE PATTERNS
After scoring all endpoints, look for patterns that repeat across many pages.
A pattern qualifies if it affects ≥ 60% of endpoints:
| Pattern to detect | How to identify |
|---|---|
| Universal missing error codes | All endpoints missing 401, 429, 500 etc. |
| Universal empty response schemas | schema: {} on most endpoints |
| Free-form body schemas | additionalProperties: true on most request bodies |
| Inline spec only / broken canonical spec | All pages embed spec inline but canonical 404s |
| One-liner descriptions everywhere | Descriptions under 10 words on most pages |
| 422-only validation errors | Only FastAPI's 422 documented, not business errors |
For each detected pattern, write a single paragraph summarising:
- What the pattern is
- How many endpoints it affects
- Root cause (e.g. FastAPI auto-generation, missing middleware docs)
- Impact on developer experience
Store as SITE_WIDE_PATTERNS[].
---
STEP 6 — COMPILE TOP ISSUES
Rank the 10 most impactful issues across all findings. Prioritise by:
1. Issues affecting the most endpoints 2. Issues on the highest-traffic / most important endpoints 3. Security-sensitive gaps (auth endpoints, credential endpoints with empty schemas) 4. Deprecated endpoints with no migration path 5. Spec availability (404 canonical spec blocks all SDK tooling)
For each issue, write:
- A concise
<strong>title</strong> - 1-2 sentence description with specific endpoint names or counts
- Badge: FAIL or WARN
Store as TOP_ISSUES[] (max 10).
---
STEP 7 — BUILD SCORECARD
For each category, calculate:
total— endpoint countpass_count— endpoints with all 5 checks passingwarn_count— endpoints with at least one WARN and no FAILfail_count— endpoints with at least one FAIL
Calculate overall totals.
Store as SCORECARD[].
---
STEP 8 — GENERATE HTML REPORT
Read references/html-template.md for the complete HTML structure, CSS variables, JavaScript, and all section markup.
Substitute all {{VARIABLE}} placeholders with real audit data, ensuring all values are HTML-escaped to prevent XSS.
Key variable map:
| Variable | Source |
|---|---|
{{DOCS_URL}} | Input URL |
{{AUDIT_DATE}} | Today's date |
{{TOTAL_ENDPOINTS}} | Count of ENDPOINT_PAGES |
{{TOTAL_PASS}} | Sum of all pass_count across categories |
{{TOTAL_WARN}} | Sum of all warn_count |
{{TOTAL_FAIL}} | Sum of all fail_count |
{{SPEC_COUNT}} | 1 if SPEC_AVAILABLE, else 0 |
{{SCORECARD_ROWS}} | Generated <tr> rows from SCORECARD |
{{PATTERN_BOX}} | Pattern box HTML (if SITE_WIDE_PATTERNS found) |
{{TOP_ISSUES_HTML}} | Generated issue row HTML from TOP_ISSUES |
{{SIDEBAR_LINKS}} | Generated sidebar <div class="sbl"> links |
{{SECTIONS_HTML}} | All section HTML for each category |
For section HTML generation rules, see references/html-template.md → Section: SECTIONS_HTML.
Output the file to:
/mnt/user-data/outputs/<slug>-api-audit-report.htmlWhere <slug> is the domain name with dots replaced by hyphens (e.g. docs.kubiya.ai → kubiya-ai).
Then call present_files to deliver it.
---
ERROR HANDLING
| Situation | Action |
|---|---|
llms.txt returns 404 | Crawl nav links from homepage; note in report |
| Individual endpoint page returns 404 | Skip it; add to "inaccessible pages" count in footer |
| Page has no OpenAPI block | Score Spec check as FAIL; still score other checks from prose |
| Page has no prose description | Score Description as FAIL with note "No prose description" |
| Endpoint page is robots.txt blocked | Note in report; do not fetch |
| More than 200 endpoints found | Batch fetching — process in groups of 20, progress-report to user |
| Canonical spec accessible | Note as positive finding; do not flag as issue |
---
WHAT NOT TO DO
- ❌ Never start generating HTML before all pages are fetched and scored
- ❌ Never skip an endpoint page — fetch every one listed in llms.txt
- ❌ Never infer scores from page titles alone — fetch the actual page content
- ❌ Never mark Response Codes as PASS just because 200 is documented
- ❌ Never mark Response Schema as PASS for
schema: {}— that is FAIL - ❌ Never mark Body Params as PASS for
additionalProperties: true— that is WARN at minimum - ❌ Never report a pattern as site-wide unless it affects ≥ 60% of endpoints
- ❌ Never put generic fix text — every fix_guidance must reference the specific endpoint
---
REFERENCE FILES
references/scoring-rules.md— Exact PASS/WARN/FAIL criteria for all 5 checks,
with worked examples. Read before scoring any endpoint.
references/html-template.md— Complete HTML/CSS/JS template with all section
markup, variable map, and rendering instructions. Read before writing any HTML.
API Docs Audit
Crawls every endpoint page on an API documentation site, scores each one across 5 quality checks, detects site-wide patterns, and produces an interactive HTML report with a summary scorecard, ranked issues, and per-endpoint fix guidance.
---
What this skill does
Most API docs have the same problems at scale: missing response schemas, one-liner endpoint descriptions, no error codes beyond 200. This skill finds all of them automatically.
It crawls every endpoint page on your API reference, scores each against 5 checks, and produces a dark-theme HTML report you can share with your team or embed in a doc review. The report includes a summary scorecard, site-wide pattern analysis (problems affecting 60%+ of endpoints), a ranked top-issues list, and per-endpoint findings with specific fix text.
Built for:
- Developer experience and DevRel teams auditing their API docs before a launch
- Technical writers running a structured gap analysis across all endpoints
- Engineering teams checking whether auto-generated docs are complete enough for external developers
---
Installation
Claude Code (Recommended)
Clone the repo — the skill activates automatically when you open it in Claude Code:
git clone https://github.com/Infrasity-Labs/dev-gtm-claude-skills.git
cd dev-gtm-claude-skills
claudeThen trigger it with:
/dev-gtm api-audit https://docs.example.com/api-referenceOr just describe what you want in natural language — Claude will activate the skill automatically.
Claude Web (Free / Pro)
1. Go to [Settings → Capabilities](https://claude.ai/settings/capabilities) and enable Code execution and file creation 2. Go to [Customize → Skills](https://claude.ai/customize/skills) 3. Click + → Create skill → Upload a skill 4. Zip this skill folder and upload it:
cd dev-gtm-claude-skills/skills
zip -r api-docs-quality-report.zip api-docs-quality-report/Upload api-docs-quality-report.zip and toggle it on.
---
How to use
Drop an API docs URL in any message:
Audit the API docs at https://docs.kubiya.ai/api-referenceCheck the API docs for https://api.example.com/docs/dev-gtm api-audit https://docs.example.com/api-referenceClaude discovers all endpoint pages automatically — you only need to provide the root docs URL.
---
The 5 checks
Every endpoint is scored against these 5 checks. Each returns pass, warn, or fail with a specific evidence note.
| # | Check | What it evaluates |
|---|---|---|
| 1 | Endpoint Description | Is there a human-written prose description above the OpenAPI block? Is it more than a one-liner? |
| 2 | OpenAPI Spec | Is there an inline OpenAPI block? Is there a canonical spec file accessible at a standard path? |
| 3 | Body Param Descriptions | Do all request body parameters have descriptions? Are schemas using additionalProperties: true (too loose)? |
| 4 | Response Codes | Are error codes documented beyond just 200? Are 401, 429, and 500 covered? |
| 5 | Response Schema | Is the 200/201 response schema fully defined? Is schema: {} used (empty schema = fail)? |
---
What the report includes
Summary scorecard — a table showing pass/warn/fail counts per endpoint category (e.g. Agents, Executions, Webhooks).
Site-wide patterns — problems affecting 60%+ of endpoints are called out as patterns with a root cause analysis and DX impact note. Common patterns detected: universal missing error codes, empty response schemas, free-form body schemas, one-liner descriptions everywhere.
Top 10 issues — ranked by impact (endpoints affected, security sensitivity, spec availability). Each issue has a concise title, a description with specific endpoint names, and a FAIL or WARN badge.
Per-endpoint findings — every endpoint gets its own section with all 5 check results, the current state, and specific fix guidance referencing that endpoint's actual content.
---
How endpoint discovery works
The skill uses a layered discovery approach:
1. Fetches <docs_url>/llms.txt — if found, extracts all API reference page links from it 2. If llms.txt is unavailable, falls back to crawling the homepage nav sidebar for API reference links 3. Checks for a canonical OpenAPI spec at standard paths (/openapi.json, /openapi.yaml, etc.) and via the Link HTTP header 4. Fetches every discovered endpoint page
Pages behind authentication are skipped and noted. If more than 200 endpoints are found, they are processed in batches of 20 with progress updates.
---
Output
An HTML file saved to /mnt/user-data/outputs/<slug>-api-audit-report.html, delivered via present_files.
The report is self-contained — no external dependencies, no CDN. Share it as a file or open it directly in a browser.
---
File structure
api-docs-quality-report/
├── SKILL.md # Skill instructions Claude follows
├── README.md # This file
└── references/
├── scoring-rules.md # Exact pass/warn/fail criteria for all 5 checks
└── html-template.md # Full HTML/CSS/JS report templateHTML Report Template Reference
This file contains the complete structure, CSS, JavaScript, and variable substitution rules for generating the audit report HTML.
Read this file entirely before writing any HTML.
---
Table of Contents
1. Design System & CSS Variables 2. Full HTML Shell 3. Header Section 4. Legend Bar 5. Sidebar 6. Scorecard Table 7. Site-Wide Pattern Box 8. Top Issues Section 9. Section Layout (per category) 10. Endpoint Card — Detailed 11. Compact Group Table 12. JavaScript 13. Variable Substitution Map 14. Section Rendering Decision Rules
---
1. Design System & CSS Variables
:root {
--bg: #06060a;
--surface: #0e0e16;
--surface2: #14141f;
--border: #232336;
--border2: #2e2e4a;
--text: #e2e2f0;
--text2: #8888aa;
--text3: #4a4a6a;
--pass: #34d399; --pass-bg: #052015; --pass-border: #064824;
--warn: #fbbf24; --warn-bg: #201500; --warn-border: #4a3300;
--fail: #f87171; --fail-bg: #200808; --fail-border: #4a1212;
--accent: #a78bfa; --accent2: #c4b5fd; --accent-bg: #12093a;
--tag-bg: #141424;
}Typography: 'Space Grotesk', sans-serif for body, 'Space Mono', monospace for code, labels, and numbers. Load both from Google Fonts.
Badge classes:
<span class="badge pass">✓ PASS</span>
<span class="badge warn">⚠ WARN</span>
<span class="badge fail">✕ FAIL</span>Status dots (5 dots per endpoint, one per check):
<span class="dot pass"></span>
<span class="dot warn"></span>
<span class="dot fail"></span>---
2. Full HTML Shell
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{SITE_NAME}} API Docs Audit Report</title>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=Space+Mono:wght@400;700&display=swap" rel="stylesheet">
<style>
/* PASTE FULL CSS HERE — see sections below */
</style>
</head>
<body>
{{HEADER}}
{{LEGEND_BAR}}
<div class="main">
{{SIDEBAR}}
<div class="content">
{{SCORECARD_SECTION}}
{{PATTERN_BOX}}
{{TOP_ISSUES_SECTION}}
<hr class="divider">
{{ALL_CATEGORY_SECTIONS}}
</div>
</div>
<script>{{JAVASCRIPT}}</script>
</body>
</html>---
3. Header Section
<div class="header">
<div class="eyebrow">API Documentation Audit</div>
<h1>{{SITE_NAME}} API Docs</h1>
<div class="header-url">{{DOCS_URL}}</div>
<div class="stats-row">
<div class="stat-cell">
<span class="sv sv-total">{{TOTAL_ENDPOINTS}}</span>
<span class="slabel">Endpoints</span>
</div>
<div class="stat-cell">
<span class="sv sv-pass">{{TOTAL_PASS}}</span>
<span class="slabel">Pass</span>
</div>
<div class="stat-cell">
<span class="sv sv-warn">{{TOTAL_WARN}}</span>
<span class="slabel">Warn</span>
</div>
<div class="stat-cell">
<span class="sv sv-fail">{{TOTAL_FAIL}}</span>
<span class="slabel">Fail</span>
</div>
<div class="stat-cell">
<span class="sv sv-accent">{{SPEC_COUNT}}</span>
<span class="slabel">OpenAPI Spec{{SPEC_SUFFIX}}</span>
</div>
<div class="stat-cell">
<span class="sv" style="color:var(--text2);font-size:18px">{{AUDIT_DATE}}</span>
<span class="slabel">Audit Date</span>
</div>
</div>
</div>{{SPEC_SUFFIX}} = "" if spec accessible, " (404)" if canonical spec returns 404.
CSS for header:
.header {
border-bottom: 1px solid var(--border);
padding: 48px 64px 40px;
position: relative;
overflow: hidden;
background: linear-gradient(135deg, #0a061a 0%, var(--bg) 60%);
}
.header::after {
content: '';
position: absolute;
top: -100px; right: -100px;
width: 500px; height: 500px;
background: radial-gradient(circle, rgba(167,139,250,0.08) 0%, transparent 60%);
pointer-events: none;
}
.eyebrow {
font-family: 'Space Mono', monospace;
font-size: 10px; color: var(--accent);
letter-spacing: 0.2em; text-transform: uppercase;
margin-bottom: 10px; display: flex; align-items: center; gap: 8px;
}
.eyebrow::before { content: ''; display: block; width: 20px; height: 1px; background: var(--accent); }
h1 { font-size: 36px; font-weight: 700; letter-spacing: -0.03em; margin-bottom: 6px; }
.header-url {
font-family: 'Space Mono', monospace; font-size: 12px; color: var(--text3);
background: var(--surface2); border: 1px solid var(--border);
display: inline-flex; align-items: center; gap: 6px;
padding: 5px 12px; border-radius: 4px; margin-bottom: 36px;
}
.header-url::before { content: '→'; color: var(--accent); font-size: 10px; }
.stats-row { display: flex; border: 1px solid var(--border); border-radius: 8px; overflow: hidden; width: fit-content; }
.stat-cell { padding: 16px 28px; border-right: 1px solid var(--border); display: flex; flex-direction: column; gap: 4px; background: var(--surface); }
.stat-cell:last-child { border-right: none; }
.sv { font-family: 'Space Mono', monospace; font-size: 26px; font-weight: 700; line-height: 1; }
.sv-total{color:var(--text)} .sv-pass{color:var(--pass)} .sv-warn{color:var(--warn)} .sv-fail{color:var(--fail)} .sv-accent{color:var(--accent2)}
.slabel { font-size: 10px; color: var(--text3); text-transform: uppercase; letter-spacing: 0.1em; }---
4. Legend Bar
<div class="legend-bar">
<span class="ll">Legend</span>
<div class="li"><span class="badge pass">✓ PASS</span> Present and correct</div>
<div class="li"><span class="badge warn">⚠ WARN</span> Present but incomplete</div>
<div class="li"><span class="badge fail">✕ FAIL</span> Missing or wrong</div>
</div>.legend-bar { padding: 12px 64px; border-bottom: 1px solid var(--border); background: var(--surface); display: flex; align-items: center; gap: 24px; flex-wrap: wrap; }
.ll { font-family: 'Space Mono', monospace; font-size: 9px; color: var(--text3); text-transform: uppercase; letter-spacing: 0.18em; }
.badge { display: inline-flex; align-items: center; gap: 4px; padding: 2px 8px; border-radius: 3px; font-family: 'Space Mono', monospace; font-size: 10px; font-weight: 700; letter-spacing: 0.06em; }
.badge.pass { background: var(--pass-bg); color: var(--pass); border: 1px solid var(--pass-border); }
.badge.warn { background: var(--warn-bg); color: var(--warn); border: 1px solid var(--warn-border); }
.badge.fail { background: var(--fail-bg); color: var(--fail); border: 1px solid var(--fail-border); }
.li { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--text2); }---
5. Sidebar
<nav class="sidebar">
<div class="sbh">Overview</div>
<div class="sbl" onclick="scrollTo('scorecard')">Summary Scorecard</div>
<div class="sbl" onclick="scrollTo('pattern')">Site-Wide Pattern</div>
<div class="sbl" onclick="scrollTo('top-issues')">Top Issues</div>
<div class="sbh" style="margin-top:8px">Sections</div>
<!-- For each category: -->
<div class="sbl" onclick="scrollTo('{{CAT_ID}}')">
{{CAT_NAME}}
<span class="sbp">
<span class="spp spp-p">{{CAT_PASS}}P</span>
<span class="spp spp-w">{{CAT_WARN}}W</span>
<span class="spp spp-f">{{CAT_FAIL}}F</span>
</span>
</div>
</nav>{{CAT_ID}} = category name lowercased, spaces replaced with hyphens. e.g. "Context Graph" → context-graph
.main { display: grid; grid-template-columns: 256px 1fr; min-height: calc(100vh - 200px); }
.sidebar { border-right: 1px solid var(--border); padding: 20px 0; position: sticky; top: 0; height: 100vh; overflow-y: auto; background: var(--surface); }
.sidebar::-webkit-scrollbar { width: 3px; } .sidebar::-webkit-scrollbar-thumb { background: var(--border2); }
.sbh { font-family: 'Space Mono', monospace; font-size: 9px; color: var(--text3); text-transform: uppercase; letter-spacing: 0.18em; padding: 8px 20px 4px; }
.sbl { display: flex; align-items: center; justify-content: space-between; padding: 7px 20px; font-size: 12px; color: var(--text2); cursor: pointer; border-left: 2px solid transparent; transition: all 0.12s; }
.sbl:hover { background: var(--surface2); color: var(--text); border-left-color: var(--border2); }
.sbp { display: flex; gap: 3px; }
.spp { font-family: 'Space Mono', monospace; font-size: 9px; padding: 1px 5px; border-radius: 2px; }
.spp-p { background: var(--pass-bg); color: var(--pass); }
.spp-w { background: var(--warn-bg); color: var(--warn); }
.spp-f { background: var(--fail-bg); color: var(--fail); }
.content { padding: 40px 56px; max-width: 1080px; }---
6. Scorecard Table
<div id="scorecard" style="margin-bottom:48px">
<div class="st">Summary Scorecard</div>
<table class="sc-table">
<thead>
<tr><th>Category</th><th>Endpoints</th><th>✓ Pass</th><th>⚠ Warn</th><th>✕ Fail</th></tr>
</thead>
<tbody>
<!-- For each category row: -->
<tr>
<td><a class="clink" onclick="scrollTo('{{CAT_ID}}')">{{CAT_NAME}}</a></td>
<td class="nt">{{CAT_TOTAL}}</td>
<td class="np">{{CAT_PASS}}</td>
<td class="nw">{{CAT_WARN}}</td>
<td class="nf">{{CAT_FAIL}}</td>
</tr>
<!-- Total row (last): -->
<tr>
<td><strong>Total</strong></td>
<td class="nt">{{TOTAL_ENDPOINTS}}</td>
<td class="np">{{TOTAL_PASS}}</td>
<td class="nw">{{TOTAL_WARN}}</td>
<td class="nf">{{TOTAL_FAIL}}</td>
</tr>
</tbody>
</table>
</div>.st { font-family: 'Space Mono', monospace; font-size: 9px; color: var(--accent); text-transform: uppercase; letter-spacing: 0.2em; margin-bottom: 14px; }
.sc-table { width: 100%; border-collapse: collapse; border: 1px solid var(--border); border-radius: 6px; overflow: hidden; margin-bottom: 48px; }
.sc-table thead tr { background: var(--surface2); border-bottom: 1px solid var(--border2); }
.sc-table th { padding: 10px 14px; text-align: left; font-family: 'Space Mono', monospace; font-size: 9px; color: var(--text3); text-transform: uppercase; letter-spacing: 0.12em; font-weight: 400; }
.sc-table th:not(:first-child) { text-align: center; }
.sc-table td { padding: 11px 14px; border-bottom: 1px solid var(--border); font-size: 13px; }
.sc-table td:not(:first-child) { text-align: center; font-family: 'Space Mono', monospace; font-size: 12px; font-weight: 700; }
.sc-table tbody tr:last-child td { border-bottom: none; background: var(--surface2); font-weight: 600; }
.sc-table tbody tr:hover:not(:last-child) { background: rgba(255,255,255,0.015); }
.np{color:var(--pass)} .nw{color:var(--warn)} .nf{color:var(--fail)} .nt{color:var(--text)}
.clink { color: var(--text); cursor: pointer; } .clink:hover { color: var(--accent2); }---
7. Site-Wide Pattern Box
Only render this section if SITE_WIDE_PATTERNS is non-empty.
<div id="pattern" style="margin-bottom:48px">
<div class="st">Site-Wide Pattern — Applies to All {{PATTERN_SCOPE}} Endpoints</div>
<div class="pbox">
{{PATTERN_PARAGRAPH_1}}
<br><br>
{{PATTERN_PARAGRAPH_2}}
<!-- Add <br><br> between each pattern paragraph -->
</div>
</div>{{PATTERN_SCOPE}} = "~N" where N is the total endpoint count, or "most" if patterns are partial. e.g. "~230" or "most"
.pbox { background: var(--accent-bg); border: 1px solid #2e1f6e; border-radius: 6px; padding: 16px 20px; margin-bottom: 20px; font-size: 13px; color: var(--text2); line-height: 1.7; }
.pbox strong { color: var(--accent2); }
code { font-family: 'Space Mono', monospace; font-size: 10px; background: var(--tag-bg); padding: 1px 5px; border-radius: 3px; color: var(--accent2); }
hr.divider { border: none; border-top: 1px solid var(--border); margin: 40px 0; }---
8. Top Issues Section
<div id="top-issues" style="margin-bottom:48px">
<div class="st">Top Priority Issues</div>
<div class="ibox">
<!-- For each issue (max 10): -->
<div class="irow">
<span class="in">{{ISSUE_NUM}}</span>
<span style="flex-shrink:0;padding-top:2px"><span class="badge {{ISSUE_SEVERITY}}">{{ISSUE_ICON}}</span></span>
<span class="it"><strong>{{ISSUE_TITLE}}:</strong> {{ISSUE_DESCRIPTION}}</span>
</div>
</div>
</div>{{ISSUE_NUM}} = zero-padded 2-digit number: 01, 02, ... 10 {{ISSUE_SEVERITY}} = fail or warn {{ISSUE_ICON}} = ✕ FAIL or ⚠ WARN
.ibox { border: 1px solid var(--border); border-radius: 6px; overflow: hidden; margin-bottom: 48px; }
.irow { display: flex; gap: 16px; padding: 14px 18px; border-bottom: 1px solid var(--border); align-items: flex-start; }
.irow:last-child { border-bottom: none; }
.in { font-family: 'Space Mono', monospace; font-size: 10px; color: var(--text3); flex-shrink: 0; padding-top: 2px; width: 20px; }
.it { font-size: 13px; color: var(--text2); line-height: 1.6; }
.it strong { color: var(--text); }---
9. Section Layout (per category)
Each category gets one <div class="as" id="{{CAT_ID}}">.
<div class="as" id="{{CAT_ID}}">
<div class="sh">
<div class="sn">{{CAT_NAME}}</div>
<span class="sc">{{CAT_TOTAL}} endpoints</span>
<span class="badge pass">{{CAT_PASS}} pass</span>
<span class="badge warn">{{CAT_WARN}} warn</span>
<span class="badge fail">{{CAT_FAIL}} fail</span>
</div>
<!-- Optional: pattern box for this category if a sub-pattern exists -->
<div class="pbox" style="margin-bottom:12px">
<strong>Pattern:</strong> {{CATEGORY_PATTERN_TEXT}}
</div>
<!-- Endpoint cards and/or group tables go here -->
{{ENDPOINTS_HTML}}
</div>.as { margin-bottom: 56px; scroll-margin-top: 20px; }
.sh { display: flex; align-items: center; gap: 10px; padding-bottom: 14px; border-bottom: 1px solid var(--border); margin-bottom: 16px; }
.sn { font-size: 20px; font-weight: 700; letter-spacing: -0.01em; }
.sc { font-size: 11px; color: var(--text3); background: var(--surface2); border: 1px solid var(--border); padding: 3px 10px; border-radius: 20px; }---
10. Endpoint Card — Detailed
Use this for notable endpoints that have at least one FAIL or a complex story worth showing in detail. Typically: the most important 1-3 endpoints per category, always including any with FAIL status that have a useful fix. Collapse by default; open the most important one.
<div class="ec {{CARD_CLASS}}" id="ep-{{EP_SLUG}}">
<div class="eh" onclick="toggle(this)">
<span class="mt {{METHOD_CLASS}}">{{METHOD}}</span>
<span class="ep">{{EP_PATH}}</span>
<span class="en">{{EP_NAME}}</span>
<div class="eds">
<!-- 5 dots, one per check, in order: desc, spec, params, codes, schema -->
<span class="dot {{CHECK1_STATUS}}"></span>
<span class="dot {{CHECK2_STATUS}}"></span>
<span class="dot {{CHECK3_STATUS}}"></span>
<span class="dot {{CHECK4_STATUS}}"></span>
<span class="dot {{CHECK5_STATUS}}"></span>
</div>
<span class="chv">▼</span>
</div>
<div class="eb">
<table class="ct">
<!-- Check row template: -->
<tr class="{{ROW_CLASS}}">
<td class="cl">{{CHECK_LABEL}}</td>
<td class="cs"><span class="badge {{CHECK_STATUS}}">{{CHECK_ICON}}</span></td>
<td>
<div class="cn">{{CURRENT_STATE}}</div>
<div class="cx"><strong>Fix:</strong> {{FIX_GUIDANCE}}</div>
<!-- Only show cx div if status is WARN or FAIL -->
</td>
</tr>
</table>
</div>
</div>`{{CARD_CLASS}}` based on worst check status:
- Any FAIL →
ec cf - Only WARNs →
ec cw - All PASS →
ec cp - Add
opento pre-expand:ec cf open
`{{METHOD_CLASS}}`:
- GET →
mt mg - POST →
mt mp - PUT →
mt mu(use orange) - DELETE →
mt md - PATCH →
mt mpatch
`{{ROW_CLASS}}`: rp (pass), rw (warn), rf (fail)
`{{CHECK_LABEL}}`: Description | OpenAPI Spec | Body Params | Response Codes | Response Schema
`{{CHECK_ICON}}`: ✓ PASS | ⚠ WARN | ✕ FAIL
Only show the Fix guidance div (cx) when status is WARN or FAIL.
.ec { border: 1px solid var(--border); border-radius: 6px; margin-bottom: 10px; overflow: hidden; }
.ec.cf { border-left: 3px solid var(--fail); }
.ec.cw:not(.cf) { border-left: 3px solid var(--warn); }
.ec.cp { border-left: 3px solid var(--pass); }
.eh { display: flex; align-items: center; gap: 10px; padding: 12px 18px; background: var(--surface); cursor: pointer; user-select: none; transition: background 0.1s; }
.eh:hover { background: var(--surface2); }
.mt { font-family: 'Space Mono', monospace; font-size: 9px; font-weight: 700; padding: 2px 6px; border-radius: 3px; flex-shrink: 0; letter-spacing: 0.05em; }
.mg { background: #052015; color: #4ade80; border: 1px solid #064824; }
.mp { background: #12093a; color: #a78bfa; border: 1px solid #2e1f6e; }
.mu { background: #1a0e00; color: #fb923c; border: 1px solid #431c00; }
.md { background: #200808; color: #f87171; border: 1px solid #4a1212; }
.mpatch { background: #111400; color: #bef264; border: 1px solid #2a3100; }
.ep { font-family: 'Space Mono', monospace; font-size: 11px; color: var(--text3); flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.en { font-size: 13px; font-weight: 500; flex-shrink: 0; color: var(--text); }
.eds { display: flex; gap: 3px; flex-shrink: 0; }
.dot { width: 7px; height: 7px; border-radius: 50%; }
.dot.pass { background: var(--pass); } .dot.warn { background: var(--warn); } .dot.fail { background: var(--fail); }
.chv { color: var(--text3); font-size: 9px; transition: transform 0.18s; flex-shrink: 0; }
.ec.open .chv { transform: rotate(180deg); }
.eb { display: none; border-top: 1px solid var(--border); }
.ec.open .eb { display: block; }
.ct { width: 100%; border-collapse: collapse; }
.ct tr { border-bottom: 1px solid var(--border); }
.ct tr:last-child { border-bottom: none; }
.ct td { padding: 11px 18px; vertical-align: top; }
.cl { width: 190px; font-family: 'Space Mono', monospace; font-size: 9px; color: var(--text3); text-transform: uppercase; letter-spacing: 0.1em; padding-top: 13px; white-space: nowrap; }
.cs { width: 72px; padding-top: 10px; }
.cn { color: var(--text2); font-size: 13px; line-height: 1.6; }
.cx { font-size: 11px; color: var(--text3); margin-top: 3px; font-style: italic; }
.cx strong { color: var(--warn); font-style: normal; }
.rp { background: rgba(52,211,153,0.02); }
.rw { background: rgba(251,191,36,0.02); }
.rf { background: rgba(248,113,113,0.03); }---
11. Compact Group Table
Use this for the remaining endpoints in a category after showing the notable cards. Also use this as the primary display for categories where the pattern is completely uniform (e.g. all endpoints fail the same two checks — just show the table).
<div style="margin-top:10px;border:1px solid var(--border);border-radius:6px;overflow:hidden;">
<table style="width:100%;border-collapse:collapse;">
<thead>
<tr style="background:var(--surface2);border-bottom:1px solid var(--border2);">
<th style="{{TH_STYLE}} text-align:left">Endpoint</th>
<th style="{{TH_STYLE}} text-align:center">Desc</th>
<th style="{{TH_STYLE}} text-align:center">Spec</th>
<th style="{{TH_STYLE}} text-align:center">Params</th>
<th style="{{TH_STYLE}} text-align:center">Codes</th>
<th style="{{TH_STYLE}} text-align:center">Schema</th>
<!-- Add Notes column only if any row has a note: -->
<th style="{{TH_STYLE}} text-align:left">Notes</th>
</tr>
</thead>
<tbody>
<!-- For each endpoint: -->
<tr style="border-bottom:{{BORDER}}">
<td style="padding:9px 14px;font-family:'Space Mono',monospace;font-size:10px;color:var(--text2);">
{{EP_PATH}}
</td>
<td style="padding:9px;text-align:center">{{BADGE_DESC}}</td>
<td style="padding:9px;text-align:center">{{BADGE_SPEC}}</td>
<td style="padding:9px;text-align:center">{{BADGE_PARAMS}}</td>
<td style="padding:9px;text-align:center">{{BADGE_CODES}}</td>
<td style="padding:9px;text-align:center">{{BADGE_SCHEMA}}</td>
<td style="padding:9px 14px;font-size:11px;color:var(--text3);">{{ROW_NOTE}}</td>
</tr>
</tbody>
</table>
<div style="padding:9px 14px;font-size:11px;color:var(--text3);border-top:1px solid var(--border);background:var(--surface2);font-style:italic;">
{{TABLE_FOOTER_NOTE}}
</div>
</div>{{TH_STYLE}} = padding:9px 14px;font-family:'Space Mono',monospace;font-size:9px;color:var(--text3);text-transform:uppercase;letter-spacing:0.1em;background:var(--surface2);border-bottom:1px solid var(--border2);font-weight:400;
{{BORDER}} = 1px solid var(--border) for all rows except last, none for last row.
{{BADGE_*}} = mini badge HTML:
<!-- pass --> <span class="badge pass" style="font-size:9px">✓</span>
<!-- warn --> <span class="badge warn" style="font-size:9px">⚠</span>
<!-- fail --> <span class="badge fail" style="font-size:9px">✕</span>{{TABLE_FOOTER_NOTE}} = summary of common issues, e.g.: "❌ All missing 400, 401, 403, 404, 500. Execute Team and Execute Team Stream have empty response schemas."
When to use a footer note: Always when the table has a pattern note. Make it specific — list actual endpoint names and actual missing codes. Never write "see above".
---
12. JavaScript
Include this script block at the end of <body>:
function toggle(h) { h.closest('.ec').classList.toggle('open'); }
function scrollTo(id) {
document.getElementById(id)?.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
// Dynamic table builder — used to inject group tables from JS arrays
// This is optional if tables are pre-rendered as HTML strings.
// Use this pattern if building tables programmatically:
const b = (s) => {
if (s === 'pass') return '<span class="badge pass" style="font-size:9px">✓</span>';
if (s === 'warn') return '<span class="badge warn" style="font-size:9px">⚠</span>';
return '<span class="badge fail" style="font-size:9px">✕</span>';
};---
13. Variable Substitution Map
| Variable | How to derive |
|---|---|
{{SITE_NAME}} | Domain name with caps: "Kubiya", "AIsa", "Fount" etc. |
{{DOCS_URL}} | Raw input URL |
{{AUDIT_DATE}} | Today's date in "Month DD, YYYY" format |
{{TOTAL_ENDPOINTS}} | Count of all endpoint pages fetched. Use "~N" if approximate. |
{{TOTAL_PASS}} | Sum of pass_count across all categories |
{{TOTAL_WARN}} | Sum of warn_count |
{{TOTAL_FAIL}} | Sum of fail_count |
{{SPEC_COUNT}} | 1 if canonical spec accessible, 0 if 404. Or count of spec files. |
{{SPEC_SUFFIX}} | " (404)" if canonical spec 404, "" otherwise |
{{CAT_ID}} | Category name → lowercase → spaces to hyphens |
{{CAT_NAME}} | Category display name (title case) |
{{CAT_TOTAL}} | Endpoint count in category |
{{CAT_PASS/WARN/FAIL}} | Count per status in category |
{{ISSUE_NUM}} | 01, 02, ... 10 |
{{ISSUE_SEVERITY}} | fail or warn |
{{EP_SLUG}} | Endpoint path → lowercase → slashes to hyphens |
{{PATTERN_SCOPE}} | "~N" total endpoints or "most" |
---
14. Section Rendering Decision Rules
For each category, decide which combination of cards and tables to use:
Rule A — Small category (≤ 5 endpoints, varied results)
Render every endpoint as an individual collapsible card. Open the worst ones by default.
Rule B — Medium category (6–15 endpoints, 1–3 notable endpoints)
Render the 1–3 most notable endpoints (highest severity or most complex findings) as detailed cards. Render the remaining endpoints in a compact group table beneath.
Rule C — Large category (> 15 endpoints, uniform pattern)
Render a pbox pattern summary at the top of the section. Render 1 representative detailed card showing the pattern. Render all remaining endpoints in a compact group table.
Rule D — All endpoints pass
No cards needed. Just render a green banner:
<div style="background:var(--pass-bg);border:1px solid var(--pass-border);border-radius:6px;padding:14px 18px;font-size:13px;color:#86efac;">
All {{CAT_TOTAL}} endpoints in this category are fully documented.
</div>Rule E — Black hole category (all endpoints have no meaningful docs)
Render a red banner summarising the problem, then a compact table.
<div class="fail-banner">
<strong style="color:var(--fail)">All {{CAT_TOTAL}} endpoints in this category are undocumented.</strong>
{{EXPLANATION}}
</div>.fail-banner { background: var(--fail-bg); border: 1px solid var(--fail-border); border-radius: 6px; padding: 14px 18px; margin-bottom: 12px; font-size: 13px; color: #fca5a5; }Rule F — Deprecated endpoints
Always give deprecated endpoints their own category section at the bottom, after all functional sections. Show each as a table row with the issue and the fix.
---
Responsive / mobile CSS
@media (max-width: 900px) {
.main { grid-template-columns: 1fr; }
.sidebar { display: none; }
.header, .legend-bar, .content { padding-left: 20px; padding-right: 20px; }
}Scoring Rules Reference
Complete PASS / WARN / FAIL criteria for each of the 5 audit checks. Read this file before scoring any endpoint.
---
CHECK 1 — Endpoint Description
Evaluates the prose text above the OpenAPI block on the page. This is the human-written description — not the OpenAPI summary or description field.
| Status | Criteria |
|---|---|
| ✅ PASS | ≥ 2 sentences of real context. Explains what the endpoint does, when to use it, and any important behaviour (async, side effects, prerequisites, auth requirements). |
| ⚠️ WARN | 1 sentence or only restates the endpoint title. Covers what it does but misses context, prerequisites, or important behaviour. |
| ❌ FAIL | No prose description at all. Or description is copy-pasted from another API (e.g. OpenAI docs text on a proxy endpoint). Or only the OpenAPI summary repeated. |
Worked examples:
PASS: "Execute an agent by submitting to Temporal workflow. This creates an execution
record and starts a Temporal workflow. The actual execution happens asynchronously on
the Temporal worker. The runner_name should come from the Composer UI where user selects
from available runners."
WARN: "Create a new agent in the organization."
FAIL: (no text above OpenAPI block)
FAIL: (text is verbatim copy of OpenAI documentation)Special cases:
- Health check endpoints with "no auth required" and no params: PASS for minimal descriptions
- Deprecated endpoints: always WARN if no deprecation callout, regardless of description quality
---
CHECK 2 — OpenAPI Spec
Evaluates whether a machine-parseable API spec is present and accessible.
| Status | Criteria |
|---|---|
| ✅ PASS | Inline OpenAPI YAML or JSON block is present on the page AND is valid (has paths, components or schemas sections). Canonical spec URL (if referenced) is accessible and returns valid JSON/YAML. |
| ⚠️ WARN | Inline spec is present but incomplete (references $ref components not defined on the page, or missing components/schemas). OR canonical spec URL is referenced but returns a non-200 status. |
| ❌ FAIL | No inline spec block on the page. No canonical spec accessible. Page is docs-only with no machine-readable schema. |
Notes:
- If the canonical spec is 404 but inline YAML is present on every page: Check 2 = PASS on
individual pages, but flag the 404 canonical spec as a TOP ISSUE (it blocks SDK generation).
- OpenAPI 3.0.x and 3.1.x are both valid. Swagger 2.0 is WARN (outdated format).
---
CHECK 3 — Body Param Descriptions
Evaluates how well the request body fields and parameters are documented.
Applies to: all fields in requestBody.content.application/json.schema, all path params, all query params.
| Status | Criteria |
|---|---|
| ✅ PASS | Every field has a description string. Required vs optional is explicit (required: true/false or listed in required: []). Enum values are explained. Nested objects have their children described. No fields typed as additionalProperties: true without explanation. |
| ⚠️ WARN | ≥ 70% of fields have descriptions, but some are missing. OR at least one key field uses additionalProperties: true but other fields are well-documented. OR an important parameter's valid values / format are not documented (e.g. a UUID field with no hint it's a UUID). |
| ❌ FAIL | The entire request body is additionalProperties: true (free-form object). OR < 50% of fields have descriptions. OR a required field has no description at all. OR the request body schema is type: object with no properties defined. |
Common patterns that trigger FAIL:
# FAIL — free-form body
requestBody:
content:
application/json:
schema:
type: object
additionalProperties: true
# FAIL — required field with no description
properties:
worker_queue_id:
type: string
title: Worker Queue Id
# no description field
required: true
# WARN — important nested object opaque
properties:
configuration:
type: object
additionalProperties: true ← no children documented
description: Agent configurationSpecial cases:
- Endpoints with no request body (GET, DELETE with path params only):
Still check path/query params. If all are described → PASS. If missing → WARN/FAIL.
- If a field references another endpoint for valid values, the description should
say so (e.g. "Use GET /api/v1/worker-queues to get valid IDs"). Missing this = WARN.
---
CHECK 4 — Response Codes
Evaluates whether the HTTP error responses are documented in the spec.
| Status | Criteria |
|---|---|
| ✅ PASS | Documents 200/201 AND all applicable error codes for the endpoint type (see table below). Each error code has a description and ideally an error schema. |
| ⚠️ WARN | Documents 200/201 AND 422 only (FastAPI auto-generated). OR documents 2-3 error codes but misses important ones for the endpoint type. |
| ❌ FAIL | Documents 200/201 only, with no error codes. OR documents only 200 (not even 422). |
Required error codes by endpoint type:
| Endpoint type | Minimum required error codes |
|---|---|
| Any authenticated endpoint | 401 (auth failure) |
| Resource by ID (GET/PUT/DELETE) | 401, 404 (not found) |
| Create resource (POST) | 400 (invalid input), 401, 409 (conflict/duplicate) |
| Any endpoint | 500 (internal error) |
| Rate-limited endpoint | 429 |
| Auth-changing operations | 403 (forbidden) |
| Temporal/async operations | 503 (no workers) |
| Streaming (SSE) endpoints | 404 (resource not found before streaming) |
Notes:
422(FastAPI validation) alone does NOT count as having error codes — it's auto-generated
and doesn't communicate business logic errors
- Error codes mentioned only in prose but not in the spec responses block = WARN
(better than nothing, but not machine-readable)
---
CHECK 5 — Response Schema
Evaluates whether the successful response body is documented.
| Status | Criteria |
|---|---|
| ✅ PASS | The 200/201 response has a full typed schema — either inline or via $ref to a defined component. All response fields have types. Key fields have descriptions. Nested objects have their children defined. |
| ⚠️ WARN | Schema exists but is incomplete. Uses additionalProperties: true for significant portions of the response. References $ref that is not resolvable on this page. OR partial schema — top-level fields defined but nested objects opaque. |
| ❌ FAIL | schema: {} — completely empty schema. OR schema: {type: object} with no properties. OR no response schema at all. OR additionalProperties: true at the top level (entire response is a free-form object). |
Worked examples:
# FAIL — empty schema
responses:
'200':
description: Successful Response
content:
application/json:
schema: {}
# FAIL — additionalProperties at top level
responses:
'200':
description: Response
content:
application/json:
schema:
type: object
additionalProperties: true
# WARN — partial schema
responses:
'200':
content:
application/json:
schema:
type: object
properties:
result:
type: object
additionalProperties: true ← nested object opaque
# PASS
responses:
'201':
content:
application/json:
schema:
$ref: '#/components/schemas/AgentResponse'
# ...where AgentResponse has all fields definedSpecial cases:
- Streaming (SSE) endpoints: FAIL if
schema: {}even if SSE format is documented in prose.
The prose docs need to be reflected in a formal schema.
- DELETE endpoints that return 204 No Content: PASS (no body is correct).
- Health check endpoints returning minimal
{status: string}: PASS if that schema is defined. - Validate Token with
additionalProperties: truewhen 7 fields are documented in prose: WARN
(the info exists but not in the schema).
---
COMPOSITE SCORING
After all 5 checks are scored for an endpoint, assign the endpoint's overall status:
| Overall | Rule |
|---|---|
| ✅ PASS | All 5 checks are PASS |
| ⚠️ WARN | At least one WARN, zero FAILs |
| ❌ FAIL | At least one FAIL |
Category score = count of endpoint-level PASS / WARN / FAIL within that category.
Overall score = sum across all categories.
---
SITE-WIDE PATTERN DETECTION THRESHOLDS
| Pattern | FAIL threshold | WARN threshold |
|---|---|---|
| Missing error codes | ≥ 80% of endpoints only have 200+422 | 60–79% |
| Empty response schemas | ≥ 50% of endpoints have schema: {} | 30–49% |
| Free-form request bodies | ≥ 40% have additionalProperties: true at body root | 20–39% |
| One-liner descriptions | ≥ 60% have descriptions ≤ 10 words | 40–59% |
| Spec unavailable | Canonical spec 404 AND inline spec missing on ≥ 30% of pages |
Only report a site-wide pattern if it meets the threshold. Do not fabricate patterns.