422 Unprocessable Entity
{
"error": {
"code": "validation_failed",
"message": "phone must be E.164",
"field": "phone",
"eventId": "0f4e2a9c-1b77-4d2e-9a03-5c8b71e6d402",
"retryable": false
}
}Branch on code, and let retryable decide whether your queue backs off or dead-letters. You should not have to encode our status codes.
Codes
| Status | Code | Meaning | What to do |
|---|---|---|---|
400 | malformed_request | Body is not JSON, or a header is missing | Fix and resend |
401 | signature_invalid | Signature mismatch, unknown key, or clock skew over 300s | Check clock and secret; do not retry blindly |
403 | key_inactive | The key is paused | Stop; contact ConnectAI |
403 | key_revoked | The key is permanently revoked | Stop; a new key must be issued |
403 | ip_not_allowed | Source address is not allowlisted | Stop; contact ConnectAI |
404 | resource_not_found | That externalId has no mapping yet | Send the parent record first |
409 | slot_unavailable | The slot is at capacity | Pick an alternative from the response |
409 | doctor_unmapped | doctorPhone matches nobody on the clinic's roster | Check the number, or ask the clinic to add them |
409 | terminal_state | Already cancelled or completed | Stop and reconcile |
422 | validation_failed | A field failed validation | Fix and resend |
429 | rate_limited | Over the per-key limit | Back off; honour Retry-After |
5xx | internal_error | Our fault | Retry with backoff |
Only 429 and 5xx are retryable. A 409 is a business answer, not a transport failure — retrying it changes nothing.
An unknown key looks like a bad signature
Both return 401 signature_invalid with the same message. That is deliberate: it stops the endpoint being used to discover which keys exist. If you are sure the key is right, check your clock and your raw-body handling.