Errors and rate limits
Every error response has the same shape, with a stable code your client can switch on:
{
"error": {
"code": "rate_limited",
"message": "Too many requests for this API key",
"retryable": true,
"retry_after_seconds": 60
}
}
Error codes
| Code | HTTP | Retry? | Meaning |
|---|---|---|---|
unauthorized | 401 | no | The key is missing, malformed, unknown, or revoked. |
forbidden | 403 | no | The key is valid but does not have the scope this endpoint needs. |
invalid_request | 400 | no | The body or query failed validation. message says what. |
not_found | 404 | no | No such conversation for this key's inbox, or it belongs to another user. |
conversation_closed | 409 | no | The conversation was resolved or closed by the team. Start a new one. |
rate_limited | 429 | yes | Wait retry_after_seconds, then retry. |
account_inactive | 402 | no | The TrolleyChat account's trial ended without a subscription. |
upstream_error | 502 | yes | The model or a dependency failed. Retry with the same message_id. |
Only rate_limited and upstream_error are worth retrying. Retry with backoff and the same message_id, so a reply that was slow rather than failed is never produced twice.
Rate limits
| Scope | Limit |
|---|---|
| Per API key | 300 requests per minute |
| Per user | 30 conversation starts per minute |
| Per conversation | 30 messages per minute |
A reply counts as one AI reply against the inbox's plan, the same as a widget reply.