Idempotency and retries
Write clients should be able to retry after network, rate-limit, and transient server failures without creating duplicate CRM records, tasks, activity entries, assets, or webhook state.
When to use a key
Send an idempotency_key on every create that might be retried by a client, job runner, webhook worker, or agent loop.
Use the same JSON body and the same key when retrying the same logical write:
curl -X POST "$SLAB5_API_BASE_URL/contacts" \
-H "Authorization: Bearer $SLAB5_WORKSPACE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Jane Doe",
"email": "jane@acme.com",
"company_name": "Acme Corp",
"idempotency_key": "contact_jane_doe_acme_2026_05_03"
}'When a create is retried with the same key and the same body, Slab5 returns the record created by the first attempt and does not create a second one. The response is the current state of that record, not a stored replay of the original response body.
Idempotency keys apply to creates only. Reads do not need them, and update, archive, and delete operations ignore an idempotency_key if one is sent — it is not stored and leaves no retry trail. Make those retries safe by targeting a specific resource ID and sending the same desired end state.
Keys are scoped to one workspace and one resource type. The same key on create_contact and create_task are unrelated and both will be created.
Key design
Use keys that are stable for one logical operation and unique across unrelated writes:
- Good:
contact_jane_doe_acme_2026_05_03 - Good:
webhook_delivery_evt_123_attempt_1 - Good:
lead_intake_run_abc_create_task - Avoid:
retry - Avoid:
create_contact - Avoid: a request ID from a previous response
Do not put secrets, raw email bodies, full prompts, or sensitive personal details in idempotency keys. They may appear in logs and support tooling.
Retryable errors
Retry only errors marked retryable in the error reference. Current retry candidates are:
rate_limited: wait forretry_after_secondsor the reset time when present.internal_error: retry with bounded exponential backoff and jitter.- Network timeouts where the client did not receive a response: retry the same body and key.
Do not retry validation, authentication, authorization, missing-scope, module-disabled, workspace, or not-found errors until the request or credential is fixed.
Conflict behavior
If a key is reused with a different body, Slab5 returns idempotency_conflict. The comparison is a fingerprint of the operation and the full request input, so any changed field counts as a different body:
{
: var(--shiki-token-string)">"color: var(--shiki-token-parameter)">: var(--shiki-token-string)">"error": {
: var(--shiki-token-string)">"color: var(--shiki-token-parameter)">: var(--shiki-token-string)">"code": : var(--shiki-token-string)">"idempotency_conflict",
: var(--shiki-token-string)">"color: var(--shiki-token-parameter)">: var(--shiki-token-string)">"message": : var(--shiki-token-string)">"The idempotency key was reused with a different request body.",
: var(--shiki-token-string)">"color: var(--shiki-token-parameter)">: var(--shiki-token-string)">"idempotency_key": : var(--shiki-token-string)">"contact_jane_doe_acme_2026_05_03",
: var(--shiki-token-string)">"color: var(--shiki-token-parameter)">: var(--shiki-token-string)">"request_id": : var(--shiki-token-string)">"req_idempotency_123",
: var(--shiki-token-string)">"color: var(--shiki-token-parameter)">: var(--shiki-token-string)">"docs_url": : var(--shiki-token-string)">"https://docs.slab5.com/api/errors#idempotency_conflict"
}
}Treat conflicts as client bugs or duplicate job-run bugs. Generate a new idempotency key only when the user or workflow is intentionally making a new write.
Client checklist
- Generate the idempotency key before the first write attempt.
- Persist the key with the job, queue item, or agent state.
- Retry with the exact same body and key.
- Log the returned
request_id. - Stop retrying when the error is not retryable.
- Use bounded retry limits so agent and webhook loops cannot run forever.