Conventions
Read these once. They apply to every endpoint in the generated reference.
Response envelope
Every non-auth, non-webhook endpoint returns:
{
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-05-14T12:00:00.000Z",
"status": "success",
"statusCode": 200,
"data": { /* payload */ },
"error": null
}
On error:
{
"requestId": "550e8400-...",
"timestamp": "2026-05-14T12:00:00.000Z",
"status": "error",
"statusCode": 403,
"data": null,
"error": {
"code": "FORBIDDEN",
"message": "Only platform admins can access this resource",
"details": {}
}
}
For paginated reads, an extra pagination field:
{
"status": "success",
"data": [/* ... */],
"pagination": { "cursor": "abc123", "hasMore": true, "limit": 20 },
"error": null
}
Exceptions to the envelope
| Endpoint | Why bare response |
|---|---|
/api/auth/* (better-auth) | Spec'd by OIDC/OAuth, has its own format |
/api/auth/oauth2/* | OAuth 2.0 spec |
/webhooks/stripe | Bare 200/4xx body, headers carry signature |
| File uploads (multipart response) | Returns { url } directly inside the envelope |
The ApiResponseInterceptor detects res.headersSent and skips wrapping. So if a controller calls res.send() directly, no envelope.
Path naming
| Pattern | Meaning |
|---|---|
/agents/:agentId/... | Resource is scoped to an agent. Path params validated by an agent existence check. |
/me/... | Resource is scoped to the current user. Requires auth. |
/admin/... | Requires platform_admin role. |
/api/auth/... | better-auth namespace. |
/webhooks/... | Inbound webhooks. |
/health | Liveness only. No envelope. |
HTTP method semantics
GET— never mutates.POSTon:idpath — partial or full update (legacy from Express conventions; should be PATCH but isn't).POSTon collection — create.PUT— used for idempotent upserts (e.g. tier quotas).DELETE— soft delete where possible.
Identifiers
| Identifier | Format |
|---|---|
| Most IDs | UUID v4 (8d1...) |
| Slugs | Kebab-case (ai-support) |
| Stripe IDs | prod_*, price_*, sub_*, cus_*, cs_* |
| Tags | [a-z0-9-]+ |
Timestamps
ISO 8601 UTC with milliseconds: 2026-05-14T12:00:00.000Z. Always.
Currencies
Lowercase ISO 4217 (usd, inr, eur). Stripe uses minor units (cents) internally; the marketplace API surfaces decimal strings ("49.00") for clarity.
Request IDs
Every response has requestId (UUID). Echo it when filing bug reports — it correlates to PostHog audit log rows.
Rate limiting
Not currently enforced at the framework level. Stripe webhook idempotency provides natural deduplication; quota consume calls deduplicate via clientReqId.
Versioning
No URL version prefix. Breaking changes will be communicated via Changelog / Release notes and (eventually) x-api-version response header.