| name | api-contract-design |
| description | Designs REST/GraphQL API contracts with clear versioning, error handling, and backward compatibility. Outputs OpenAPI spec or GraphQL schema with examples, deprecation strategy, and test cases. |
| allowed-tools | Write |
| effort | medium |
API Contract Design
When to activate
During architecture design phase when defining service boundaries and integration points. Triggered by: component breakdown complete, external integrations identified, or third-party API consumption planned. Should be done before any implementation starts (API-first design).
When NOT to use
Not for implementing APIs (this is contract design, not coding). Not for existing APIs that are already in production (use versioning-strategy instead). Not for internal-only communication (use message queues or direct calls). Not without clear understanding of consumers and use cases.
Design Checklist
-
API Scope
-
Resource Design
-
Error Handling
-
Security & Auth
-
Versioning & Compatibility
-
Documentation & Testing
Output Format
API Specification
Overview
Title: [API name]
Version: [version]
Base URL: https://api.company.com/v1
Contact: [name, email]
License: [MIT, etc.]
Authentication
Type: OAuth2 / API Key / mTLS
Scheme: Bearer [token]
or
Header: Authorization: Bearer {access_token}
Scopes: [read:orders, write:orders, read:customers, etc.]
Endpoints (organized by resource)
GET /resource/{id}
Description: Fetch a single [resource]
Parameters:
id (path, required): Resource ID
Response (200 OK):
{
"id": "123",
"name": "Example",
"created_at": "2026-06-13T10:30:00Z",
"status": "active"
}
Errors:
401: Not authenticated
403: Forbidden (insufficient scope)
404: Resource not found
429: Rate limited (too many requests)
POST /resource
Description: Create a new [resource]
Request Body:
{
"name": "New Item",
"type": "premium"
}
Response (201 Created):
{
"id": "124",
"name": "New Item",
"type": "premium",
"created_at": "2026-06-13T10:31:00Z",
"status": "pending"
}
Errors:
400: Validation failed (see body for details)
401: Not authenticated
422: Unprocessable entity (data conflict)
PUT /resource/{id}
Description: Update a [resource] (full replacement)
Parameters:
id (path, required): Resource ID
Request Body:
{
"name": "Updated Item",
"type": "premium",
"status": "active"
}
Response (200 OK):
{
"id": "123",
...
}
Errors:
400: Validation failed
404: Not found
409: Conflict (version mismatch or concurrent update)
PATCH /resource/{id}
Description: Partial update [resource]
Request Body (only fields to update):
{
"status": "inactive"
}
Response (200 OK): [Updated resource]
Errors: [same as PUT]
DELETE /resource/{id}
Description: Delete a [resource]
Response (204 No Content): [empty]
Errors:
404: Not found
409: Cannot delete (foreign key constraint, etc.)
GET /resource?filter=value&sort=name&page=1&per_page=20
Description: List [resources] with filtering and pagination
Query Parameters:
filter (optional): Filter by [criteria]
sort (optional): Sort by [field]. Prefix with - for DESC
page (optional, default: 1): Page number
per_page (optional, default: 20): Results per page
Response (200 OK):
{
"data": [
{ ... },
{ ... }
],
"pagination": {
"page": 1,
"per_page": 20,
"total": 156,
"pages": 8
}
}
Error Response Format (consistent across all endpoints)
{
"error": {
"code": "VALIDATION_ERROR",
"message": "One or more fields are invalid",
"details": [
{
"field": "email",
"message": "Must be a valid email address",
"code": "INVALID_FORMAT"
}
]
}
}
Status Codes
| Code | Meaning | When to use |
|---|
| 200 | OK | Successful GET, PUT, PATCH |
| 201 | Created | Successful POST (resource created) |
| 204 | No Content | Successful DELETE, or POST with no response body |
| 400 | Bad Request | Client validation error (bad format, missing field) |
| 401 | Unauthorized | Missing or invalid authentication |
| 403 | Forbidden | Valid auth but insufficient permissions |
| 404 | Not Found | Resource doesn't exist |
| 409 | Conflict | Version mismatch, duplicate resource, etc. |
| 422 | Unprocessable Entity | Valid format but business logic violated |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Server Error | Unexpected error (not client's fault) |
Versioning Strategy
Approach: URL path versioning (/v1/, /v2/)
Compatibility Rules:
- Can add optional fields to request/response (old clients ignore them)
- Cannot remove required fields
- Cannot change field data type
- Can add new endpoints
- Can deprecate old endpoints (with warning)
Deprecation Timeline:
- Current: v1 fully supported
- Supported: v1 (6 months remaining)
- Deprecated: v1 (supported for 6 months, then sunset)
Migration Guide:
# Migrating from /v1/ to /v2/
Changes:
- `created_at` is now ISO 8601 format (was Unix timestamp)
- `status` field moved inside `metadata` object
- New required field: `account_id`
Before:
GET /v1/orders/123
{
"id": "123",
"created_at": 1623600600,
"status": "shipped"
}
After:
GET /v2/orders/123
{
"id": "123",
"created_at": "2026-06-13T10:30:00Z",
"metadata": {
"status": "shipped"
},
"account_id": "acme"
}
Rate Limiting
- Authenticated: 1000 req/min per client
- Unauthenticated: 100 req/min per IP
- Spike allowance: 2x limit for 10 seconds
Headers returned:
RateLimit-Limit: 1000
RateLimit-Remaining: 956
RateLimit-Reset: 1623600660
Webhooks (if applicable)
POST https://customer.example.com/webhooks/orders
Event: order.created
Payload:
{
"event_id": "evt_123456",
"event_type": "order.created",
"timestamp": "2026-06-13T10:30:00Z",
"data": {
"id": "ord_789",
"customer_id": "cust_456",
"amount": 129.99
}
}
Signature (HMAC-SHA256):
X-Signature: sha256=abc123def456...