Perché il retry cieco è pericoloso
Con gli SMS un retry non è gratis. Se la richiesta va in timeout, il client non sa nulla dell'esito: forse la richiesta non è mai arrivata, forse l'SMS è già partito. Ripetere la chiamata "per sicurezza" nel secondo caso significa due SMS al destinatario e due addebiti sul saldo.
Il caso classico non è nemmeno un retry scritto a mano: è la libreria HTTP con i retry automatici attivi di default. Verso un'API che muove denaro, ogni retry deve essere esplicito e idempotente.
L'header Idempotency-Key
La soluzione è un header: Idempotency-Key, con un valore scelto da te che identifica quel singolo invio logico. Nient'altro cambia nella richiesta.
curl
curl -X POST https://sms-jet.com/api/v1/sms/transactional \
-H "Authorization: Bearer gsms_…" \
-H "Idempotency-Key: order-1042" \
-H "Content-Type: application/json" \
-d '{ "to": "+393331234567", "text": "…", "sender_type": "system" }'La semantica del replay
Al primo invio la chiave viene registrata insieme all'impronta del corpo della richiesta. Da lì in poi: stessa chiave e stesso corpo → viene restituita di nuovo la risposta originale, senza nuovo invio e senza nuovo addebito. Vale anche per gli errori definitivi: si ripresentano identici.
Stessa chiave con un corpo diverso → 409 IDEMPOTENCY_CONFLICT. È una protezione, non un fastidio: una chiave identifica un messaggio, non uno slot riutilizzabile.
409 Conflict
409 Conflict
{
"code": "IDEMPOTENCY_CONFLICT",
"message": "This Idempotency-Key was already used with a different request body"
}SEND_OUTCOME_UNKNOWN: l'esito è in sospeso
C'è un terzo caso: un tentativo precedente con quella chiave è partito ma non si è mai risolto (timeout o crash a metà). L'API non conosce l'esito e si rifiuta di indovinare: risponde 503 SEND_OUTCOME_UNKNOWN.
Cosa fare: non fare retry alla cieca e non cambiare chiave — con una chiave nuova il doppio invio torna possibile. Controlla il registro messaggi, oppure riprova più tardi con la stessa chiave: quando l'esito si risolve, il retry lo ottiene in replay.
503 Service Unavailable
503 Service Unavailable
{
"code": "SEND_OUTCOME_UNKNOWN",
"message": "A previous attempt with this Idempotency-Key has not been resolved yet. Do not retry blindly — poll the message log or retry later with the same key."
}Come scegliere le chiavi
La chiave giusta di solito esiste già nel tuo sistema:
- Una chiave per messaggio logico, non per tentativo: order-1042 per la conferma dell'ordine 1042, identica a ogni retry.
- Mai valori casuali generati a ogni tentativo: una chiave nuova a ogni retry disattiva l'idempotenza proprio quando serve.
- Derivala da un identificatore che possiedi già: l'id dell'ordine, della riga di coda, dell'evento.
- Le chiavi sono per-app: due app diverse possono usare lo stesso valore senza interferire.
