Errors
Every error response has the same shape. Codes are stable strings you can branch on. Messages are for people and can change.
{ "error": { "code": "invalid_request", "message": "limit must be at most 200", "details": [], "request_id": "req_01JA8X4K3T9V2C7M5N6P8R0S1W" }}Every response, successful or not, carries a Request-Id header. Include it when you contact support.
| Code | HTTP | Meaning | What to do |
|---|---|---|---|
invalid_request | 400 | A parameter or body failed validation. details lists the problems. | Fix the request. |
unauthorized | 401 | The key is missing, malformed, revoked or unknown. | Check the Authorization header. |
key_environment_mismatch | 401 | A test key was used against production, or the reverse. | Use test keys with sandbox and live keys with production. |
forbidden | 403 | The key lacks the scope, or the role cannot do this. | Create a key with the needed scope. |
not_found | 404 | The resource does not exist, or belongs to another organization. | Check the id. |
idempotency_conflict | 409 | The same idempotency key was reused with a different body. | Use a new key, or resend the original body. |
payload_too_large | 413 | The body is over 1 MB. | Send smaller batches. |
invalid_event | 422 | The event does not match the schema. | See the event’s details. |
invalid_signature | 422 | The event signature did not verify. | Check the key and the canonical form. |
pan_detected | 422 | A card number was found in the event. | Remove it. HalvorAI never stores card numbers. |
environment_mismatch | 422 | The event’s environment differs from the key’s. | Send test events with test keys. |
unknown_source | 422 | The event names a source that is not registered. | Register the source first. |
unprocessable | 422 | The request is valid but cannot be applied in the current state. | Read the message. |
rate_limited | 429 | Too many requests. | Wait for the Retry-After seconds. |
internal_error | 500 | Something failed on our side. | Retry with backoff, and report the request_id if it persists. |
unavailable | 503 | The service is temporarily unavailable. | Retry with backoff. |
Per-event errors in a batch
Section titled “Per-event errors in a batch”POST /v1/events returns 202 for a batch even when some events are bad. Each event gets its own result, and a rejected one
carries an error with the same codes, for example:
{ "id": "evt_01JA8Q...", "status": "rejected", "error": { "code": "invalid_signature", "message": "..." } }Check every result, not just the HTTP status. See send events.
Retrying
Section titled “Retrying”Retry 429, 500 and 503 with exponential backoff and jitter. Do not retry other 4xx codes unchanged: they will fail
the same way. Sending an event again is safe, because the event id is its idempotency key.