Entitlement Grant
The payload sent to your webhook endpoint when an entitlement grant is created, delivered, fails, or is revoked.
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.
Entitlement Grant Webhook Events
These events fire whenever a customer’s entitlement grant changes state, for example when a license key is generated, a Discord role is assigned, a download link is provisioned, or access is revoked. Subscribe to these events to keep your application in sync with what each customer can access.| Event | Description |
|---|---|
entitlement_grant.created | A new grant row was created. Status is delivered immediately for license keys, and pending for every other integration. |
entitlement_grant.delivered | The grant transitions to delivered. The customer now has access to the entitled platform, file, or license key. |
entitlement_grant.failed | Delivery failed and is not being retried. Inspect error_code and error_message. |
entitlement_grant.revoked | Access was withdrawn. Inspect revocation_reason to understand why. |
EntitlementGrantResponse payload documented in the schema below.
Event Triggers
entitlement_grant.created
A grant row was just inserted. The grant always has a stableid from this point on, even if its status changes. Use this event to record that fulfillment is in progress.
For license keys the row is inserted directly with status: "delivered" and delivered_at populated, so a single created event is followed by no further state changes unless the grant is later revoked.
For every other integration the row arrives with status: "pending". A delivered or failed event follows once delivery completes:
- OAuth-based integrations (Discord, GitHub, Notion) include an
oauth_urlthe customer must visit to complete consent. The grant stayspendinguntil the customer authorizes. - Platform-direct integrations (Telegram, Framer, Digital Files) sit in
pendingonly briefly while the platform call runs, then move todelivered.
entitlement_grant.delivered
The grant transitioned frompending to delivered. The customer now has the access described by the entitlement. Use this event to unlock dependent features in your own systems, for example to provision a workspace, send a custom welcome email, or mark a “fulfilled” flag.
The payload’s delivered_at field captures when delivery completed. For grants that arrived delivered on creation, you’ll receive created and delivered events back to back.
entitlement_grant.failed
Delivery was attempted and failed with a non-retryable error. Theerror_code and error_message fields explain the failure. Common causes include a revoked OAuth token, a denied platform permission, or a missing target (e.g., a deleted Discord guild).
entitlement_grant.revoked
Access was withdrawn at the platform level: Discord role removed, GitHub collaborator removed, license key disabled, file download URLs no longer issued. Therevocation_reason field records the trigger.
revocation_reason | Trigger |
|---|---|
subscription_cancelled | The customer’s subscription was cancelled (subscription.cancelled event). |
subscription_on_hold | The subscription is on hold due to failed renewal (subscription.on_hold). Recoverable: a successful retry produces a re-grant. |
subscription_expired | The subscription reached the end of its term (subscription.expired). |
plan_changed | The plan changed; old grants are revoked before new ones are issued (subscription.plan_changed). |
refund | A refund was processed for the original one-time payment (refund.succeeded). |
manual | A merchant revoked the grant via the API or dashboard. Manual revokes are not auto-regranted on subscription renewal. |
license_key_disabled | The license key behind a license-key grant was disabled. The grant is re-activated automatically if the key is re-enabled. |
platform_external | The platform side of an integration drifted out of sync (for example, a Discord role was removed manually, the GitHub App lost repository access, or a reconciliation pass detected a missing target). The grant is not auto-regranted on subscription renewal until the underlying platform issue is resolved. |
Payload Variants
Thedata field is always an EntitlementGrantResponse object. Two integration types attach extra nested objects:
license_keyis included when the entitlement integration type islicense_key. It contains the generated key, expiry, and activation usage.digital_product_deliveryis included when the integration type isdigital_files. It contains presigned download URLs, the optionalinstructions, and the optionalexternal_url.
null; the relevant configuration is captured in the entitlement itself, not the grant.
Sample Payloads
License key delivered (entitlement_grant.delivered)
Digital files delivered (entitlement_grant.delivered)
Discord role created and pending (entitlement_grant.created)
Grant revoked on subscription cancellation (entitlement_grant.revoked)
Delivery failed (entitlement_grant.failed)
Integration Tips
- Wait for
entitlement_grant.deliveredbefore unlocking dependent features. Apayment.succeededevent tells you the money cleared; it does not tell you the customer has the GitHub repo or the Discord role yet. Thedeliveredevent is the source of truth for fulfillment. - Map
revocation_reasonto retention flows. Asubscription_on_holdrevoke usually means the customer’s card failed and the next renewal will re-grant access. Amanualorsubscription_cancelledrevoke is intentional. Treat them differently in customer messaging. - Use the grant
idas your idempotency key. A single grant emits at most onecreatedevent and at most one terminal event (deliveredorfailed), and at most onerevokedevent. Re-deliveries from the webhook system can repeat events; dedupe on the grantidplustype. - Inspect
license_keyanddigital_product_deliveryto recognize the integration type. The grant payload itself does not carry the integration type, but exactly one of these nested objects is populated for license-key and digital-files entitlements. - For OAuth-based grants, surface
oauth_urlto the customer. Theentitlement_grant.createdevent for Discord, GitHub, or Notion subscriber flows includes anoauth_urlandoauth_expires_at. Email it to the customer or display it in your app to unblock delivery.
Detailed view of a single entitlement grant: who it's for, its lifecycle state, and any integration-specific delivery payload.
Identifier of the business that owns the grant.
Timestamp when the grant was created.
Identifier of the customer the grant was issued to.
Identifier of the entitlement this grant was issued from.
Unique identifier of the grant.
Arbitrary key-value metadata recorded on the grant.
Lifecycle status of the grant.
Pending, Delivered, Failed, Revoked Timestamp when the grant was last modified.
Timestamp when the grant transitioned to delivered, when applicable.
Digital-product-delivery payload, present when the entitlement
integration is digital_files.
Machine-readable code reported when delivery failed, when applicable.
Human-readable message reported when delivery failed, when applicable.
License-key delivery payload, present when the entitlement integration
is license_key.
Timestamp when oauth_url stops being valid, when applicable.
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.
Identifier of the payment that triggered this grant, when applicable.
Reason recorded when the grant was revoked, when applicable.
Timestamp when the grant transitioned to revoked, when applicable.
Identifier of the subscription that triggered this grant, when applicable.