api-design-review
Review an HTTP API for the decisions that become permanent once a client depends on them, and say plainly what is breaking.
Save as .claude/skills/api-design-review/SKILL.md in a repository, or under ~/.claude/skills/api-design-review/SKILL.md to have it everywhere. The copy button takes the whole file, frontmatter included.
--- name: api-design-review description: Review an HTTP API design for consistency, evolvability and the mistakes that become permanent once clients depend on them. Use when designing or reviewing an endpoint, a REST API, a response shape, or asking whether a change is breaking. --- # API design review The cost of an API mistake is not the mistake. It is that you cannot take it back once something depends on it. Review with that asymmetry in mind: spend effort on the decisions that are hard to reverse and little on the rest. ## What is expensive to change Review these hardest, because fixing them later needs a version or a migration: 1. **Resource names and URL structure.** 2. **Field names and types in responses.** Especially an identifier's type. A numeric id that later needs to be a string breaks every typed client. 3. **Whether a field can be null**, and whether it is always present. 4. **Pagination.** Retrofitting it onto an endpoint that returned an array is a breaking change, and every list endpoint eventually needs it. 5. **The error shape.** Clients branch on it, so changing it breaks handling. ## Checks ### Shape - A list endpoint returns an object, not a bare array, so pagination and metadata can be added later without breaking anything. - Field naming is consistent across the whole API. Pick one convention and never mix. - Dates are ISO 8601 with a timezone. Never a bare local date for a timestamp, never epoch seconds without saying so. - Money is an integer in minor units with an explicit currency, never a float. - Enumerated values are strings, documented, and clients are told that new values may appear. ### Semantics - The method matches the effect. GET never changes state, ever, including "a harmless counter". - Status codes mean what they say: 400 for a malformed request, 401 for not authenticated, 403 for authenticated but not allowed, 404 for absent, 409 for a conflict, 422 for well formed but invalid, 429 for rate limited. Returning 200 with an error in the body is a real fault. - Anything that can be retried is idempotent, or takes an idempotency key. Networks retry whether or not the API expects it. ### Errors - A machine readable code, a human readable message, and where relevant the field at fault. - The code is stable and documented. Clients must never parse the message. - No internal detail: no stack traces, no SQL, no internal hostnames. ### Lists - Pagination from the start. Prefer cursors over offsets for anything that changes while being read, because offsets skip and repeat rows. - A documented maximum page size, enforced. - A stable, total sort order, so pages do not overlap. ### Boundaries - Authorisation checked in the handler, not assumed from the caller. - Every input validated, with limits on length and size. - A missing resource and a forbidden resource return the same thing when the difference would leak existence. ## Is this change breaking Breaking: removing a field, renaming one, narrowing a type, making an optional input required, adding a validation rule, changing a status code, changing an error code, reordering an array that was implicitly ordered. Not breaking: adding an optional input, adding a response field, adding a new enum value *if* clients were told to expect them, relaxing validation. The grey area is anything clients might depend on that you did not document. Assume they do. ## Report back 1. Anything that will be expensive to change later, first. 2. Inconsistencies with the rest of the API, with the convention to follow. 3. Whether each proposed change is breaking, and for whom. 4. What is fine, so the author knows it was considered.
what it needs
Nothing. This skill is instructions only, so it works in any agent that reads SKILL.md with nothing installed and no account. Save the file and it is live in the next session.
the instructions, in full
API design review
The cost of an API mistake is not the mistake. It is that you cannot take it back once something depends on it. Review with that asymmetry in mind: spend effort on the decisions that are hard to reverse and little on the rest.
What is expensive to change
Review these hardest, because fixing them later needs a version or a migration:
- Resource names and URL structure.
- Field names and types in responses. Especially an identifier's type. A numeric id that later needs to be a string breaks every typed client.
- Whether a field can be null, and whether it is always present.
- Pagination. Retrofitting it onto an endpoint that returned an array is a breaking change, and every list endpoint eventually needs it.
- The error shape. Clients branch on it, so changing it breaks handling.
Checks
Shape
- A list endpoint returns an object, not a bare array, so pagination and metadata can be added later without breaking anything.
- Field naming is consistent across the whole API. Pick one convention and never mix.
- Dates are ISO 8601 with a timezone. Never a bare local date for a timestamp, never epoch seconds without saying so.
- Money is an integer in minor units with an explicit currency, never a float.
- Enumerated values are strings, documented, and clients are told that new values may appear.
Semantics
- The method matches the effect. GET never changes state, ever, including "a harmless counter".
- Status codes mean what they say: 400 for a malformed request, 401 for not authenticated, 403 for authenticated but not allowed, 404 for absent, 409 for a conflict, 422 for well formed but invalid, 429 for rate limited. Returning 200 with an error in the body is a real fault.
- Anything that can be retried is idempotent, or takes an idempotency key. Networks retry whether or not the API expects it.
Errors
- A machine readable code, a human readable message, and where relevant the field at fault.
- The code is stable and documented. Clients must never parse the message.
- No internal detail: no stack traces, no SQL, no internal hostnames.
Lists
- Pagination from the start. Prefer cursors over offsets for anything that changes while being read, because offsets skip and repeat rows.
- A documented maximum page size, enforced.
- A stable, total sort order, so pages do not overlap.
Boundaries
- Authorisation checked in the handler, not assumed from the caller.
- Every input validated, with limits on length and size.
- A missing resource and a forbidden resource return the same thing when the difference would leak existence.
Is this change breaking
Breaking: removing a field, renaming one, narrowing a type, making an optional input required, adding a validation rule, changing a status code, changing an error code, reordering an array that was implicitly ordered.
Not breaking: adding an optional input, adding a response field, adding a new enum value if clients were told to expect them, relaxing validation.
The grey area is anything clients might depend on that you did not document. Assume they do.
Report back
- Anything that will be expensive to change later, first.
- Inconsistencies with the rest of the API, with the convention to follow.
- Whether each proposed change is breaking, and for whom.
- What is fine, so the author knows it was considered.
check its answers by hand
A skill that reasons well about wrong data is still wrong. For the first few cases, run the same question through a tool that cannot be mistaken.
Fetch a URL from the server, follow redirects, and inspect response and security headers.
Open the tool