SteveSteve

Error Handling

Error envelopes, status codes, request IDs, and throttling behavior across Steve's external API.

Every /api/v1 route uses RFC 9457 Problem Details for non-2xx responses.

Steve returns RFC 9457 Problem Details as application/problem+json across workflow, job, company, submission, fraud, and webhook-test endpoints.

Example:

{
  "type": "https://docs.steve.openloyalty.io/errors/validation-error",
  "title": "Unprocessable Content",
  "status": 422,
  "detail": "Validation failed",
  "instance": "/api/v1/workflows/receipt-ocr/sessions",
  "requestId": "req_pY3n7TsQ4rL8mV2bX9aK",
  "errors": [
    {
      "field": "fileCount",
      "message": "is required",
      "code": "required"
    }
  ]
}

Field meanings:

FieldMeaning
typeMachine-readable error category URI
titleShort HTTP-status summary
statusHTTP status code
detailHuman-readable explanation
instanceRequest path
requestIdCorrelation id for support and debugging
errorsOptional field-level validation errors

Request IDs

All external routes support caller-supplied request IDs:

  • send X-Request-Id on the request
  • Steve echoes it back as Request-Id
  • if you omit it, Steve generates one

Use the Request-Id value when escalating API issues.

Common error type slugs

Problem-detail routes use type URIs in the form:

https://docs.steve.openloyalty.io/errors/{slug}

Common slugs:

SlugMeaning
bad-requestMalformed JSON or invalid request body
unauthorizedMissing, malformed, or invalid API key
forbiddenRevoked key or insufficient access
key-not-company-scopedKey is valid but cannot use company-scoped endpoints
not-foundWorkflow, session, submission, or fraud match not found
conflictResource is in the wrong state for the requested transition
goneSession expired
validation-errorRequest body failed field-level validation
files-missingRequired files were not uploaded before submit
rate-limit-exceededCaller exceeded the configured limit
internal-errorUnexpected server-side failure

Status code guide

StatusWhere it appearsMeaning
200Reads and most state transitionsRequest succeeded
201Create workflow sessionSession created
400Any external routeMalformed JSON or invalid request shape
401All authenticated routesMissing or invalid credentials
403All authenticated routesRevoked key, wrong key owner, or missing company scope
404Workflow, job, submission, and fraud routesResource not found
409Submit, approve, and similar transitionsResource state conflict
410Session submit flowSession expired
422Validation-heavy routesSemantically invalid request
429All external routesRate limit exceeded
500Any routeUnexpected failure

Rate-limit buckets

Steve enforces fixed one-minute windows per API key.

BucketEndpointsLimit
readGET /api/v1/companies, GET /api/v1/jobs/{sessionId}, workflow reads, submission reads, fraud reads300 req/min
sessionPOST /api/v1/workflows/{slug}/sessions60 req/min
submitPOST /api/v1/workflows/{slug}/sessions/{sessionId}/submit20 req/min
writesubmission approve, reject, cancel, and fraud resolve actions60 req/min
webhook_testPOST /api/v1/webhooks/test10 req/min

Successful responses include:

  • X-RateLimit-Limit
  • X-RateLimit-Remaining
  • X-RateLimit-Reset

429 responses also include:

  • Retry-After

Retry guidance

Use this policy:

  1. Do not retry ordinary 4xx responses.
  2. Retry 429 after waiting for Retry-After.
  3. Retry transient 5xx failures with exponential backoff and jitter.
  4. Use clientSubmissionId when creating sessions so create retries stay idempotent.

Practical advice

  • Keep headroom under published limits instead of running exactly at the ceiling.
  • Prefer webhook delivery over tight polling loops.
  • Treat 422 as a request-contract issue, not a transient server issue.
  • On manual review actions, refetch the submission after a successful write if your UI needs the latest state.
  • Expect camelCase fields in every success and error body, and ISO 8601 strings for body timestamps.

On this page