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

# Octroi de droits

> La charge utile envoyée à votre point de terminaison webhook lorsqu'un octroi de droits est créé, livré, échoue, ou est révoqué.

## Événements Webhook d'Octroi de Droits

Ces événements se déclenchent chaque fois que l'état de l'octroi de droits d'un client change, par exemple lorsqu'une clé de licence est générée, un rôle Discord est attribué, un lien de téléchargement est provisionné, ou l'accès est révoqué. Abonnez-vous à ces événements pour garder votre application synchronisée avec ce à quoi chaque client peut accéder.

| Event                         | Description                                                                                                                                                                                                            |
| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `entitlement_grant.created`   | Une nouvelle ligne de subvention a été créée. Le statut est `delivered` immédiatement pour les clés de licence auto-remplies, et `pending` pour les clés de licence remplies manuellement et chaque autre intégration. |
| `entitlement_grant.delivered` | La subvention passe au statut livré. Le client a maintenant accès à la plateforme, au fichier ou à la clé de licence attribués.                                                                                        |
| `entitlement_grant.failed`    | La livraison a échoué et ne sera pas retentée. Inspectez `error_code` et `error_message`.                                                                                                                              |
| `entitlement_grant.revoked`   | L'accès a été retiré. Inspectez `revocation_reason` pour comprendre pourquoi.                                                                                                                                          |

Les quatre événements partagent la même charge utile `EntitlementGrantResponse` documentée dans le schéma ci-dessous.

***

## Déclencheurs d'Événements

### entitlement\_grant.created

Une ligne d'octroi vient d'être insérée. L'octroi a toujours une `id` stable à partir de ce moment, même si son statut change. Utilisez cet événement pour enregistrer que l'exécution est en cours.

Pour les **clés de licence auto-remplies**, la ligne est insérée directement avec `status: "delivered"` et `delivered_at` remplis, donc un seul événement `created` est suivi de aucun autre changement d'état à moins que la subvention ne soit révoquée plus tard.

Pour les **clés de licence remplies manuellement** (droits avec `fulfillment_mode: manual`), la ligne arrive avec `status: "pending"` et aucun objet `license_key` — il n'y a pas encore de clé. Cet événement est votre signal qu'une clé attend l'exécution ; fournissez-la via [`POST /grants/{grant_id}/license-key`](/api-reference/entitlements/fulfill-license-key), qui déclenche alors `entitlement_grant.delivered`. Voir [Exécution manuelle](/features/license-keys#manual-fulfillment).

Pour **toute autre intégration**, la ligne arrive avec `status: "pending"`. Un événement `delivered` ou `failed` suit une fois la livraison terminée :

* **Intégrations basées sur OAuth** (Discord, GitHub, Notion) incluent un `oauth_url` que le client doit visiter pour compléter son consentement. La subvention reste `pending` jusqu'à ce que le client autorise.
* **Intégrations directes à la plateforme** (Telegram, Framer, Fichiers numériques) restent dans `pending` seulement brièvement pendant que l'appel de la plateforme s'exécute, puis passent à `delivered`.

### entitlement\_grant.delivered

La subvention est passée de `pending` à `delivered`. Le client a maintenant l'accès décrit par le droit. Utilisez cet événement pour débloquer des fonctionnalités dépendantes dans vos propres systèmes, par exemple pour provisionner un espace de travail, envoyer un e-mail de bienvenue personnalisé, ou marquer un indicateur "rempli".

Le champ `delivered_at` de la charge utile capture le moment où la livraison a été accomplie. Pour les subventions qui arrivent `delivered` à la création, vous recevrez les événements `created` et `delivered` consécutivement.

### entitlement\_grant.failed

La livraison a été tentée et a échoué avec une erreur non réessayer. Les champs `error_code` et `error_message` expliquent l'échec. Les causes courantes incluent un token OAuth révoqué, une permission de plateforme refusée, ou une cible manquante (par exemple, un serveur Discord supprimé).

<Tip>
  Traitez `entitlement_grant.failed` comme une action à entreprendre. Le client a payé mais n'a pas obtenu d'accès. Signalez les échecs à votre équipe de support ou déclenchez une nouvelle subvention une fois que le problème sous-jacent est résolu.
</Tip>

### entitlement\_grant.revoked

