Webhooks

Start a run of a task, or answer a gate, from any system that can send an HTTP request.

Start a task

Every task has a webhook. POST the run's input as JSON, with your enterprise's webhook secret in a header:

curl -X POST https://api.zerohuman.com/v1/webhooks/{enterprise}/tasks/{task} \
  -H "x-os-webhook-secret: $WEBHOOK_SECRET" \
  -H "content-type: application/json" \
  -d '{"issue": 123}'

{enterprise} is your enterprise's slug and {task} the task's. The task's Triggers panel in the portal shows its exact URL and a curl to copy, never the secret.

The body is the run's input. If the task is already running when the call arrives, the new run waits until it can start. A webhook starts the task the same way its schedule or a person does: the same version, the same gates, the same spend limits.

Answer a gate

A gate can be decided from outside the portal too:

curl -X POST https://api.zerohuman.com/v1/webhooks/{enterprise}/gates/{gateId} \
  -H "x-os-webhook-secret: $WEBHOOK_SECRET" \
  -H "content-type: application/json" \
  -d '{"decision": "approve", "note": "Ship it."}'

decision is approve, reject or request_changes, and note is optional. The decision is recorded exactly as one made on Gates is, by the same rules: a gate already decided cannot be decided again. Whoever holds the webhook secret can approve what goes live, so keep it as close as you would a signing key.

The secret

The secret is your enterprise's own. Every enterprise gets one when it is created, and it is stored only as a hash, so nobody can read it back, the portal included. Settings → Webhooks rotates it and shows the new secret exactly once; the previous one stops working at once. The secret an enterprise starts with is never shown, so using webhooks starts with a rotation.

A wrong secret and an unknown enterprise get the same 401, so a call cannot tell you which enterprises exist.

History and replay

Webhooks in the portal lists every call your enterprise's webhooks received: what was sent (with credentials left out and sensitive fields redacted), what the API answered, what happened next, and a link to the execution it started or continued. A call accepted means the webhook was consumed, not that its work has finished.

A delivery can be replayed: the original request is applied again, through the task's or gate's current rules, as a new delivery linked to the original. Replaying a task starts a new run; replaying a gate decision continues its execution, and a gate already decided refuses it. Calls that never authenticated, deliveries still in progress, and bodies that contain the webhook secret cannot be replayed.

With an API token scoped webhooks:read:

Call What it does
GET /v1/webhooks Deliveries, paginated, with search, sorting, and status and kind filters.
GET /v1/webhooks/{id} One delivery: its request, response and what followed.
POST /v1/webhooks/{id}/replay Replay it (webhooks:write). Send { "idempotencyKey": "<uuid>" }: the same key returns the same replay rather than dispatching twice.