平台网页钩子
平台 Webhooks 会在你的组织或其项目中发生某些事情时向你控制的 URL 发送 HTTP 请求。例如,项目暂停或成员加入都会触发事件。你可以使用它们来构建事件驱动的集成,而不需要轮询管理 API。
🌐 Platform Webhooks send an HTTP request to a URL you control when something happens on your organization or one of its projects. For example, a project pausing and a member joining both raise events. Use them to build event-driven integrations without polling the Management API.
平台 Webhooks 是一个抢先体验功能
平台 Webhooks 对在早期访问白名单上的组织可用。来自白名单之外的组织的请求会被 API 拒绝,并返回 403 和错误代码 access_disabled。
🌐 Platform Webhooks is available to organizations on an early access allowlist. The API rejects a request from an organization outside the allowlist with 403 and the error code access_disabled.
有关完整的 API 参考,请参阅管理 API 参考中的 组织 Webhooks 和 项目 Webhooks。
🌐 For the complete API reference, see Organization Webhooks and Project Webhooks in the Management API reference.
- 设置端点 会创建接收事件的配置。
- 验证 webhook 签名 是你的监听器在信任一次传送之前所需要的检查。
- 发送测试事件 会触发向你的监听器发送一个测试事件。
- 投递行为 涉及重试和历史保留。
- 幂等性和顺序 涵盖了幂等性、重复以及到达顺序。
- 最佳实践 涵盖了编写可靠、健壮的 webhook 监听器的最佳方法。
概念 #
🌐 Concepts
终端 #
🌐 Endpoints
一个 endpoint 是用于配置事件传送位置和方式的设置。它包含一个 URL、一个签名密钥、它订阅的事件类型,以及可选的自定义头信息。
🌐 An endpoint is a configuration for where and how to deliver events. It has a URL, a signing secret, the event types it subscribes to, and optional custom headers.
你可以为一个组织或组织内的特定项目注册一个端点。每个端点可以订阅一个或多个事件类型,或者订阅 * 来接收所有事件。
🌐 You register an endpoint against an organization or a specific project within an organization. Each endpoint subscribes to one or more event types, or to * to receive all events.
一个端点的范围要么是针对整个组织,要么是针对单个项目:
🌐 An endpoint is scoped to either an organization or a single project:
- 一个面向组织的端点会接收组织中的每个组织事件和每个项目事件。
- 一个项目范围内的端点只会接收该项目的事件。
端点 URL 必须可以公开访问,并且必须解析到一个非保留的 IP 地址。
🌐 Endpoint URLs must be publicly reachable and must resolve to a non-reserved IP address.
活动 #
🌐 Events
一个 事件 代表了在你的组织或项目中发生的事情。每个事件都有一个 ID、一个类型字符串,比如 v1.project.paused,一个时间戳,以及一个有效载荷。
🌐 An event represents something that happened in your organization or project. Each event has an ID, a type string such as v1.project.paused, a timestamp, and a payload.
每种事件类型都以一个版本开头,例如 v1,它表示特定的 payload 形状。相同事件的不同版本可能有不同的载荷形状。
🌐 Each event type starts with a version, for example v1, that represents the specific payload shape. Different versions of the same event can have different payload shapes.
一个项目事件会发送到匹配的项目范围端点以及同一组织内的任何组织范围端点。每个端点都会收到事件的独立副本,并带有自己的 id。
🌐 A project event is delivered to both a matching project-scoped endpoint and any organization-scoped endpoints in the same organization. Each endpoint gets its own copy of the event, with its own id.
有关所有事件类型及其有效载荷的完整列表,请参见 平台 Webhook 事件。
🌐 For the full list of event types and their payloads, see Platform Webhook Events.
送货 #
🌐 Deliveries
一次 delivery 是对特定事件向特定端点的一个 HTTP POST 尝试。Supabase 会记录每一次 delivery 尝试以及它的结果、响应状态和响应内容。
🌐 A delivery is one HTTP POST attempt for a specific event to a specific endpoint. Supabase records every delivery attempt along with its outcome, response status, and response body.
信封和有效载荷 #
🌐 Envelope and payload
每次交付都会发送一个 JSON 数据体,其结构如下,无论类型如何:
🌐 Every delivery sends a JSON body with the following structure regardless of the type:
{ "id": "<event UUID>", "type": "<event-type>", "timestamp": "<ISO 8601 UTC>", "payload": { "organization_slug": "<org-slug>", "project_ref": "<project-ref>" // event-specific fields }}例如:
🌐 For example:
{ "id": "01941e0e-e04e-7e43-b8e5-c4d79862b8df", "type": "v1.project.paused", "timestamp": "2025-01-01T12:00:00.000Z", "payload": { "organization_slug": "my-org", "project_ref": "abcdefghijklmnoprstu", "reason": "inactivity", "actor": null }}payload 对象是特定于事件的。对于组织范围的事件,project_ref 总是 null。有关每个事件有效载荷字段的详细信息,请参见 平台 Webhook 事件。
🌐 The payload object is event-specific. For organization-scoped events, project_ref is always null. For details on each event's payload fields, see Platform Webhook Events.
版本控制 #
🌐 Versioning
每种事件类型都会嵌入一个版本前缀,用来标识有效负载的结构。v1.project.paused 中的 v1 表示该事件的有效负载遵循版本 1 的格式。当有效负载发生破坏性更改——比如字段被删除、重命名或类型改变——就会引入一个新版本。
🌐 Each event type embeds a version prefix that identifies the payload schema. The v1 in v1.project.paused means the payload follows the version 1 shape for that event. A new version is introduced when the payload changes in a breaking way — fields are removed, renamed, or given a different type.
当 Supabase 发布新版本时,两个版本会在过渡期同时发布。为 v1 构建的处理器在你添加对 v2 的支持时,会不间断地接收 v1 事件。过渡窗口是有限的,所以把不认识的版本当作升级处理器的信号。
🌐 When Supabase introduces a new version, both versions are emitted simultaneously for a transition period. A handler built for v1 keeps receiving v1 events uninterrupted while you add support for v2. The transition window is finite, so treat an unrecognized version as a signal to upgrade your handler.
可以在现有版本中添加新字段而无需升级版本。设计你的处理器时,让它忽略不认识的字段,这样它在同一版本内仍能向前兼容。
🌐 New fields can be added to an existing version without a version bump. Design your handler to ignore fields it does not recognize so that it stays forward compatible within a version.
设置一个端点 #
🌐 Set up an endpoint
通过管理 API 创建一个端点:
🌐 Create an endpoint through the Management API:
- 要创建一个组织范围的端点:
POST https://api.supabase.com/v2/organizations/{organization_slug}/webhooks/endpoints - 要创建一个项目范围的端点:
POST https://api.supabase.com/v2/projects/{project_ref}/webhooks/endpoints
一个端点具有以下属性:
🌐 An endpoint takes these attributes:
| 属性 | 必填 | 备注 |
|---|---|---|
url | 是 | 必须使用解析到非保留 IP 地址的域名。API 会拒绝解析到保留范围的 URL。 |
event_types | 是 | 至少选择一种类型。端点只接收你列出的类型。使用 * 可以接收所有事件。 |
signing_secret | 是 | 8 到 64 个字符。每次传送都会用它进行签名。 |
custom_headers | 否 | 额外的请求头。键和值都是字符串,每一项都必须是有效的 HTTP 头。 |
description | 否 | 自由文本,用于标识端点。 |
enabled | 否 | 默认值是 true。禁用时,队列中已有事件会生成 skipped 状态的传送记录但不实际发送;禁用后到达的事件会被丢弃且不会生成传送记录。 |
curl -X POST 'https://api.supabase.com/v2/organizations/{your-org-slug}/webhooks/endpoints' \ -H 'Authorization: Bearer <personal-access-token>' \ -H 'Content-Type: application/json' \ -d '{ "data": { "type": "endpoint", "attributes": { "url": "https://your-domain.com/webhooks/supabase", "signing_secret": "your-signing-secret", "description": "Track pause and restore events for all projects", "event_types": [ { "type": "v1.project.paused" }, { "type": "v1.project.restored" } ] } } }'要接收所有事件类型,请将 event_types 设置为 [{ "type": "*" }]。使用 * 还包括将来新增的事件类型,因此在设计处理程序时,要能够优雅地处理未识别的类型——参见 Versioning。
🌐 To receive all event types, set event_types to [{ "type": "*" }]. Using * also covers event types added in the future, so design your handler to handle unrecognized types gracefully — see Versioning.
成功的请求会返回带有端点 ID 的 201,你可以用它来发送测试事件、列出传送记录,以及更新或删除端点。
🌐 A successful request returns 201 with the endpoint's ID, which you can use to send test events, list deliveries, and update or delete the endpoint.
有关完整的操作集,请参阅管理 API 参考中的项目 Webhooks和组织 Webhooks部分。每个操作在两种范围内都存在。
🌐 For the full set of operations, see the Project webhooks and Organization webhooks sections of the Management API reference. Every operation exists at both scopes.
验证网页钩子签名 #
🌐 Verify webhook signature
每次投递都遵循 标准 Webhooks 规范,并携带以下这些头信息:
🌐 Every delivery follows the Standard Webhooks specification and carries these headers:
| 头部 | 描述 |
|---|---|
webhook-id | 事件 ID。每次重试同一事件时都是一样的。 |
webhook-timestamp | Unix 时间戳(秒),表示投递尝试签名的时间。 |
webhook-signature | 对 {webhook-id}.{webhook-timestamp}.{raw body} 的 HMAC-SHA256 签名,形式为 v1,<base64>。 |
在将其解析为 JSON 之前,先检查一下 原始请求体:
🌐 Verify against the raw request body before you parse it as JSON:
如果你的 signing_secret 是一个以 whsec_ 为前缀的 Base64 编码值:
🌐 If your signing_secret is a base64-encoded value prefixed with whsec_:
import { Webhook } from 'standardwebhooks'const webhook = new Webhook(process.env.WEBHOOK_SECRET)try { const event = webhook.verify(rawBody, { 'webhook-id': req.headers['webhook-id'], 'webhook-timestamp': req.headers['webhook-timestamp'], 'webhook-signature': req.headers['webhook-signature'], }) // event is the verified, event.payload can be used} catch { return res.status(400).send('Invalid signature')}关于其他语言的验证库,请参阅标准 Webhooks 文档。
🌐 For verification libraries in other languages, see the Standard Webhooks documentation.
在不漏掉投递的情况下更换签名密钥:
🌐 To rotate a signing secret without missing deliveries:
- 将新的密钥添加到你的监听器,并接受用旧密钥或新密钥签署的内容。
- 把端点更新为使用新的密钥。
- 从你的监听器中移除旧的秘密。
发送一个测试事件 #
🌐 Send a test event
使用测试事件来验证端到端的连接。它会经过与真实事件相同的流程,并存储在传送历史中,只是自动重试功能被禁用了。
🌐 Use a test event to verify connectivity end to end. It goes through the same pipeline as a real event and is stored in delivery history, except that automatic retries are disabled.
curl -X POST https://api.supabase.com/v2/organizations/{your-org-slug}/webhooks/endpoints/{your-endpoint-id}/test \ -H 'Authorization: Bearer <personal-access-token>' \ -H 'Content-Type: application/json' \ --data '{ "data": { "type": "event", "attributes": { "type": "v1.project.paused" } } }'请求体中的事件类型是可选的。如果没有指定,API 会使用该端点订阅的类型。
🌐 The event type in the body is optional. Without one, the API uses a type the endpoint subscribes to.
测试事件的负载包含 "is_test": true,所以在触发任何副作用之前先检查一下这个字段。
🌐 A test event's payload includes "is_test": true, so check that field before you trigger any side effect.
送货行为 #
🌐 Delivery behavior
Supabase 认为,当你的端点在 20 秒内返回 2xx 响应时,投递是成功的。非 2xx 响应或超时都会被视为失败,并触发带指数回退的重试:
🌐 Supabase considers a delivery successful when your endpoint returns a 2xx response within 20 seconds. A non-2xx response or a timeout counts as a failure and triggers a retry with exponential backoff:
| 尝试次数 | 上一次失败后的等待时间 | 事件发生后的大约时间 |
|---|---|---|
| 1 | 不适用 | 立即 |
| 2 | 10秒 | 约10秒 |
| 3 | 30秒 | 约40秒 |
| 4 | 1分30秒 | 约2分10秒 |
| 5(最后一次) | 4分30秒 | 约6分40秒 |
在五次失败尝试后,送达会被标记为失败,不会再重试。Supabase 不会在发生这种情况时通知你——查看送达历史记录以找到永久失败的送达。
🌐 After five failed attempts, the delivery is marked as failed with no further retries. Supabase does not notify you when this happens — check delivery history to find permanently failed deliveries.
你可以手动重试一次投递。手动重试只尝试一次:如果失败,不会有自动重试。
🌐 You can retry a delivery manually. A manual retry is a single attempt: if it fails, no automatic retries follow.
投递记录会保留14天。过期的投递不再显示,也无法重试。
🌐 Delivery history is retained for 14 days. An expired delivery is no longer listed and can't be retried.
幂等性和顺序 #
🌐 Idempotency and ordering
投递至少一次:Supabase 会重试失败的投递,所以你的端点可能会收到相同的事件不止一次。使用请求体中的 id 字段作为去重键。同一个 id 总是表示同一个事件。
🌐 Deliveries are at-least-once: Supabase retries failed deliveries, so your endpoint may receive the same event more than once. Use the id field in the request body as a deduplication key. The same id always represents the same event.
由于网络延迟或小问题,送达的消息和事件也可能会出现顺序错乱。你需要设计你的 webhook 监听器来处理重复和顺序错误的事件。
🌐 Deliveries and events may also arrive out of order due to network delays or hiccups. You need to design your webhook listener to account for duplicates and out-of-order events.
最佳实践 #
🌐 Best practices
使用 webhook 事件和构建可靠、生产级 webhook 监听器的最佳做法:
🌐 Best practices for consuming webhook events and building a reliable, production-grade webhook listener:
- 在处理事件之前,务必先验证一下 webhook 的签名。
- 在处理有效负载之前,先检查事件类型的版本前缀。对于不支持的版本,不要处理,直接返回
2xx,并对未识别的版本进行日志记录或警报——过渡窗口是有限的,所以尽快升级你的处理器。 - 立即响应 webhook,并异步处理每个事件,例如放到任务队列里。
- 在持久化存储中跟踪已经处理过的事件以去重,例如在数据库中。
- 记录上一次处理事件的时间戳,并丢弃同类型的过时事件。不要依赖事件的顺序。
- 把 webhook 事件当作触发动作的信号,而不是资源的状态。
- 把 webhook 的自动重试当作防止网络分区或超时的保障,而不是用来补救监听器逻辑错误。
- 定期检查发送历史记录,查看永久失败的发送。Supabase 不会在所有重试耗尽时通知你。