Intempt Docs
Developer DocsAPI Reference

HTTP Error Codes Reference

The HTTP status codes, error response shapes, and per-service error codes you'll see calling the Intempt API, plus how they differ by service.

Overview

Every Intempt platform service sits behind a single gateway at api.intempt.com/v1, but the services behind it don't all return errors in the same shape. This reference covers the status codes you'll see, the two error response formats in use across the platform, and the named error codes each service documents, so you can write one error handler that branches correctly instead of assuming a single format everywhere.

📘 Good to know

See Authentication for how requests are authenticated before they reach a service, and Project Limits for the requests-per-minute cap tied to your organization's tier.

Standard HTTP status codes

StatusMeaningWhere you'll see it
200SuccessAll services
201Resource createdAuthentication, Data Hub
202Accepted, processing asyncaudit (exports)
204Success, no contentAuthentication, Billing, Data Hub (typically DELETE)
303See Other (redirect)auth (OAuth/SSO callback flows)
307Temporary redirectauth (OAuth/SSO callback flows)
400Invalid request body or parametersAll services
401Missing or invalid authenticationAll services
402Payment required (insufficient balance, spend cap exceeded)payments only
403Insufficient permissions, or a policy blocks the actionAuthentication, Audit, Data Hub, Analytics, Billing, Audiences
404Resource not foundAuthentication, Billing, Audit, Data Hub, Analytics, Audiences
409Conflict (duplicate operation, blocked state)auth, payments
422Validation error (field constraints)payments
429Rate limitedGateway (all services), and payments at the application level
500Internal server errorBilling, Data Hub, Analytics
501Not implemented (intentional stub)auth (two SCIM group endpoints only, to avoid IdP error loops)
503Dependent service unavailablepayments (Stripe or metric service down)

Error response formats

Two different error body shapes exist across the platform. Which one you get depends on which service handled the request, not on the status code.

Format A: { error, message, details }

Used by payments. Defines an ErrorResponse schema in its OpenAPI spec: a machine-readable error string, a human-readable message, and an optional details object.

{
  "error": "insufficient_balance",
  "message": "Wallet balance is too low for this operation",
  "details": {
    "required": 500,
    "available": 120
  }
}

Format B: { errors: [{ message }] }

Used by the Authentication, Audit, Data Hub, Analytics and Audiences endpoints. These return a list of messages rather than a single machine-readable code:

{
  "errors": [
    { "message": "Brand abc-123 does not exist" }
  ]
}

errors is always an array, since a single request can fail more than one validation check at once:

{
  "errors": [
    { "message": "Name is required" },
    { "message": "Color must be a hex value" }
  ]
}

There's no error code or details field in this format. Don't parse or switch on the message string; it's meant for display, not for branching logic.

Data Hub endpoints have one more variant, for DELETE requests blocked by existing relations:

{
  "description": "Deletion cannot be performed because the entity has relations",
  "relations": [
    { "type": "JOURNEY", "id": "j_123", "name": "Welcome flow" }
  ]
}

Empty-body errors

Some 401 and 403 responses carry no body at all. On Data Hub and Analytics endpoints, some 401 and 403 responses return an empty body, not the errors array shown above. Check the status code first; don't assume a body is always present.

Rate limiting (429)

Rate limiting happens at two different layers, with two different response shapes.

At the gateway, requests to routes marked rate-limited (a majority of route groups, including Authentication, Billing, Analytics, Data Hub, integrations and several others) are checked against a token bucket before they ever reach the upstream service. If the bucket is empty, the gateway itself returns:

HTTP/1.1 429 Too Many Requests

(empty body)

with these response headers:

X-RateLimit-Replenish-Rate: 30
X-RateLimit-Burst-Capacity: 50
X-RateLimit-Requested-Tokens: 1

The upstream service never sees the request in this case, so there's no error/message body to parse, only the status and headers.

Inside payments, a 429 can also come from the service itself (not the gateway), and in that case it uses payments' own Format A error body like any other payments error.

