messageblueDOCS
Open Toldo

API reference

Errors and limits

One error shape, a closed set of tags, and the daily ceilings a client has to expect.

One shape

Every non-2xx answer from Toldo is the same JSON object.

{
  "error": {
    "tag": "QuotaExceeded",
    "message": "This assistant has reached its daily message limit. Please try again tomorrow.",
    "detail": "live"
  }
}
Field Notes
tag A member of the closed set below. Branch on this, never on the message
message For a person to read. Wording can change without notice
detail Optional machine-readable specifics — which quota, which guard

The tags

The set is closed: a new failure mode gets a new tag rather than a widened message on an old one.

Tag Status What it means for a client
Invalid 400 The body or the parameters did not parse. Fix the request; retrying is pointless
Unauthenticated 401 Missing, malformed, unknown or other-assistant key. All four look identical
Forbidden 403 Authenticated, but not yours
NotFound 404 No such thing
Conflict 409 The thing is in the wrong state — for the messages endpoint, the assistant is not live
QuotaExceeded 429 A daily cap was reached. detail says which
NotConfigured 501 This installation has no credentials for that feature
Upstream 502 Something Toldo depends on failed

Limits

There is no per-second rate limit on the messages endpoint. What there is instead:

A daily cap per assistant. Every reply a live assistant makes counts against it, across every place it lives — the API, the website bubble, Telegram, everywhere. Reaching it returns 429 with detail: "live", and the message is written for the person who is waiting, not for you: “This assistant has reached its daily message limit. Please try again tomorrow.”

A site-wide daily ceiling across every assistant on the installation. This one exists to bound the bill and is normally far away, but it is real. Reaching it also returns 429, with a detail that begins global-.

A burst limit per conversation of 12 turns a minute, inside the conversation itself. Sending a long backlog of messages under one conversation_id as fast as you can will hit it.

Caps are counted per UTC day and reset at midnight UTC. They are set per installation, so ask whoever runs yours what the numbers are.

Handling it

const response = await fetch(url, { method: "POST", headers, body });
if (!response.ok) {
  const { error } = (await response.json()) as {
    error: { tag: string; message: string; detail?: string };
  };
  switch (error.tag) {
    case "QuotaExceeded":
      return retryTomorrow(error.detail); // never a tight retry loop
    case "Conflict":
      return tellSomeoneTheAssistantIsPaused();
    case "Unauthenticated":
      return alertOnCredentials(); // the key, not the caller
    default:
      throw new Error(`${error.tag}: ${error.message}`);
  }
}

A 502 is the one worth a single retry, after a pause. 400, 401, 403, 404 and 501 will not change if you send the same request again.

Next: The website bubble.

Loading the index…

to move to openesc to close