Skip to main content

Error codes

The bot API reports failures with an HTTP status code and a detailCode. The same status code can call for different handling, so branch on detailCode as well.

Some responses have no detailCode. The gateway in front checks the token, permissions, and call quota first, so a 401, 403, or 429 blocked there has no detailCode. Branch on the status code first, and look at detailCode only when it is there.

Response format​

The bot runtime API responds in RFC 7807 format.

{
"instance": "/v1/live",
"status": 404,
"title": "Not Found",
"detailCode": "OAPI_MNGR_0301",
"message": "The DJ is not broadcasting right now.",
"userId": null,
"extra": null,
"timestamp": 1788407119681
}
FieldDescription
detailCodeThe value that decides your handling. This is the contract.
messageA human-readable explanation. The wording can change, so do not branch on it. It is also not guaranteed to match your service language, so do not show it to your users — supply your own copy instead.
statusThe HTTP status code.
titleThe standard reason phrase for the status (Not Found, Forbidden, …). Do not branch on it.
instanceThe path you called.
userId · extraSlots the internal framework fills. On the bot runtime API they are always null.
timestampWhen the response was built, in epoch milliseconds.

userId and extra are not for you to use. They are still present as null, so locking your schema with additionalProperties: false breaks here. Let unknown fields through.

Errors on the event stream arrive as SSE frames. GET /v1/live/events responds as text/event-stream, so even a failed connection has a body that starts with data:. Parsing that body directly as JSON throws. Branch on the HTTP status code, and if you need the body, strip the data: prefix first.

HTTP/2 404
content-type: text/event-stream;charset=UTF-8

data:{"status":404,"detailCode":"OAPI_MNGR_0301","message":"The DJ is not broadcasting right now."}

Codes a bot will meet​

These come from every endpoint except the two under /v1/oauth. The endpoint list shows what those are.

400 — fix the request​

detailCodeWhat happenedWhat to do
OAPI_MNGR_0103The request is malformed — a broken body, a missing required field, a missing Content-Type, or the wrong method.Fix the request format. Sending the same request again gives the same result.
OAPI_MNGR_0108The message is empty, including whitespace-only.Check what you are sending. Resending the same request gives the same result.
OAPI_MNGR_0109The message is longer than 200 characters. An emoji can count as two.Shorten it.

401 — a human has to authorize again​

detailCodeWhat happenedWhat to do
noneThe token is missing or invalid. Expiry, revocation, the DJ disconnecting the integration, and app suspension all land here.Try refresh once; if it is still 401, stop and ask the DJ to authorize again.
OAPI_MNGR_0203Same as above. Within a minute of a token being revoked, it can arrive with this code.Same as above.

Do not retry a 401 forever. Once the integration is disconnected, retrying fails every time while burning through your call quota.

The reasons are not broken out because doing so would leak whether a token exists. The bot's response is re-authorization in every case.

403 — blocked by a permission or a state​

detailCodeWhat happenedWhat to do
noneThe permission this endpoint needs is missing.Re-consent is enough. The integration itself is alive.
OAPI_MNGR_0208You cannot post to this broadcast right now — chat is frozen, or the bot is chat-banned.The two cases are not distinguished. Retry shortly, and tell the DJ if it keeps failing.
OAPI_MNGR_0209The bot cannot join the broadcast. The DJ may have blocked it.Neither re-consent nor re-authorization helps. The DJ has to lift it.

Tell a 403 apart by whether it has a detailCode. Without one, a permission is missing; with one, it is a broadcast-side state (0208, 0209).

Do not lump 401 and 403 together. 401 means the DJ has to authorize from scratch, while a 403 for a missing permission just needs an extra permission. Handling them the same way tears down a perfectly good integration.

404 — not on air​

detailCodeWhat happenedWhat to do
OAPI_MNGR_0301The DJ is not broadcasting right now.This is a state, not an error. On the event stream, back off and connect again; on a read, call again shortly.

429 — too many calls​

detailCodeWhat happenedWhat to do
OAPI_MNGR_0302You are sending chat too frequently.Wait a moment and send again.

OAPI_MNGR_0302 arrives not only when your bot sends too much but also when chat in that broadcast is busy overall. The limit is shared across the broadcast, so slowing your own rate may not clear it right away.

A 429 with no detailCode is the API call quota. That response has an empty body. Parsing the body throws, so branch on the status code alone. There are two quotas.

QuotaWhen exceededWhen to call again
Per second429, no body, no Retry-AfterRetry with exponential backoff.
Per day429, no body · with Retry-AfterWait Retry-After seconds. It lasts until the next reset (midnight).

The daily quota is counted separately for the app total and for each permission, and you are blocked when either is exceeded. The x-ratelimit-daily-scope header tells you which one you hit — if present, only that permission is blocked; if absent, the whole app is.

Slow down early using the response headers​

The quota headers are attached to successful responses too. Watch these values and slow down before you get a 429.

HeaderMeaning
x-ratelimit-remainingThe per-second tokens left right now. At 0, the next request gets a 429.
x-ratelimit-replenish-rateHow many tokens are refilled per second.
x-ratelimit-burst-capacityThe ceiling for a short burst.
x-ratelimit-daily-limitThe daily call limit.
x-ratelimit-daily-remainingHow many calls are left today.
x-ratelimit-daily-resetWhen the next reset happens, in epoch seconds.
x-ratelimit-daily-scopeSent only on a daily-quota 429. The permission that was hit.

The event stream (GET /v1/live/events) has no quota headers — that path does not count against the call quota. The per-second quota is separate for each DJ integration, while the daily quota is summed across the app. Adding integrations does not raise the daily total. You can also see today's usage in the app details of the Developer Center console.

500 — server error​

detailCodeWhat happenedWhat to do
OAPI_MNGR_1002Server error.Retry with exponential backoff. Fixing the request changes nothing.

It can come from any endpoint. Unlike a 502 (OAPI_MNGR_2302, OAPI_MNGR_2303), it does not tell you where it came from, so there is one thing to do: retry with exponential backoff.

502 — try again shortly​

detailCodeWhat happenedWhat to do
OAPI_MNGR_2302Broadcast server error.Retry with exponential backoff.
OAPI_MNGR_2303Chat server error.Retry with exponential backoff.

Token endpoint errors use a different format​

POST /v1/oauth/token and POST /v1/oauth/revoke follow the OAuth standard (RFC 6749) as is. The field is error, not detailCode. The full spec is in Tokens.

{
"error": "invalid_grant",
"error_description": "code is expired"
}
errorHTTPWhat happenedWhat to do
invalid_request400A required parameter is missing.Check the request format.
invalid_client401The Client ID or Client Secret does not match.Check your credentials.
invalid_grant400The code or refresh_token expired or was already used.A code must be used within 60 seconds. If the refresh_token expired, ask the DJ to authorize again.
unsupported_grant_type400An unsupported grant_type.Use authorization_code or refresh_token.