Your organization's plan tier sets the requests-per-minute cap the gateway enforces; see Project Limits for the exact numbers per tier.

Per-service error codes

Auth

The auth service's OAuth endpoints (/oauth/token, /oauth/revoke) are a documented exception: on a 400, they return a narrower OAuthErrorResponse shape, { error, error_description }, matching the OAuth 2.0 spec rather than the platform's general format.

CodeStatusContext
authorization_pending, access_denied, expired_token, invalid_grant, invalid_request, unsupported_grant_type400OAuth device-flow and token-exchange errors, returned as OAuthErrorResponse

Every other auth error (missing auth, forbidden, not-found, validation) uses Format B — a plain errors[].message string, not a fixed code list. For example:

// API key not found
{ "errors": [{ "message": "API key not found" }] }
// Missing required field
{ "errors": [{ "message": "Name is required" }] }

Audit

Uses Format B — no fixed error code list, each message is generated per failure. For example:

// Missing from/to
{ "errors": [{ "message": "Export requires 'from' and 'to' date range" }] }
// Nonexistent export job ID
{ "errors": [{ "message": "Export job not found: <id>" }] }

A nonexistent export job ID returns 400, not 404.

Payments

Wallet errors

CodeStatusDescription
insufficient_balance402Wallet balance is too low for the requested operation
subscription_paused409Cannot deduct while the subscription is paused
SPEND_CAP_EXCEEDED402Org-level or member-level monthly spend cap would be exceeded
attribute_budget_exceeded402Monthly budget for this AI attribute is exhausted
attribute_budget_paused402This AI attribute has been paused (budget exceeded)
already_refunded409The transaction has already been refunded
transaction_not_found404No transaction found with the given ID
operation_not_found404The operationKey does not exist in the catalog

Subscription errors

CodeStatusDescription
no_active_subscription404The org does not have an active subscription
subscription_not_found404No subscription cache found for the org
invalid_seat_type400The seat type is not valid for the current tier
minimum_seats400Cannot remove the last full seat
feature_disabled404The requested feature flag is not enabled

Data Hub, Analytics, Audiences

These three services don't define a fixed error code list. They return the errors[].message array shown under Format B above, with the message text generated per failure rather than drawn from an enum.

Retry guidance

  • 4xx errors aren't retryable. Fix the request instead.
  • 429, gateway-level: back off and retry, using the X-RateLimit-* headers to pace your next request.
  • 503 (payments): the circuit breaker for a dependent service (Stripe or metric) is open. Retry after 30 seconds.
  • 500: retry with exponential backoff, up to 3 attempts.

Use cases

  1. Writing one API client wrapper that checks the response shape (error/message/details vs. errors[]) before deciding how to surface an error to a user, since the field names differ by service.
  2. Detecting gateway-level rate limiting by checking for a 429 with an empty body and the X-RateLimit-* headers, versus an application-level 429 from payments that has a JSON body.
  3. Handling 402 insufficient_balance from payments by prompting the org to top up credits, using the details.required and details.available fields to show exactly how much is missing.
  4. Handling a 400 deletion-blocked response from a Data Hub endpoint by listing the blocking relations to the user instead of just showing the generic description string.
  5. Retrying a payments call after a 503, waiting the documented 30 seconds before the next attempt instead of retrying immediately.
  6. Parsing an OAuth 400 from the auth service's token or revoke endpoints using error/error_description, not the platform's general error/message/details shape.
  7. Distinguishing a payments 422 validation error (field constraints) from a 400 (malformed request) when deciding whether to show a form-field error or a generic failure message.
  8. Treating a bodyless 401/403 from Data Hub or Analytics as a pure status-code check, since there's no message field to log or display in that case.

Where to go next

  • Authentication for how API keys and JWTs are validated before a request reaches any service.
  • Project Limits for the per-tier requests-per-minute cap enforced at the gateway.
  • Track Data for a worked example of the 200/400 responses on a specific endpoint.

On this page