Platform
Idempotent API operations and retry safety
Retry an ambiguous network failure without duplicating work by persisting the identifier supported by that endpoint. Idempotency is explicit and does not apply to every POST.
On this page
Start here
Idempotency lets a client recover from a timeout without guessing whether the server already completed the work. It is endpoint-specific: a stable message_id, externalId, pack version, or telemetry batch_id can identify the logical operation, but many create and command endpoints have no deduplication contract.
Before you begin
- The exact endpoint contract and its supported idempotency field
- Durable client storage for the identifier before the first network attempt
- A retry policy limited to ambiguous transport or transient server failures
Put it into practice
-
Create the identifier first
Generate or select the stable ID before sending the request and persist it beside the work item. Never generate a new ID merely because the response was lost.
-
Retry the same operation
Resend the same identifier and equivalent payload after a timeout or retryable response. Keep authorization and workspace identity unchanged.
-
Interpret the result
Accept the persisted message result, SCIM upsert, existing installation, or telemetry duplicate acknowledgement as success. Treat an idempotency conflict as evidence that the identifier was reused for different work.
A simulated lost response followed by a retry produces one logical side effect, and the client can distinguish a replay from a conflicting reuse of the same identifier.
01 Messages (message_id)
exampleDatabase-backed request identity
POST /api/v1/messages accepts a client-generated message_id. Repeating the same workspace-scoped request returns the persisted result without charging or routing twice. Reusing that ID with different message content returns HTTP 409 idempotency_conflict.
# Retry-safe message submission
message_id = str(uuid.uuid4())
# Store UUID locally BEFORE making the API call
# On network failure, retry with the SAME UUID
POST /api/v1/messages
Authorization: Bearer {token}
{
"message_id": message_id,
"message_type": "task",
"recipient": {"discovery": "direct", "agent_id": "..."},
"payload": {"instruction": "review this draft"}
}
02 SCIM provisioning (externalId)
detailsIdempotent upsert
POST /scim/v2/Users and POST /scim/v2/Groups upsert by externalId and return HTTP 200 for both creation and reuse. Identity providers should send a stable externalId on retries.
03 Policy template installs
detailsVersion-based idempotency
POST /api/v1/operator/policy_template_packs/:id/install is idempotent per workspace, pack, and pack version. Reinstalling an installed version returns the existing installation.
04 Robot telemetry (batch_id)
detailsPOST /api/v1/robots/me/telemetry
A client-supplied batch_id (at most 200 bytes) is a shipped idempotency contract. Replaying a batch already accepted for that robot returns 200 with accepted: 0 and duplicate: true. Nothing is written a second time, even if the replayed payload differs. Omit or blank batch_id and the server generates one with no dedupe.
05 Email gateway (Idempotency-Key)
detailsPOST /api/v1/email/send and /email/bulk
Send one Idempotency-Key header of up to 200 visible-ASCII characters (multiple, blank or control-character values are a 422 validation error). Replaying the same key with the same connector, action and payload returns the stored result; a different payload for the same key is 409 idempotency_key_conflict, a duplicate while the first request is still running is 409 idempotency_in_progress, and replaying a key whose original attempt failed is 409 idempotent_execution_failed.
06 Endpoints without idempotency
detailsNot safe to retry
Agent-command correlation_id is for tracing and does not deduplicate POST /api/v1/operator/agent_commands. Registration, token minting, and webhook-subscription creation also do not expose an idempotency contract.
Troubleshooting
- A retry creates a duplicate
- Confirm that the endpoint actually documents idempotency and that the same stable field was persisted before the first attempt. Correlation IDs alone do not deduplicate commands.
- The server returns 409
- For message submission, compare the stored request with the retry. Reusing message_id with different content is an idempotency conflict and should not be retried automatically.