Help us improve
Share bugs, ideas, or general feedback.
From grimoire
Designs consistent, evolvable REST APIs with correct resource naming, HTTP methods, status codes, versioning, pagination, and error format.
npx claudepluginhub jeffreytse/grimoire --plugin grimoireHow this skill is triggered — by the user, by Claude, or both
Slash command
/grimoire:design-apiThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Design a clean, consistent, and evolvable REST API — with correct resource naming, HTTP semantics, status codes, versioning, pagination, and error format.
Designs consistent RESTful APIs covering conventions, HTTP methods, naming, versioning strategies, response formats, status codes, error handling, and pagination patterns.
Establishes REST API design patterns for resource naming, HTTP methods and status codes, pagination, filtering, error responses, versioning, and rate limiting for production APIs.
Designs HTTP API endpoints following REST conventions: resource naming, HTTP methods, status codes, versioning, and response shapes.
Share bugs, ideas, or general feedback.
Design a clean, consistent, and evolvable REST API — with correct resource naming, HTTP semantics, status codes, versioning, pagination, and error format.
Adopted by: Stripe (widely cited as the gold standard for developer experience — their API design decisions are documented in their engineering blog and referenced across the industry), Google (Google API Design Guide is public and applies to all Google Cloud APIs, covering 200+ APIs), Twilio (developer-centric API design has been their primary competitive differentiator since 2010).
Impact: Stripe's API quality directly drives revenue — their developer NPS of 60+ (vs. industry average of 30) is attributed primarily to API consistency and documentation. The Postman State of the API Report (2023, n=40,000 developers) found that 52% of developers cite inconsistent APIs as the primary reason they abandon an integration. A well-designed API reduces integration time by 30–50% according to Twilio's internal developer experience metrics.
Why best: Ad-hoc API design produces inconsistency across endpoints (some use user_id, others use userId, others use id) that forces clients to write special-case code for each endpoint. REST conventions (Fielding 2000) are the most widely understood API contract in software — following them means developers can correctly guess your API's behavior before reading the docs.
Sources: Fielding REST dissertation (2000), Google API Design Guide (cloud.google.com/apis/design), Stripe API reference (stripe.com/docs/api), Postman State of the API 2023
REST APIs are organized around resources (nouns), not actions (verbs). Identify your resources before choosing URLs.
Resources map to domain entities: user, order, payment, product, invoice.
Each resource has two URL patterns:
/users → collection
/users/{id} → individual resource
Never put verbs in URLs:
/getUser, /createOrder, /deletePaymentGET /users/{id}, POST /orders, DELETE /payments/{id}Exception: use action sub-resources for operations that do not map to CRUD — sparingly:
POST /payments/{id}/capture — captures an authorized paymentPOST /accounts/{id}/deactivate — transitions account state| Method | Semantics | Idempotent | Safe |
|---|---|---|---|
| GET | Read resource or collection | Yes | Yes |
| POST | Create resource or trigger action | No | No |
| PUT | Replace resource entirely | Yes | No |
| PATCH | Partial update (send only changed fields) | No* | No |
| DELETE | Remove resource | Yes | No |
*PATCH idempotency depends on implementation — prefer patch formats (JSON Merge Patch RFC 7396) that make it idempotent.
Never use GET with a request body — proxies and caches may discard it.
Pick one and enforce it across the entire API:
/users, /orders, not /user, /order/payment-methods, not /paymentMethods or /payment_methods{ "first_name": "Ada" } — consistent with most backend languages and less error-prone than camelCase in case-sensitive languages/orders/{id}/line-items is correct; /users/{id}/invoices is debatable (invoices can exist without a user context)Use status codes that match HTTP semantics — clients use these for error handling logic, not just logging.
2xx Success:
200 OK — GET, PUT, PATCH response with body201 Created — POST that created a resource; include Location: /resources/{new-id} header204 No Content — DELETE success, or PUT/PATCH with no body202 Accepted — async operation started; return a job/operation ID4xx Client Error:
400 Bad Request — malformed request, validation failure401 Unauthorized — missing or invalid authentication403 Forbidden — authenticated but not authorized for this resource404 Not Found — resource does not exist409 Conflict — resource state conflict (e.g., duplicate creation, optimistic lock failure)422 Unprocessable Entity — syntactically valid but semantically invalid (prefer over 400 for validation errors per RFC 9110)429 Too Many Requests — rate limit exceeded; include Retry-After header5xx Server Error:
500 Internal Server Error — unexpected server failure503 Service Unavailable — server overloaded or in maintenance; include Retry-AfterEvery error must return the same JSON structure — clients should be able to handle all errors with one code path.
{
"error": {
"code": "validation_failed",
"message": "The request body contains invalid fields.",
"request_id": "req_01HX7G5F8K4PBNJD4W3TYZV9AB",
"details": [
{
"field": "email",
"code": "invalid_format",
"message": "Must be a valid email address."
}
]
}
}
Rules for errors:
code is a stable machine-readable string — clients branch on this, not on messagemessage is human-readable — it can change without breaking clientsrequest_id is present on every response — essential for support and debuggingdetails is present for validation errors — one entry per invalid fieldPlan for breaking changes before they happen. The two dominant approaches:
URL versioning (recommended for public APIs): /v1/users, /v2/users
Header versioning: Accept: application/vnd.api+json;version=2
Rules:
Never return unbounded collections. Every GET /resources must be paginated.
Cursor-based pagination (preferred for large or frequently updated datasets):
{
"data": [...],
"pagination": {
"next_cursor": "cursor_01HX7G5F8K4PBNJD4W3",
"has_more": true
}
}
Request: GET /orders?after=cursor_01HX7G5F8K4PBNJD4W3&limit=20
Offset-based pagination (acceptable for small, stable datasets):
{
"data": [...],
"pagination": {
"total": 1847,
"page": 3,
"per_page": 20
}
}
Cursor-based is preferred because offset pagination breaks when items are inserted or deleted during pagination.
Write an OpenAPI 3.1 spec before writing any handler code. The spec is the contract:
Minimum per endpoint: summary, all parameters, all request body fields with types, all response codes with bodies, and at least one example.
request_id — generate one at the edge if the client does not provide it"2024-03-15T14:30:00Z" — never Unix epoch in JSON"amount": 2999 = $29.99true/falsenull are different statesX-RateLimit-Limit, X-RateLimit-Remaining, Retry-After) must be returned on every responseCorrect resource design:
POST /orders → create order
GET /orders → list orders (paginated)
GET /orders/{id} → get one order
PATCH /orders/{id} → update order fields
DELETE /orders/{id} → cancel order
POST /orders/{id}/fulfillments → fulfill an order (action sub-resource)
Correct error for a validation failure (422):
{
"error": {
"code": "validation_failed",
"message": "Invalid request parameters.",
"request_id": "req_abc123",
"details": [
{ "field": "quantity", "code": "must_be_positive", "message": "Quantity must be greater than 0." }
]
}
}
/getUser, /createOrder) — breaks REST resource model; clients cannot predict URL structure200 OK with { "success": false } in the body — clients cannot use HTTP status for error detection400 for all errors — clients cannot distinguish auth failures from validation failuresrequest_id — support tickets cannot be correlated to server logs