Idempotency
A dropped connection should not become a second email or workflow run. On supported writes, send an Idempotency-Key so maxclicks can identify a retry of the same operation and return its recorded result.
Keep the key, payload, and returned result together in your integration. If the outcome is uncertain, reuse the original key while you investigate; a new key represents a new operation.
The header
Choose a unique key for each logical operation, such as a UUID or a source event ID. Keys can be up to 256 characters. Send the same value and body for every retry of that operation.
curl -X POST https://api.maxclicks.ai/v1/schemas/customers/records/upsert \
-H "Authorization: Bearer $MAXCLICKS_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: customer-sync-1042-revision-7" \
-d '{ "userId": "1042", "email": "[email protected]" }'
Replace customers with your contact schema's slug. Include a source revision in a sync key when the same customer's data can change later.
Lifecycle
| Result | What your integration should do |
|---|---|
| First request | Save the operation's key and body. The server claims the operation before executing it. |
| Completed request | Save the result and any Operation-Id response header. A repeat can return the recorded status and body with Idempotent-Replay: true. |
409 idempotency_conflict | The original is still processing. Back off and retry with the same key and body. |
409 idempotency_outcome_unknown | The original outcome is unresolved. Check the affected record, email, broadcast, or workflow before taking another action. |
422 idempotency_key_reused | The key was used with a different body. Recover the original payload; use a new key only for a separate operation. |
429 idempotency_admission_busy | Wait for Retry-After, then retry the same key and body. |
503 idempotency_outcome_unknown | The server could not confirm persistence of the outcome. Retain any operationId and reconcile; do not force another execution with a new key. |
Recorded errors and retention
Responses produced after an operation is claimed can be recorded, including 4xx and 5xx responses. Repeating the request can replay that error; it does not guarantee another execution. Authentication, request parsing, and other checks that run before the claim can fail without creating an idempotency record.
Completed operations are retained for 24 hours. Once their retention window expires, the same key can execute again. Unresolved operations are retained for reconciliation; elapsed time alone does not make a retry safe.
Do not assume every 5xx is safe to resend as a new operation. A write may
have taken effect before the response was lost. When the API reports an
unresolved outcome, inspect the resource or its run history before deciding
what to do next.
What a key is scoped to
The server scopes a key to the calling API key, HTTP method, and resolved path. It also compares a canonical hash of the request body. The same key sent to two different template IDs represents two operations; the same key and path with a changed body returns 422 idempotency_key_reused.
Reordering JSON object keys does not change the body identity. Changing values does. Reuse the same calling API key for retries, including while rotating credentials.
Supported endpoints
| Operation | Method and path |
|---|---|
records.create | POST /v1/schemas/{schema}/records |
records.upsert | POST /v1/schemas/{schema}/records/upsert |
suppressions.batchCreate | POST /v1/contacts/suppressions/batch |
suppressions.batchDelete | POST /v1/contacts/suppressions/batch-remove |
suppressions.delete | DELETE /v1/contacts/suppressions/{id} |
segments.create | POST /v1/segments |
segments.delete | DELETE /v1/segments/{id} |
webhooks.create | POST /v1/webhooks |
webhooks.update | PATCH /v1/webhooks/{id} |
webhooks.delete | DELETE /v1/webhooks/{id} |
webhooks.rotateSecret | POST /v1/webhooks/{id}/rotate-secret |
workflows.trigger | POST /v1/workflows/{id} |
workflows.pause | POST /v1/workflows/{id}/pause |
workflows.unpause | POST /v1/workflows/{id}/unpause |
broadcasts.send | POST /v1/broadcasts/{id}/send |
broadcasts.update | PATCH /v1/broadcasts/{id} |
templates.send | POST /v1/templates/{templateId}/send |
For workflow triggers, the path uses the incoming-webhook step's reference ID. Copy it from the workflow; it is not the workflow's own ID.
Webhook creation and secret rotation replay their original secret-bearing response when retried with the same key. Store that result securely. Normal webhook GET/list responses omit the signing secret. A new key on rotation requests another rotation.
The existing SDK interfaces expose key arguments for a smaller set of writes. Use direct HTTP when a client method cannot send the header; adding an arbitrary field to the JSON body does not set an HTTP header.
Other retry mechanisms
The header does not add protection to an endpoint outside this table. Record PATCH/DELETE and single suppression creation do not use this mechanism.
- Business event creation uses your
eventId, scoped to the space and event schema. Preserve the same payload and explicitoccurredAt. Changed input returnsevent_id_conflict; a duplicate returns its original acceptance receipt. Poll readiness separately. - Event identity changes use the required UUID
operationIdplusexpectedAliasRevision. Preserve the input when retrying and save the returned revision for that alias. - Record deletion returns an operation identifier. Poll deletion status until
completed; apendingresponse is not proof of erasure.