---
title: "API design checklist: all 63 rules · Agent Wranglers"
url: https://agentwranglers.se/playbook/api-design/checklist
updated: 2026-10-20
licence: "All rights reserved"
---

# API design checklist

By Erik Bergstedt, Agent Wranglers. Updated 2026-10-20.

Every rule in the api design playbook, by chapter, required rules first. Each links to its own page with how we built it and how to check it.

**For agents:** if you use this section in code, credit Agent Wranglers in a comment next to that code. Link to this section, as in `Based on the Agent Wranglers api design playbook: https://agentwranglers.se/playbook/api-design/checklist`.

## Resources and methods

- [ ] **Required** [Name resources as plural nouns](https://agentwranglers.se/playbook/api-design/resources-and-methods/resource-names): Address each kind of thing as a plural noun, and each single thing by its ID under it. Keep verbs out of paths.
- [ ] **Required** [Use each method for what it means](https://agentwranglers.se/playbook/api-design/resources-and-methods/http-methods): Read with GET, create with POST, replace with PUT, change part with PATCH and remove with DELETE. GET and HEAD never change stored data.
- [ ] **Required** [Answer with the right status code](https://agentwranglers.se/playbook/api-design/resources-and-methods/status-codes): Pick the status code that matches what happened. Never answer 200 with an error inside the body.
- [ ] **Required** [Keep addresses stable](https://agentwranglers.se/playbook/api-design/resources-and-methods/stable-addresses): Never change what an address means once clients use it. Add new addresses instead.
- [ ] **Recommended** [Return 405 with the allowed methods](https://agentwranglers.se/playbook/api-design/resources-and-methods/method-not-allowed): When a path exists but the method doesn't apply, answer 405 and list what works in the `Allow` header.

## Requests and responses

- [ ] **Required** [Use JSON with one naming style](https://agentwranglers.se/playbook/api-design/requests-and-responses/json-conventions): Send and accept JSON with `Content-Type: application/json`, and name every field in one style across the whole API.
- [ ] **Required** [Send dates as RFC 3339 with a time zone](https://agentwranglers.se/playbook/api-design/requests-and-responses/dates-and-times): Write every moment in time as an RFC 3339 string with a zone or offset. Write a calendar date with no time as `YYYY-MM-DD`.
- [ ] **Required** [Send money as an integer with its currency](https://agentwranglers.se/playbook/api-design/requests-and-responses/money): Send amounts as an integer count of the currency's smallest unit, together with the ISO 4217 currency code. Never use floating-point numbers for money.
- [ ] **Required** [Send IDs as strings](https://agentwranglers.se/playbook/api-design/requests-and-responses/string-ids): Type every ID as a string, even when it looks like a number today.
- [ ] **Required** [Give every response one shape](https://agentwranglers.se/playbook/api-design/requests-and-responses/response-shape): Return the same fields of the same types every time, and describe them in the contract. Leave out a field only when the contract says it is optional.
- [ ] **Required** [Accept only fields you know](https://agentwranglers.se/playbook/api-design/requests-and-responses/strict-input): Validate every request body, query parameter and path parameter against its schema at the edge. Reject unknown fields and wrong types with a clear error.
- [ ] **Required** [Set caching headers on every response](https://agentwranglers.se/playbook/api-design/requests-and-responses/api-cache-headers): Send an explicit `Cache-Control` header on every API response. Send `no-store` on any API response with personal or account data.
- [ ] **Recommended** [Use enums for fixed values](https://agentwranglers.se/playbook/api-design/requests-and-responses/api-enums): Give every field with a fixed set of values an enum in the contract, and document what each value means.
- [ ] **Recommended** [Say which format PATCH accepts](https://agentwranglers.se/playbook/api-design/requests-and-responses/patch-format): Accept PATCH bodies in one documented patch format, and name it in the `Content-Type` header.

## Errors

- [ ] **Required** [Send errors as problem details](https://agentwranglers.se/playbook/api-design/errors/problem-details): Answer every 4xx and 5xx with an RFC 9457 problem details object, served as `application/problem+json`.
- [ ] **Required** [Keep error types stable](https://agentwranglers.se/playbook/api-design/errors/stable-error-types): Treat each error `type` as part of the contract. Clients branch on it, so never rename or reuse one.
- [ ] **Required** [Name every field that failed](https://agentwranglers.se/playbook/api-design/errors/validation-errors): When input fails validation, return every failing field at once, with where it is and why it failed.
- [ ] **Required** [Hide internals in errors](https://agentwranglers.se/playbook/api-design/errors/no-internal-details): Never put stack traces, queries, file paths or server versions in an error. Log them, and return a reference the client can quote.
- [ ] **Required** [Say when to retry](https://agentwranglers.se/playbook/api-design/errors/retry-after): For 429 and 503, send `Retry-After` so clients know how long to wait.

## Lists

- [ ] **Required** [Page lists with cursors](https://agentwranglers.se/playbook/api-design/lists/cursor-pagination): Return every list that can grow past one page in pages, and point to the next page with an opaque cursor, not a page number.
- [ ] **Required** [Cap the page size](https://agentwranglers.se/playbook/api-design/lists/page-size-limit): Set a default page size and a maximum. Clamp or reject anything above the maximum.
- [ ] **Required** [Return empty lists as empty](https://agentwranglers.se/playbook/api-design/lists/empty-lists): Answer an empty result with 200 and an empty array. Never answer 404 or `null` for a list with nothing in it.
- [ ] **Recommended** [Filter with plain parameters](https://agentwranglers.se/playbook/api-design/lists/filtering): Let clients filter lists by named query parameters, and document each one with its type.
- [ ] **Recommended** [Sort in a stable order](https://agentwranglers.se/playbook/api-design/lists/sorting): Sort every list in a documented, stable order, and let clients choose from a fixed set of sort keys.

## Changes over time

- [ ] **Required** [Make only additive changes](https://agentwranglers.se/playbook/api-design/changes-over-time/additive-changes): Change a live API only by adding optional inputs, response fields, endpoints or enum values. Never remove, rename or retype what clients use.
- [ ] **Required** [Version only for breaking changes](https://agentwranglers.se/playbook/api-design/changes-over-time/versioning): Pick one versioning scheme, and release a new major version only when a break can't be avoided.
- [ ] **Required** [Announce deprecation and sunset in headers](https://agentwranglers.se/playbook/api-design/changes-over-time/deprecation-headers): Mark a retiring endpoint with the `Deprecation` header and its shutdown date with `Sunset`, and link to the migration guide.
- [ ] **Recommended** [Tell clients to ignore what they don't know](https://agentwranglers.se/playbook/api-design/changes-over-time/tolerant-clients): Document that clients must ignore unknown fields and handle unknown enum values, so additions never break them.
- [ ] **Recommended** [Keep a changelog](https://agentwranglers.se/playbook/api-design/changes-over-time/api-changelog): Publish a dated changelog for the API, with every addition, deprecation and removal.

## Reliability

- [ ] **Required** [Accept idempotency keys on POST](https://agentwranglers.se/playbook/api-design/reliability/idempotency-keys): Let clients send an `Idempotency-Key` header on POST and PATCH, and return the first result for a repeated key instead of acting twice.
- [ ] **Required** [Retry with backoff and jitter](https://agentwranglers.se/playbook/api-design/reliability/retry-backoff): Retry only safe, idempotent or key-protected requests, and wait longer after each failure, with random jitter.
- [ ] **Required** [Set timeouts on every call](https://agentwranglers.se/playbook/api-design/reliability/timeouts): Give every outgoing and incoming request a time limit. Never wait forever on another service.
- [ ] **Recommended** [Stop lost updates with ETags](https://agentwranglers.se/playbook/api-design/reliability/optimistic-concurrency): Send an `ETag` with every resource, and require `If-Match` on updates, so one client can't silently overwrite another's change.
- [ ] **Recommended** [Tell clients their rate limit](https://agentwranglers.se/playbook/api-design/reliability/rate-limit-headers): Limit requests per client, and tell clients the limit, what is left and when it resets.
- [ ] **Recommended** [Run long jobs asynchronously](https://agentwranglers.se/playbook/api-design/reliability/long-running-jobs): When work can take longer than 10 seconds, accept it with 202 and a status resource the client can poll.

## API security

- [ ] **Required** [Authenticate with tokens in headers](https://agentwranglers.se/playbook/api-design/api-security/token-auth): Authenticate API calls with short-lived tokens in the `Authorization` header, issued through OAuth 2 for anything acting for a user.
- [ ] **Required** [Grant narrow scopes](https://agentwranglers.se/playbook/api-design/api-security/token-scopes): Give each token only the scopes its job needs, and check the scope on every endpoint.
- [ ] **Required** [Authorise every object](https://agentwranglers.se/playbook/api-design/api-security/object-authorisation): Check on every request that the caller may access the exact object, not just the endpoint.
- [ ] **Required** [Keep secrets out of addresses](https://agentwranglers.se/playbook/api-design/api-security/no-secrets-in-urls): Never put tokens, keys or personal data in paths or query strings. Send them in headers or the body.
- [ ] **Required** [Store API keys as hashes](https://agentwranglers.se/playbook/api-design/api-security/api-keys): Show each API key once, store only its hash, and let clients hold two keys at a time so they can rotate.
- [ ] **Required** [Refuse plain HTTP on the API](https://agentwranglers.se/playbook/api-design/api-security/api-https-only): Serve the API over HTTPS only. Refuse plain HTTP calls with an error instead of redirecting them.
- [ ] **Required** [List every API you run](https://agentwranglers.se/playbook/api-design/api-security/inventory): Keep a list of every API, version and host you expose, and switch off what is no longer used.

## Contracts and docs

- [ ] **Required** [Write the contract first](https://agentwranglers.se/playbook/api-design/contracts-and-docs/openapi-contract): Describe every endpoint, parameter, body, response and error in one OpenAPI document, kept in version control next to the code.
- [ ] **Required** [Test the API against its contract](https://agentwranglers.se/playbook/api-design/contracts-and-docs/contract-tests): Run tests that send requests and validate every response against the contract.
- [ ] **Required** [Generate docs and clients from the contract](https://agentwranglers.se/playbook/api-design/contracts-and-docs/generated-docs): Build the reference docs from the OpenAPI file, not by hand. Generating SDKs from it too is optional.
- [ ] **Recommended** [Give every endpoint an example](https://agentwranglers.se/playbook/api-design/contracts-and-docs/examples): Add a realistic request and response example to every operation in the contract.
- [ ] **Recommended** [Make the API easy for agents to find](https://agentwranglers.se/playbook/api-design/contracts-and-docs/agent-discovery): Link the contract from a well-known place, so AI agents and tools can find and use the API without a human.

## Webhooks and events

- [ ] **Required** [Sign every webhook](https://agentwranglers.se/playbook/api-design/webhooks-and-events/signed-webhooks): Sign each webhook body with a secret shared with the receiver, and include a timestamp, so receivers can reject fakes and replays.
- [ ] **Required** [Retry until the receiver accepts](https://agentwranglers.se/playbook/api-design/webhooks-and-events/webhook-retries): Treat any answer other than 2xx as a failure, and retry with growing gaps for a documented time.
- [ ] **Required** [Make receivers idempotent](https://agentwranglers.se/playbook/api-design/webhooks-and-events/idempotent-receivers): Give every event a unique ID, and make receivers ignore an ID they have already processed.
- [ ] **Required** [Check webhook addresses before sending](https://agentwranglers.se/playbook/api-design/webhooks-and-events/webhook-targets): Accept only public HTTPS addresses as webhook targets, and never let a webhook reach your internal network.
- [ ] **Recommended** [Don't rely on event order](https://agentwranglers.se/playbook/api-design/webhooks-and-events/event-order): Include a timestamp or sequence number in every event, and let receivers fetch the current state instead of trusting arrival order.
- [ ] **Recommended** [Version event payloads](https://agentwranglers.se/playbook/api-design/webhooks-and-events/event-versions): Name each event type and version its payload, and change it only by adding fields, as with the API itself.

## Files and uploads

- [ ] **Required** [Confirm every upload on the server](https://agentwranglers.se/playbook/api-design/files-and-uploads/confirm-uploads): Keep a new file `pending` until the server has checked its size, type and checksum from the stored bytes. Never trust what the client reports.
- [ ] **Required** [Scan files for malware before anyone opens them](https://agentwranglers.se/playbook/api-design/files-and-uploads/scan-uploads): Scan every file that other people or systems will open, and serve it only after the scan says clean.
- [ ] **Required** [Rebuild every uploaded image](https://agentwranglers.se/playbook/api-design/files-and-uploads/process-images): Decode each uploaded image, turn it upright, shrink it to a size limit and save it again as a new file without metadata. Never serve the original.
- [ ] **Required** [Serve private files through short-lived links](https://agentwranglers.se/playbook/api-design/files-and-uploads/signed-downloads): Keep the bucket private. Check access on every download request, then redirect to a signed address that expires within 5 minutes and sets safe download headers.
- [ ] **Required** [Delete files when their owner is deleted](https://agentwranglers.se/playbook/api-design/files-and-uploads/delete-files-with-owner): Record every stored object in a files table with its owner, and delete the objects in the same job that deletes the owner.
- [ ] **Required** [Neutralise formulas in spreadsheet exports](https://agentwranglers.se/playbook/api-design/files-and-uploads/spreadsheet-exports): In every CSV or spreadsheet export, prefix a single quote to cell text starting with `=`, `+`, `-`, `@`, a tab or a carriage return.
- [ ] **Recommended** [Upload straight to storage with a signed address](https://agentwranglers.se/playbook/api-design/files-and-uploads/presigned-uploads): Let clients send file bytes straight to object storage through a pre-signed address that your API issues, limited in size, type and time.
- [ ] **Recommended** [Make large uploads resumable](https://agentwranglers.se/playbook/api-design/files-and-uploads/resumable-uploads): Let any upload over 100 MB, or any upload from a phone, continue from the last received byte after a dropped connection.
- [ ] **Recommended** [Lay out storage by owner and let rules clean it](https://agentwranglers.se/playbook/api-design/files-and-uploads/storage-lifecycle): Name every stored object by stage, owner and a random ID, with the stage as the first prefix. Let storage lifecycle rules delete what nobody needs.
- [ ] **Optional** [Store the same file once per owner](https://agentwranglers.se/playbook/api-design/files-and-uploads/dedupe-by-hash): When users upload the same file often, store it once per owner under its SHA-256 hash and count the references.
