Activated Cloud
← App Store

Design an HTTP API

Activated Cloud✓ Officialactivated/design-http-api

No ratings yet6 installsv1.0.0Updated Oct 6, 2026● Unknown

Free · MIT

About

Designs or changes an HTTP API so clients can rely on it: resource-oriented URLs, correct methods and status codes, consistent naming, structured problem-details errors, token-based pagination, idempotency keys for safe retries, optimistic concurrency, authorisation on every object, versioning and a strict rule for what counts as breaking, all written into an OpenAPI description with contract tests. Use when adding or changing endpoints, reviewing an API design or planning a new version. Not for debugging calls to an existing API (use debug-root-cause).

Software Development

Documentation

From SKILL.md · v1.0.0 · what the agent reads when it loads this skill3 files: SKILL.md, references/CREDITS.md, references/api-conventions.md

Design an HTTP API

An API is a contract that outlives the code behind it: once a client depends on a field, a status code or an error shape, changing it breaks someone you may never meet. Good design is consistent (one way of doing each thing), predictable (standard HTTP semantics), safe to retry, and explicit about what may change. Match the existing API's conventions first; consistency beats any guideline.

When to use

  • "Add an endpoint for X", "design the API for this feature", "review this API spec", "we need a v2".
  • Changing a request or response shape, a status code or an error format.
  • Exposing an internal service to partners or the public.

What you need

  • The use cases: who calls this, what they need to do, how often, from where (browser, mobile app, server).
  • The existing API's conventions: naming case, error format, pagination style, auth, versioning. Read two or three existing endpoints and any OpenAPI file (search_files for openapi, swagger).
  • Consumers of anything you change: search the repo and other repos you can reach for the path and field names; ask the owner about external clients.
  • The conventions reference: references/api-conventions.md.

