API Overview¶
Base URL¶
All API endpoints are prefixed with /api/v1. The health check is the only exception — it lives at /health (no prefix, no version).
Versioning¶
URI-based versioning: /api/v1/tenants, /api/v2/tenants.
When a breaking change is needed, a new version controller is created alongside the existing one. Old versions continue working. This is the same approach used by Stripe, GitHub, and Twilio.
A breaking change is: removing a field, changing a field's type, changing the meaning of a status code, or removing an endpoint. Adding new optional fields is NOT a breaking change.
Authentication¶
(Coming in Phase 1)
Bearer token via the Authorization header:
Error Response Format¶
Every error from the API has this exact shape:
{
"statusCode": 400,
"message": "Validation failed",
"error": "Bad Request",
"correlationId": "13850bd4-08fe-4f22-bd5a-b09e13bc6ec2",
"timestamp": "2026-03-29T11:43:32.488Z",
"path": "/api/v1/tenants"
}
| Field | Type | Description |
|---|---|---|
statusCode |
number | HTTP status code |
message |
string or string[] | Human-readable error. Validation errors return an array of messages |
error |
string | HTTP status name |
correlationId |
string (UUID) | Unique request identifier for log tracing |
timestamp |
string (ISO 8601) | When the error occurred |
path |
string | The requested URL path |
Correlation ID¶
Every request gets a unique x-correlation-id header (UUID v4). If the client sends their own, the server respects it. This ID appears in:
- Every error response body
- Every log line on the server
- The
x-correlation-idresponse header
Use it when reporting issues: "Request 13850bd4-... returned a 500." Support can grep logs instantly.
Validation¶
Request bodies are validated automatically using class-validator decorators on DTOs. Invalid requests get a 400 with specific field-level errors:
{
"statusCode": 400,
"message": [
"name must be a string",
"slug must be shorter than or equal to 100 characters"
],
"error": "Bad Request",
"correlationId": "...",
"timestamp": "...",
"path": "/api/v1/tenants"
}
Extra fields not defined in the DTO are rejected (forbidNonWhitelisted). This prevents mass-assignment attacks.
Health Check¶
Returns 200 if all dependencies are healthy, 503 if any are down:
{
"status": "ok",
"info": {
"postgres": { "status": "up" }
},
"error": {},
"details": {
"postgres": { "status": "up" }
}
}
No authentication required. No API prefix. Used by load balancers and Kubernetes probes.