Purpose
The api-designer skill provides comprehensive guidance for designing robust, RESTful APIs and function contracts that serve as clear interfaces for feature implementations. This skill helps the Architecture Designer agent create well-structured, documented, and maintainable API designs that follow industry best practices.
This skill emphasizes:
-
REST Principles: Proper resource design, HTTP method usage, and status codes
-
Clear Contracts: Well-defined request/response schemas
-
Error Handling: Consistent error response formats
-
Authentication: Security patterns and authorization strategies
-
Documentation: Comprehensive API documentation for consumers
The api-designer skill ensures that APIs are intuitive, consistent, and provide excellent developer experience for both internal and external consumers.
When to Use
This skill auto-activates when the agent describes:
-
"Design API endpoints for..."
-
"Create REST API for..."
-
"Define function contract for..."
-
"Specify request/response schemas..."
-
"Design authentication for..."
-
"Plan API structure with..."
-
"Define error responses for..."
-
"Create API documentation for..."
Provided Capabilities
- REST API Endpoint Design
What it provides:
-
Resource identification and naming
-
HTTP method selection (GET, POST, PUT, PATCH, DELETE)
-
URL structure and path parameters
-
Query parameter design
-
Status code selection
-
Idempotency considerations
REST Principles:
-
Resources as nouns, not verbs
-
HTTP methods for actions
-
Stateless design
-
Standard status codes
-
HATEOAS (optional)
Example:
from pydantic import BaseModel, Field from typing import List, Optional from datetime import datetime
==================== RESOURCE: Users ====================
class UserCreate(BaseModel): """Request schema for creating user.""" username: str = Field(..., min_length=3, max_length=50) email: str = Field(...) full_name: str = Field(..., min_length=1, max_length=200)
class UserResponse(BaseModel): """Response schema for user.""" id: int username: str email: str full_name: str is_active: bool created_at: datetime
class UserUpdate(BaseModel): """Request schema for updating user (all optional).""" email: Optional[str] = None full_name: Optional[str] = None is_active: Optional[bool] = None
class UserList(BaseModel): """Response schema for user list with pagination.""" items: List[UserResponse] total: int page: int page_size: int total_pages: int
API Endpoints
""" POST /api/v1/users Create new user GET /api/v1/users List users (with pagination) GET /api/v1/users/{user_id} Get user by ID PUT /api/v1/users/{user_id} Update user (full replace) PATCH /api/v1/users/{user_id} Update user (partial) DELETE /api/v1/users/{user_id} Delete user
Query Parameters for GET /api/v1/users:
- page: int = 1 (pagination)
- page_size: int = 20 (items per page)
- search: str = None (search filter)
- is_active: bool = None (status filter)
- sort_by: str = "created_at"
- sort_order: str = "desc"
Status Codes:
- 200 OK: Successful GET, PUT, PATCH
- 201 Created: Successful POST
- 204 No Content: Successful DELETE
- 400 Bad Request: Invalid input
- 401 Unauthorized: Authentication required
- 403 Forbidden: Insufficient permissions
- 404 Not Found: Resource not found
- 409 Conflict: Resource conflict (duplicate)
- 422 Unprocessable Entity: Validation error
- 500 Internal Server Error: Server error """
- Request/Response Schema Design
What it provides:
-
Input validation schemas
-
Output serialization schemas
-
Partial update schemas
-
List/pagination schemas
-
Error response schemas
Schema Patterns:
Create Request (POST):
class ResourceCreate(BaseModel): """All fields required for creation.""" name: str = Field(..., min_length=1, max_length=200) description: Optional[str] = Field(None, max_length=1000) category: str = Field(...)
Update Request (PUT - Full Replace):
class ResourceUpdate(BaseModel): """All fields required for full update.""" name: str = Field(..., min_length=1, max_length=200) description: Optional[str] = Field(None, max_length=1000) category: str = Field(...)
Partial Update Request (PATCH):
class ResourcePatch(BaseModel): """All fields optional for partial update.""" name: Optional[str] = Field(None, min_length=1, max_length=200) description: Optional[str] = Field(None, max_length=1000) category: Optional[str] = None
Response Schema:
class ResourceResponse(BaseModel): """Response includes ID and audit fields.""" id: int name: str description: Optional[str] category: str created_at: datetime updated_at: Optional[datetime]
class Config:
orm_mode = True # Enable ORM integration
List Response with Pagination:
class PaginatedResponse(BaseModel): """Generic paginated response.""" items: List[ResourceResponse] total: int = Field(..., description="Total number of items") page: int = Field(..., description="Current page number", ge=1) page_size: int = Field(..., description="Items per page", ge=1, le=100) total_pages: int = Field(..., description="Total number of pages")
@property
def has_next(self) -> bool:
"""Check if there's a next page."""
return self.page < self.total_pages
@property
def has_previous(self) -> bool:
"""Check if there's a previous page."""
return self.page > 1
3. Error Response Formats
What it provides:
-
Consistent error structure
-
Error codes and types
-
Detailed validation errors
-
User-friendly messages
-
Debug information (optional)
Standard Error Response:
from typing import Optional, List, Dict, Any
class ValidationError(BaseModel): """Individual validation error.""" field: str = Field(..., description="Field name with error") message: str = Field(..., description="Error message") code: str = Field(..., description="Error code")
class ErrorResponse(BaseModel): """Standard error response.""" error: str = Field(..., description="Error type (e.g., 'validation_error')") message: str = Field(..., description="Human-readable error message") details: Optional[List[ValidationError]] = Field(None, description="Validation errors") request_id: Optional[str] = Field(None, description="Request ID for tracking") timestamp: datetime = Field(default_factory=datetime.utcnow)
class Config:
schema_extra = {
"example": {
"error": "validation_error",
"message": "Request validation failed",
"details": [
{
"field": "email",
"message": "Invalid email format",
"code": "invalid_format"
}
],
"request_id": "req_abc123",
"timestamp": "2025-10-29T10:00:00Z"
}
}
Error Types
""" validation_error: Request validation failed (400) authentication_error: Authentication failed (401) authorization_error: Insufficient permissions (403) not_found_error: Resource not found (404) conflict_error: Resource conflict (409) rate_limit_error: Rate limit exceeded (429) internal_error: Internal server error (500) """
- Authentication and Authorization
What it provides:
-
Authentication patterns (JWT, OAuth2, API Key)
-
Authorization strategies (RBAC, ABAC)
-
Token validation
-
Permission checking
-
Security headers
JWT Authentication Example:
from pydantic import BaseModel, Field from typing import Optional, List
class LoginRequest(BaseModel): """Login request schema.""" username: str = Field(..., min_length=1) password: str = Field(..., min_length=1)
class TokenResponse(BaseModel): """Token response schema.""" access_token: str = Field(..., description="JWT access token") token_type: str = Field(default="bearer", description="Token type") expires_in: int = Field(..., description="Token expiration in seconds") refresh_token: Optional[str] = Field(None, description="Refresh token")
class TokenPayload(BaseModel): """JWT token payload.""" sub: int = Field(..., description="User ID (subject)") username: str = Field(..., description="Username") roles: List[str] = Field(default_factory=list, description="User roles") exp: int = Field(..., description="Expiration timestamp")
API Endpoints
""" POST /api/v1/auth/login Login and get token POST /api/v1/auth/refresh Refresh access token POST /api/v1/auth/logout Logout (invalidate token)
Authentication Header: Authorization: Bearer <access_token>
Example: Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... """
Role-Based Access Control (RBAC):
from enum import Enum
class UserRole(str, Enum): """User roles for RBAC.""" ADMIN = "admin" MANAGER = "manager" USER = "user" GUEST = "guest"
class Permission(str, Enum): """Permissions for resources.""" CREATE = "create" READ = "read" UPDATE = "update" DELETE = "delete"
Permission Matrix
""" Resource: Users
- ADMIN: create, read, update, delete
- MANAGER: read, update
- USER: read (own profile only)
- GUEST: read (public profiles only)
Endpoint Protection: POST /api/v1/users Requires: admin GET /api/v1/users Requires: admin, manager GET /api/v1/users/{user_id} Requires: authenticated PUT /api/v1/users/{user_id} Requires: admin OR owner DELETE /api/v1/users/{user_id} Requires: admin """
- Rate Limiting
What it provides:
-
Rate limit strategies
-
Rate limit headers
-
Error responses for exceeded limits
-
Quota management
Rate Limit Design:
class RateLimitInfo(BaseModel): """Rate limit information.""" limit: int = Field(..., description="Requests allowed per window") remaining: int = Field(..., description="Requests remaining") reset: int = Field(..., description="Unix timestamp when limit resets") window: int = Field(..., description="Time window in seconds")
Rate Limit Headers
""" X-RateLimit-Limit: 100 X-RateLimit-Remaining: 95 X-RateLimit-Reset: 1730203200 X-RateLimit-Window: 3600
Rate Limit Tiers:
- Anonymous: 10 requests/hour
- Authenticated: 100 requests/hour
- Premium: 1000 requests/hour
- Admin: Unlimited
Status Code: 429 Too Many Requests Response: { "error": "rate_limit_exceeded", "message": "Rate limit exceeded. Try again in 3600 seconds.", "limit": 100, "window": 3600, "reset": 1730203200 } """
- API Versioning
What it provides:
-
Versioning strategies
-
Version migration paths
-
Backward compatibility
-
Deprecation notices
Versioning Strategies:
URL Path Versioning (Recommended):
/api/v1/users /api/v2/users
Pros: Clear, explicit, easy to route Cons: URLs change between versions
Header Versioning:
GET /api/users Accept-Version: v1
GET /api/users Accept-Version: v2
Pros: Clean URLs Cons: Less visible, harder to test in browser
Query Parameter Versioning:
/api/users?version=1 /api/users?version=2
Pros: Flexible Cons: Easy to forget, pollutes query params
Deprecation Example:
class DeprecationWarning(BaseModel): """Deprecation warning in response header.""" deprecated: bool = True sunset_date: str = "2026-01-01" replacement_url: str = "/api/v2/users" documentation: str = "https://api.example.com/docs/migration/v1-to-v2"
Response Headers for Deprecated Endpoint
""" X-API-Deprecated: true X-API-Sunset: 2026-01-01 X-API-Replacement: /api/v2/users Link: <https://api.example.com/docs/migration/v1-to-v2>; rel="deprecation" """
Usage Guide
Step 1: Identify Resources
Requirements → Identify nouns → Define resources → Name endpoints
Step 2: Design Endpoints
Resources → HTTP methods → URL structure → Path/query params
Step 3: Define Schemas
Create schemas → Update schemas → Response schemas → Error schemas
Step 4: Plan Authentication
Identify auth needs → Choose strategy → Define tokens → Permission model
Step 5: Error Handling
Identify error cases → Standard format → Status codes → Error messages
Step 6: Rate Limiting
Define tiers → Set limits → Response headers → Exceeded handling
Step 7: Documentation
OpenAPI spec → Examples → Authentication guide → Error reference
Step 8: Versioning Strategy
Choose approach → Migration plan → Deprecation policy → Documentation
Best Practices
Use Proper HTTP Methods
-
GET: Retrieve resources (idempotent, safe)
-
POST: Create resources (non-idempotent)
-
PUT: Full replace (idempotent)
-
PATCH: Partial update (idempotent)
-
DELETE: Remove resource (idempotent)
Consistent Naming
-
Use plural nouns: /users , /posts
-
Use kebab-case: /user-profiles
-
Avoid verbs: /users not /getUsers
Status Codes
-
2xx: Success
-
4xx: Client errors
-
5xx: Server errors
-
Be specific: 201 for created, 204 for no content
Pagination
-
Always paginate lists
-
Provide total count
-
Include next/previous links (HATEOAS)
Filtering and Sorting
-
Use query params: ?status=active&sort=created_at
-
Document available filters
-
Provide defaults
Security
-
Always use HTTPS
-
Validate all input
-
Rate limit requests
-
Use proper authentication
Resources
api-design-guide.md
Comprehensive API design guide including:
-
REST principles and best practices
-
GraphQL patterns (if applicable)
-
Request/response schema design
-
Error response formats
-
Authentication/authorization patterns
-
Rate limiting strategies
-
API versioning approaches
-
Documentation standards
function-design-patterns.md
Function contract design patterns:
-
Function signature design
-
Parameter patterns (required, optional, defaults)
-
Return type patterns
-
Error handling in functions
-
Async function patterns
-
Type hints for functions
-
Docstring standards
Example Usage
Input (from Architecture Designer agent):
"Design REST API for a task management system with tasks, projects, users, and comments."
Output (api-designer skill provides):
Complete API design with endpoints and schemas
from pydantic import BaseModel, Field from typing import List, Optional from datetime import datetime from enum import Enum
==================== ENUMS ====================
class TaskStatus(str, Enum): """Task status options.""" TODO = "todo" IN_PROGRESS = "in_progress" DONE = "done"
class TaskPriority(str, Enum): """Task priority levels.""" LOW = "low" MEDIUM = "medium" HIGH = "high"
==================== REQUEST SCHEMAS ====================
class TaskCreate(BaseModel): """Create task request.""" title: str = Field(..., min_length=1, max_length=200) description: Optional[str] = Field(None, max_length=2000) project_id: int = Field(..., gt=0) assignee_id: Optional[int] = Field(None, gt=0) priority: TaskPriority = TaskPriority.MEDIUM due_date: Optional[datetime] = None
class TaskUpdate(BaseModel): """Update task request (partial).""" title: Optional[str] = Field(None, min_length=1, max_length=200) description: Optional[str] = Field(None, max_length=2000) assignee_id: Optional[int] = Field(None, gt=0) status: Optional[TaskStatus] = None priority: Optional[TaskPriority] = None due_date: Optional[datetime] = None
class CommentCreate(BaseModel): """Create comment request.""" content: str = Field(..., min_length=1, max_length=1000)
==================== RESPONSE SCHEMAS ====================
class TaskResponse(BaseModel): """Task response schema.""" id: int title: str description: Optional[str] project_id: int assignee_id: Optional[int] status: TaskStatus priority: TaskPriority due_date: Optional[datetime] created_at: datetime updated_at: Optional[datetime] created_by: int
class Config:
orm_mode = True
class TaskListResponse(BaseModel): """Paginated task list response.""" items: List[TaskResponse] total: int page: int page_size: int total_pages: int
==================== API ENDPOINTS ====================
""" Base URL: /api/v1
Authentication: Bearer token Rate Limit: 100 requests/hour per user
Tasks
POST /tasks Create new task GET /tasks List tasks (paginated, filtered) GET /tasks/{task_id} Get task by ID PATCH /tasks/{task_id} Update task DELETE /tasks/{task_id} Delete task
Comments on Tasks
POST /tasks/{task_id}/comments Add comment to task GET /tasks/{task_id}/comments List task comments DELETE /comments/{comment_id} Delete comment
Query Parameters for GET /tasks:
- page: int = 1
- page_size: int = 20
- project_id: int (filter by project)
- assignee_id: int (filter by assignee)
- status: TaskStatus (filter by status)
- priority: TaskPriority (filter by priority)
- search: str (search in title/description)
- sort_by: str = "created_at" (sort field)
- sort_order: str = "desc" (asc or desc)
Status Codes:
- 200 OK: Successful GET, PATCH
- 201 Created: Successful POST
- 204 No Content: Successful DELETE
- 400 Bad Request: Invalid input
- 401 Unauthorized: Not authenticated
- 403 Forbidden: Insufficient permissions
- 404 Not Found: Task not found
- 422 Unprocessable Entity: Validation error
- 429 Too Many Requests: Rate limit exceeded
- 500 Internal Server Error: Server error
Permissions:
- Create task: Authenticated user
- List tasks: Authenticated user (filtered by access)
- Get task: Task assignee, project member, or admin
- Update task: Task assignee, project owner, or admin
- Delete task: Task creator, project owner, or admin """
Integration
Used By:
- @architecture-designer (Primary) - Phase 2 sub-agent for architecture design
Integrates With:
-
architecture-planner skill - API contracts defined after component structure
-
data-modeler skill - Uses data models for request/response schemas
Workflow Position:
-
Analysis Specialist completes requirements analysis
-
Architecture Designer receives analysis
-
architecture-planner skill designs component structure (Step 3)
-
data-modeler skill designs data models (Step 4)
-
api-designer skill designs API contracts (Step 5)
-
Results synthesized into PRP
Version: 2.0.0 Auto-Activation: Yes Phase: 2 - Design & Planning Created: 2025-10-29