Idempotency
Retry safely; never send a customer the same message twice.
Networks fail after the request arrived and before the answer reached you. If your POS retries a send at that moment, the customer gets the invoice twice - unless the retry can be recognised. That is what the Idempotency-Key header is for, and it is required on every write to the public API.
How it works
Idempotency-Key: INV-10025-WHATSAPP
- The key and a hash of the request body are looked up for your application.
- First time: the request runs and its answer - status and body - is stored under the key.
- Same key, same body, within 24 hours: the stored answer is returned verbatim, with
Idempotency-Replayed: true. Nothing runs again; no second message. - Same key, different body:
409 idempotency_conflict. The key was reused for something else, which is a bug on the caller's side worth surfacing. - Same key while the first request is still running:
409with a message saying to retry shortly.
Keys are scoped to your application and expire after 24 hours.
Choosing a key
Use the identity of the thing you must not do twice, not a random value:
| Sending… | Key |
|---|---|
| an order confirmation | ORDER-10025-CONFIRMATION |
| an invoice on WhatsApp | INV-10025-WHATSAPP |
| a payment reminder, one per day | INV-10025-REMINDER-2026-09-15 |
| a contact from your CRM | CRM-CUSTOMER-88213 |
A random UUID also works and is the right choice when the request genuinely has no natural identity - but then your retry logic must reuse the same UUID, which means persisting it before the first attempt.
Retrying safely
attempt = 0
until success or attempt == 5:
response = POST /messages with the same Idempotency-Key
if response is 2xx: success
if response is 429: wait Retry-After seconds
if response is 5xx or a network error: wait 2^attempt seconds
if response is 4xx (other than 429): stop - retrying cannot change the answer
A 409 idempotency_conflict is a stop, and a log line: two different requests shared a key.
What is not idempotent
Reads have no key and need none. Webhook deliveries to you are a different mechanism: they carry X-Event-ID, and you dedupe on it - see Webhooks.
https://communication-api.artofluminaire.com
Every response carries X-Request-ID; quote it when you write to support.