L'accès a été retiré au niveau de la plateforme : rôle Discord supprimé, collaborateur GitHub supprimé, clé de licence désactivée, URL de téléchargement de fichiers non émises. Le champ `revocation_reason` enregistre le déclencheur.

| `revocation_reason`      | Déclencheur                                                                                                                                                                                                                                                                                                                                                                           |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `subscription_cancelled` | L'abonnement du client a été annulé (événement `subscription.cancelled`).                                                                                                                                                                                                                                                                                                             |
| `subscription_on_hold`   | L'abonnement est en attente en raison d'un renouvellement échoué (`subscription.on_hold`). Récupérable : une tentative réussie produit une nouvelle subvention.                                                                                                                                                                                                                       |
| `subscription_expired`   | L'abonnement a atteint la fin de sa durée (`subscription.expired`).                                                                                                                                                                                                                                                                                                                   |
| `plan_changed`           | Le plan a changé ; les anciennes subventions sont révoquées avant que de nouvelles ne soient émises (`subscription.plan_changed`).                                                                                                                                                                                                                                                    |
| `refund`                 | Un remboursement a été traité pour le paiement initial unique (`refund.succeeded`).                                                                                                                                                                                                                                                                                                   |
| `manual`                 | Un marchand a révoqué la subvention via l'API ou le tableau de bord. Les révocations manuelles ne sont pas auto-attribuées lors du renouvellement de l'abonnement.                                                                                                                                                                                                                    |
| `license_key_disabled`   | La clé de licence derrière une subvention de clé de licence a été désactivée. La subvention est réactivée automatiquement si la clé est réactivée.                                                                                                                                                                                                                                    |
| `platform_external`      | Le côté plateforme d'une intégration s'est désynchronisé (par exemple, un rôle Discord supprimé manuellement, l'application GitHub a perdu l'accès au dépôt, ou un relevé de comptes a détecté une cible manquante). La subvention n'est pas automatiquement réattribuée lors du renouvellement de l'abonnement jusqu'à ce que le problème de la plateforme sous-jacente soit résolu. |

***

## Variantes de charge utile

Le champ `data` est toujours un objet `EntitlementGrantResponse`. La charge utile transporte un champ `integration_type` (par exemple `license_key`, `digital_files`, `discord`) pour que vous puissiez reconnaître directement le type de subvention. Deux types d'intégration attachent également des objets imbriqués supplémentaires :

* **`license_key`** est inclus lorsque `integration_type` est `license_key` **et qu'une clé a été émise**. Il contient la clé générée, l'expiration et l'utilisation d'activation. Pour une subvention remplie manuellement toujours dans `pending`, cet objet est `null` jusqu'à ce que vous remplissiez la subvention.
* **`digital_product_delivery`** est inclus lorsque `integration_type` est `digital_files`. Il contient des URL de téléchargement présignées, le `instructions` optionnel, et le `external_url` optionnel.

Pour tous les autres types d'intégration (Discord, GitHub, Telegram, Framer, Notion) les deux champs imbriqués sont `null` ; la configuration pertinente est capturée dans le droit lui-même, pas dans la subvention.

***

## Charges utiles d'exemple

### Clé de licence délivrée (`entitlement_grant.delivered`)

```json theme={null} theme={null}
{
  "business_id": "bus_H4ekzPSlcg",
  "type": "entitlement_grant.delivered",
  "timestamp": "2026-05-01T10:25:33.000000Z",
  "data": {
    "id": "grant_8VbC6JDZzPEqfBPUdpj0K",
    "business_id": "bus_H4ekzPSlcg",
    "entitlement_id": "ent_9xY2bKwQn5MjRpL8d",
    "customer_id": "cus_abc123",
    "external_id": "lk_AAA111BBB222",
    "payment_id": "pay_a1b2c3d4",
    "subscription_id": null,
    "status": "delivered",
    "integration_type": "license_key",
    "license_key": {
      "key": "PRO-AAAA-BBBB-CCCC-DDDD",
      "expires_at": "2027-05-01T00:00:00Z",
      "activations_used": 0,
      "activations_limit": 5
    },
    "digital_product_delivery": null,
    "delivered_at": "2026-05-01T10:25:33Z",
    "revoked_at": null,
    "revocation_reason": null,
    "error_code": null,
    "error_message": null,
    "oauth_url": null,
    "oauth_expires_at": null,
    "metadata": null,
    "created_at": "2026-05-01T10:25:33Z",
    "updated_at": "2026-05-01T10:25:33Z"
  }
}
```

