# Fiabilité : idempotence et reprises

> Comment l'API se comporte en cas de panne, de doublon ou de rejeu, et comment écrire une intégration robuste.

Une intégration de messagerie doit survivre aux pannes réseau, aux redémarrages et aux doublons. Voici ce que fait l'API pour vous, et ce qu'il vous reste à faire.

## Idempotence de l'envoi

Un envoi n'est pas anodin : le rejouer par erreur enverrait **deux fois** le même message à un client. L'en-tête `Idempotency-Key` l'empêche.

```bash
curl -X POST "$INBOX_API/messages" \
  -H "Authorization: Bearer $INBOX_KEY" -H "Content-Type: application/json" \
  -H "Idempotency-Key: rdv-2026-11-14-client-42" \
  -d '{ … }'
```

| Situation | Résultat |
|---|---|
| Première requête avec cette clé | Le message est accepté et enregistré. |
| Même clé, **même** requête | Aucun nouveau message : vous récupérez le message d'origine, avec l'en-tête `Idempotent-Replayed: true`. |
| Même clé, requête **différente** | `422 idempotency_conflict`. |
| Deux requêtes simultanées avec la même clé | L'une réussit, l'autre est refusée (`409`) : réessayez. |

<ErrorExample name="idempotency_conflict" />

**Choisissez une clé qui décrit l'intention métier** (`rappel-rdv-<id du rendez-vous>`), pas un identifiant aléatoire régénéré à chaque tentative : c'est ce qui rend le rejeu sûr.

## L'envoi est asynchrone

`202 Accepted` signifie **accepté**, pas **délivré**. Le message est en file, puis envoyé :

- **Panne passagère de Meta ou du réseau, limite de débit** : le message est réessayé automatiquement, jusqu'à 6 tentatives avec une attente croissante.
- **Refus définitif** (template introuvable, destinataire non joignable…) : pas de nouvelle tentative inutile ; le message passe tout de suite en `failed`, avec le code et le motif de Meta.
- **Débit** : les envois sont régulés **par numéro** (50 messages par seconde par défaut) : en cas de pic, ils attendent leur tour sans être perdus.
- **Redémarrage ou perte de la file** : un message resté `queued` plus de 5 minutes est repris automatiquement par une tâche de maintenance.

## Statuts : jamais en arrière

Meta peut livrer ses notifications en double ou dans le désordre. Bref Point Inbox applique une règle simple : un statut ne fait **que progresser** (`queued` → `sent` → `delivered` → `read`), et `failed` ne remplace pas un message déjà `read`. Même un `read` qui arrive avant son `delivered` est accepté, et l'étape manquante est complétée.

## Webhooks : au moins une fois

Chaque événement est livré **au moins une fois**, jamais « exactement une fois ». Votre récepteur doit donc être **idempotent** :

1. Vérifiez la signature.
2. Enregistrez l'`id` de l'événement (une table avec une contrainte d'unicité suffit).
3. S'il existe déjà, répondez `200` sans rien refaire.
4. Sinon, traitez, puis répondez `200`.

Voir [Webhooks](/docs/guides/webhooks) pour le calendrier des reprises et le rejeu.

## Ce qu'il vous reste à faire

- **Mettez un délai d'attente** sur vos appels (10 à 30 secondes) et **réessayez avec la même `Idempotency-Key`** en cas d'expiration, d'erreur réseau ou de `5xx`.
- **Ne réessayez pas** une erreur `4xx` sans corriger la requête : elle échouera pareil.
- Répondez à vos webhooks **en moins de 10 secondes** ; déléguez le travail long à une file chez vous.
- Surveillez `GET /webhook-deliveries?status=failed` et vos messages `failed`.

<Callout type="note" title="Une limite connue">
Si le service s'arrête exactement entre l'envoi à Meta et l'enregistrement du résultat, un nouvel essai pourrait produire un doublon. La fenêtre est très courte et ne peut pas être supprimée, car Meta n'offre pas de clé d'idempotence à l'envoi.
</Callout>
