> ## Documentation Index
> Fetch the complete documentation index at: https://docs.boble.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Structure du payload

> À quoi ressemble chaque livraison de webhook Boble — l'enveloppe, les en-têtes et l'objet data.

Chaque événement que Boble envoie à votre endpoint suit la même structure. Les seuls champs qui changent d'un événement à l'autre sont le `type` et le contenu de `data.object`. Tout le reste est constant.

## L'enveloppe

```json theme={null}
{
  "id": "0d6d31a3-2b9e-4d9f-9a51-3b9c3f8e6c10",
  "type": "user_promotion.access_granted",
  "api_version": "2026-01-01",
  "created": 1717497600,
  "livemode": true,
  "data": {
    "object": { /* le payload spécifique à l'événement */ }
  }
}
```

<ResponseField name="id" type="string">
  Identifiant unique de cette livraison. Le renvoi (resend) d'un même événement logique utilise un nouvel `id`, pas celui d'origine.
</ResponseField>

<ResponseField name="type" type="string">
  Le nom de l'événement. Voir [Types d'événements](/fr/webhooks/event-types) pour la liste complète et ce que chacun signifie.
</ResponseField>

<ResponseField name="api_version" type="string">
  Version du schéma du payload. Vaut actuellement toujours `2026-01-01`. Si le schéma évolue un jour, la nouvelle version sera annoncée et l'ancienne continuera de fonctionner pendant une période de transition.
</ResponseField>

<ResponseField name="created" type="number">
  Timestamp Unix en **secondes** indiquant le moment où Boble a émis l'événement.
</ResponseField>

<ResponseField name="livemode" type="boolean">
  `true` en production, `false` dans tout environnement de test (y compris un événement de test déclenché manuellement hors production).
</ResponseField>

<ResponseField name="data.object" type="object">
  Le payload spécifique à l'événement. Sa forme dépend du `type`. La structure complète de chaque événement est dans [Types d'événements](/fr/webhooks/event-types).
</ResponseField>

## Les en-têtes

Boble ajoute trois en-têtes sur chaque livraison :

| En-tête           | Valeur                                                                                            |
| ----------------- | ------------------------------------------------------------------------------------------------- |
| `Content-Type`    | `application/json`                                                                                |
| `User-Agent`      | `Boble-Webhook/1.0` — utile pour filtrer le trafic Boble dans vos logs.                           |
| `Boble-Signature` | La signature HMAC du body. Voir [Vérification de signature](/fr/webhooks/signature-verification). |
| `Boble-Event-Id`  | Correspond à l'`id` dans le body. Un renvoi utilise un **nouvel** id.                             |

## Le même acheteur partout

Tous les événements `user_promotion.*` partagent les mêmes objets imbriqués `user` et `promotion`. Conséquence : une fois que vous savez extraire l'email de l'acheteur d'un événement, le même chemin marche pour tous les événements.

### Objet `user`

| Champ                | Description                                                               |
| -------------------- | ------------------------------------------------------------------------- |
| `id`                 | UUID de l'acheteur.                                                       |
| `email`              | Adresse email. Peut valoir `null` si le compte a été supprimé.            |
| `firstname`          | Prénom. Peut valoir `null`.                                               |
| `lastname`           | Nom. Peut valoir `null`.                                                  |
| `username`           | Nom d'utilisateur sur Boble. Peut valoir `null`.                          |
| `avatar`             | URL complète de l'avatar. `null` si non défini.                           |
| `preferred_language` | Code IETF : `en`, `fr`, `es`. Peut valoir `null`.                         |
| `created_at`         | Timestamp ISO-8601 de la création du compte acheteur. Peut valoir `null`. |

### Objet `promotion`

| Champ      | Description                                                                |
| ---------- | -------------------------------------------------------------------------- |
| `id`       | UUID de la promotion.                                                      |
| `title`    | Titre de la promotion.                                                     |
| `type`     | `free`, `one_time`, ou `subscription`.                                     |
| `currency` | Code ISO-4217 en majuscules : `EUR`, `USD`, `GBP`.                         |
| `amount`   | Prix dans la devise de la promotion. `null` pour les promotions gratuites. |

## Le même id à travers le cycle de vie

Pour un même couple acheteur/promotion, le `data.object.id` reste **identique** sur tous les événements. Le premier `access_granted` et un `canceled` ultérieur pour le même accès porteront le **même** `data.object.id`. Utilisez-le si vous voulez suivre le parcours d'un acheteur d'un événement à l'autre.

L'`id` racine (et l'en-tête `Boble-Event-Id`) est unique **par livraison** — différent à chaque événement, et de nouveau différent à chaque renvoi.

## Éviter la double exécution

Boble ne réessaie pas automatiquement, mais un **renvoi** manuel envoie une livraison flambant neuve, avec le même `data.object` mais un `id` d'enveloppe différent.

Si votre handler a des effets de bord que vous ne voulez pas appliquer deux fois (envoyer un reçu, donner un rôle externe), basez votre clé d'idempotence sur une valeur dérivée de `data.object` — typiquement `data.object.id` combiné au `type` de l'événement. Ainsi, l'original et tous ses renvois retombent sur la même clé.
