Skip to content

Error handling

Invora APIs use standard gRPC status codes, transcoded to HTTP status codes on the REST surface. The body is a google.rpc.Status: a numeric code, a human-readable message, and an optional details array carrying typed, @type-tagged payloads (most commonly google.rpc.BadRequest for field-level validation errors).

Status codes

gRPC code HTTP When
OK (0) 200 Success
INVALID_ARGUMENT (3) 400 Missing required fields, invalid enum values, malformed input, business-rule validation failure
UNAUTHENTICATED (16) 401 Missing or invalid access token
PERMISSION_DENIED (7) 403 Missing scope, wrong tenant, or missing plan entitlement
NOT_FOUND (5) 404 Document key, party, plan, etc. does not exist
ALREADY_EXISTS (6) 409 Key collision on create
ABORTED (10) 409 concurrencyStamp mismatch — another write happened
FAILED_PRECONDITION (9) 400 Operation invalid for the current state (e.g. editing a frozen document)
RESOURCE_EXHAUSTED (8) 429 Rate or plan limit reached
UNAVAILABLE (14) 503 Upstream/temporary failure (e.g. a tax authority) — retry with backoff
INTERNAL (13) 500 Unexpected server error

Error body shape

A validation failure (INVALID_ARGUMENT) returns field-level detail in a google.rpc.BadRequest:

curl -X POST https://gateway.invora.app/api/v2/documents \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "changes": { "content": { "invoice": {} } } }'
Response (400 validation error)
{
  "code": 3,
  "message": "invalid argument",
  "details": [
    {
      "@type": "type.googleapis.com/google.rpc.BadRequest",
      "fieldViolations": [
        { "field": "changes.content", "description": "invoice content is required" }
      ]
    }
  ]
}
  • code is the numeric gRPC status (3 = INVALID_ARGUMENT).
  • message is a short, human-readable summary.
  • details[].fieldViolations[] lists each offending field (a path into your request) and a description of what went wrong.

Other detail types may appear (e.g. google.rpc.ErrorInfo for machine-readable reason/domain), all tagged by their @type. Read details by their @type; do not parse the free-text message.

Authentication & authorization errors

Response (401)
{ "code": 16, "message": "unauthenticated" }

A missing or expired token returns UNAUTHENTICATED (401); a valid token without the required scope or entitlement returns PERMISSION_DENIED (403). See Authentication for tokens and scopes.

Concurrency

A stale concurrencyStamp on a write returns ABORTED (409). Re-read the resource, take the fresh stamp, and retry once. See Entity versioning.

Retry strategy

Error Retry? Strategy
UNAVAILABLE (503) Yes Exponential backoff (e.g. 1s, 2s, 4s, 8s)
RESOURCE_EXHAUSTED (429) Yes Back off and retry after a delay
ABORTED (409, concurrency) Yes Re-read, take the fresh stamp, retry once
INTERNAL (500) Maybe Retry once; if it persists, contact support
INVALID_ARGUMENT, NOT_FOUND, PERMISSION_DENIED, … No Fix the request and resubmit