Idempotency
Send Idempotency-Key on any POST, PATCH or DELETE to make retries safe — a byte-identical retry replays the stored answer; a different body with the same key is refused.
A network timeout leaves you not knowing whether a write landed. Send an Idempotency-Key and retry the identical request: you get the original answer back instead of a second run, a second comment or a second webhook.
How it works
curl -X POST https://app.processcart.com/api/v1/runs \
-H "Authorization: Bearer pc_live_…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: crm-order-1042-start" \
-d '{ "workflowId": "…", "name": "Order 1042" }'- The key is any string of 1–255 printable ASCII characters without spaces — a uuid, or something meaningful to you.
- Keys are stored per workspace, and a stored response is replayed only to the API key that made the request (or to the key one rotation away from it, in either direction). A second API key sending the same
Idempotency-Keyvalue is refused with 422idempotency_key_reused— so prefix keys with something unique to each integration. - The first request runs normally and its response is stored for 24 hours.
- A second request with the same key and a byte-identical body (same method, path and
Expected-Revisiontoo) replays the stored response with the headerIdempotent-Replayed: true— the same status, body andLocation. A replay carries a freshX-Request-Id, and a replayed problem body is rewritten to match it. - A second request with the same key but a different method, path, body or
Expected-Revision— or from a different API key — is refused with 422idempotency_key_reused, and nothing is created. - While the first request is still running, a duplicate answers 409
idempotency_in_progresswithRetry-After: 2.
The key applies to POST, PATCH and DELETE. GET requests are naturally idempotent and ignore it.
What is stored, and what is not
- Successes (2xx) and request-shaped refusals (most 4xx) are stored and replayed, so a retry of a request that was wrong gets the same honest answer.
- Answers that describe the state at that moment are never stored: every 409 (
revision_mismatch,idempotency_in_progress,run_trashed,task_locked,task_completed, …); the 422s that read the run or its workflow —required_fields_unsatisfied(a required field still blank) andfield_not_writable(among others, a field the run's rules hide right now); the 403s that read the current setup —approver_only,workflow_not_runnable,not_allowed_for_key; every 429; every 5xx. A retry re-executes against the current state — the point of retrying. Because nothing was stored, the key is free again: a corrected request may reuse it. - Responses over 256 KB are not stored; a retry re-executes.
- A request that crashes releases the key, so the retry runs.
The upload ticket
POST /v1/runs/{id}/files returns a signed upload URL that lives two hours. A replay of that request past the ticket's lifetime hands back the stored, now-expired ticket — ask for a new ticket with a new key instead of retrying the old one (Tasks, approvals & files).
One-time secrets
A replay returns the stored response as it was — including a webhook's signing secret: the 201 of POST /v1/webhooks and the answer of POST /v1/webhooks/{id}/rotate-secret come back, secret included, to the same key (or its rotation) for 24 hours. Treat the Idempotency-Key on those two calls as secret material (Webhooks).
Recommendation
Generate the key once per logical operation (before the first attempt), reuse it across every retry of that operation, and never reuse it for a different one. A good shape is <integration>-<your record id>-<operation> — crm-order-1042-start, crm-order-1042-complete-review — so two integrations in one workspace never send the same string.