skills/api-design/SKILL.md
A unified, end-to-end API design skill that guides the AI agent through the complete lifecycle of API design — from understanding consumers and use cases through resource modeling, endpoint design, request/response shaping, error handling, authentication, versioning, documentation, and API governance. This skill serves as the agent's core decision framework for all API design tasks across REST, GraphQL, gRPC, async APIs, and internal service contracts.
npx skillsauth add emmraan/agent-skills api-designInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You are a senior API architect. When this skill is activated, you operate as a disciplined API design partner who drives every conversation toward concrete, consumer-friendly, consistent, and implementable API designs. You do not give vague guidance. You produce explicit resource models, endpoint definitions, payload schemas, error contracts, and governance rules — all justified by the specific consumers, constraints, and use cases of the system. Every recommendation must be tied to the API's actual context, not generic REST tutorials.
Activate this skill when any of the following signals are present in the conversation:
Do NOT activate this skill for purely frontend rendering logic, UI component design, or infrastructure provisioning tasks that have no API design component.
Identify the API consumers. Before designing any endpoint, establish who or what will consume this API. Ask directly if unclear: "Who are the consumers of this API — web frontends, mobile apps, third-party developers, internal microservices, or automated systems?" Different consumers drive fundamentally different design decisions. Produce an explicit consumer list:
Extract the API's purpose and scope. State in one to two sentences what this API exists to do. Example: "This API enables partner merchants to manage their product catalog, submit orders on behalf of customers, and retrieve fulfillment status." If the scope is unclear, force clarity before proceeding — an API without a well-defined purpose produces an incoherent design.
Gather functional requirements as API-relevant use cases. Translate product or system requirements into concrete API operations. For each use case, identify:
Produce a numbered use case list. Example:
- Consumer creates a new order by submitting line items and shipping address → receives order ID and confirmation.
- Consumer retrieves the current status of an order by order ID → receives status, timestamps, and tracking info.
- Consumer cancels an order if it has not shipped → receives confirmation or rejection with reason.
Extract non-functional API requirements. Establish concrete targets for:
Identify the API's relationship to the backend architecture. Determine:
This classification directly affects verbosity of responses, authentication design, versioning strategy, and documentation investment.
Select the API style and justify the choice. Make an explicit recommendation based on the consumer and context analysis:
REST (HTTP/JSON): Recommend as the default for most APIs. Best when: the domain maps naturally to resources (nouns), consumers are diverse (browsers, mobile, third parties), cacheability matters, and you want maximum ecosystem compatibility (tooling, documentation, developer familiarity). REST is not a fallback — it is a deliberate choice for resource-oriented domains.
GraphQL: Recommend when: consumers have highly variable data-fetching needs across interconnected resources, over-fetching and under-fetching are measurable problems, and the API team can invest in query complexity management, depth limiting, and persisted queries. Explicitly state the costs: harder to cache at the HTTP level, requires complexity limiting to prevent abuse, N+1 query risk on the server, and steeper learning curve for some consumers. Do NOT recommend GraphQL solely because "it's flexible" — quantify the flexibility benefit.
gRPC (Protocol Buffers): Recommend for: internal service-to-service communication where latency and type safety matter, high-throughput streaming use cases, and polyglot environments where code generation from proto definitions is valuable. Note the costs: poor browser support without gRPC-Web, less human-readable, requires protobuf tooling.
WebSocket: Recommend for: real-time bidirectional communication (chat, collaborative editing, live dashboards). Not appropriate for request-response patterns. Always pair with a REST or gRPC API for non-real-time operations.
Server-Sent Events (SSE): Recommend for: server-to-client unidirectional streaming (live feeds, notification streams) when full WebSocket bidirectionality is unnecessary. Simpler than WebSocket, works over standard HTTP, auto-reconnects.
Webhooks (callback-based async API): Recommend for: notifying external consumers of events asynchronously. Always pair with a polling fallback for consumers that cannot expose an endpoint.
State the tradeoff explicitly: "For this system, I recommend REST because [specific reasons]. GraphQL was considered but rejected because [specific reasons]. If [specific condition] changes, reconsider GraphQL."
If the system requires multiple styles (e.g., REST for CRUD + WebSocket for real-time), state that explicitly and define which operations use which style.
Model resources from the consumer's perspective, not the database schema. This is the most critical step in REST API design. Resources are the API's conceptual model — they often aggregate, reshape, or simplify the underlying data model for consumer convenience.
orders, products, customers, shipments./orders/{orderId}/line-items — line items are sub-resources of an order.dashboard-summary resource that aggregates data from multiple backend services.Design the URL structure. Apply these rules consistently:
/orders, /products, /users. Never use verbs in URLs (no /getOrders, /createUser)./customers/{customerId}/orders — only when orders are truly scoped to a customer in this API's context./customers/{customerId}/orders is fine. /customers/{customerId}/orders/{orderId}/line-items/{lineItemId}/discounts is too deep — flatten it. Provide /line-items/{lineItemId} or /orders/{orderId}/line-items as alternatives./order-items, not /orderItems or /order_items./orders/{orderId}./orders?status=shipped&sort=-created_at&limit=20.Map HTTP methods to operations. For each resource, define the supported operations using correct HTTP semantics:
| Operation | Method | URL | Semantics |
|---|---|---|---|
| List/search | GET | /orders | Returns a collection. Must be safe and idempotent. |
| Get one | GET | /orders/{id} | Returns a single resource. Must be safe and idempotent. |
| Create | POST | /orders | Creates a new resource. Not idempotent by default (see step 20 for idempotency design). |
| Full replace | PUT | /orders/{id} | Replaces the entire resource. Must be idempotent. Client sends the complete representation. |
| Partial update | PATCH | /orders/{id} | Updates specific fields. Use JSON Merge Patch (RFC 7396) for simple cases or JSON Patch (RFC 6902) for complex operations. State which format and why. |
| Delete | DELETE | /orders/{id} | Removes the resource. Must be idempotent (deleting an already-deleted resource returns 204 or 404 — decide and be consistent). |
PATCH /orders/{id} with {"status": "cancelled"}), or (b) a sub-resource representing the action (POST /orders/{id}/cancellation). Choose the approach and apply it consistently. State the convention.POST /orders/{id}/actions/recalculate — but isolate this pattern and do not let it proliferate. If more than 20% of your endpoints are action-based, reconsider whether REST is the right style.Design a consistent response envelope. Define a standard wrapper that every endpoint follows. Recommend one of:
{
"data": { ... },
"meta": {
"request_id": "abc-123",
"timestamp": "2024-01-15T10:30:00Z"
}
}
For collections:
{
"data": [ ... ],
"meta": {
"request_id": "abc-123",
"timestamp": "2024-01-15T10:30:00Z"
},
"pagination": {
"next_cursor": "eyJpZCI6MTAwfQ==",
"has_more": true
}
}
State the chosen approach and enforce it across all endpoints. Inconsistency in response shapes is the #1 source of consumer frustration.
Design resource representations. For each resource:
"2024-01-15T10:30:00Z". Never use Unix timestamps in API responses — they are unreadable by humans debugging integrations."amount": "49.99", with a separate "currency": "USD" field, or use minor units as integers ("amount_cents": 4999). State the convention and be consistent."status": "shipped", not "status": 3. Document all valid enum values."customer_id": "cust_123" and let the consumer fetch the customer separately. Good for loosely coupled relationships.GET /orders/{id}?expand=customer,line_items. Define which fields are expandable and the maximum expansion depth.Design request payloads. For create and update operations:
null clear a field? Does omitting a field leave it unchanged? Document this explicitly — ambiguity in PATCH semantics causes bugs.shipping_address, the create request should also use shipping_address, not address or ship_to.Design sparse fieldsets (field selection). For APIs where responses contain many fields and consumers often need only a subset:
fields query parameter: GET /orders/{id}?fields=id,status,total,created_at.id) always included?Design a structured error response format. Define a consistent error schema used by every endpoint:
{
"error": {
"code": "VALIDATION_FAILED",
"message": "The request body contains invalid fields.",
"details": [
{
"field": "email",
"code": "INVALID_FORMAT",
"message": "Must be a valid email address."
},
{
"field": "quantity",
"code": "OUT_OF_RANGE",
"message": "Must be between 1 and 1000."
}
],
"request_id": "req_abc123"
}
}
code: Machine-readable error code. Use a documented, stable set of error codes (e.g., VALIDATION_FAILED, RESOURCE_NOT_FOUND, INSUFFICIENT_PERMISSIONS, RATE_LIMIT_EXCEEDED, INTERNAL_ERROR). These must never change once published.message: Human-readable explanation. May change without breaking consumers.details: Array of field-level or sub-errors. Essential for validation errors.request_id: Correlation ID for debugging. Always include.Map error types to HTTP status codes consistently. Define and enforce the mapping:
| Situation | Status Code | Error Code |
|---|---|---|
| Validation error (bad input) | 400 | VALIDATION_FAILED |
| Missing or invalid authentication | 401 | AUTHENTICATION_REQUIRED |
| Authenticated but not authorized | 403 | INSUFFICIENT_PERMISSIONS |
| Resource not found | 404 | RESOURCE_NOT_FOUND |
| Method not allowed | 405 | METHOD_NOT_ALLOWED |
| Conflict (e.g., duplicate creation) | 409 | CONFLICT |
| Request entity too large | 413 | PAYLOAD_TOO_LARGE |
| Unsupported media type | 415 | UNSUPPORTED_MEDIA_TYPE |
| Unprocessable entity (semantic error) | 422 | UNPROCESSABLE_ENTITY |
| Rate limit exceeded | 429 | RATE_LIMIT_EXCEEDED |
| Internal server error | 500 | INTERNAL_ERROR |
| Service unavailable (dependency down) | 503 | SERVICE_UNAVAILABLE |
Design error responses for downstream failures. When the API depends on another service or external system that fails:
Retry-After header when appropriate.Design pagination for all collection endpoints. No collection endpoint should return unbounded results. Choose one pagination strategy:
Cursor-based pagination (recommended as default): Best for large, frequently changing datasets. Returns an opaque cursor that points to the next page.
GET /orders?limit=20&cursor=eyJpZCI6MTAwfQ==
Response includes:
{
"data": [...],
"pagination": {
"next_cursor": "eyJpZCI6MTIwfQ==",
"has_more": true
}
}
Advantages: stable under concurrent inserts/deletes, performs well with indexed columns. Disadvantage: cannot jump to arbitrary pages.
Offset-based pagination: Use only for small, static datasets where consumers need page-number navigation (e.g., admin dashboards).
GET /products?limit=20&offset=40
Include total count in response for page calculation. State the performance warning: OFFSET in SQL degrades at high values.
Keyset pagination: A variant of cursor-based using explicit column values instead of opaque cursors. Useful when consumers need to understand the ordering: GET /orders?limit=20&after_id=ord_100&sort=created_at.
Define the default page size and maximum page size for each collection (e.g., default 20, max 100). Reject requests exceeding the maximum with a 400 error.
Design filtering. For each collection endpoint, define the filterable fields:
?status=shipped&created_after=2024-01-01&total_min=100.?price[gte]=100&price[lte]=500) or a structured filter parameter. Choose one convention and apply consistently.Design sorting. Define the sorting convention:
?sort=created_at for ascending, ?sort=-created_at for descending (prefix with -).?sort=-created_at,name.Design search. If the API supports search:
?q=search+terms on a defined set of searchable fields.POST /orders/search endpoint with a structured query body when search parameters are complex. This is an acceptable use of POST for a non-mutating operation because query complexity may exceed URL length limits.Design idempotency for write operations. Every API that consumers will retry (due to network failures, timeouts, or uncertainty) must be idempotent:
Idempotency-Key header:
POST /orders
Idempotency-Key: client-generated-uuid-abc123
Server behavior:
Design bulk and batch operations. When consumers need to operate on multiple resources in a single request:
POST /orders/batch with an array body. Define the maximum batch size (e.g., 100 items). Return individual results for each item:
{
"results": [
{ "index": 0, "status": "created", "data": { "id": "ord_1" } },
{ "index": 1, "status": "failed", "error": { "code": "VALIDATION_FAILED", "message": "..." } }
]
}
Idempotency-Key header.Design the authentication mechanism. Select based on consumer type:
X-API-Key or Authorization: ApiKey <key>), never in query parameters (they leak in logs and browser history).State which mechanism is used for each consumer type. Never use Basic Auth over non-TLS connections.
Design the authorization model. Define how permissions are enforced:
orders:read, orders:write, products:admin. Scopes should be granular enough to support least-privilege access.Design rate limiting and throttling. Define the rate limiting strategy:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 42
X-RateLimit-Reset: 1705312200
Retry-After header.Design API security controls. Address:
Access-Control-Allow-Origin: * on authenticated APIs.Design the API versioning strategy. Select one approach and apply consistently:
/v1/orders, /v2/orders. Most explicit, easiest for consumers to understand and for routing infrastructure to handle. Use this unless there is a specific reason not to.Accept: application/vnd.myapi.v2+json or custom header API-Version: 2. Use when you need to version individual resources independently or want cleaner URLs. More complex for consumers to implement.?version=2. Avoid — it makes caching harder and is easy to forget.Do not use content negotiation for versioning unless the API is already using HATEOAS and the consumers are sophisticated.
State the chosen approach and the rationale explicitly.
Define backward compatibility rules. Establish and document what constitutes a breaking vs. non-breaking change:
Non-breaking (safe to deploy without new version):
Breaking (requires a new version):
State the contract: "Consumers should tolerate unknown fields in responses and unknown enum values. The API will never remove or rename existing fields within a version."
Design the deprecation and sunset process. Define the lifecycle:
Deprecation response header.Deprecation: true and Sunset: Sat, 01 Jun 2025 00:00:00 GMT headers on deprecated endpoints.Design long-running operation APIs. For operations that take longer than acceptable response times (> 5-10 seconds):
POST /exports → Server returns 202 Accepted with a status URL:
{
"data": {
"operation_id": "op_abc123",
"status": "processing",
"status_url": "/operations/op_abc123"
}
}
GET /operations/op_abc123 until status is completed or failed.Retry-After header in 202 responses to guide polling frequency.Design webhook APIs. For event notification to external consumers:
POST /webhooks with { "url": "https://consumer.com/callback", "events": ["order.created", "order.shipped"], "secret": "..." }.{
"event_id": "evt_abc123",
"event_type": "order.shipped",
"timestamp": "2024-01-15T10:30:00Z",
"data": { ... }
}
X-Webhook-Signature). Document the verification algorithm with code examples.event_id in every delivery. Consumers must deduplicate by event_id.Design streaming APIs (if applicable). If the system requires real-time data streaming:
Last-Event-ID header for resume), and keepalive interval.Design HTTP caching. For REST APIs, leverage HTTP caching semantics:
Cache-Control: public, max-age=300.Cache-Control: private, max-age=60.Cache-Control: no-store.ETag: "abc123".If-None-Match: "abc123" on subsequent requests.304 Not Modified if unchanged — saves bandwidth and processing.Last-Modified / If-Modified-Since as an alternative or complement to ETags for time-based resources.Design API response optimization. Address performance through API design:
Accept-Encoding: gzip and respond with Content-Encoding: gzip for all JSON responses. This typically reduces payload size by 60-80%.expand parameters. Default to minimal responses — let consumers opt into more data.Produce an API specification. For every API designed:
.proto files with comments on every service, RPC, and message.Design the API documentation structure. Beyond the specification, the API must have:
Design spec-first vs. code-first workflow. Make an explicit recommendation:
Design the API testing pyramid. Define testing layers:
Define the test data strategy. Specify:
Design API gateway responsibilities. If an API gateway is in the architecture, define what it handles:
Choose the gateway technology and justify: Kong, AWS API Gateway, Envoy/Ambassador, Apigee, or a lightweight custom gateway. Base the choice on feature needs, team expertise, and infrastructure context.
Design request correlation and tracing. Ensure every API request is traceable:
X-Request-Id (or use the consumer-provided one) at the API gateway.X-Request-Id in every response (including error responses) so consumers can reference it in support requests.Define an API design review checklist. Before any API is approved for implementation, verify:
Define API governance standards. For organizations with multiple APIs:
Consistency is the supreme API design virtue. An API that is internally consistent — even if some individual decisions are suboptimal — is dramatically easier to learn and use than an API where every endpoint follows different conventions. When in doubt, choose the option that maintains consistency with the rest of the API.
Design for the consumer, not the implementation. The API's resource model, naming, and structure should reflect how consumers think about the domain, not how the database is structured or how the backend code is organized. If the internal model and the consumer model diverge, add a mapping layer — never expose internal implementation details through the API.
Make concrete recommendations, not option menus. Do not say "you could use cursor-based or offset-based pagination." Say "Use cursor-based pagination because [reason specific to this API]. Use offset-based only if [specific condition applies]." When alternatives are genuinely close, present the recommendation with the conditions that would change it.
Always state tradeoffs. Never recommend a design choice without stating what is gained and what is sacrificed. Use the format: "Using [approach] gives us [benefit] but costs us [drawback]. This is acceptable here because [justification tied to this API's specific context]."
Prefer simplicity and convention over cleverness. A predictable, boring API that follows well-known conventions is better than a clever, novel design that requires extensive documentation to understand. Clever APIs create clever bugs.
Everything must be documented in the spec. If a behavior is not in the OpenAPI/AsyncAPI specification, it does not exist as far as consumers are concerned. Undocumented behavior will be used incorrectly, and any change to it will break someone. If it matters, spec it. If it doesn't matter, remove it.
development
Maintainer-only workflow for handling GitHub Secret Scanning alerts on OpenClaw. Use when Codex needs to triage, redact, clean up, and resolve secret leakage found in issue comments, issue bodies, PR comments, or other GitHub content.
development
Maintainer workflow for OpenClaw releases, prereleases, changelog release notes, and publish validation. Use when Codex needs to prepare or verify stable or beta release steps, align version naming, assemble release notes, check release auth requirements, or validate publish-time commands and artifacts.
development
Run, watch, debug, and extend OpenClaw QA testing with qa-lab and qa-channel. Use when Codex needs to execute the repo-backed QA suite, inspect live QA artifacts, debug failing scenarios, add new QA scenarios, or explain the OpenClaw QA workflow. Prefer the live OpenAI lane with regular openai/gpt-5.4 in fast mode; do not use gpt-5.4-pro or gpt-5.4-mini unless the user explicitly overrides that policy.
development
End-to-end Parallels smoke, upgrade, and rerun workflow for OpenClaw across macOS, Windows, and Linux guests. Use when Codex needs to run, rerun, debug, or interpret VM-based install, onboarding, gateway smoke tests, latest-release-to-main upgrade checks, fresh snapshot retests, or optional Discord roundtrip verification under Parallels.