Webhooks
Register a webhook to be notified when runs reach a terminal state — no polling required. The completion payload carries the run’s result inline, so a webhook is often all you need for “fire a run, handle the output later.”
Register a webhook
Section titled “Register a webhook”Create a registration with the URL to call and the events you care about (see the API reference for the exact request). You receive a signing secret — store it; you’ll use it to verify deliveries.
Events selectable on an organization-level registration (org-wide, or scoped to one agent or one brain):
| Event | Fires when |
|---|---|
run.created |
A run starts. |
run.completed |
The run finished successfully. |
run.failed |
The run errored. |
run.abandoned |
The run was cancelled. |
task.created / task.completed / task.expired |
A task changes state. |
operation.succeeded / operation.failed / operation.abandoned |
An operation reaches a terminal state. |
knowledge.corrected |
An accepted brain change was corrected — the one push signal for off-platform consumers of a brain. |
Delivery payload
Section titled “Delivery payload”Each delivery is a POST with a JSON body:
{ "event": "run.completed", "timestamp": "2026-06-04T10:00:00.000Z", "data": { "runId": "0c7f1e9a-2b4d-4f3a-9c1e-8a2d6f4b0c7f", "state": "completed", "status": "succeeded", "output": { "summary": "Report summarized in three bullet points." } }}The data carries the caller-facing status and the terminal result —
output for run.completed, failureReason / failureCategory for
run.failed, abandonReason for run.abandoned. You usually don’t need a
follow-up GET.
Verify the signature
Section titled “Verify the signature”Every delivery includes an X-Nicia-Signature header: sha256=<hex>, an
HMAC-SHA256 of the raw request body keyed by your signing secret. Always
verify it before trusting a payload.
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(rawBody: string, header: string, secret: string): boolean { const expected = "sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex"); const a = Buffer.from(header); const b = Buffer.from(expected); return a.length === b.length && timingSafeEqual(a, b);}Retries
Section titled “Retries”Failed deliveries are retried with backoff. Make your handler idempotent —
the same event may arrive more than once. Respond 2xx to acknowledge.