Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Design, change, or review an HTTP/JSON API surface — endpoints, request/response shapes, authentication and authorization, pagination, idempotency, rate limits, versioning, and deprecations. Use when adding or modifying an HTTP endpoint, reviewing an OpenAPI spec or HTTP route diff, or deciding whether an HTTP API change breaks consumers. Do not use as a protocol-compatibility checklist for gRPC/protobuf, GraphQL, WebSockets, or other non-HTTP/JSON interfaces.
.claude/skills/amazingang-old-coder-api/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 111% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 257% | 0% |
| case-12 | ✗→✓ | ▲ Improved | 144% | 0% |
| case-16 | ✗→✓ | ▲ Improved | 73% | 0% |
| case-19 | ✗→✓ | ▲ Improved | 127% | 0% |
Inspired by Sean Goedecke, Everything I know about good API design (2025-08-24).
This skill covers HTTP/JSON contract and operability concerns. Its compatibility rules assume JSON consumers. For gRPC/protobuf, GraphQL, WebSockets, or another protocol, apply the transport-independent principles only alongside that protocol's own compatibility rules. This is not a substitute for a full application-security review.
Good APIs are boring. For the people who build them, an API is a product. For the people who use them, it is a tool in the way of something else. Every minute a consumer spends thinking about your API instead of their goal is waste. An interesting API is a bad API — or would be a better one if it were less interesting.
Two failure modes an agent falls into by default, and this skill exists to stop both:
Composition with old-coder: when both skills apply, this skill owns the HTTP/JSON contract while old-coder owns workflow order, SPEC approval, the gauntlet, and EVIDENCE. Run Step 0 and the gates before SPEC approval; put the surviving API constraints and risks into SPEC and verify them through the gauntlet. For review-only work with no implementation, use this skill's review format without manufacturing a development loop.
Answer these three, out loud, before writing a route:
| Question | Why it changes the work | |---|---| | Public or internal? Can you ship code for every consumer? | Internal: breaking changes are affordable, complex authentication is fine, non-engineer ergonomics don't matter. Public: none of that holds. | | Existing surface or greenfield? | Existing → run references/breaking-changes.md first; compatibility outranks every improvement below. | | Does the product's resource model support this API? | API design tracks the product's basic resources. If the resources are awkward (state machines with no name, records that only exist inside a job, parent/child relations that aren't modeled), the API will be awkward no matter how carefully you design it. Say so instead of papering over it. |
Honesty rule for step 0: when the ugliness comes from the underlying model, name it and propose the model fix as the real option. A background-job-polling interface bolted onto a read that should be a read is how the worst APIs happen — technical constraints that the UI hides get laid bare in the API, forcing consumers to understand far more of your system than they should have to.
Run every gate. Use ✓ only for a verified pass, ✗ + concrete fix for a verified failure, N/A + reason only when the gate truly does not apply, and ? + reason when it remains unverified. Never skip silently.
A competent consumer should be able to guess this endpoint before reading any docs.
/issues, /projects, /users), plural, stable.400 for a general client error; use 422 only when the content type and syntax are valid but the contained instructions cannot be processed. Use 404 for missing and 429 for rate-limited.id, created_at, next_page, url. Match the names the rest of this API already uses — internal consistency beats external convention when they conflict.Applies only to changes on an existing surface. Full matrix in references/breaking-changes.md.
user.address → user.details.address), narrowing an enum, or tightening validation is a break. Don't, even if it's neater. The HTTP referer header is a misspelling and it is still there.Many server-to-server integrations start life as a curl or a 20-line script. For developer-facing server-to-server APIs, default to simple, scoped, revocable API keys.
Authentication identifies a caller; it does not authorize an action. For every endpoint, identify the actor, action, resource, and tenant boundary.
tenant_id, owner ID, role, or scope without checking it against the authenticated principal.A 500 or a timeout tells the caller nothing about whether the action happened. Without an idempotency key, the caller must choose between a lost operation and a duplicate one.
DELETE /comments/32 (the ID is the key — the retry just 404s). Exception: non-ID-scoped operations like "delete the most recent".references/patterns.md.UI users are limited by the speed of their hands. Anything you expose via API is called at the speed of code, forever, in a loop, by someone who read no docs.
while true loop costs you. Fan-outs, /index endpoints, bulk imports, and anything doing per-record work in a request are the dangerous ones.X-RateLimit-Remaining and Retry-After so well-behaved clients can back off — that metadata is what lets you set stricter limits than you otherwise could.WHERE id > :cursor ORDER BY id LIMIT :n stays fast at record one million; OFFSET gets slower every page and the migration away from it later is expensive.next_page (URL or cursor) so consumers don't compute it.If a field needs an extra service call, a join over a big table, or a computation, don't put it in the default response.
?include=subscription / an includes[] array; keep the default response cheap and constant-cost.Read the response as a stranger. Does using it correctly require knowing how you store things?
next_comment_id chains the client must walk; a POST /fetch_job + poll dance for what should be a GET; internal enum values; internal table IDs; pagination whose page size depends on your shard layout.Guard against over-design as hard as under-design:
/v1/ prefix is itself a public product choice, not a free placeholder. Adopt path or header versioning only when the product's compatibility policy calls for it; do not build multi-version negotiation before a second version exists.includes or cursors to internal endpoints with one caller and a bounded result set. The Pagination and Expensive fields gates are about potentially large or expensive responses. For Idempotency, caller count does not remove retry risk: omit it only when the operation is already idempotent or duplicate effects are explicitly acceptable.When reviewing rather than writing, report only findings that survive verification and skip taste. For repository code, specs, and diffs, cite file:line. For published contracts outside the repository, cite a stable URL and exact section; source-code evidence must use an immutable commit permalink, not a moving branch. A missing public guarantee means consumers cannot rely on the behavior; it does not prove that the backend lacks an undocumented implementation. Give a gate ✓ only when the reviewed evidence supports it. When repository context is available, inspect beyond the diff instead of treating silence as a pass. If the input is intentionally limited and further evidence is unavailable, use ? (unverified: <reason>); reserve N/A for a gate that truly does not apply. Gate summaries evaluate the artifact under review, not the hypothetical state after suggested fixes. Order: breaks first, security boundaries second, incidents third, ergonomics last.
## API review: <surface>
Scope: public|internal · greenfield|existing
### Breaking changes (blocking)
- <field/endpoint> — <what breaks for a consumer doing X> — file:line
Fix: <additive alternative>
### Security boundary
- <authentication/authorization finding> — <credential or unauthorized action/resource> — file:line
### Incident risk
- <idempotency/blast-radius finding> — <the loop or retry that hurts> — file:line
### Ergonomics
- <boring/pagination/field-cost/implementation-leak finding> — file:line
### Gates: Boring <status> · Compatibility <status> · Authentication <status> · Authorization <status> · Idempotency <status> · Blast radius <status> · Pagination <status> · Expensive fields <status> · No implementation leakage <status>If nothing survives, say so plainly — an empty review is a valid result.
references/breaking-changes.md — compatibility matrix, versioning playbook, deprecation sequence. Read before changing any existing endpoint.references/patterns.md — implementation recipes: idempotency keys, cursor pagination, rate-limit headers, includes.references/examples.md — three compact, local examples: an existing route/spec diff, a greenfield proposal, and an established HTTP RPC route. Read only when a concrete calibration example is useful.| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 20,644 | 37,266 | +81% | 1 | 1 | 0% | 4,195 | 8,852 | +111% | 0 | 0 | — |
case-02 | fail→fail | 49,348 | 7,686 | -84% | 1 | 1 | 0% | 3,632 | 3,297 | -9% | 0 | 0 | — |
case-03 | fail→pass | 21,133 | 24,204 | +15% | 1 | 1 | 0% | 1,748 | 6,237 | +257% | 0 | 0 | — |
case-04 | pass→pass | 21,303 | 7,876 | -63% | 1 | 1 | 0% | 1,819 | 4,174 | +129% | 0 | 0 | — |
case-05 | pass→pass | 12,429 | 6,138 | -51% | 1 | 1 | 0% | 1,688 | 3,741 | +122% | 0 | 0 | — |
case-06 | pass→pass | 12,620 | 12,535 | -1% | 1 | 1 | 0% | 2,083 | 4,261 | +105% | 0 | 0 | — |
case-07 | pass→pass | 26,622 | 12,101 | -55% | 1 | 1 | 0% | 2,784 | 4,755 | +71% | 0 | 0 | — |
case-08 | pass→pass | 41,172 | 24,031 | -42% | 1 | 1 | 0% | 2,710 | 4,510 | +66% | 0 | 0 | — |
case-09 | pass→pass | 14,994 | 7,231 | -52% | 1 | 1 | 0% | 2,335 | 3,997 | +71% | 0 | 0 | — |
case-10 | pass→pass | 10,529 | 7,277 | -31% | 1 | 1 | 0% | 1,874 | 4,100 | +119% | 0 | 0 | — |
case-11 | pass→pass | 27,921 | 10,107 | -64% | 1 | 1 | 0% | 2,329 | 4,309 | +85% | 0 | 0 | — |
case-12 | fail→pass | 9,713 | 7,094 | -27% | 1 | 1 | 0% | 1,549 | 3,787 | +144% | 0 | 0 | — |
case-13 | pass→pass | 21,533 | 21,195 | -2% | 1 | 1 | 0% | 2,471 | 4,828 | +95% | 0 | 0 | — |
case-14 | pass→pass | 18,401 | 13,873 | -25% | 1 | 1 | 0% | 2,568 | 4,900 | +91% | 0 | 0 | — |
case-15 | pass→pass | 14,234 | 7,772 | -45% | 1 | 1 | 0% | 2,198 | 3,907 | +78% | 0 | 0 | — |
case-16 | fail→pass | 17,707 | 10,810 | -39% | 1 | 1 | 0% | 2,554 | 4,411 | +73% | 0 | 0 | — |
case-17 | pass→pass | 13,400 | 7,677 | -43% | 1 | 1 | 0% | 1,917 | 3,997 | +109% | 0 | 0 | — |
case-18 | pass→pass | 9,506 | 5,992 | -37% | 1 | 1 | 0% | 1,532 | 3,790 | +147% | 0 | 0 | — |
case-19 | fail→pass | 14,511 | 10,225 | -30% | 1 | 1 | 0% | 2,004 | 4,550 | +127% | 0 | 0 | — |
case-20 | pass→pass | 52,474 | 19,006 | -64% | 1 | 1 | 0% | 3,022 | 6,183 | +105% | 0 | 0 | — |
case-21 | pass→pass | 24,490 | 20,822 | -15% | 1 | 1 | 0% | 3,833 | 6,159 | +61% | 0 | 0 | — |
case-22 | pass→fail | 31,088 | 15,612 | -50% | 1 | 1 | 0% | 3,369 | 5,115 | +52% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 21 counted toward the lift figure. The other 1 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +18 percentage points is the difference between those two pass rates over the 21 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.