Passer au contenu principal

É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.
EventDescription
entitlement_grant.createdUne 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.deliveredLa subvention passe au statut livré. Le client a maintenant accès à la plateforme, au fichier ou à la clé de licence attribués.
entitlement_grant.failedLa livraison a échoué et ne sera pas retentée. Inspectez error_code et error_message.
entitlement_grant.revokedL’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, qui déclenche alors entitlement_grant.delivered. Voir Exécution manuelle. 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é).
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.

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_reasonDéclencheur
subscription_cancelledL’abonnement du client a été annulé (événement subscription.cancelled).
subscription_on_holdL’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_expiredL’abonnement a atteint la fin de sa durée (subscription.expired).
plan_changedLe plan a changé ; les anciennes subventions sont révoquées avant que de nouvelles ne soient émises (subscription.plan_changed).
refundUn remboursement a été traité pour le paiement initial unique (refund.succeeded).
manualUn 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_disabledLa 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_externalLe 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)

{
  "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é.
{
  "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)

{
  "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)

{
  "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)

{
  "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)

{
  "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.

Detailed view of a single entitlement grant: who it's for, its lifecycle state, and any integration-specific delivery payload.

brand_id
string
requis

Brand id this grant belongs to.

business_id
string
requis

Identifier of the business that owns the grant.

created_at
string<date-time>
requis

Timestamp when the grant was created.

customer_id
string
requis

Identifier of the customer the grant was issued to.

entitlement_id
string
requis

Identifier of the entitlement this grant was issued from.

id
string
requis

Unique identifier of the grant.

integration_type
enum<string>
requis

The integration type of the grant's entitlement (e.g. license_key).

Options disponibles:
discord,
telegram,
github,
figma,
framer,
notion,
digital_files,
license_key
metadata
object
requis

Arbitrary key-value metadata recorded on the grant.

status
enum<string>
requis

Lifecycle status of the grant.

Options disponibles:
Pending,
Delivered,
Failed,
Revoked
updated_at
string<date-time>
requis

Timestamp when the grant was last modified.

delivered_at
string<date-time> | null

Timestamp when the grant transitioned to delivered, when applicable.

digital_product_delivery
Digital Product Delivery · object

Digital-product-delivery payload, present when the entitlement integration is digital_files.

error_code
string | null

Machine-readable code reported when delivery failed, when applicable.

error_message
string | null

Human-readable message reported when delivery failed, when applicable.

license_key
object

License-key delivery payload, present when the entitlement integration is license_key.

oauth_expires_at
string<date-time> | null

Timestamp when oauth_url stops being valid, when applicable.

oauth_url
string | null

Customer-facing OAuth URL for OAuth-style integrations. Populated during the customer-portal accept flow; null until the customer completes that step, and on grants for non-OAuth integrations.

payment_id
string | null

Identifier of the payment that triggered this grant, when applicable.

revocation_reason
string | null

Reason recorded when the grant was revoked, when applicable.

revoked_at
string<date-time> | null

Timestamp when the grant transitioned to revoked, when applicable.

subscription_id
string | null

Identifier of the subscription that triggered this grant, when applicable.

Dernière modification le 9 juin 2026