Api design
Use when designing REST endpoints, defining error envelopes, setting a versioning or deprecation policy, choosing pagination shape, adding idempotency to mutations, reviewing API contracts, or when two services need a stable interface between them.From its SKILL.md
npx -y skills add aneja5/forge-skills --skill api-designAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
9.3 KB, ~2.2k tokens by cl100k_base, as published. Nobody here has run it
API Design
Overview
Define the project's API conventions before endpoints proliferate. Output is .forge/api-design.md — verbs and status codes, the error envelope schema, versioning + deprecation policy, pagination/filter contract, rate-limiting envelope, auth/authz envelope, idempotency rules for mutations, and the public-vs-internal API boundary. Consumed by architecture-and-contracts (per-module contracts inherit these), incremental-implementation (endpoints must conform), and code-review-and-quality (PR review checklist).
When to Use
- A new service or new public endpoint is being designed
- Two services need a stable interface and there's no project-wide envelope
- Error shapes vary across endpoints and clients have to special-case each one
- A breaking change is about to ship and there's no versioning policy
- A list endpoint is being added and pagination shape is unclear
- A mutation endpoint will be retried by clients and there's no idempotency story
When NOT to Use
- A single trivial endpoint is being added to a service that already has documented conventions
- Internal-only RPC inside one service (use module contracts via
architecture-and-contracts) - GraphQL or gRPC — adapt the principles but the file should call out the protocol
Common Rationalizations
| Thought | Reality |
|---|---|
| "We'll version when we break something" | By then clients are coupled to the unversioned shape. Versioning costs nothing on day one, costs a migration on day 200. |
| "Every endpoint is different, no need to standardize" | Inconsistency is a bug, not flexibility. Every divergent error shape costs clients a special case. |
| "Just return 200 with an error field" | Clients can't distinguish success from failure without parsing the body. Proxies and CDNs cache 200s aggressively. |
| "Internal APIs don't need contracts" | Internal APIs become external APIs the moment a second team touches them. Two consumers = production interface. |
| "PATCH replaces the resource" | RFC 7396 says PATCH merges. Clients that depend on merge semantics will break silently if you replace. |
| "Idempotency keys are only for payments" | Network retries happen on every endpoint. Without idempotency, retries create duplicates everywhere. |
Red Flags
- Different error shapes per endpoint (
{ error: "..." }here,{ message: "..." }there, raw string elsewhere) - No version prefix or header anywhere — first breaking change requires a fork
200 OKwith{ "success": false }in the bodyPATCHthat replaces the resource entirelyPOST /paymentswith no idempotency key support- An "internal" endpoint with no auth, reachable from the public internet
OFFSET/LIMITpagination on a feed that will exceed 10k rows- A field added to a response is the only change — no version bump, no deprecation notice
Precedence with architecture contracts
.forge/contracts/<module>.md is the authoritative source for module boundaries, operations, types, and error cases. api-design.md is the subordinate that defines how those operations map to HTTP (verbs, paths, envelope shape, error codes, versioning).
Rule: If .forge/contracts/ exists, read every contract first. Your API design must be consistent with the operations and error types they define. If a contract specifies RefundError with cases TransactionNotFound | RefundWindowExpired | AlreadyRefunded, the matching HTTP design must surface those exact codes — no inventing new ones, no collapsing them into a generic 400.
If a contract is missing an operation you need to expose (e.g., the PRD calls for refunds but PaymentService contract has no refund), do NOT add it to api-design.md alone — file /feedback targeting the contract so it gets updated upstream. Otherwise the two artifacts will diverge and forge-sync will flag a CONFLICT.
If no contracts exist yet, api-design.md may define the envelope policy in isolation, but the module boundaries it references will be back-filled when architecture-and-contracts runs.
Core Process
Step 0: Read existing contracts (if any)
Glob .forge/contracts/*.md. For each contract found:
- Extract every operation in
Provides - Extract input/output types
- Extract error types and their conditions
Hold these as the source of truth. Every endpoint you design must trace back to a contract operation (or be flagged as new and worth a feedback entry).
Step 1: Define base conventions
- Verbs:
GET(read),POST(create or non-idempotent action),PUT(replace),PATCH(merge per RFC 7396),DELETE(remove). - Status codes:
2xxsuccess only.4xxfor client errors.5xxfor server errors. Never200 OKwitherror: true. - Resource naming: plural nouns (
/users/{id},/orders). - Snake or camel: pick one for the wire format and never mix.
Step 2: Write the error envelope schema
Every error response uses the same shape. Write it in .forge/api-design.md:
{
"error": {
"code": "RESOURCE_NOT_FOUND",
"message": "User not found.",
"field": "user_id",
"request_id": "01HABC...",
"details": {}
}
}
code— stable, machine-readable, screaming-snake-case. Clients pattern-match on this, never onmessage.message— human-readable, non-technical when surfaced to end users.field— populated for validation errors only.request_id— always populated; matches the correlation ID fromobservability.details— optional, structured per error code.
Cross-reference the error-handling-and-resilience taxonomy (transient / permanent / user-correctable maps to retryable HTTP codes).
Step 3: Set versioning and deprecation policy
Choose one mechanism (URI /v1/, header Accept: application/vnd.api+json;version=1, or query ?version=1) and apply it everywhere. Document:
- How a version is introduced (always start at
v1, neverv0). - When a version is required to bump (any field removed, renamed, or type-changed; status-code semantics changed; required-vs-optional flip).
- Deprecation window (minimum 6 months for public, 30 days for internal-only).
- Deprecation signaling:
Deprecation: <date>header + warnings indetails.
Step 4: Pagination, filtering, sorting
- Pagination: cursor preferred. Standard shape:
{ items: [], next_cursor: string|null, prev_cursor: string|null }.OFFSET/LIMITonly allowed for small admin endpoints. - Filtering:
?status=activefor exact match,?status=active,pendingforOR,?created_after=...for ranges. No nested-query DSL unless the product justifies one. - Sorting:
?sort=fieldascending,?sort=-fielddescending. Multi-sort:?sort=-created_at,id.
Step 5: Idempotency, rate limiting, auth envelopes
- Idempotency: every mutation accepts
Idempotency-Key: <client-generated>. Server stores result keyed by(endpoint, key)for at least 24h. Replays return the original response. - Rate limiting: every response includes
RateLimit-Limit,RateLimit-Remaining,RateLimit-Reset.429on exceedance withRetry-After. - Auth: standard
Authorization: Bearer <token>. Errors use stable codes (AUTH_MISSING,AUTH_EXPIRED,AUTH_INVALID,FORBIDDEN). Never leak existence —403and404for "exists but you can't see it" both return404at the public boundary.
Step 6: Document the public/internal boundary
In .forge/api-design.md: list every endpoint as public (versioned, deprecation-aware, hardened) or internal (faster iteration, still auth-gated, never reachable from internet). Internal endpoints get their own subdomain or VPC routing. Mixing the two on the same hostname guarantees future leaks.
Step 7: Header
Prepend a forge:meta header (generated_by: api-design, generated_at: <ISO 8601 UTC with Z>, depends_on: [.forge/architecture.md] — paths only, never hashes, generated_from: {.forge/architecture.md: <upstream content_hash AT generation time>}, content_hash: <sha256 first 8 of THIS file's body>). See forge-dependency-graph.
Verification
- Existing
.forge/contracts/*.mdread (or noted as absent); every endpoint traces back to a contract operation - No endpoint invents an error code that contradicts the contract's named errors
- If a needed operation is missing from contracts, a
/feedbackentry was filed targeting the contract — not silently added here -
.forge/api-design.mdwritten - Every endpoint returns errors in the standard envelope (code, message, request_id)
- Every endpoint has a version (URI, header, or query) — none unversioned
- Every mutation supports an
Idempotency-Keyheader (or has a documented reason it doesn't need one) - Every paginated list uses the standard cursor envelope
- No
200 OKwith{ "success": false }anywhere - Every response includes
RateLimit-*headers and arequest_id - Public-vs-internal labels assigned for every endpoint, with separate hostnames
- Breaking changes have a documented deprecation window before the cut-over
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.
Gives 2 of the 12 instructions most api design skills give in ~2.2k tokens
Counted across 224 of the 224 authors here whose files we hold, read 2026-09-06
- Use plural nouns for resource nameshere, and in 50 of 224, across 49 files
- Implement pagination for all list endpointshere, and in 42 of 224, across 32 files
- Use cursor-based pagination for large datasetsin 42 of 224, across 40 files
- Return appropriate HTTP status codes for all responsesin 31 of 224, across 21 files
- Use URL path versioning for API changesin 29 of 224, across 19 files
- Use HTTP methods semantically for CRUD operationsin 22 of 224, across 13 files
- Use HTTP methods for resource actionsin 20 of 224, across 18 files
- Use plural nouns in kebab-case for resource URLsin 18 of 224, across 9 files
- Enforce authentication and authorization on all resourcesin 17 of 224, across 7 files
- Use standard HTTP status codesin 17 of 224
- Validate all incoming request data against a schemain 16 of 224, across 7 files
- Limit URL nesting to two levelsin 16 of 224
Said here and by no other author read
- Define API conventions before endpoints proliferate
- Choose one versioning mechanism and apply it everywhere
- Label every endpoint as public or internal
- File feedback for missing contract operations
- Use 2xx for success and 4xx or 5xx for errors
- Prepend forge meta header to the design file
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.