Developer docs Integrations

Webhooks

Get POSTed whenever task events happen in your project — completions, failures, and approval gates — with signed payloads and automatic retries.

How it works

Register a webhook on a project with a URL, an optional signing secret, and the events you care about. When a matching event fires, allybuild POSTs a JSON payload to your URL. Webhooks are per project: every active webhook subscribed to the event receives its own delivery.

Events

EventFires when
task_completedA task finished successfully, including auto-approved runs
task_failedA task errored out
task_pending_approvalA task parked at an approval gate and is waiting on a human

Payload

Every delivery posts the same envelope. For task events the task record is embedded:

{
  "id": "b6c7d8e9-...",
  "event": "task_completed",
  "project_id": "3f1c2b4a-...",
  "resource_type": "task",
  "resource_id": "9a2b8c7d-...",
  "title": "weekly report finished",
  "body": "",
  "timestamp": "2026-10-01T12:00:00.000Z",
  "task": {
    "id": "9a2b8c7d-...",
    "name": "weekly-report",
    "status": "success",
    "result": null,
    "error_message": null,
    "pr_url": null
  }
}

Headers and signatures

HeaderValue
X-Webhook-EventThe event type, e.g. task_completed
X-Notification-IdThe delivery id from the payload envelope
X-Webhook-Signaturesha256=<hex> — HMAC-SHA256 of the raw request body keyed with your signing secret; sent only when a secret is configured

Verify the signature over the raw body before parsing it:

import crypto from "node:crypto";

app.post("/webhooks/allybuild", express.raw({ type: "application/json" }), (req, res) => {
  const expected = "sha256=" + crypto.createHmac("sha256", SECRET).update(req.body).digest("hex");
  if (req.headers["x-webhook-signature"] !== expected) {
    return res.status(401).end();
  }

  const payload = JSON.parse(req.body.toString());
  // route on payload.event
  res.sendStatus(200);
});

Reliability

  • Each attempt has a 10-second timeout; a non-2xx response or a network error counts as a failed attempt
  • Failed deliveries retry up to 3 times with back-off — roughly 30s, 60s, then 120s
  • Every attempt is logged per webhook: status, HTTP status code, attempt number, and a response-body excerpt
  • Return 2xx as soon as you have accepted the event, and process heavy work asynchronously