Skip to content

Webhooks

A webhook connects an agent to other systems in one of two directions. An inbound trigger is a URL an external service POSTs to fire a run. An outbound notification is a URL Industric POSTs to when a run finishes. You create each kind separately from the agent's Webhooks panel; picking the direction is the first step of New webhook.

The agent must be runnable
Inbound triggers fire background runs. An agent created without the Run capability rejects webhook calls.

Inbound: trigger a run

Pick Inbound trigger, name it, and choose whether calls must be signed:

  • None. The bearer token is the only credential. Simplest, fine for trusted CI or scripts.
  • Auto-generate. Industric mints a signing secret; callers must send a matching HMAC signature header.
  • Custom. Paste a secret the caller already has (for example one Slack gave you).
Copy the token now
The token, and the HMAC secret if you chose one, are shown once at create time and never again. If you lose them, Rotate mints fresh credentials at the same URL.
The New webhook dialog with an agent selected and three choices: Inbound trigger, GitHub issues trigger and Outbound notification
New webhook: pick the agent and direction first; the form then shows only that direction's settings.
The New inbound webhook form with an optional name, a Require bearer token toggle that is on, a Validate request signature toggle that is off, and Advanced settings
The inbound form. The bearer token is required by default; turn on signature validation to also require an HMAC-SHA256 signature.

The panel lists inbound and outbound webhooks separately. Each trigger shows its POST URL with a Copy URL action, badges for signing and rate limits, and a last-used line. A power toggle pauses a trigger without deleting it.

A trigger can also fire from GitHub without any caller of your own: set a GitHub trigger label and the agent runs whenever that label is added to an issue in the connected repository, with the issue as its input.

The Webhooks panel listing inbound and outbound webhooks in separate groups
The Webhooks panel: each trigger shows its invoke URL and whether signing and rate limits are on.

Call the trigger

The invoke URL carries the webhook's id; the token travels in the Authorization header, not the URL.

POST/api/v1/webhooks/agents/:id/invoke

Fire a run of the agent this webhook belongs to. Returns 200 with the run's identifiers on success, 403 if the agent can't run in the background, 413 if the body is over 64 KB, and 429 when the trigger's rate limit is exceeded.

Parameters

:idpathRequired
The webhook's id, shown in the Webhooks panel (this is the webhook id, not the agent id).
AuthorizationheaderRequired
Bearer <token>, using the token shown once at create time. X-Webhook-Token: <token> is accepted as an alternative for callers that strip Authorization.
X-Exo-Signatureheader
sha256=<hex>, an HMAC-SHA256 of the raw request body. Required only when the webhook was created with a secret. X-Hub-Signature-256 (GitHub-style) is also accepted.
bodyjson
Any JSON, up to 64 KB. Delivered to the run as the AGENT_WEBHOOK_PAYLOAD environment variable.

Copy the URL from the panel and POST to it:

terminal· bash
curl -X POST 'https://exo.adaptive.live/api/v1/webhooks/agents/<webhook-id>/invoke' \
-H 'Authorization: Bearer wh_<token>' \
-H 'Content-Type: application/json' \
-d '{"issue":"PROJ-1234"}'

If the webhook requires a signature, add the HMAC header. This is exactly what the panel's example generates:

terminal· bash
BODY='{"issue":"PROJ-1234"}'
curl -X POST 'https://exo.adaptive.live/api/v1/webhooks/agents/<webhook-id>/invoke' \
-H 'Authorization: Bearer wh_<token>' \
-H 'Content-Type: application/json' \
-H "X-Exo-Signature: sha256=$(printf %s "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -r | cut -d' ' -f1)" \
-d "$BODY"

A successful call returns 200 with { namespace, agent_name, invocation_name, phase }. The invocation_name is the run you can open in Sessions. Dispatch waits up to 30 seconds for the cluster; a timeout returns 504.

Outbound: completion callback

Pick Outbound notification and set the destination URL. When a run finishes (success, failure, or cancelled), Industric POSTs the result there with an X-Exo-Event: agent.run.completed header:

POST body· json
1{
2 "agent": "triage-bot",
3 "namespace": "production",
4 "run": "triage-bot-8f2a",
5 "status": "Succeeded",
6 "started_at": "2026-07-03T10:00:00Z",
7 "completed_at": "2026-07-03T10:02:11Z",
8 "token_usage": { "prompt": 4120, "completion": 880, "total": 5000 },
9 "output": "…run output, tail-capped…",
10 "output_truncated": false
11}
  • Signing. Set a secret (auto-generated or your own) and Industric signs each call with X-Exo-Signature: sha256=<hex>, so you can verify the call came from Industric.
  • Output size. The run output is capped (256 KB by default, keeping the tail); output_truncated flags when it was trimmed.
  • Private endpoints. Check Proxy through the Operator when the destination is only reachable from inside your cluster's network.
  • Delivery. Reply 2xx to acknowledge. Industric retries on transport errors, 5xx, and 429; other 4xx are dropped. Failures never fail the run; they land in the agent's event log.

Rate limits & advanced settings

Both forms have a collapsed Advanced settings section. Blank means unlimited or the stated default.

  • Rate limit (calls / minute), inbound. Calls over the budget get 429 with Retry-After.
  • Delivery rate limit (per minute), outbound. Deliveries over the budget are dropped and recorded in the event log.
  • Retries, outbound. Extra delivery attempts (default 2; -1 disables retries).
  • Timeout (seconds per attempt), outbound. Default 15, max 300.

Edit, rotate, revoke

  • Edit changes the name and settings; the direction is fixed. Picking Auto-generate on an outbound webhook mints a new signing secret, shown once.
  • Rotate mints a fresh token (and HMAC secret if one existed) for an inbound trigger, shown once. The URL stays the same; the old credentials stop working immediately.
  • Revoke deletes the webhook and stops it immediately. External callers start getting 401s, so re-mint and update them to recover.
Who can manage them
Creating, editing, and revoking webhooks needs the deployments write permission. Without it the Webhooks panel is hidden.