Error Contract
Handle structured API errors without parsing messages
API failures use a structured envelope:
{
"code": "FORBIDDEN",
"message": "Cannot modify transaction - attached to published statement",
"issues": [],
"context": { "lockReason": "statement" }
}codeis the stable error class for program behavior.issuescontains field or domain-specific validation details.contextcontains typed recovery information such as lock reasons, supported archive outcome, next action, or retry delay. It is omitted when the error carries no recovery information.messageis display text and must not be parsed.linksappears only on strict query-validation errors and points to the public interactive reference (docs) and OpenAPI document (schema). It is never an empty object.
Codes And HTTP Status
code values on the wire map to HTTP status as follows:
code | HTTP status |
|---|---|
BAD_REQUEST | 400 |
UNAUTHORIZED | 401 |
FORBIDDEN | 403 |
NOT_FOUND | 404 |
METHOD_NOT_SUPPORTED | 405 |
CONFLICT | 409 |
RATE_LIMITED | 429 |
UNPROCESSABLE_CONTENT | 422 |
INTERNAL_SERVER_ERROR | 500 |
NOT_IMPLEMENTED | 501 |
SERVICE_UNAVAILABLE | 503, or 504 when a database connection is interrupted |
Branch on code, not on raw status, and treat unknown codes as
non-retryable failures.
Lock Context
Lock failures are 403 FORBIDDEN and identify the lock in context:
- Mutation locks return
context.lockReason, either"statement"(attached to a published owner statement) or"period"(blocked by books closing). Period locks also includecontext.booksClosedAt, the first open date inYYYY-MM-DDformat. - Delete locks on referenced entities return
400 BAD_REQUESTwithcontext.lockReasons(an array of human-readable reason strings) andcontext.suggestedOnLocked(for example"archive") when the operation supports an alternative outcome such as archiving instead of deleting.
Other domain errors can include nextAction or supportAction. Present only
the actions returned or documented for that operation. See
Requests, Locks & Issues.
Query Errors
Query parameters are strict. An unsupported parameter returns 400 with
links to the API schema and an issue that identifies the operation schema
location. Remove the parameter; it is not silently ignored.
{
"code": "BAD_REQUEST",
"message": "Invalid query parameters. Compare the request with the OpenAPI schema and remove unsupported parameters.",
"issues": [
{
"message": "Unrecognized key: \"foo\"",
"schema": "#/paths/transactions/get"
}
],
"links": {
"docs": "https://api.vrplatform.app/spec",
"schema": "https://api.vrplatform.app/openapi.json"
}
}Rate Limits
Webhook subscription, verification-test, and replay operations enforce the
limits documented in Webhooks. An excess
request returns RATE_LIMITED with HTTP 429 and Retry-After. Other API
surfaces do not currently enforce application-level quotas.
Availability
Transient database connectivity exhaustion is returned as
SERVICE_UNAVAILABLE. Retry only idempotently and with bounded backoff. A
domain-specific service-unavailable response can include structured delay or
pending-resource context.
See Error handling and retry policy and Requests, Locks & Issues.
