API
Error Handling
Error Response Format
All error responses follow this shape:
{
"error": "Human-readable error message",
"code": "ERROR_CODE"
}| Field | Type | Description |
|---|---|---|
error | string | Human-readable error description |
code | string | Machine-readable error code for programmatic handling |
Error Codes
| Code | HTTP Status | Description |
|---|---|---|
UNAUTHORIZED | 401 | Missing or invalid embed token |
FORBIDDEN | 403 | Token is valid but the requesting domain is not allowed |
NOT_FOUND | 404 | Bot not found or disabled |
RATE_LIMITED | 429 | Too many requests for this (botId, domain) pair |
MESSAGE_CAP_EXCEEDED | 429 | Monthly message limit reached for the organization's plan |
INVALID_REQUEST | 400 | Malformed request body or missing required fields |
INTERNAL_ERROR | 500 | Unexpected server error |
SERVICE_UNAVAILABLE | 503 | Service temporarily unavailable |
MESSAGE_CAP_EXCEEDED
When the monthly message cap is exceeded, the error response includes additional fields:
{
"error": "MESSAGE_CAP_EXCEEDED",
"code": "MESSAGE_CAP_EXCEEDED",
"tier": "free",
"limit": 100,
"resetAt": "2026-03-01T00:00:00.000Z"
}| Field | Type | Description |
|---|---|---|
tier | string | The organization's plan name |
limit | number | Monthly message limit for the plan |
resetAt | string | ISO 8601 timestamp when the limit resets |
Security
The runtime validates requests in this exact order:
- Parse the
Originheader - Verify the embed token signature and expiration
- Compare the token's
domainclaim to theOriginhost - Check the database allowlist for
(botId, domain) - Apply rate limits per
(botId, domain)
Auth errors always return a generic "Unauthorized" message to prevent information leakage about which bots or domains exist.