跳转到主要内容

权限授予 Webhook 事件

每当客户的权限授予状态发生变化时,这些事件将触发,例如生成许可证密钥、分配 Discord 角色、提供下载链接或撤销访问。订阅这些事件以保持您的应用程序与每个客户的可访问性同步。
事件描述
entitlement_grant.created创建了新的授权行。对于自动获取的许可证密钥,状态立即为 delivered,手动获取许可证密钥及其他集成则为 pending
entitlement_grant.delivered授权状态转为已交付。客户现在可以访问授权的平台、文件或许可证密钥。
entitlement_grant.failed交付失败且不再重试。检查 error_codeerror_message
entitlement_grant.revoked访问被撤销。检查 revocation_reason 了解原因。
所有四个事件共享相同的 EntitlementGrantResponse 负载,如下模式所述。

事件触发器

entitlement_grant.created

授予行刚刚插入。从此时起,即使状态发生变化,授予始终具有稳定的 id。使用此事件记录履行正在进行中。 对于自动获取的许可证密钥,授权行直接插入,填充了 status: "delivered"delivered_at,因此只能由一个 created 事件跟随,除非授权后来被撤销,否则不会有进一步的状态变化。 对于手动获取的许可证密钥(使用 fulfillment_mode: manual 的授权),授权行到达时有 status: "pending" 但没有 license_key 对象 — 还没有密钥。此事件表示有一个密钥等待获取;通过POST /grants/{grant_id}/license-key提供它,随后触发 entitlement_grant.delivered。查看手动获取 对于其他集成,授权行到达带有 status: "pending"。完成交付后,紧跟着发生 deliveredfailed 事件:
  • 基于 OAuth 的集成(Discord、GitHub、Notion)包括一个顾客必须访问的 oauth_url 来完成许可。授权保持为 pending,直到顾客授权。
  • 平台直接集成(Telegram、Framer、数字文件)仅在平台调用运行时短暂保持 pending,然后转为 delivered

entitlement_grant.delivered

授权由 pending 转为 delivered。客户现在拥有所描述的授权访问。使用此事件解锁你自己的系统中的依赖功能,例如配置工作区、发送自定义欢迎邮件或标记为 “已完成”。 负载的 delivered_at 字段记录了交付完成的时间。对于已在创建时到达 delivered 的授权,你将连续收到 createddelivered 事件。

entitlement_grant.failed

交付尝试失败且出现不可重试的错误。error_codeerror_message 字段解释了失败原因。常见原因包括撤销 OAuth 令牌、被拒绝的平台权限或缺少的目标(例如,已删除的 Discord 公会)。
entitlement_grant.failed 视为可操作的。顾客付款但未获得访问。向支持团队报告失败或在解决根本问题后触发重授权。

entitlement_grant.revoked

访问在平台层面被撤销:Discord 角色被移除,GitHub 协作者被移除,许可证密钥被禁用,不再提供文件下载网址。 revocation_reason 字段记录触发。
revocation_reason触发器
subscription_cancelled客户的订阅被取消 (subscription.cancelled 事件)。
subscription_on_hold由于续订失败,订阅被暂挂 (subscription.on_hold)。可恢复:成功重试可产生重授权。
subscription_expired订阅已到期 (subscription.expired)。
plan_changed计划更改;旧的授权在发行新的授权前被撤销 (subscription.plan_changed)。
refund为原始一次性付款处理了退款 (refund.succeeded)。
manual商户通过 API 或控制面板撤销了授权。手动撤销不会在订阅续订时自动重新授权。
license_key_disabled许可证密钥授权背后的许可证密钥被禁用。如果密钥被重新启用,授权会自动重新激活。
platform_external集成平台的某一面漂移失调(例如,Discord 角色被手动删除,GitHub 应用失去仓库访问,或和解过程中检测到缺少的目标)。授权在订阅续订时不会自动重新授权,直到底层平台问题得到解决。

负载变体

data 字段始终是一个 EntitlementGrantResponse 对象。负载带有 integration_type 字段(例如 license_key, digital_files, discord),以便你可以直接识别授权类型。两种集成类型还附加了额外的嵌套对象:
  • integration_typelicense_key 且已发布密钥时,包括 license_key。它包含生成的密钥、过期时间和激活使用情况。对于仍在 pending 的手动获取的授权,此对象为 null,直到你完成授权。
  • integration_typedigital_files 时,包括 digital_product_delivery。它包含预签名的下载网址、可选的 instructions 和可选的 external_url
对于所有其他集成类型(Discord、GitHub、Telegram、Framer、Notion),两个嵌套字段均为 null;相关的配置记录在授权本身中,而不是在授权行中。

示例负载

许可证密钥已交付 (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"
  }
}

许可证密钥等待手动获取 (entitlement_grant.created)

当顾客购买了一个使用 fulfillment_mode: manual 的许可证密钥授权的产品时触发。授权行 pending 尚无 license_key 对象 — 商家必须提供密钥。
{
  "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"
  }
}

数字文件已交付 (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"
  }
}

Discord 角色已创建并挂起 (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"
  }
}

订阅取消时撤销授权 (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"
  }
}

交付失败 (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"
  }
}

集成提示

  • 在解锁依赖功能之前等待 entitlement_grant.delivered 一个 payment.succeeded 事件告诉你资金已到位;但并不表示客户已经拥有 GitHub 仓库或 Discord 角色。delivered 事件是获取的可信来源。
  • revocation_reason 映射到保留流程。 subscription_on_hold 撤销通常意味着客户的信用卡失败,下一次续订将重新授予访问权限。manualsubscription_cancelled 撤销是有意的。在客户消息中区别对待。
  • 使用授权 id 作为幂等性密钥。 单个授权最多发出一个 created 事件和最多一个终端事件(deliveredfailed),最多发出一个 revoked 事件。来自 webhook 系统的重新交付可以重复事件;去重使用授权 id 加上 type
  • 读取 integration_type 以识别授权类型。 负载直接携带 integration_type(例如 license_key, digital_files, discord)。license_keydigital_product_delivery 嵌套对象在相应的授权完成后填充;手动获取的许可证密钥授权在你完成前保持 pending 带有 integration_type: "license_key"null license_key
  • 对于基于 OAuth 的授权,将 oauth_url 显示给客户。 Discord、GitHub 或 Notion 订阅流的 entitlement_grant.created 事件包括 oauth_urloauth_expires_at。将其通过电子邮件发送给客户或在你的应用中显示以解除交付阻塞。

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

brand_id
string
必填

Brand id this grant belongs to.

business_id
string
必填

Identifier of the business that owns the grant.

created_at
string<date-time>
必填

Timestamp when the grant was created.

customer_id
string
必填

Identifier of the customer the grant was issued to.

entitlement_id
string
必填

Identifier of the entitlement this grant was issued from.

id
string
必填

Unique identifier of the grant.

integration_type
enum<string>
必填

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

可用选项:
discord,
telegram,
github,
figma,
framer,
notion,
digital_files,
license_key
metadata
object
必填

Arbitrary key-value metadata recorded on the grant.

status
enum<string>
必填

Lifecycle status of the grant.

可用选项:
Pending,
Delivered,
Failed,
Revoked
updated_at
string<date-time>
必填

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.

最后修改于 2026年6月9日