API 参考
Webhook 事件
Webhook 事件类型目录,以及每种事件携带的载荷。
每个 webhook 都以相同的签名信封投递;只有 data 会随 type 不同而变化。本页介绍每一种事件类型。有关签名、投递、重试和重放,请参阅Webhooks。
订单事件
订单 webhook 分为两类,分清两者是用好它们的关键:
order.notification是您据以履约的事件。 它把订单内容交给您处理,在交接时触发一次,其投递被视为关键投递。请围绕它构建您的接受 / 拒绝 / 备好 / 完成流程。order.created、order.paid和order.cancelled是生命周期事件。 每个事件以摘要数据报告订单生命中的一个时刻,用于知悉和对账。由于order.notification只触发一次且不会更新,您要通过这些事件了解该快照之前或之后发生的事情,最重要的是取消,或稍后才完成的支付。
有关这些事件在一笔订单的整个生命周期中的触发顺序,请参阅Webhook 何时触发。
| 类型 | 触发时机 | 订阅它的目的 | data |
|---|---|---|---|
order.validation_requested |
在通知之前,Maple 请您确认订单可以履约(仅当您订阅时) | 在向顾客收款之前预先校验可售状态和价格 | 完整订单内容(与 order.notification 结构相同),另加 validation_id |
order.notification |
订单在校验和支付把关之后交给您履约 | 接收并履约订单,集成的核心 | 完整订单内容(见下文) |
order.created |
订单创建时,早于通知,且内容比通知更精简 | 为分析镜像新订单;发现从未到达您的订单(例如在校验阶段被拒绝) | order_id、location_id、status、total、items、created_at |
order.paid |
支付完成,可能发生在交接_之后_(到店付款、链接支付) | 无需轮询即可对账支付 | order_id、location_id、status、amount、payment_method、payment_source |
order.cancelled |
顾客或门店取消,通常发生在您收到通知_之后_ | 停止履约已取消的订单 | order_id、location_id、status、reason |
我应该订阅哪些订单事件?
- 履约订单(POS 系统): 订阅
order.notification和order.cancelled,接收订单,并在订单被取消时停止。如果您需要预先校验,再加上order.validation_requested。 - 报表或对账(OMS、控制台、分析): 还需订阅
order.created和order.paid,以镜像订单和支付的生命周期。
order.notification 载荷
order.notification(以及 order.validation_requested)在 data 中携带订单内容,足以开始履约。它与订单资源使用相同的字段名和以整数美分表示的金额,但对象更小,包含以下键(与所有载荷一样,请忽略您不认识的键):
{ "order_id": "ord_...", "location_id": "str_...", "fulfillment_type": "delivery", "scheduled_for": null, "delivery_address": { "street": "123 Main Street", "unit": "Apt 4B", "city": "New York", "state": "NY", "zip": "10001", "notes": "Leave at the side door" }, "customer": { "name": "Alex", "phone": "+14155551234", "phone_last_four": "1234" }, "line_items": [ { "name": "Latte", "menu_entity_id": "itm-latte", "quantity": 1, "base_price": 450, "modifiers": [{ "name": "Oat milk", "menu_entity_id": "mod-oat", "quantity": 1, "price": 75 }] } ], "totals": { "currency": "USD", "subtotal": 525, "tax": 42, "surcharge": 0, "tip": 0, "delivery_fee": 599, "delivery_tip": 0, "total": 1166 }}order.validation_requested 携带相同的 data,另加一个 validation_id。请在您的 POST /v1/orders/{orderId}/validation_result 中原样回传它,使结果与本次尝试绑定;针对已过期或更早尝试的结果会被拒绝。参见接收订单。
这是订单的内容,而非实时状态,其中没有 status 或 payment。如需权威的当前状态(状态、支付、每项税额、时间戳),请获取 GET /v1/orders/{orderId}。生命周期事件只携带上面列出的摘要字段。
对于尽快(ASAP)订单,scheduled_for 为 null。如果有值(ISO 8601,UTC),则表示顾客选择了该取餐/配送时间。通知仍会立即到达,因此请按 scheduled_for 备餐,而不是在收到时就开始。
对于自取订单,以及没有存储地址的配送订单,delivery_address 为 null。存在时,六个键全部包含;notes 包含配送说明,unit 或 notes 可能为 null。
并非状态推送
Maple 不会为每一次状态变化发出 webhook。由您自己推动的状态变化(accept、ready、complete)不会回传,到达 IN_DELIVERY 或支付变为 refunded / voided / failed 也没有对应事件。请读取订单资源获取当前状态,参阅订单生命周期。
预订事件
预订事件会投递给每个已获授权访问该门店、其允许的权限范围包含 bookings:read 且订阅与事件类型匹配的活跃应用。它们不要求应用拥有 POS 连接。该门店的预订灰度发布开关必须已启用。
| 类型 | 报告的变化 |
|---|---|
booking.created |
预订已创建,或首次从其提供方处观察到 |
booking.confirmed |
请求已确认 |
booking.updated |
预订已变更 |
booking.cancelled |
预订已取消 |
booking.declined |
请求已拒绝 |
booking.seated |
客人已入座 |
booking.completed |
服务已完成 |
booking.no_show |
客人被标记为未到店 |
这八种事件使用相同的 data 结构。嵌入的 booking 与 GET 资源一致:
{ "location_id": "str_example", "previous_status": null, "booking": { "object": "booking", "id": "bkg_example", "livemode": false, "created": 1789063200, "version": 1, "location_id": "str_example", "status": "confirmed", "service_state": null, "confirmation_code": "AB1234", "starts_at": "2026-09-12T22:00:00.000Z", "starts_at_local": "2026-09-12T18:00", "service_date": "2026-09-12", "duration_minutes": 90, "party_size": 2, "guest": { "first_name": "Alex", "last_name": "Quinn", "phone": "+12125551234", "phone_last_four": "1234", "email": null }, "notes": null, "channel": "api_partner", "provider": "maple", "cancellation_policy": null, "experience": null, "updated_at": "2026-09-10T18:01:00.000Z" }}载荷是首次分发时的最新预订状态,而不是该次状态变化的不可变快照。previous_status 为 null。因此,事件名称描述的变化可能早于嵌入预订的当前状态。某个接收方的事件一旦被记录,重试时会保持该载荷和事件标识不变。
每个原始 outbox 事件在每个应用/环境下都有稳定的标识。即使两次变更在同一预订版本上分发,对同一预订的两次变更仍然是各自独立的事件。请按事件 ID 去重,并按预订的 id/version 对账;不要依赖投递顺序。独立的开发者消费方不会推进宾客沟通游标。此版本不转发任何楼面事件;候位变化有自己的事件。
某个接收方入队失败,不会阻止其他符合条件的应用或订阅入队。重试会复用同一订阅的事件标识。预订事件的发布最多进行三次分发尝试,每次尝试都有有限次数的内部重试。格式错误的内部事件和已用尽分发尝试的事件会被保留,供运维人员排查,而不会阻塞后续事件或无限重试。这与 HTTP webhook 投递重试是两回事。投递出现缺口后,请使用 GET /v1/bookings?updated_since=… 对账当前预订状态。
候位事件
候位事件会送达与预订事件相同的应用:每个已获授权访问该门店、其允许的权限范围包含 bookings:read 且订阅与事件类型匹配的活跃应用。只要该门店的预订灰度发布开关已启用,Maple 原生门店就会发送这些事件。请参阅运行候位。
| 类型 | 报告的变化 |
|---|---|
waitlist_entry.created |
一组客人加入候位 |
waitlist_entry.notified |
Maple 已发短信通知该组客人餐桌已备好 |
waitlist_entry.seated |
该组客人已入座,生成一条散客(walk-in)预订 |
waitlist_entry.removed |
客人自行离开(removed_by_guest),或店方将其移除(removed_by_venue) |
waitlist_entry.expired |
已通知的客人未在时限内回复 |
这五种事件使用相同的 data 结构。嵌入的 waitlist_entry 与 GET /v1/waitlist_entries/{entryId} 一致:
{ "location_id": "str_example", "waitlist_entry": { "object": "waitlist_entry", "id": "bkw_example", "livemode": false, "created": 1789063200, "location_id": "str_example", "status": "seated", "party_size": 2, "position": null, "quoted_wait": { "parties_ahead": 1, "wait_minutes": { "min": 15, "max": 30 } }, "joined_at": "2026-09-10T18:00:00.000Z", "notified_at": "2026-09-10T18:20:00.000Z", "booking_id": "bkg_example", "channel": "api_partner", "guest": { "first_name": "Alex", "last_name": "Quinn", "phone": "+12125551234", "phone_last_four": "1234", "email": null } }}与预订事件一样,载荷是首次分发时的最新候位条目状态:在客人入座之后才送达的 waitlist_entry.created,显示的已经是 seated。请根据事件类型判断发生了什么,根据 waitlist_entry.status 判断条目当前所处的状态。每次变化在每个应用和环境下都有各自的事件 ID;请按事件 ID 去重,不要依赖投递顺序。投递尝试、重试以及格式错误内部事件的隔离,均遵循上述预订事件的规则。入座还会创建一条散客预订,该预订由您订阅的预订事件报告。
门店(location)事件
这些事件跟踪您对门店的访问权限,即连接生命周期:
| 类型 | 触发时机 | data(示意) |
|---|---|---|
store.provisioned |
商户授予您的应用对某门店的访问权限 | location_id |
store.deprovisioned |
授权被撤销;您已失去对该门店的访问权限 | location_id |
store.status.changed |
您与某门店的连接发生变化 | location_id、status(connected / disconnected) |
菜单事件
| 类型 | 触发时机 | data(示意) |
|---|---|---|
menu.sync.completed |
某门店的菜单同步完成 | location_id |
menu.sync.failed |
某门店的菜单同步失败 | location_id |
请参阅发布菜单。
测试事件
webhook.test 通过 POST /v1/webhook_subscriptions/{id}/test 按需投递。请在任何真实流量到来之前用它验证您的处理程序。真实业务活动绝不会发出它。