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.