Webhooks

実行をポーリングする代わりに、完了時に通知を受け取ります。

エンドポイントを一度登録したら、その webhook_id run を開始すると、状態があなたの購読しているものに変わった時点でこちらから呼び出します。通常は1分以内です。

エンドポイントを登録する

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 run に対して — そしてその 署名シークレット (whsec_…)は1回だけ表示されます。APIキーと同様に保存してください。

GET {base_url}/webhooks
DELETE {base_url}/webhooks/{webhook_id}

GET (runs:read)はエンドポイントの一覧を返し、シークレットは返しません。 DELETE (runs:write)は1件を削除します。そのエンドポイント宛てでまだ保留中の配信は破棄されます。シークレットをローテーションするには、新しいエンドポイントを登録して run をそちらへ移してください。

イベント

イベント発火するタイミング
workflow_run.succeededrun が完了し、アセットの準備が整ったとき
workflow_run.failedrun が停止したとき エラー 理由を示します
workflow_run.needs_inputrun が Morphic Studio で人の入力待ちのとき
workflow_run.expiredrun が有効期間内に完了しなかったとき

配信

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 }
  }
}

その run オブジェクトは同じ形状です ワークフロー run を読み取る を返します、 assets が含まれます — そのため、成功した配信には追加の呼び出しは不要です。これはイベント発生時点の run の状態であり、配信の再試行が続く限り、アセットリンクは約1日有効です。

すべての配信を検証する

HMAC-SHA256 を計算します {timestamp}.{raw body} 署名シークレットで計算し、次と比較します v1 を定数時間で比較します。使用するのは 生の body です。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を知った誰でも、run が成功したと通知できます。署名を検証し、5分より古い timestamp は拒否してください。

再試行と順序

  • 応答 2xx 以内に 10秒。まず応答を返し、その後で処理してください — ハンドラが遅いと失敗とみなされます。
  • 非2xx 2xx 応答でない場合、またはタイムアウトは約1日指数バックオフで再試行され、その後破棄されます。リダイレクトは追従されず、失敗として扱われます。
  • エンドポイントが購読しているイベントだけが送信されます。別の状態に達した run については何も通知されません。
  • 配信は 順不同かつ複数回届くことがあります。キーにするのは Morphic-Event-Id そしてすでに処理したものは無視してください。
  • ある 成功した 配信は、run の自分側の記録が存在する前に届くことがあります。エラーにせずそれを処理してください — あるいは、その run についてはポーリングにフォールバックしてください。

webhook は最適化であり、保証ではありません。まだ受け取っていない run をポーリングする遅めのスイープを維持してください。そうすれば、あなた側の1回の不具合デプロイで1日の出力を失うことはありません。

このページの内容