### Clé de licence en attente de réalisation manuelle (`entitlement_grant.created`)

Déclenché lorsque qu'un client achète un produit dont le droit de clé de licence utilise `fulfillment_mode: manual`. La subvention est `pending` sans objet `license_key` pour l'instant — le marchand doit fournir la clé.

```json theme={null} theme={null}
{
  "business_id": "bus_H4ekzPSlcg",
  "type": "entitlement_grant.created",
  "timestamp": "2026-05-01T10:24:00.000000Z",
  "data": {
    "id": "grant_8VbC6JDZzPEqfBPUdpj0K",
    "business_id": "bus_H4ekzPSlcg",
    "entitlement_id": "ent_9xY2bKwQn5MjRpL8d",
    "customer_id": "cus_abc123",
    "external_id": null,
    "payment_id": "pay_a1b2c3d4",
    "subscription_id": null,
    "status": "pending",
    "integration_type": "license_key",
    "license_key": null,
    "digital_product_delivery": null,
    "delivered_at": null,
    "revoked_at": null,
    "revocation_reason": null,
    "error_code": null,
    "error_message": null,
    "oauth_url": null,
    "oauth_expires_at": null,
    "metadata": null,
    "created_at": "2026-05-01T10:24:00Z",
    "updated_at": "2026-05-01T10:24:00Z"
  }
}
```

### Fichiers numériques délivrés (`entitlement_grant.delivered`)

```json theme={null} theme={null}
{
  "business_id": "bus_H4ekzPSlcg",
  "type": "entitlement_grant.delivered",
  "timestamp": "2026-05-01T10:30:12.000000Z",
  "data": {
    "id": "grant_2P9rQwYvMxTnKoCb4",
    "business_id": "bus_H4ekzPSlcg",
    "entitlement_id": "ent_files_J3kLmN4oP5",
    "customer_id": "cus_abc123",
    "external_id": "pay_a1b2c3d4",
    "payment_id": "pay_a1b2c3d4",
    "subscription_id": null,
    "status": "delivered",
    "integration_type": "digital_files",
    "license_key": null,
    "digital_product_delivery": {
      "files": [
        {
          "file_id": "df_a4f6c1de",
          "download_url": "https://files.dodopayments.com/.../pro-bundle.zip?Signature=...",
          "filename": "pro-bundle.zip",
          "content_type": "application/zip",
          "file_size": 18742390,
          "expires_in": 900
        }
      ],
      "instructions": "Unzip and run setup.sh from the project root.",
      "external_url": null
    },
    "delivered_at": "2026-05-01T10:30:12Z",
    "revoked_at": null,
    "revocation_reason": null,
    "error_code": null,
    "error_message": null,
    "oauth_url": null,
    "oauth_expires_at": null,
    "metadata": null,
    "created_at": "2026-05-01T10:30:12Z",
    "updated_at": "2026-05-01T10:30:12Z"
  }
}
```

### Rôle Discord créé et en attente (`entitlement_grant.created`)

```json theme={null} theme={null}
{
  "business_id": "bus_H4ekzPSlcg",
  "type": "entitlement_grant.created",
  "timestamp": "2026-05-01T10:31:00.000000Z",
  "data": {
    "id": "grant_DiscordPending5L",
    "business_id": "bus_H4ekzPSlcg",
    "entitlement_id": "ent_discord_patrons",
    "customer_id": "cus_abc123",
    "external_id": "sub_pro_monthly_001",
    "payment_id": null,
    "subscription_id": "sub_pro_monthly_001",
    "status": "pending",
    "integration_type": "discord",
    "license_key": null,
    "digital_product_delivery": null,
    "delivered_at": null,
    "revoked_at": null,
    "revocation_reason": null,
    "error_code": null,
    "error_message": null,
    "oauth_url": "https://discord.com/oauth2/authorize?...",
    "oauth_expires_at": "2026-05-08T10:31:00Z",
    "metadata": null,
    "created_at": "2026-05-01T10:31:00Z",
    "updated_at": "2026-05-01T10:31:00Z"
  }
}
```

### Subvention révoquée lors de l'annulation d'un abonnement (`entitlement_grant.revoked`)

