Skip to content
GasketDocs

Webhooks

Signed events pushed to your endpoints.

Add an endpoint under Webhooks and choose events. Gasket POSTs each event as JSON, signed with the endpoint's secret (shown once).

json
{
  "id": "evt_1042",
  "object": "event",
  "type": "run.completed",
  "created_at": "2026-10-15T12:41:11.000Z",
  "request_id": "req_...",
  "data": { "agent_id": "agt_...", "action_id": null, "run_id": "run_...", "status": "completed", "cost_micros": 20255 }
}
Events you can subscribe to
run.completed, run.failed, run.requires_approval
approval.requested, approval.approved, approval.rejected, approval.expired
action.authorized, action.denied, action.succeeded, action.failed

Delivery

  • Headers: Gasket-Signature, Gasket-Delivery (id), Gasket-Event (type).
  • Respond 2xx within 10 seconds. Anything else is retried after 1m, 5m, 30m, 2h and 6h, then marked failed.
  • Every attempt is listed on the endpoint's page with your response; redeliver any delivery from there.
  • HTTPS only, public addresses only, no redirects.
verify.js
import crypto from "node:crypto";

// header: the Gasket-Signature header, "t=<unix seconds>,v1=<hex>"
// body:   the raw request body, exactly as received
export function verifyGasket(header, body, secret) {
  const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  if (!t || !v1 || Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
  const expected = crypto.createHmac("sha256", secret).update(`${t}.${body}`).digest("hex");
  return expected.length === v1.length && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}

Something wrong or missing? Tell us · Stuck? Help center