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 秒. 请先确认收到,再处理工作——处理器太慢会被视为失败。
  • 非2xx 2xx 响应或超时会以指数退避方式重试,持续约一天,然后被丢弃。不会跟随重定向,并且会被视为失败。
  • 只会发送端点所订阅的事件;到达其他状态的运行不会对此有任何报告。
  • 投递可能会 乱序到达且不止一次. 以 Morphic-Event-Id 为键,忽略你已处理过的投递。
  • 一个 成功的 投递可能在你自己的运行记录存在之前到达。请处理这种情况,而不是报错——或者对这些运行回退为轮询。

Webhook 只是优化,不是保证。保留一个缓慢的扫描,轮询那些你还没收到通知的运行,这样你这边的一次错误部署就不会丢失一天的输出。

本页内容