This document defines the API Standards & Architecture for the CMS Pricing API. It consolidates API design standards, layer architecture, contracts, and implementation patterns into a single authoritative source. All API development must conform to this standard.
Status: Adopted v1.0
Owners: Platform Engineering (API Guild)
Consumers: Product, Engineering, Data, Ops
Change control: ADR + Architecture Board review
Cross-References:
- DOC-master-catalog-prd-v1.0.md: Master system catalog and dependency map
- STD-observability-monitoring-prd-v1.0: API service monitoring and observability
- STD-api-security-and-auth-prd-v1.0: Authentication, authorization, and security requirements
- STD-qa-testing-prd-v1.0: API testing requirements and quality gates
- STD-data-architecture-prd-v1.0: Data pipeline integration and observability
Goals
- Establish clear layer boundaries with single responsibility per layer
- Enable contract-first development with OpenAPI as SSOT
- Ensure testable, maintainable, and scalable API architecture
- Provide consistent error handling and observability across all layers
- Support external customer APIs with proper security and rate limiting
- Define comprehensive API standards for contracts, versioning, and lifecycle
Non-Goals
- Business logic specific to pricing calculations (covered in domain engines)
- Real-time streaming APIs (covered by separate streaming standard)
- Internal-only APIs without external customer requirements
- Security implementation details (covered in Security PRD)
Single Source of Truth: /api-contracts/openapi.yaml must be updated first. Code, tests, SDKs, docs, and changelog entries follow that PR.
SemVer Rules:
- MINOR/PATCH for backward-compatible changes
- MAJOR for breaking changes with ≥90 day deprecation window
- Deprecations advertise via
X-Deprecationheader and docs banner
CI Gates:
- Spectral lint, contract-test suite, and router/spec drift check block merges on failure
- Contract tests auto-generated from SSOT; drift blocks merge
Release Workflow:
- Git tag (
vMAJOR.MINOR.PATCH) equals spec version - Tag builds SDKs (TypeScript, Python) and Postman collection as release assets
- Publishes rendered docs to
/docs/<version>(with/docstracking main)
Request Format:
- JSON; UTF-8; snake_case fields
- Idempotency via
Idempotency-Keyon mutating endpoints - Content-Type:
application/json
Response Format:
- JSON object with top-level
data,meta,tracewhere applicable - Standard error envelope:
{
"error": {
"code": "string",
"message": "human-readable",
"details": [{"field":"path","issue":"desc"}]
},
"trace": {"correlation_id":"uuid"}
}Data-Backed Responses:
- Include
trace.vintageandtrace.hashfor reproducibility trace.vintagemaps to snapshot selected by APItrace.hashis dataset bundle digest proving exact data slice
Pagination:
- Cursor-based pagination:
page_size(≤100),next_cursor - Consistent across all list endpoints
Filtering:
- Whitelisted query params only
- Server rejects unknown filters with 400
- Document all supported filters in OpenAPI
Sorting:
- Format:
sort=field:asc|desc - Support multiple fields:
sort=name:asc,date:desc
Standard HTTP Codes:
- 400: Bad Request (validation errors)
- 401: Unauthorized (missing/invalid API key)
- 403: Forbidden (insufficient permissions)
- 404: Not Found (resource not found)
- 409: Conflict (idempotency key conflict)
- 422: Unprocessable Entity (business rule violations)
- 429: Too Many Requests (rate limit exceeded)
- 5xx: Server errors
Retry Behavior:
- Honor
Retry-Afterheader for 429/503 - Client idempotent retries allowed where specified
- Exponential backoff recommended
Correlation IDs:
- Require
X-Correlation-Id(generated if absent) - Echo in all responses and logs
- Use UUIDv4 format
Observability Integration:
- Structured logging with correlation IDs
- Metrics collection for all endpoints
- Trace context propagation
- Performance monitoring
Location: cms_pricing/routers/
Responsibilities:
- HTTP request/response handling
- Request validation using Pydantic schemas
- Authentication and authorization enforcement
- Rate limiting and security middleware
- Error shaping and HTTP status code mapping
- Response formatting and serialization
- Correlation ID management
What Goes Here:
- FastAPI route handlers (
@router.get,@router.post) - Pydantic request/response models
- HTTP-specific validation logic
- API key verification
- Rate limiting configuration
- CORS and security headers
What Doesn't:
- Business logic or calculations
- Direct database access
- Domain-specific pricing rules
- Complex data transformations
Example:
@router.get("/codes/price")
async def price_single_code(
request: Request,
zip: str,
code: str,
setting: str,
year: int,
db: Session = Depends(get_db),
api_key: str = Depends(verify_api_key)
):
# Input validation
if not zip.isdigit() or len(zip) != 5:
raise HTTPException(status_code=400, detail="ZIP must be exactly 5 digits")
# Delegate to service layer
pricing_service = PricingService(db)
result = await pricing_service.price_single_code(zip, code, setting, year)
return resultLocation: cms_pricing/services/
Responsibilities:
- Business orchestration and workflow coordination
- Multi-engine coordination (MPFS, OPPS, ASC, etc.)
- Transaction management and database session handling
- Business rule enforcement and validation
- Cross-cutting concerns (logging, tracing, metrics)
- Error handling and exception mapping
What Goes Here:
- Service classes that coordinate multiple engines
- Business workflow orchestration
- Transaction boundaries
- Cross-service communication
- Business validation rules
- Audit logging and tracing
What Doesn't:
- HTTP-specific concerns
- Direct database queries (use repositories)
- Domain calculations (use engines)
- Request/response formatting
Example:
class PricingService:
def __init__(self, db: Session):
self.db = db
self.mpfs_engine = MPFSEngine(db)
self.opps_engine = OPPSEngine(db)
async def price_single_code(self, zip: str, code: str, setting: str, year: int):
# Business orchestration
if setting == "office":
return await self.mpfs_engine.calculate_price(zip, code, year)
elif setting == "outpatient":
return await self.opps_engine.calculate_price(zip, code, year)
else:
raise ValueError(f"Unknown setting: {setting}")Location: cms_pricing/engines/
Responsibilities:
- Core business logic and calculations
- Domain-specific algorithms and rules
- Data validation and business constraints
- Domain model management
- Pricing calculations and formulas
What Goes Here:
- Pricing calculation engines (MPFS, OPPS, ASC, etc.)
- Domain models and value objects
- Business rule implementations
- Calculation algorithms
- Domain validation logic
What Doesn't:
- HTTP concerns
- Database access (use repositories)
- External service calls
- Infrastructure concerns
Example:
class MPFSEngine:
def __init__(self, db: Session):
self.db = db
self.rvu_repo = RVURepository(db)
self.gpci_repo = GPCIRepository(db)
async def calculate_price(self, zip: str, code: str, year: int):
# Domain logic
rvu = await self.rvu_repo.get_rvu(code, year)
gpci = await self.gpci_repo.get_gpci(zip, year)
if not rvu or not gpci:
raise ValueError("Required data not found")
# Pricing calculation
price = rvu.total_rvu * gpci.total_gpci * self.get_conversion_factor(year)
return PriceResult(code=code, price=price, components=rvu, gpci=gpci)Location: cms_pricing/models/ and repositories
Responsibilities:
- Database access and ORM management
- Data persistence and retrieval
- Query optimization and performance
- Data mapping and transformation
- Transaction management
What Goes Here:
- SQLAlchemy models
- Repository pattern implementations
- Database queries and operations
- Data mapping logic
- Connection management
What Doesn't:
- Business logic
- HTTP concerns
- Domain calculations
- External service calls
- Transport → Service: Routers can call services
- Service → Domain: Services can call engines
- Service → Data: Services can call repositories
- Domain → Data: Engines can call repositories
- All → Observability: All layers can emit metrics/logs
- Transport → Domain: Routers cannot call engines directly
- Transport → Data: Routers cannot access database directly
- Domain → Service: Engines cannot call services
- Data → Domain: Repositories cannot call engines
- Data → Service: Repositories cannot call services
# Service layer with dependency injection
class PricingService:
def __init__(
self,
mpfs_engine: MPFSEngine,
opps_engine: OPPSEngine,
observability: ObservabilityService
):
self.mpfs_engine = mpfs_engine
self.opps_engine = opps_engine
self.observability = observability- Latency: p95 ≤ 200ms (read), p95 ≤ 400ms (write)
- Throughput: Declare RPS target per service; perf tests enforce
- Availability: 99.9% baseline monthly
- Response Time: p95 ≤ 300ms for reference endpoints
- Horizontal scaling with stateless workers
- Database connection pooling
- Caching strategies for reference data
- Async/await for I/O operations
- RED metrics (Rate, Errors, Duration)
- Custom business metrics
- Performance regression detection
- Capacity planning metrics
- Unit Tests: Individual components in isolation
- Integration Tests: Service-to-service communication
- Contract Tests: OpenAPI compliance verification
- End-to-End Tests: Full API workflow validation
- All endpoints must have unit tests
- Contract tests auto-generated from OpenAPI
- Integration tests for critical paths
- Performance tests for SLA validation
Reference: QTS PRD Section 7 for detailed testing requirements
- Auto-generate SDKs (TypeScript, Python) on tag
- Publish Postman collection
- Examples must match live sandbox
- OpenAPI-first documentation
- Interactive API explorer
- Code examples for all endpoints
- Migration guides for breaking changes
- Endpoint added only if: OpenAPI approved; examples provided; error cases defined; contract tests pass in CI
- All layers follow dependency boundaries
- Performance budgets met
- Security requirements satisfied
- CI blocks merges when Spectral lint or contract tests fail
- Each API code PR references the spec diff
- Tagged releases upload SDKs/Postman assets and publish versioned docs
- STD-observability-monitoring-prd-v1.0: API service monitoring, performance metrics, and alerting
- STD-api-security-and-auth-prd-v1.0: Authentication, authorization, rate limiting, and security middleware
- STD-qa-testing-prd-v1.0: API testing requirements, contract testing, and quality gates
- STD-data-architecture-prd-v1.0.md: Data pipeline integration and data-backed response requirements
- API Observability: This PRD Section 5.3 → Observability PRD Section 2.2 (API Service Volume)
- API Security: This PRD Section 3.1 → Security PRD Section 3.1 (AuthN & AuthZ)
- API Testing: This PRD Section 6 → QTS PRD Section 7 (Test Types & Minimums)
- Data Integration: This PRD Section 2.2 → DIS PRD Section 3.6 (API Readiness)
End of API Standards & Architecture PRD v1.0