Errors
Every API error code referenced below.
Error response shape
Non-2xx responses are JSON.
{
"error": "Human-readable message"
}Status codes
| HTTP | Typical error | Cause | Remedy | Retryable |
|---|---|---|---|---|
| 400 | Bad Request | Request body failed validation. | Fix the payload. Check endpoint reference. | No |
| 401 | Unauthorized | API key missing, malformed, or revoked. | Send Authorization: Bearer hm_live_... | No |
| 403 | Forbidden | Valid credentials, insufficient permission or plan. | Upgrade plan or check agent access. | No |
| 404 | Not Found | Agent or resource does not exist. | Verify the agent ID and endpoint URL. | No |
| 413 | Payload Too Large | Message over 4,000 characters. | Shorten the message body. | No |
| 429 | Rate limit exceeded | Quota or rate limit hit. | Back off and retry after Retry-After. | Yes, with backoff |
| 500 | Internal Server Error | Unhandled server-side failure. | Retry with exponential backoff. | Yes, with backoff |
| 502 | Bad Gateway | Upstream model call failed. | Retry with backoff. | Yes, with backoff |
| 503 | Service Unavailable | Service temporarily overloaded. | Retry with backoff. | Yes, with backoff |
Retry guidance
Treat the Retryable column as authoritative.
import time
import random
import requests
RETRYABLE = {429, 500, 502, 503}
def request_with_retry(method, url, headers=None, json=None, max_attempts=5):
for attempt in range(max_attempts):
resp = requests.request(method, url, headers=headers, json=json)
if resp.status_code < 400 or resp.status_code not in RETRYABLE:
return resp
retry_after = resp.headers.get("Retry-After")
delay = float(retry_after) if retry_after else min(2 ** attempt, 30) + random.random()
time.sleep(delay)
return respRate limits
Limits apply to chat and handoff-ticket requests, checked independently.
| Scope | Default limit | Window |
|---|---|---|
| Per IP | 30 requests | 60s |
| Per session | 20 requests | 60s |
| Per agent | 120 requests | 60s |