Reference

Errors

Failures use the OpenAI error envelope and a small set of stable codes. Read the code rather than the message, because messages are written for humans and may be reworded while codes are part of the contract.

The envelope

403 Forbidden
{
  "error": {
    "message": "Access denied for this request.",
    "type": "authentication_error",
    "param": null,
    "code": "access_denied"
  }
}

All four fields are always present. param is null when no single field is at fault, and names the offending one when there is:

400 Bad Request
{
  "error": {
    "message": "The \"temperature\" parameter is recognised but not supported by this API.",
    "type": "invalid_request_error",
    "param": "temperature",
    "code": "unsupported_parameter"
  }
}
FieldPurpose
messageHuman readable. Fine to log, not something to branch on
typeError family: invalid_request_error, authentication_error, rate_limit_error or server_error
paramThe request field at fault, when one field is to blame, for example model, stream, or a nested messages[1].name. null otherwise
codeStable machine code. This is what your code should switch on

Every status and code

StatusCodeCauseWhat to do
400invalid_request_errorThe body is not valid for this API: a malformed field, a size limit, or no user messageFix the request. Retrying will not help
400streaming_not_supportedA truthy stream was sent, or any stream_options. "true" and 1 count as truthy; false and omitting it are both fineRemove the flag. Responses arrive complete
400model_not_foundmodel was missing, empty, not a string, or not wicarax-zenithSend model: "wicarax-zenith". See Models
400unsupported_parameterA recognized OpenAI field this API does not implement, such as temperature, max_tokens, tools, response_format and the rest. param names itRemove the field. It was refused, not ignored
400unknown_parameterA field this API does not recognise at all, including a misspelling like mesagesCheck the spelling against the request reference
401invalid_api_keyNo Authorization header at allSend the header. This is the only case that reports a missing credential
403access_deniedThe credential or the origin was refused, for any of several reasons. A publishable key on /v1/chat/completions is one of themSee the section below
404The path does not exist. Unknown paths answer with the server default shape rather than the envelope aboveCheck the endpoint list
413The request body exceeded about 100 KBSend less history, or shorten the message
429tenant_plan_rate_limit_exceededYour plan's rpm or rpd, counted per account across every agent and channelWait for Retry-After seconds, then retry
429auth_probe_rate_limit_exceededThe failed-credential budget for your network block. A working integration never touches itFix the credential. Retrying the same bad key spends more of the budget
5xxserver_errorA failure on our sideRetry with exponential backoff
Note

Running out of monthly requests is not an error. The call returns 200 with a short unavailability message and zero usage. See Chat Completions.

Why 403 will not tell you more

A malformed key, an unknown key, a key of the wrong class, an inactive agent and a disallowed origin all answer with the same 403 access_denied. This is deliberate. A publishable key is visible in page source, and if the response distinguished those cases, anyone could take a scraped key and confirm whether it was real, or discover which agents exist.

The response intentionally does not identify which case applied. Check the key state, agent state, key type, and configured allowed origin before retrying.

If you see 403 while integratingCheck first
From a browserThe site origin is registered exactly, including the scheme, subdomain and port
From a server or curlYou are using the secret key. A publishable key requires a browser origin
On /v1/chat/completions with a wcx_pk_ keyChat requires a secret key. A publishable key is valid on widget bootstrap, agent config and models, and on nothing that reaches the model
Everywhere, suddenlyThe key was rotated or revoked, or the agent was deactivated
On a new agentIt has no installation yet, or the installation allows no origin
A 400, not a 403, on your first callDifferent problem: you are authenticated. Check param; it is almost always a missing model or a sampling field the API refuses

Two different 429s

Both carry Retry-After in seconds and have different codes, because the right response to each is different.

  • tenant_plan_rate_limit_exceeded means requests across your account exceed its current per-minute plan allowance. Pace valid traffic before retrying.
  • auth_probe_rate_limit_exceeded means too many refused credentials came from the same network in a short time. Fix the credential or the origin first, because retrying with the same broken setup will keep failing.
Heads up

If you were seeing 403 repeatedly while debugging and it turned into 429 auth_probe_rate_limit_exceeded, you have exhausted the failure budget rather than hit a plan limit. Correct the credential, wait for the window to pass, and try once.

Note

Current plans do not configure a separate daily throughput cap. Monthly request quota is the longer-window consumption limit and is separate from these 429 responses.

Handling failures well

  • Branch on code, never on message text.
  • Respect Retry-After instead of retrying on a fixed interval.
  • Retry 5xx with exponential backoff. Never retry a 400, and never retry a 403 without changing something.
  • Show the visitor a short, human message. The raw error text is for your logs.
  • Keep failures out of the conversation transcript, or the assistant will read them back as its own words.

The official SDK maps all of this onto a single WicaraxError with status, type, code, param and retryAfter. See Official SDK.

One SDK error never appears on this page, because it never reaches the network: calling chat.send on a client built with a publishable key throws locally with code: "secret_key_required" and no status. It is a configuration mistake rather than a refusal, so isAuthError is deliberately false for it.

Tip

Branch on the code, respect Retry-After, and log the message. That handles every case on this page without a single string comparison against human-readable text.

Disclaimer

The uniform 403 is deliberate and will not be made more specific because a detailed response could be used to confirm whether a scraped key is real.