BrightshoalDocsOpen console
MenuErrors

API basics

Errors

What a failed request returns, the codes you will meet most, and one function that reads every error body the API sends.

A request that fails returns an HTTP status of 400 or above and a JSON body. Branch on the status first, then on the code inside the body. The message, when there is one, is written for people and can change; the code is the stable part.

The body

Every error puts a string code in error, often with a message, and sometimes with fields that belong to that code:

Response 400
{
  "error": "invalid_request",
  "message": "Invalid assistant payload.",
  "issues": [
    { "code": "too_small", "path": ["config", "agent", "instructions"], "message": "Too small: expected string to have >=1 characters" }
  ]
}
Response 403
{ "error": "insufficient_role", "required": "owner", "actual": "admin" }

Schema validation failures usually add issues, listing each field that did not pass with its path. Other invalid_request errors carry only a message, so treat issues as optional.

/** The stable error code from any failed response, plus the rest of the body. */
async function readError(res) {
  const body = await res.json().catch(() => null)
  const code = body?.error ?? `http_${res.status}`
  return { status: res.status, code, message: body?.message, body, requestId: res.headers.get('x-request-id') }
}

const res = await fetch('https://api.myleadsfactory.tech/api/assistants', {
  headers: { Authorization: `Bearer ${process.env.MLF_API_KEY}` },
})
if (!res.ok) {
  const e = await readError(res)
  throw new Error(`${e.status} ${e.code}${e.message ? `: ${e.message}` : ''} (request ${e.requestId})`)
}

Codes you will meet

StatusCodeWhat it means
400invalid_requestThe body or query failed validation; issues, when present, says which field. Also returned (with only a message) when an org-wide key creates something without project_id.
400validation_errorOne named input is unusable; message names it.
401unauthorizedNo Authorization: Bearer header ("Missing or malformed Authorization header."), or the key is unknown or revoked ("Invalid or revoked API key.").
403read_only_keyA read-only key tried anything but GET, HEAD or a knowledge search.
403insufficient_roleThe route needs a role a key does not have. The body also carries required and actual.
403project_key_not_allowedThe route needs an org-wide key, and this one is pinned to a project (listing members, for example). Creating a project with a project key answers 403 invalid_request with the message A project-scoped key cannot create projects.
404not_foundNothing with that id in this key's reach. A resource in another project or organization also reads as 404, never 403.
409variesThe resource is in a state that refuses the request: conversation_ended, already_taken_over, publication_not_ready and others, listed in each guide.
422variesWell-formed but not acceptable, for example credential_rejected when a provider turns down a credential.
429rate_limitedToo many requests this minute. See rate limits.
429key_quota_exceeded, voice_busyPublic chat only: a publishable key used a budget its whole site shares. See publishable key budgets.
500internal_errorOur fault. The body carries request_id.
502, 504vendor_unavailable, vendor_timeoutA provider behind the request (telephony, a voice or model vendor) failed or timed out.
503variesA capability is not configured on this deployment, such as telephony_not_configured or knowledge_not_configured.

When a provider rejects a request outright, its status is passed through with "error": "vendor_rejected" and the provider's details.

Request ids

Every response carries an x-request-id header. From your server, send your own x-request-id and it is echoed back, so you can tie our logs to yours; otherwise one is generated. A browser on public chat can read the header but not send its own. A 500 also puts it in the body:

Response 500
{ "error": "internal_error", "request_id": "6f1c2d9e-0a7b-4c3e-9f12-5b8d0e4a7c61" }

Include it when you write to us about a failure.

Retry or not

  • 429: wait for the Retry-After seconds, then retry. Rate limits has a helper.
  • 500, 502, 503, 504: retry with exponential backoff (for example 1 s, 2 s, 4 s, then give up). A create that timed out may have succeeded, so read before you create again.
  • Any other 4xx: do not retry unchanged. Fix the request, the key or the resource state first.

Stuck on errors?