Use the Idempotency-Key header to retry Avox API requests safely without ever sending the same WhatsApp message twice.
Networks fail. A request can time out after WhatsApp has already accepted the message, and a plain
retry would send it a second time. The Idempotency-Key header stops that: retry with the same
key and the same body, and Avox returns the first result instead of sending again.
Make one key per message
Generate a new value for each message you intend to send - a UUID is ideal, or an id from your
own system such as order-A-1042-shipped.
Send it as a header
Idempotency-Key: 6f1d2c1e-8a4b-4c7e-9a51-3b0f2d9e7c10
Store it with the job
Save the key next to the message in your queue or database, so every retry of that message reuses it.
Retry with the same key and the same body
If a request times out or fails with a retryable error, send it again unchanged.
curl -X POST https://api.avox.com/api/messages/send \
-H "Authorization: Bearer $AVOX_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-A-1042-shipped" \
-d '{"to":"15551234567","type":"template","template":{"name":"order_shipped","language":{"code":"en_US"}}}'
// The key comes from your own job record, so a retried job reuses it.
async function sendShippedNotice(order) {
const res = await fetch("https://api.avox.com/api/messages/send", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.AVOX_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": `order-${order.id}-shipped`,
},
body: JSON.stringify({
to: order.phone,
type: "template",
template: { name: "order_shipped", language: { code: "en_US" } },
}),
});
const replayed = res.headers.get("Idempotent-Replayed") === "true";
return { status: res.status, replayed, body: await res.json() };
}
import os
import requests
def send_shipped_notice(order):
res = requests.post(
"https://api.avox.com/api/messages/send",
headers={
"Authorization": f"Bearer {os.environ['AVOX_API_KEY']}",
"Idempotency-Key": f"order-{order['id']}-shipped",
},
json={
"to": order["phone"],
"type": "template",
"template": {"name": "order_shipped", "language": {"code": "en_US"}},
},
timeout=30,
)
replayed = res.headers.get("Idempotent-Replayed") == "true"
return res.status_code, replayed, res.json()
The header is optional. Without it, every request is treated as new. It's strongly recommended for anything sent from a queue, a scheduled job, a webhook handler, or any code that retries on its own.
A replayed body still holds the first request's request_id and message_id. The
X-Request-Id header on the replay is new, so you can tell the two calls apart in your logs.
In the dashboard, every replay is listed as its own request:
Test keys follow exactly the same rules, so you can check your retry logic in test mode before going live.
Not every outcome is stored against the key. This decides what a retry does.
meta_unavailable means WhatsApp didn't answer in time, so nobody knows whether the message went
out. Avox frees the key so you decide what to do. If a duplicate would matter (a payment
reminder, a one-time code), check Logs for a status update on the message, or ask the
customer, before sending again.
idempotency_conflict. Make a new key
per message.See Rate limits for which errors to retry and how long to wait.
Last updated