Reference

Webhook request format

How to call an Agent Program's webhook: the URL, content types, the X-Willo-Signature scheme, idempotency and responses.

Generated from Willo's codeOpen in ClaudeOpen in ChatGPT
On this page

Each Agent Program with a webhook trigger has its own URL. A POST to it starts one run. See Webhook triggers for setup, signing examples and no-code recipes.

Request

POST https://api.willo.ai/webhooks/programs/<public_id>/<secret>
Content-Type: application/json
Idempotency-Key: 3f1c2a9e-…

{"lead": "alice@example.com"}
PartRule
URLShown once on the Agents tab. The last path segment is the secret; Rotate issues a new URL and the old one stops at once.
Content-Typeapplication/json, application/x-www-form-urlencoded, text/plain
BodyUp to 64 KB. An empty POST is a plain "go".
idempotency-keyOptional. Repeats with the same key run once. Without it, the same body on the same UTC day runs once. Keys over 200 characters are hashed.
x-willo-signatureOptional, or required when the owner turns signing on. See below.

Signature

PartValue
Headerx-willo-signature: t=<unix seconds>,v1=<hex HMAC-SHA256>
Signed message<t>.<raw body>
Keythe URL's last path segment
AlgorithmHMAC-SHA256, lowercase hex
Window5 minutes either side of Willo's clock

Signing limits replays of a captured call. It does not protect a leaked URL; rotating does.

What Willo keeps

The parsed body becomes the run's input, with values shaped like secrets removed. Of the request headers only these are kept: content-type, idempotency-key, user-agent, x-willo-signature.

Responses

StatusCodeMeaning
200duplicateA repeat of a call already accepted (same Idempotency-Key, or the same body on the same UTC day). No second run.
202queuedThe call was accepted and one run is queued.
400invalid_bodyThe body could not be parsed as the Content-Type it declares.
401invalid_secretThe URL's secret is wrong.
401signature_expiredThe signature's timestamp is outside the allowed window.
401signature_invalidThe signature does not match the body.
401signature_malformedThe X-Willo-Signature header is not in the t=<unix>,v1=<hex> format.
401signature_requiredThe trigger requires a signature and the call carries no X-Willo-Signature header.
404unknown_triggerNo active trigger has this URL. It was rotated, revoked, archived, or never existed.
409program_not_activeThe program is paused, still a draft, or no longer uses a webhook trigger.
413payload_too_largeThe body is over the size limit.
415unsupported_media_typeThe Content-Type is not JSON, form-encoded or plain text.
429daily_capThe program has reached its runs-a-day limit. Calls run again tomorrow. Sent with Retry-After.
429queue_fullThe program has the most events it may hold waiting. Retry after Retry-After. Sent with Retry-After.
429rate_limitedToo many calls in the last minute. Wait for Retry-After. Sent with Retry-After.
503unavailableWebhook triggers are temporarily unavailable. Retry later, honouring Retry-After when it is sent.