跳到正文
CalabDocs

开发者文档目前仅提供英文版。

Errors and limits

The REST error format, common bot error codes and what to do about them, rate and plan limits, retries and idempotency.

Error format

Every non-2xx response carries an ApiError body:

JSON
{
  "code": "ERROR_CODE_FORBIDDEN",
  "message": "not available for bots",
  "field": "",
  "reason": "BOT_NOT_ALLOWED"
}
FieldMeaning
codea code from the ErrorCode enum — branch on it
messagetext for logs, not for parsing: the wording may change
fieldthe request field (lowerCamelCase) for ERROR_CODE_VALIDATION and some conflicts
reasona refinement when a code has several causes: BOT_NOT_ALLOWED, PLAN_LIMIT, FEATURE_DISABLED…
used · limitthe counter and the cap, when the error is about a limit

The full code list is in Error codes.

Common bot errors

HTTPcode / reasonMeaningWhat to do
400ERROR_CODE_BAD_REQUESTmalformed JSON or parametersfix the request
401ERROR_CODE_UNAUTHENTICATEDno token, or it is wrong, revoked or reissuedstop and get a new token
403ERROR_CODE_FORBIDDEN, reason: "BOT_NOT_ALLOWED"a route for people onlydon't retry — see what is closed to bots
403ERROR_CODE_FORBIDDENa missing permission; message names itgive the bot a role or an override
403ERROR_CODE_BOT_BLOCKEDthe person blocked the botstop writing to that person
403ERROR_CODE_WORKSPACE_SUSPENDEDthe workspace is suspended: writing is refused, reading workswait until it is lifted
404ERROR_CODE_NOT_FOUNDno such object — or it is hidden from the botcheck VIEW_ROOM / VIEW_BOARD and overrides
409ERROR_CODE_CONFLICT, reason: "PLAN_LIMIT"a plan limit (used / limit)free up room or change the plan
409ERROR_CODE_CONFLICT, reason: "REACTION_LIMIT"at most 3 different reactions per member per messageremove a reaction
409ERROR_CODE_CONFLICT, reason: "FEATURE_DISABLED" (field)a board feature is off and the request sets its fielddrop the field or turn the feature on
409ERROR_CODE_ROOM_FULLthe voice room is fullwait
413ERROR_CODE_FILE_TOO_LARGE · …_FILE_QUOTA_EXCEEDED · …_PAYLOAD_TOO_LARGEsize or quotashrink the file
422ERROR_CODE_VALIDATION (field)an invalid field valuefix the field
429ERROR_CODE_RATE_LIMITED + Retry-Aftera rate limitwait Retry-After seconds and retry with the same nonce
503ERROR_CODE_UNAVAILABLEa temporary failureretry with backoff

Limits

WhatValue
Bot requests30 per second (burst 30)
Bot messages20 per minute across all rooms and DMs, plus the common 5 messages per 5 s per room
New DMs10 at once, 30 per hour
File uploads30 at once, 120 per hour; size and quota by plan
A messageup to 4000 characters, up to 20 attachments, nonce up to 64 characters
Commandsup to 100, name up to 32 characters, description up to 256
Gateway1 socket per token, frames up to 64 KiB
Bots per workspaceby plan: Free 1, Team 5, Business 20; a bot also takes a member seat

Requests per second and messages per minute are plenty for integrations but not for mass mailing. On a self-hosted server the operator can change the bot limits with BOT_RATE_PER_SEC and BOT_MESSAGES_PER_MIN. All limits: Limits.

Retries and idempotency

  • 429 — wait Retry-After seconds and retry. The SDK does this itself up to maxRetries times.
  • 5xx and network drops — retry with exponential backoff.
  • Messages — send them with a nonce: a retry with the same nonce returns the message already created (200) instead of a duplicate.
  • Forms — submit with a stable UUID nonce: identical submissions create one task.
  • Events (gateway, webhook) may arrive twice — dedupe by delivery id, seq or press id.
  • 4xx other than 429 — don't retry without changing the request.
async function call(url, init, tries = 5) {
  for (let i = 0; ; i++) {
    const res = await fetch(url, init);
    if (res.status !== 429 && res.status < 500) return res;
    if (i === tries - 1) return res;
    const wait = Number(res.headers.get('Retry-After')) || 2 ** i;
    await new Promise((r) => setTimeout(r, wait * 1000));
  }
}

FAQ

The bot doesn't see a room or messages. It lacks VIEW_ROOM: the room is private, restricted (only through an override on it), or the bot's role grants no access. Check the roles and the room's overrides.

The bot doesn't answer /cmd. Nobody registered the command, or several bots in the room did — write /cmd@username. Messages written by bots are never commands.

Do I need the gateway if I have a webhook? No. A webhook bot works through REST and the webhook and counts as online while it makes requests. Voice events, though, arrive only over the gateway.

Two processes with one token? No — the second pushes the first out. To scale, use a webhook with processing by id, or several bots.

Does the bot hear itself in a call? No: LiveKit never sends a participant its own tracks. The SDK skips the bot's own messages and reactions by default (receiveOwn: true turns them on).