Errors

HTTP status codes, error codes and response shapes returned by the Kustomer API, what causes each, and which to retry.

The Kustomer API uses standard HTTP status codes. 2xx means success, 4xx means something about the request needs to change, and 5xx means something went wrong on Kustomer's side.

Error shape

Most errors return an errors array. Each entry has a machine-readable code, a human-readable message, and optional meta:

400 Bad Request
{
  "errors": [
    {
      "code": "bad_request",
      "message": "Invalid request body or parameters.",
      "meta": {}
    }
  ]
}

Some endpoints return the JSON:API error object instead, with status, code, title, detail, source (pointing at the bad field) and meta. Write your error handling to accept both:

Node.js
interface KustomerError {
  code?: string
  message?: string
  title?: string
  detail?: string
  status?: string
  source?: { pointer?: string; parameter?: string }
}

async function errorMessage(res: Response): Promise<string> {
  const body = await res.json().catch(() => null)
  const err: KustomerError | undefined = body?.errors?.[0]
  if (!err) return `HTTP ${res.status}`
  const where = err.source?.pointer ?? err.source?.parameter
  return [err.code, err.message ?? err.detail ?? err.title, where && `at ${where}`].filter(Boolean).join(': ')
}

Status codes

StatusCodeCommon causesRetry?
400bad_requestInvalid JSON, missing required fields, wrong types, unknown enum values, bad custom attribute suffixNo: fix the request
401unauthorizedMissing Authorization header, missing Bearer prefix, expired or deleted key, request from outside the key's IP rangeNo: fix the credentials
403forbiddenThe key is valid but lacks a role the operation needsNo: use a key with the right role
404not_foundWrong ID, a lookup such as externalId= found nothing, or the record was deletedNo. For lookups, 404 is a normal "not found" answer.
409conflictDuplicate unique value, or a concurrent write to the same resource, such as a company hierarchy edgeSometimes: retry if the error says another write was in progress
414uri_too_longToo many IDs in a query stringNo: batch the IDs
422unprocessableThe request is valid but breaks a business ruleNo
429noneOver the org-wide or an object rate limitYes, after x-ratelimit-reset
5xxnoneA temporary problem on Kustomer's sideYes, with exponential backoff

Retry policy

2xx

429

5xx or network error

409 'in progress'

other 4xx

Response

Status

Done

Wait until x-ratelimit-reset
+ jitter, then retry

Exponential backoff
0.5s, 1s, 2s, 4s… (max 5 tries)

Fail: log code and message, fix the request

Diagram source
flowchart TD
  R[Response] --> S{Status}
  S -- "2xx" --> OK[Done]
  S -- "429" --> W["Wait until x-ratelimit-reset<br/>+ jitter, then retry"]
  S -- "5xx or network error" --> B["Exponential backoff<br/>0.5s, 1s, 2s, 4s… (max 5 tries)"]
  S -- "409 'in progress'" --> B
  S -- "other 4xx" --> F[Fail: log code and message, fix the request]

Retrying a POST that timed out can create duplicates. Make creates safe to repeat by setting externalId and looking it up before you retry. See Making requests.

Bulk job errors

Bulk jobs report errors per item instead of failing the whole request. When you send _clientRef, each result has a status of created, updated or error, and failed items include an error reason. See Bulk operations.

Debugging checklist

  1. Log the status, the errors array, the method and path. Never log the Authorization header.
  2. Reproduce the request with the Try it panel on the operation's reference page, or with kusty api ... --dry-run to see exactly what would be sent.
  3. Check the roles on the reference page against your key's roles from GET /v1/auth/tokens/current.
  4. For 400 errors, compare your body with the request schema on the reference page. Custom attribute names must end in a valid suffix.