```json theme={null} theme={null}
{
  "business_id": "bus_H4ekzPSlcg",
  "type": "entitlement_grant.revoked",
  "timestamp": "2026-06-15T08:12:44.000000Z",
  "data": {
    "id": "grant_8VbC6JDZzPEqfBPUdpj0K",
    "business_id": "bus_H4ekzPSlcg",
    "entitlement_id": "ent_9xY2bKwQn5MjRpL8d",
    "customer_id": "cus_abc123",
    "external_id": "sub_pro_monthly_001",
    "payment_id": null,
    "subscription_id": "sub_pro_monthly_001",
    "status": "revoked",
    "integration_type": "license_key",
    "revocation_reason": "subscription_cancelled",
    "license_key": {
      "key": "PRO-AAAA-BBBB-CCCC-DDDD",
      "expires_at": null,
      "activations_used": 1,
      "activations_limit": 5
    },
    "digital_product_delivery": null,
    "delivered_at": "2026-05-01T10:25:33Z",
    "revoked_at": "2026-06-15T08:12:44Z",
    "error_code": null,
    "error_message": null,
    "oauth_url": null,
    "oauth_expires_at": null,
    "metadata": null,
    "created_at": "2026-05-01T10:25:33Z",
    "updated_at": "2026-06-15T08:12:44Z"
  }
}
```

### Livraison échouée (`entitlement_grant.failed`)

```json theme={null} theme={null}
{
  "business_id": "bus_H4ekzPSlcg",
  "type": "entitlement_grant.failed",
  "timestamp": "2026-05-01T10:36:21.000000Z",
  "data": {
    "id": "grant_GhFailed7Z",
    "business_id": "bus_H4ekzPSlcg",
    "entitlement_id": "ent_github_repo",
    "customer_id": "cus_abc123",
    "external_id": "pay_a1b2c3d4",
    "payment_id": "pay_a1b2c3d4",
    "subscription_id": null,
    "status": "failed",
    "integration_type": "github",
    "license_key": null,
    "digital_product_delivery": null,
    "delivered_at": null,
    "revoked_at": null,
    "revocation_reason": null,
    "error_code": "github_permission_denied",
    "error_message": "Repository access could not be granted: the GitHub App installation no longer has permission on this repository.",
    "oauth_url": null,
    "oauth_expires_at": null,
    "metadata": null,
    "created_at": "2026-05-01T10:36:00Z",
    "updated_at": "2026-05-01T10:36:21Z"
  }
}
```

***

## Conseils d'intégration

* **Attendez `entitlement_grant.delivered` avant de débloquer des fonctionnalités dépendantes.** Un événement `payment.succeeded` vous indique que l'argent a été reçu ; il ne vous dit pas que le client a le dépôt GitHub ou le rôle Discord. L'événement `delivered` est la source de vérité pour l'exécution.
* **Mappez `revocation_reason` aux flux de rétention.** Une révocation `subscription_on_hold` signifie généralement que la carte du client a échoué et que le prochain renouvellement réattribuera l'accès. Une révocation `manual` ou `subscription_cancelled` est intentionnelle. Traitez-les différemment dans la communication avec le client.
* **Utilisez la subvention `id` comme votre clé d'idempotence.** Une seule subvention émet au plus un événement `created` et au plus un événement terminal (`delivered` ou `failed`), et au plus un événement `revoked`. Les re-livraisons du système webhook peuvent répéter les événements ; dédupliquez sur la subvention `id` plus `type`.
* **Lisez `integration_type` pour reconnaître le type de subvention.** La charge utile transporte `integration_type` directement (par exemple `license_key`, `digital_files`, `discord`). Les objets imbriqués `license_key` et `digital_product_delivery` sont remplis une fois que leurs subventions respectives sont livrées ; une subvention de clé de licence remplie manuellement reste `pending` avec `integration_type: "license_key"` et une `null` `license_key` jusqu'à ce que vous l'exécutiez.
* **Pour les subventions basées sur OAuth, présentez `oauth_url` au client.** L'événement `entitlement_grant.created` pour les flux d'abonnés Discord, GitHub, ou Notion inclut un `oauth_url` et `oauth_expires_at`. Envoyez-le par e-mail au client ou affichez-le dans votre application pour débloquer la livraison.
