Rate limiting

Limits apply per API key. Two dimensions determine the limit that applies to a given request: Endpoint type — message send endpoints (POST /v1/messages) have tighter limits than read or list endpoints. Agent management and billing queries each have their own bands. Workspace tier — as your channel tier increases, rate limits on that channel’s send endpoint increase automatically. No new API key is required — limits are applied to your existing key when the tier upgrade takes effect.

Response headers

Every API response includes headers that show your current limit state for that endpoint:
HeaderMeaning
X-RateLimit-LimitMaximum requests allowed in the current window
X-RateLimit-RemainingRequests remaining in the current window
X-RateLimit-ResetUnix timestamp when the window resets
When the limit is exceeded, the response is 429 Too Many Requests:
error
string
rate_limit_exceeded
retry_after
number
Seconds until the rate limit window resets.

Handling 429s

Back off with exponential jitter — avoid tight retry loops that compound failures. A pattern that works well:
const delay = Math.min(baseDelay * 2 ** attempt, maxDelay) + Math.random() * jitter;
await sleep(delay);
For high-volume sends, use the retry_after value from the response rather than guessing the backoff interval.
Current rate limits for your workspace tier and endpoint types are visible in the API section of your dashboard. Enterprise limits are negotiated via support.

Idempotency

For POST /v1/messages and other mutating routes, send an idempotency key so retries after timeouts do not create duplicate sends.
LocationExample
HeaderIdempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Body"idempotency_key": "550e8400-e29b-41d4-a716-446655440000"
Use one key per logical operation. Pass the key in the Idempotency-Key header on mutating routes (POST /v1/messages, billing top-ups).
Retrying a mutating request without an idempotency key after a timeout may result in a duplicate send. Always include a key for message sends.

Dates and times

  • All timestamps use ISO 8601 in UTC: 2026-03-28T14:30:00Z
  • Durations in payloads use string form where documented: 3600s, 24h

Pagination

List endpoints (webhooks, transactions, invoices) return cursor- or offset-based results per route documentation. Prefer cursor pagination for large or frequently changing datasets — offset results may skip or duplicate items if records are added or removed between pages.
cursor
string
Opaque token returned in the previous response. Pass as a query parameter to fetch the next page.
has_more
boolean
true when additional pages exist.

Error envelope

All errors share a consistent shape — see Errors for the full status code catalog.
{
  "error": "validation_error",
  "message": "content.text is required for MESSAGE type",
  "details": [
    { "field": "content.text", "message": "Required field missing" }
  ]
}

Correlation

Responses include an X-Request-Id header for support correlation. Include this value when filing bug reports or contacting support.

Content negotiation

Default content type is application/json. Binary or PDF responses (for example invoice downloads) return their Content-Type in the response header.