VRPlatformVRPlatform

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" }
}
  • code is the stable error class for program behavior.
  • issues contains field or domain-specific validation details.
  • context contains typed recovery information and diagnostics such as IDs, refs, invalid values, provider responses, causes, lock reasons, supported archive outcomes, next actions, or retry delays. It is omitted when the error carries no structured details.
  • message is static display text for one error condition. It never embeds UUIDs, refs, emails, indexes, raw values, or other request-specific data and must not be parsed.
  • retryable appears as true only when the server explicitly classifies the same idempotent operation as safe to retry. Its absence means do not retry unchanged unless the operation documents another recovery rule.
  • Retry-After can accompany a retryable error when the server has a concrete delay. Respect the header before retrying the same idempotency identity.
  • links appears 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:

codeHTTP status
BAD_REQUEST400
UNAUTHORIZED401
FORBIDDEN403
NOT_FOUND404
METHOD_NOT_SUPPORTED405
CONFLICT409
RATE_LIMITED429
UNPROCESSABLE_CONTENT422
INTERNAL_SERVER_ERROR500
NOT_IMPLEMENTED501
SERVICE_UNAVAILABLE503, or 504 when a database connection is interrupted
INVALID_PLAID_CONNECT_MODE400
PLAID_CONNECTION_NOT_FOUND404
PLAID_CONNECT_IN_PROGRESS409
PLAID_CONNECT_CONFIGURATION_CHANGED409
PLAID_CONNECT_EXPIRED410
PLAID_CONNECT_REQUIRES_NEW_LINK422
PLAID_PROVIDER_UNAVAILABLE503

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 include context.booksClosedAt, the first open date in YYYY-MM-DD format.
  • Delete locks on referenced entities return 400 BAD_REQUEST with context.lockReasons (an array of human-readable reason strings) and context.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.

Plaid Connect Recovery Context

PLAID_CONNECT_REQUIRES_NEW_LINK returns one strict recovery directive:

{ "restartMode": "create" }

or:

{
  "restartMode": "replace",
  "connectionId": "6a4fb5d4-9822-46dc-9812-9e6742fda0bc"
}

Create recovery never includes connectionId; replace recovery always does. Provider failures can add plaidErrorCode, plaidErrorMessage, or plaidRequestId to the same context. Use the recovery directive for the next POST /plaid/connect and do not infer a different mode from client state.

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. The API uses bounded retries for compiled database selects after known connection failures, including an established connection being lost, but does not replay mutations or raw or unknown queries after an ambiguous connection loss. Callers should 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.

On this page