Skip to content
watsi

Conventions

Idempotency

How to retry a write on the watsi API without applying it twice using the Idempotency-Key header, which routes accept it and how long it is remembered.

If a write is cut off halfway (the network drops, your process restarts), you don’t know whether watsi applied it. The Idempotency-Key header lets you retry it safely: if the first one was already applied, the retry returns its result instead of writing again.

curl -X POST "https://api.watsi.ai/v1/actions/contacts/create" \
  -H "Authorization: Bearer $WATSI_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed" \
  -d '{ "name": "María Pérez", "phone_number": "5215598765432" }'

Which routes accept it

The writes that are not idempotent by nature. Reads, and writes that leave the same state however many times you repeat them (such as setting a conversation’s status), ignore it because they are already safe to retry. This list is generated from the contract:

MethodPathScopesIdempotency-Key
POST/v1/actions/agents/createagents:writeYes
POST/v1/actions/agents/knowledgeknowledge:writeYes
POST/v1/actions/agents/knowledge/deleteknowledge:writeYes
POST/v1/actions/agents/updateagents:writeYes
POST/v1/actions/attachments/create-uploadagents:writeYes
POST/v1/actions/attachments/finalizeagents:writeYes
POST/v1/actions/attachments/import-urlagents:writeYes
POST/v1/actions/contacts/createcontacts:writeYes
POST/v1/actions/crm/tagscrm:writeYes
POST/v1/actions/crm/tags/removecrm:writeYes
POST/v1/actions/lifecycle/attach-filelifecycle:writeYes
POST/v1/actions/voice/configagents:writeYes

How it behaves

CaseResponse
First request with a keyRuns normally
Same key, same body, within 30 seconds200 with the first one’s stored response; nothing is written
Same key, different body, within 30 seconds409 IDEMPOTENCY_KEY_REUSED
Same key after 30 secondsTreated as a new request
The first request failedNothing was stored: the retry runs again
  • A key is scoped per workspace and per route: the same key on contacts/create and on crm/tags are two different keys.
  • The body comparison is by content: the order of JSON properties does not matter.
  • The 30-second window is meant for the immediate retry of a request that was cut off, not for deduplicating jobs that span hours. For that, keep your own record of which operations you completed.

Good practice

  • One key per logical operation, generated before the first attempt (a UUID v4 works) and reused on every retry of that operation.
  • Never the same key for two different operations: the second body gets a 409.
  • If you get 409 IDEMPOTENCY_KEY_REUSED, it is a bug in your code (two operations shared a key), not something a retry fixes.

contacts/create is also an upsert: sending the same phone, wa_id or email updates the existing contact instead of duplicating it, even without an Idempotency-Key and outside the window. The response says so with "created": false.