application/json and is versioned under /v1.
The full machine-readable spec is available at /api-reference/openapi.json and is rendered at API reference.
Base URL
CUSTODY_ENV=production to enforce HTTPS for tenant URLs and webhook endpoints. Development environments may use HTTP.
Resource families
| Family | Path prefix | What it manages |
|---|---|---|
| Tenants | /v1/tenants | Tenant accounts (multi-tenant deployments) |
| Vaults | /v1/vaults | Logical groupings of wallets |
| Wallets | /v1/wallets | Quantum Chain addresses with registered post-quantum public keys |
| Transactions | /v1/transactions | Outbound transfers, lifecycle, signing payloads |
| Approvals | /v1/transactions/{id}/approvals | Multi-approver workflow |
| Policies | /v1/policies | Spending limits, whitelists, approval rules |
| Webhooks | /v1/webhooks | Outbound event delivery |
| Assets | /v1/assets | Token metadata for QRC-20 and native assets |
| Users | /v1/users | Human users (when SSO is enabled) |
| Roles | /v1/roles | RBAC role definitions and assignments |
| Credentials | /v1/credentials | API keys, rotation, revocation |
| Audit | /v1/audit | Tamper-evident audit log |
| Compliance | /v1/compliance | AML screenings and travel-rule data |
Authentication
All non-public endpoints require authentication. Two modes are supported.API key
POST /v1/credentials and scoped to a tenant. Rotate with POST /v1/credentials/rotate.
SSO (OIDC)
Enabled withCUSTODY_SSO_ENABLED=true. Exchange your IdP token for a Qustody access token at POST /v1/auth/login, then send it in the Authorization: Bearer header. Refresh with POST /v1/auth/refresh.
Idempotency
Every mutating endpoint accepts anIdempotency-Key header. Duplicate requests with the same key within 24 hours return the original response without re-executing.
Pagination
List endpoints use cursor-based pagination:has_more is false, next_cursor is null. limit defaults to 50 and may not exceed 200.
Filtering and sorting
Common query parameters:| Param | Type | Notes |
|---|---|---|
status | string | Filter transactions by state (e.g. PENDING_SIGNATURE) |
wallet_id | uuid | Scope to one wallet |
vault_id | uuid | Scope to one vault |
created_after, created_before | RFC3339 | Time range |
sort | string | created_at:desc (default) or created_at:asc |
Rate limits
Default rate limit is 120 requests per minute per API key with a burst of 20. Configurable viaCUSTODY_RATE_LIMIT_RPM and CUSTODY_RATE_LIMIT_BURST.
When exceeded, the API returns 429 Too Many Requests with Retry-After header.
CUSTODY_RATE_LIMIT_REDIS_URL so limits are enforced cluster-wide.
Standard response shape
Successful single-resource responses return the resource directly:Error envelope
All errors return:Standard headers
| Header | Direction | Purpose |
|---|---|---|
Authorization: Bearer <token> | Request | Authentication |
Idempotency-Key: <uuid> | Request (mutating) | Safe retries |
X-Request-ID | Response | Trace ID; quote it in support requests |
Retry-After | Response (429, 503) | Seconds to wait before retrying |
X-Webhook-ID, X-Webhook-Timestamp, X-Webhook-Signature | Webhook delivery | HMAC-SHA256 verification |
Versioning
The API is versioned in the URL path (/v1). Breaking changes ship as /v2 with a deprecation period announced in the changelog. Additive changes (new fields, new endpoints) are released within /v1.
Health
| Endpoint | Auth required | Purpose |
|---|---|---|
GET /healthz | No | Liveness |
GET /readyz | No | Readiness (DB + signer) |
GET /metrics | No (port 9090) | Prometheus metrics |