Errors & Idempotency
Every endpoint returns a stable JSON envelope on failure. Status codes follow the standard HTTP conventions; error strings are short, machine-readable identifiers rather than free-form messages so callers can branch on them safely.
Error Envelope
The error field is a stable identifier (e.g. invalid_signature, rate_limited, scope_required:leads:write). Validation errors surface a comma-separated summary of the failed Zod constraints.
Status Codes
| Name | Type | Description |
|---|---|---|
200 OK | success | Request succeeded. POST returns 201 for inserted leads, 200 for patched/deduped. |
201 Created | success | New lead inserted via POST. |
400 Bad Request | error | Malformed JSON, missing/invalid Zod field, or empty PATCH body. The error string describes the failure. |
401 Unauthorized | error | Missing or invalid auth headers. Codes: missing_auth, invalid_public_key, invalid_signature, signature_expired, invalid_bearer. |
403 Forbidden | error | Authenticated but lacking the required scope. Format: scope_required:<scope>. |
404 Not Found | error | Endpoint disabled by feature flag, OR resource does not exist within your account. Returned generically to prevent cross-account probing. |
405 Method Not Allowed | error | Unsupported HTTP method for this route. |
409 Conflict | error | A concurrent write changed the lead between read and write. Safe to retry. |
429 Too Many Requests | error | Rate limit exceeded. Honor the Retry-After header (seconds). |
500 Internal Server Error | error | Unexpected server failure. Safe to retry with exponential backoff. |
Idempotency
POST /api/v1/insertion/leads is idempotent on (account_id, campaign_id, external_lead_id). Re-submitting the same tuple with the same payload is a no-op (response status deduped). Re-submitting with mutated fields will patch the existing lead in place (response status patched).
The optional Idempotency-Key request header is accepted and logged for your debugging, but the (account_id, campaign_id, external_lead_id) tuple is the canonical idempotency key.
Rate Limits
Rate limits are enforced per API key, sharing the bucket with the internal /api/leads/ingest path. When exceeded, the server returns 429 rate_limitedwith a Retry-After header (integer seconds). Sustained traffic above your ceiling will continue to 429 until the window rolls over.
Retry Strategy
Recommended retry policy:
- 4xx (except 429): Do not retry. The error is a permanent client-side problem (bad payload, missing scope, wrong key). Fix and re-submit.
- 429: Wait
Retry-Afterseconds, then retry. If you see sustained 429s, contact support to raise your ceiling. - 5xx and network errors: Retry with exponential backoff (1s, 2s, 4s, 8s, capped at 30s) up to 5 attempts. POST is safely idempotent — retrying the same payload produces
dedupedon second attempt. - 409 conflict on PATCH: Re-read the lead via GET, merge your changes, and re-PATCH.