Error Handling
All errors follow a consistent JSON format with a machine-readable code, human-readable message, and a request ID for debugging.
Error Format
{
"error": {
"code": "validation_error",
"message": "Profile must include email, phone, or external_id",
"field": "email",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}| Field | Type | Description |
|---|---|---|
code | string | Machine-readable error code |
message | string | Human-readable description |
field | string | null | Which field caused the error (validation errors only) |
request_id | string | Unique request ID — include this when contacting support |
Error Codes
| Code | HTTP Status | Description |
|---|---|---|
validation_error | 400 | Invalid or missing input fields |
unauthorized | 401 | Missing or invalid API key |
forbidden | 403 | API key lacks the required scope |
not_found | 404 | Resource does not exist |
rate_limit_exceeded | 429 | Too many requests — see Rate Limiting |
database_error | 500 | Internal storage error |
upstream_error | 502 | An internal service failed to respond |
internal_error | 500 | Unexpected server error |
Handling Errors
4xx errors are client errors — fix the request and retry.
429 errors — wait for the duration in the Retry-After header, then retry.
5xx errors — retry with exponential backoff. If the error persists, include the request_id when contacting support.
Idempotency
For POST /v1/events, include an idempotency_key in the request body. If a request with the same key has already been processed, the API returns the original response instead of creating a duplicate.
{
"event_type": "placed_order",
"profile": { "email": "[email protected]" },
"properties": { "order_id": "ORD-1234", "value": 99.99 },
"idempotency_key": "order-ORD-1234"
}