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
1. 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
"""
2. 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)
"""
4. 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
"""
5. 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
}
"""
6. 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:
/usersnot/getUsers
- •Use plural nouns:
- •
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
- •Use query params:
- •
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