Concepts
Idempotency
Which endpoints require an Idempotency-Key, how the key is scoped, what happens on replay and on conflict, and how to choose good keys.
A screening run pulls reports and may bill. A retried request must therefore never start a second run. Every endpoint that creates a run requires an Idempotency-Key header and uses it to return the original result on replay.
Which endpoints require a key?
| Endpoint | Key scope |
|---|---|
POST /workflows/{id}/trigger |
Per API client (the key is stored as execution_key = api_client:<client id>:<your key>) |
POST /workflows/{id}/batches |
Per API client, compared against a SHA-256 of the rows JSON |
POST /executions/{id}/re_evaluations |
Per source execution (reeval:<execution id>:<your key>) |
Omitting the header returns 422 missing_idempotency_key. The header value is limited to 200 characters (the trigger namespace prefix consumes some of the stored key, so keep values comfortably shorter).
What happens on replay?
- Same key, same body → the original response, again:
202 Acceptedwith the same execution or batch. Nothing new is created. - Same key, different body →
409 idempotency_conflict. The stored request is not overwritten. A trigger answers with the message below; a batch says "…with a different request." instead. - Concurrent identical requests are race-safe: both callers receive the same execution; the loser of the insert race re-reads the winner's row.
{
"error": {
"code": "idempotency_conflict",
"message": "The Idempotency-Key has already been used with a different request payload.",
"details": [],
"request_id": "7d4c2f48-…",
"doc_url": "https://cascades.crscreditapi.com/docs/api/errors/idempotency_conflict"
}
}How do I choose a key?
Derive it from the upstream business event, not from the attempt: an application id, an order id, a submission id. Then a retry from a timeout, a crashed worker or a redeploy naturally carries the same key. Never reuse a key across distinct events, and never generate a fresh UUID per attempt — that defeats the protection entirely.
Idempotency-Key: rv-app-20491 # good: the application id
Idempotency-Key: 3fa85f64-… # fine, if you persist it before the first attempt
Idempotency-Key: $(uuidgen) # bad: every retry is a new runThe same discipline applies to external_reference: it is not an idempotency key (two triggers with the same reference create two runs), but it is what you correlate results and webhooks back to.