Method

  1. Model resources, not actions. Name the nouns (orders, invoices, members) and their relationships. URLs identify resources: /orders, /orders/{order_id}, /orders/{order_id}/refunds. Use plural nouns, lowercase, and one path-segment case consistently (kebab-case is common: /payment-methods). Keep nesting to one level of ownership. For operations that are not CRUD (cancel, resend), either model the result as a resource (POST /orders/{id}/cancellation) or use a clearly named action segment the codebase already uses (POST /orders/{id}:cancel or /orders/{id}/cancel); pick one style per API.

  2. Use methods for what they mean.

    Method Use Safe Idempotent
    GET Read yes yes
    POST Create, or trigger a non-idempotent action no no (make it idempotent with a key, step 6)
    PUT Replace a whole resource at a known URL no yes
    PATCH Partial update no not by default
    DELETE Remove no yes (a repeat returns 204 or 404, consistently)
    GET never changes state. Do not tunnel writes through GET or reads through POST without a reason (very large queries are the usual exception).
  3. Return precise status codes. 200 with a body, 201 with a Location header for creation, 202 for accepted asynchronous work (with a way to poll it), 204 with no body. Client errors: 400 malformed, 401 not authenticated, 403 authenticated but not allowed, 404 not found (also for objects the caller may not know exist), 409 conflict with current state, 412 precondition failed (stale ETag), 415 wrong content type, 422 well-formed but invalid, 429 rate limited with Retry-After. Server errors: 500, 502, 503 with Retry-After, 504. Never return 200 with an error inside the body.

  4. One error shape everywhere. Use problem details (RFC 9457, media type application/problem+json) unless the API already has a format: type, title, status, detail, instance, plus an errors list for field validation. Never put stack traces, SQL or internal hostnames in responses; log them server-side with a request ID and return the ID.

  5. Paginate every list from day one. Prefer opaque page tokens: the client sends page_size and page_token, the response returns items and next_page_token (absent on the last page). Tokens survive inserts and deletes and scale to large tables; offset pagination (offset, limit) skips or repeats rows when data shifts and gets slow deep in a table, so keep it for small, stable lists. Cap page_size and document the default and the maximum. Filtering and sorting through documented query parameters (status=paid, sort=-created_at).

  6. Make retries safe. Networks fail after the server has acted, so clients retry. For POSTs that create things or move money, accept an Idempotency-Key header: store the key with the result, and return the same result for a repeat with the same key (and an error if the same key arrives with a different body). Keep keys for a documented period (often 24 hours).

  7. Prevent lost updates. Return an ETag (or a version field) on reads; require If-Match on updates to resources that several actors edit; answer 412 when it is stale.

  8. Name and format data consistently. One case for JSON fields across the API (match what exists; snake_case and camelCase are both common). Timestamps in RFC 3339 with time zone (2026-10-05T14:10:00Z). Money as an integer in minor units plus a currency code, or a decimal string, never a float. IDs as strings (they may not stay numeric). Enums as lowercase strings, documented, and clients told to tolerate unknown values. Booleans named as questions (is_active, has_payment_method).

  9. Secure every endpoint. Authenticate every request unless deliberately public. Authorise on the object, not just the route: check that this caller may read or change this specific order (the most common API vulnerability is fetching another customer's object by changing an ID). Validate input at the boundary (types, lengths, ranges, allowed values). Rate limit, especially auth and expensive endpoints. Never put secrets or personal data in URLs (they end up in logs). Set CORS narrowly for browser clients.

  10. Decide compatibility before you ship. The breaking-change list in references/api-conventions.md is the rule. Adding optional request fields, new response fields, new endpoints and new enum values (when clients were told to tolerate them) is compatible. Removing or renaming anything, changing types, formats or defaults, adding required inputs, tightening validation and changing status codes or error shapes is breaking. Breaking changes need a new version (in the path, a header or a media type: follow the API's existing scheme) and a deprecation period for the old one, announced with dates. Ask the owner before shipping any breaking change.

  11. Write it down as OpenAPI. Update or create the OpenAPI description with every path, parameter, schema, example and error response. Lint it if the project has a linter (Spectral is a common open-source one: npx @stoplight/spectral-cli lint openapi.yaml). Generate or check docs from it.

  12. Test the contract. Tests for each endpoint: happy path with exact status and body shape; each documented error (400, 401, 403, 404, 409, 422); authorisation (another user's ID returns 404 or 403); pagination across page boundaries; idempotent retry returns the same result. Where the project supports it, validate responses against the OpenAPI schema in tests.

  13. Review with a consumer's eyes. Before building, show the design (endpoints, example requests and responses, errors) to the owner or the client team (show_card or a short doc). Changing a design is cheap; changing a shipped API is not.

Output

The endpoint design (method, path, request, response, errors, auth, pagination and idempotency notes) in the OpenAPI file plus a short summary for review, then the implementation and contract tests if asked to build it. For changes to existing endpoints: a compatibility verdict (compatible or breaking, with the reason) and the migration and deprecation plan when breaking. A design summary for review looks like this:

POST /orders/{order_id}/refunds
Auth: staff token with `refunds:write`; caller must belong to the order's merchant (else 404).
Headers: Idempotency-Key required (24 h retention).
Body: { "amount": 1500, "currency": "EUR", "reason": "damaged" }   amount in minor units, at most captured minus refunded
201: refund object + Location: /orders/{order_id}/refunds/{refund_id}
Errors (problem+json): 404 order not found, 409 order not captured, 422 amount too high or currency mismatch, 429 rate limited
Compatibility: new endpoint, no change to existing ones.
Tests: happy path, each error, another merchant's order returns 404, repeat with same key returns the same refund.

Checks before you finish

  • Methods, status codes and the error shape follow the rules above and match the existing API.
  • Every list endpoint is paginated with a capped page size.
  • Every endpoint authenticates, authorises on the specific object, and validates input.
  • The change is classified as compatible or breaking against the list, and breaking changes have the owner's approval and a versioning plan.
  • The OpenAPI description and contract tests are updated and pass.

Pitfalls

  • Inventing a new style per endpoint. Inconsistency is a tax on every client. Copy the existing conventions.
  • Unpaginated lists. They work in development and fall over at the first big customer.
  • 200 with {"error": ...}. Clients, proxies and monitoring all trust the status code.
  • Leaking internals in errors. Stack traces and SQL in responses help attackers, not users.
  • Floats for money, local times without zones. Both corrupt data quietly.
  • "Small" breaking changes. Renaming a field or tightening validation breaks someone. Classify every change.
  • Checking the route but not the object. Logged-in users fetching other customers' records by ID is the classic API breach.

Versions

v1.0.0currentOct 6, 2026

Listed from the source repository.

Reviews

No reviews yet. Be the first.

Write a review