Error handling and retry playbook
Every error uses the same envelope, so your handler can parse one shape across all endpoints:
{ "success": false, "error": { "code": "VALIDATION_ERROR", "message": "...", "details": {} }, "timestamp": "..." }
What each status means
| You got | It means | What to do |
|---|---|---|
400 (VALIDATION_ERROR, INVALID_STATUS_TRANSITION, COMMENT_REQUIRED, INVALID_DISPLAY_ID, USER_NOT_FOUND, BULK_ACTION_FIELD_MISMATCH, BULK_LIMIT_EXCEEDED) | The request is wrong: bad enum case, malformed identifier, disallowed transition, unknown user | Fix the request. Never retry as-is. |
401 UNAUTHORIZED | Missing, invalid, or expired token | Rotate the token under Integrations → Alerts API |
403 (TWO_WAY_DISABLED, MODULE_ACCESS_DENIED, INCIDENT_ARCHIVED, org or user inactive) | The token works but this action is not allowed | Surface to an operator; retrying will not help |
404 (INCIDENT_NOT_FOUND, EVENT_NOT_FOUND, JOB_NOT_FOUND) | Well-formed identifier, no matching entity in your org tree | Treat as permanent for that identifier. For events, consider whether a rescan replaced it (see SOAR playbooks) |
429 RATE_LIMIT_EXCEEDED | Per-token budget exhausted | Wait for the Retry-After seconds, then retry the same request |
5xx (INTERNAL_ERROR, INTEGRATION_PLATFORM_UNAVAILABLE, INTEGRATION_AUTH_UNAVAILABLE, INTEGRATION_REQUEST_TIMEOUT) | Transient upstream failure | Retry with exponential backoff. Single writes, comments, and remediations are idempotent, so a retry after a timeout is safe and returns the prior result |
Retry rules of thumb
- 4xx is on you. Fix the request before sending it again. The only exception is
429, which is a pacing problem, not a request problem. - 5xx is transient. Back off and retry. Because single writes are idempotent, a retry after a timeout returns the prior result rather than duplicating the action.
- Respect
Retry-After. On a429, the header tells you exactly how long to wait.
Garbage vs missing on event routes
An identifier that is not a valid event_record_identifier at all returns
400 VALIDATION_ERROR, not 404. This lets you tell "you sent garbage" apart
from "that event does not exist" and react accordingly.