Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Design RESTful APIs with OpenAPI 3.1/3.2, resource modeling, HTTP semantics, versioning, pagination, HATEOAS, and OWASP API Security.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-06 | ✗→✓ | ▲ Improved | 357% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 327% | 0% |
| case-18 | ✗→✓ | ▲ Improved | 402% | 0% |
| case-11 | ✓→✓ | = Same ✓ | 300% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 127% | 0% |
Purpose: Design production-ready RESTful APIs following industry best practices with OpenAPI 3.1/3.2 specifications, resource-oriented architecture, HTTP semantics compliance, flexible pagination/filtering strategies, HATEOAS hypermedia support (Richardson Level 3), and OWASP API Security Top 10 2023 mitigations.
When to Use:
Complements:
api-graphql-designer: Use GraphQL for flexible client-driven queries; use REST for simple CRUD and public APIs.api-contract-testing: Validate OpenAPI specs with Pact or Spring Cloud Contract.security-api-gateway-configurator: Deploy REST APIs with gateway-level auth, throttling, and monitoring.Delegates to:
api-design-validator: Validates generated OpenAPI specs for schema compliance and security hardening.Mandatory Inputs:
domain_model: At least one entity with attributes (e.g., User: {id, email, name}).use_cases: Minimum one use case (e.g., "List all users", "Create a new order").Validation Steps:
maturity_target = 3, verify client can handle HATEOAS links (many clients only support Level 2).domain_model.Goal: Generate a basic RESTful API with OpenAPI 3.1 spec for a single resource using standard CRUD operations.
Steps:
domain_model (e.g., "User").GET /users, POST /usersGET /users/{id}, PUT /users/{id}, PATCH /users/{id}, DELETE /users/{id}GET /users: List all users (paginate with ?limit=20&offset=0 by default).POST /users: Create a new user (return 201 Created with Location header).GET /users/{id}: Retrieve a single user (return 200 OK or 404 Not Found).PUT /users/{id}: Replace entire user (idempotent, return 200 OK).PATCH /users/{id}: Partial update (return 200 OK or 204 No Content).DELETE /users/{id}: Delete user (idempotent, return 204 No Content).yaml openapi: 3.1.0 info: title: User API version: 1.0.0 paths: /users: get: summary: List users parameters:
in: query schema: {type: integer, default: 20}
in: query schema: {type: integer, default: 0} responses: 200: description: List of users content: application/json: schema: type: array items: {$ref: '#/components/schemas/User'} post: summary: Create user requestBody: required: true content: application/json: schema: {$ref: '#/components/schemas/User'} responses: 201: description: User created headers: Location: {schema: {type: string}} components: schemas: User: type: object required: email, name] properties: id: {type: string, format: uuid} email: {type: string, format: email} name: {type: string}
Token Budget: ≤2k tokens (single resource, basic CRUD).
Goal: Design a multi-resource API with versioning, pagination (cursor or offset), filtering, and OWASP API Security mitigations.
Steps:
GET /orders?userId=123 (query filtering).GET /users/{userId}/orders (if relationship is always accessed via parent)./users/{id}/settings where settings don't exist independently)./v1/users, /v2/users (most common, clear, cacheable).X-API-Version: 2 (clean URIs, harder to test in browser).Accept: application/vnd.api.v2+json (REST purist, complex for clients).pagination_preference):GET /orders?limit=20&offset=40{data: [...], pagination: {total: 150, limit: 20, offset: 40}}GET /orders?limit=20&after=cursorXYZ{data: [...], pagination: {nextCursor: "abc123", hasMore: true}}GET /orders?limit=20&afterId=1000&afterCreatedAt=2025-10-26T12:00:00ZGET /orders?status=completed&minAmount=100GET /orders?sort=createdAt:desc,amount:asc (multi-column).id as tiebreaker).userId matches authenticated user before returning /users/{userId}.yaml openapi: 3.1.0 info: title: E-commerce API version: 2.0.0 servers:
paths: /orders: get: summary: List orders parameters:
in: query schema: {type: integer, default: 20, maximum: 100}
in: query schema: {type: string}
in: query schema: {type: string, enum: pending, completed, cancelled]} security:
responses: 200: description: List of orders content: application/json: schema: type: object properties: data: {type: array, items: {$ref: '#/components/schemas/Order'}} pagination: type: object properties: nextCursor: {type: string} hasMore: {type: boolean} components: securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: JWT schemas: Order: type: object required: id, userId, status, total] properties: id: {type: string, format: uuid} userId: {type: string, format: uuid} status: {type: string, enum: pending, completed, cancelled]} total: {type: number, format: decimal}
Token Budget: ≤6k tokens (multi-resource, versioning, pagination, security).
Goal: Design a Richardson Level 3 REST API with HATEOAS hypermedia links, webhooks, bulk operations, and advanced security.
Steps:
_links section with self, related, and action links.json { "id": "order-123", "status": "pending", "total": 99.99, "_links": { "self": {"href": "/v2/orders/order-123"}, "user": {"href": "/v2/users/user-456"}, "items": {"href": "/v2/orders/order-123/items"}, "cancel": {"href": "/v2/orders/order-123/cancel", "method": "POST"}, "pay": {"href": "/v2/orders/order-123/pay", "method": "POST"} } }
webhooks section:yaml webhooks: orderCreated: post: requestBody: content: application/json: schema: type: object properties: event: {type: string, example: "order.created"} data: {$ref: '#/components/schemas/Order'} responses: 200: description: Webhook received
GET /users?ids=1,2,3 (return array of users).POST /users/batch with array of users in body (return array of created users).PATCH /users/batch with array of {id, changes} objects.Idempotency-Key header for POST/PATCH to prevent duplicate operations.?createdAt[gte]=2025-01-01&createdAt[lt]=2025-02-01?q=laptop (searches across multiple fields).?status[in]=pending,completed (OR), ?status[ne]=cancelled (NOT EQUAL).GET /users/{id} returns ETag: "abc123" header.If-None-Match: "abc123" on next request.304 Not Modified if resource unchanged (saves bandwidth).json { "type": "https://api.example.com/errors/validation-error", "title": "Validation Error", "status": 400, "detail": "Email field is required", "instance": "/v2/users", "errors": [ {"field": "email", "message": "Email is required"} ] }
X-RateLimit-Limit: 100X-RateLimit-Remaining: 95X-RateLimit-Reset: 1698345600 (Unix timestamp)429 Too Many Requests with Retry-After header.Deprecation: true and Sunset: 2026-04-26 headers to v1 responses.Token Budget: ≤12k tokens (HATEOAS, webhooks, bulk ops, advanced security, caching).
Ambiguity Resolution:
versioning_strategy not specified:/v1/, /v2/) as it's most widely adopted and easiest to test.pagination_preference not specified:maturity_target not specified:/users, not /user)./createUser is wrong; use POST /users instead).GET /orders?userId=123) for flexibility.GET /users/{id}/profile where profile can't exist without user).Stop Conditions:
DELETE /users/{id}.id + createdAt.Thresholds:
limit=100 to prevent resource exhaustion. Return 400 Bad Request if limit > 100./users/{id}/orders/{orderId} is OK; /users/{id}/orders/{orderId}/items/{itemId} is too deep → flatten to /order-items/{id}).Required Fields:
typescript{ openapi_spec: { openapi: "3.1.0" | "3.2.0"; info: { title: string; version: string; // Semantic version (1.0.0, 2.1.0) description?: string; }; servers: Array<{ url: string; // https://api.example.com/v2 description?: string; }>; paths: { [path: string]: { // /users, /users/{id}, etc. [method: string]: { // get, post, put, patch, delete summary: string; parameters?: Array<{ name: string; in: "query" | "path" | "header"; schema: object; // JSON Schema required?: boolean; }>; requestBody?: { required: boolean; content: { "application/json": { schema: object; }; }; }; responses: { [statusCode: string]: { description: string; content?: { "application/json": { schema: object; }; }; }; }; security?: Array<object>; }; }; }; components: { schemas: { [name: string]: object; // JSON Schema definitions }; securitySchemes?: { [name: string]: { type: "http" | "apiKey" | "oauth2" | "openIdConnect" | "mutualTLS"; scheme?: "bearer" | "basic"; bearerFormat?: "JWT"; }; }; }; webhooks?: { // OpenAPI 3.1+ only [name: string]: { post: object; // Outbound webhook definition }; }; }; resource_design: { resources: Array<{ name: string; // User, Order, Product uri_template: string; // /users, /users/{id} http_methods: { GET?: string; // Description (e.g., "List all users") POST?: string; PUT?: string; PATCH?: string; DELETE?: string; }; relationships: Array<{ related_resource: string; relationship_type: "one-to-many" | "many-to-many" | "one-to-one"; uri_pattern: string; // /users/{userId}/orders or /orders?userId={userId} }>; }>; richardson_level: 0 | 1 | 2 | 3; }; versioning_config: { strategy: "uri" | "header" | "media-type"; current_version: string; // v2, 2.0.0 supported_versions: string[]; // [v1, v2] deprecation_timeline?: { deprecated_version: string; sunset_date: string; // ISO 8601 }; migration_guide: string; // Breaking changes, timeline }; pagination_config: { method: "offset" | "cursor" | "keyset"; parameters: { limit: { default: number; // 20 max: number; // 100 }; offset?: number; // For offset-based cursor?: string; // For cursor-based sortKey?: string; // For keyset-based }; response_format: { data_field: string; // "data" or "items" metadata_field: string; // "pagination" or "meta" metadata_shape: object; // {total, limit, offset} or {nextCursor, hasMore} }; }; security_recommendations: Array<{ owasp_category: string; // API1: BOLA, API2: Broken Auth, etc. risk: "high" | "medium" | "low"; mitigation: string; // Specific action (e.g., "Validate userId matches JWT") implementation: string; // Code snippet or config example }>; hateoas_links?: { // Only if maturity_target >= 3 link_relations: Array<{ rel: string; // self, related, action href_template: string; // /users/{id}, /users/{id}/orders method?: string; // POST, DELETE (for action links) }>; example_response: object; // JSON with _links section }; }
Optional Fields:
webhooks: Array of webhook definitions (event name, payload schema, delivery guarantees).caching_strategy: Object with ETag usage, Cache-Control headers, max-age values.rate_limiting: Object with limits per endpoint, throttling algorithm (token bucket, leaky bucket).bulk_operations: Array of batch endpoints (/users/batch, etc.) with idempotency requirements.Format: OpenAPI spec in YAML or JSON. Resource design and recommendations in Markdown.
Input:
yamldomain_model: User: {id: uuid, email: string, name: string} Order: {id: uuid, userId: uuid, status: enum, total: decimal} Product: {id: uuid, name: string, price: decimal} use_cases: - "List all orders for a user" - "Create a new order" - "Search products by name" versioning_strategy: "uri" pagination_preference: "cursor" security_requirements: auth: "OAuth2 (Bearer JWT)" rate_limit: "100 requests/minute per user"
Output (T2 Summary):
yamlRichardson Level: 2 (HTTP verbs + multiple resources) Versioning: URI-based (/v1/, /v2/) Pagination: Cursor-based (after parameter, nextCursor in response) Resources: - /v1/users (GET, POST) - /v1/users/{id} (GET, PUT, PATCH, DELETE) - /v1/orders (GET, POST) + ?userId filter + ?after cursor - /v1/orders/{id} (GET, PUT, PATCH, DELETE) - /v1/products (GET, POST) + ?q=search query Security: OAuth2 Bearer JWT, rate limit 100/min via X-RateLimit headers OWASP Mitigations: - API1 BOLA: Validate userId in JWT matches /users/{id} access - API4 Rate Limit: 100 req/min via API gateway (429 response if exceeded) - API8 Misconfiguration: No stack traces in production (generic 500 message)
Link to Full Example: See skills/api-rest-designer/examples/ecommerce-api-design.txt
HATEOAS Response Example:
json{ "id": "order-456", "status": "pending", "total": 149.99, "_links": { "self": {"href": "/v2/orders/order-456"}, "user": {"href": "/v2/users/user-789"}, "items": {"href": "/v2/orders/order-456/items"}, "cancel": {"href": "/v2/orders/order-456/cancel", "method": "POST"}, "pay": {"href": "/v2/payments", "method": "POST", "body": {"orderId": "order-456"}} } }
Webhook Definition (OpenAPI 3.1):
yamlwebhooks: orderCompleted: post: summary: Notifies when an order is completed requestBody: content: application/json: schema: type: object properties: event: {type: string, example: "order.completed"} timestamp: {type: string, format: date-time} data: {$ref: '#/components/schemas/Order'} responses: 200: description: Webhook acknowledged
Token Budget Compliance:
Validation Checklist:
/users, not /user).total, nextCursor, hasMore).201 Created or 200 OK with resource in body.swagger-cli validate).maturity_target (or Level 2 by default).Safety & Auditability:
deprecated: true in OpenAPI and Sunset header in responses.Access-Control-Allow-Origin settings if API is public-facing.Determinism:
id as tiebreaker if primary sort isn't unique).Idempotency-Key header to prevent duplicate resource creation.Official Specifications:
Security:
REST Design Patterns:
Pagination:
Versioning:
Complementary Skills:
api-graphql-designer: Alternative to REST for flexible client queries.api-contract-testing: Validate OpenAPI specs with Pact, Spring Cloud Contract.security-api-gateway-configurator: Deploy REST APIs with gateway-level auth, rate limiting, monitoring.api-design-validator: Automated OpenAPI spec validation and security hardening.Other measured skills in the registry, with their headline benchmark lift.