Idempotency keys
A network error after you sent sendMessage leaves you not knowing whether the message went out. Send an Idempotency-Key and retry with the same key: you get the first answer again instead of a second message.
Usage#
POST https://bots.qivuna.com/v1/sendMessage
Authorization: Bot 7123456789:qvb1_…
Idempotency-Key: upd-1044-reply
Content-Type: application/json
{"chat_id": 4503600212045337, "text": "Your order is confirmed."}
- The key is 1–64 printable ASCII characters (no spaces). Anything else answers
400 bad_requestwithparameter: Idempotency-Key. - It applies to write methods (marked “write” in the reference): sends, edits, deletes, moderation, commands, descriptions,
setWebhook,setMyEncryptionKey,answerCallbackQuery,leaveChat. On read methods, and ondeleteWebhook(which is safe to repeat), the header is ignored. - Keys are per bot. Derive them from what you are doing, e.g.
upd-<update_id>-reply, or use a UUID you store with the job.
Behaviour#
| Situation | Answer |
|---|---|
| First request with a key | Runs normally. A successful (2xx) answer is stored. |
| Same key, same request, within 1 hour | The stored answer, byte for byte, with its status. Nothing runs again. The X-Request-Id header is new. |
| Same key, different request | 409 idempotency_mismatch. |
| Same key while the first request is still running | 409 idempotency_in_progress at once. Retry shortly with the same key. |
| The first request was refused (4xx, 5xx) | Nothing is stored: a retry runs again and may succeed. |
- “Same request” means the same method and the same parameters. For uploads, the file's bytes count, not its temporary name. A
setWebhooksecret enters the comparison only as a keyed hash; it is never stored. - Stored answers live for 1 hour, and a bot keeps at most its last 1,000 keys. Older keys are forgotten: reusing one then runs the request again.
- Stored answers may contain message text; they are deleted when they expire, or at once when the bot changes owners.
When to retry#
- Lost connection, timeout,
500 internal: retry writes only with the same key. 429 rate_limited: waitretry_after, then retry with the same key.409 idempotency_in_progress: retry shortly with the same key.503 platform_paused: back off for minutes, not seconds.- Other 4xx: do not retry unchanged.
The SDKs add a generated key to every write and retry exactly these cases.