Webhook
在运行结束时收到通知,而不是轮询检查。
只需注册一次端点,然后传递其 webhook_id 当你启动一次运行时,我们会在该运行变为你订阅的状态时通知你——通常在一分钟内。
注册端点
POST {base_url}/webhooks
{
"url": "https://hooks.example.com/morphic",
"events": ["workflow_run.succeeded", "workflow_run.failed"],
"description": "故事流水线"
}需要 runs:write. URL 必须是公开的 https 且其中不含任何凭据;一个组织最多可拥有 10 个端点。
响应会携带该端点的 201 响应会携带该端点的 id ——将其作为 webhook_id 在一次运行中传入——以及其 签名密钥 (whsec_…),仅显示一次。请像保存 API 密钥一样保存它。
GET {base_url}/webhooks
DELETE {base_url}/webhooks/{webhook_id}GET (runs:read)列出你的端点,但绝不会列出它们的密钥。 DELETE (runs:write)删除一个;其仍在等待投递的配送将被丢弃。要轮换密钥,请注册一个新的端点并将你的运行迁移到那里。
事件
| 事件 | 触发时机 |
|---|---|
workflow_run.succeeded | 运行已完成;其资源已就绪 |
workflow_run.failed | 运行已停止; 错误 说明原因 |
workflow_run.needs_input | 运行正在等待 Morphic Studio 中的某个人 |
workflow_run.expired | 运行在其有效期内始终未完成 |
投递内容如下
POST https://hooks.example.com/morphic
Morphic-Event-Id: 0192f4a0-3c71-7b92-8e14-5d6f2a9b3c07
Morphic-Delivery-Id: 0192f4a0-4d82-7c03-9f25-6e7a3b0c4d18
Morphic-Event-Type: workflow_run.succeeded
Morphic-Signature: t=1790056632,v1=3f9c1a…
{
"id": "0192f4a0-3c71-7b92-8e14-5d6f2a9b3c07",
"type": "workflow_run.succeeded",
"created_at": "2026-09-22T04:17:12Z",
"api_version": "v1",
"data": {
"run": { "id": "0192f3d0-…", "status": "succeeded", "assets": [], "credits_used": 1792 }
}
}响应会携带该端点的 运行 对象的结构相同 读取一次工作流运行 返回, 资源 已包含——因此一次成功的投递无需后续调用。它反映的是事件触发时运行的状态,并且只要该投递仍在重试,其资源链接大约会保持一天有效。
验证每次投递
计算以下内容的 HMAC-SHA256: {timestamp}.{raw body} 使用你的签名密钥,并将其与 v1 进行常量时间比较。请使用 原始 请求体,在任何 JSON 解析之前。
import crypto from 'node:crypto';
function verify({ rawBody, header, secret }) {
const parts = Object.fromEntries(
header.split(',').map((pair) => pair.split('=')) ,
);
const expected = crypto
.createHmac('sha256', secret)
.update(`${parts.t}.${rawBody}`)
.digest('hex');
const signed = Buffer.from(expected);
const given = Buffer.from(parts.v1);
if (signed.length !== given.length) return false;
if (!crypto.timingSafeEqual(signed, given)) return false;
// 拒绝重放的投递。
return Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
}未验证的 webhook 端点就是一扇敞开的门:任何知道该 URL 的人都可以告诉你某次运行已成功。请验证签名,并拒绝时间戳早于五分钟前的请求。
重试与顺序
- 响应
2xx在 10 秒. 请先确认收到,再处理工作——处理器太慢会被视为失败。 - 非
2xx2xx 响应或超时会以指数退避方式重试,持续约一天,然后被丢弃。不会跟随重定向,并且会被视为失败。 - 只会发送端点所订阅的事件;到达其他状态的运行不会对此有任何报告。
- 投递可能会 乱序到达且不止一次. 以
Morphic-Event-Id为键,忽略你已处理过的投递。 - 一个
成功的投递可能在你自己的运行记录存在之前到达。请处理这种情况,而不是报错——或者对这些运行回退为轮询。
Webhook 只是优化,不是保证。保留一个缓慢的扫描,轮询那些你还没收到通知的运行,这样你这边的一次错误部署就不会丢失一天的输出。