POST /v0/webhooks on AgentMail, POST /repos/{owner}/{repo}/hooks on GitHub), every
matching event the world later sends reaches that URL. Each delivery is signed the vendor’s way, so
your receiver verifies it with the secret the world returned when the webhook was registered.
Every delivery is also recorded in the world’s request ledger, so a check can grade it whether or
not anything was listening.
Declare where subscriptions live
Add a[webhooks] table to connector.toml. It names the entity the vendor’s own routes write
subscriptions into, and the fields of that entity that matter:
gateway worlds schema check refuses a table that names an entity or field the contract does not
declare.
Send from a handler
A route handler callsctx.deliver with the event type, the body and, when the table declares one,
the scope:
from gateway_env import webhooks, then webhooks.deliver(...).
Every subscription that is enabled, wants the event and holds its scope gets one delivery. A
delivery is kept only when the call that sent it succeeds: a call that answers 4xx or 5xx, or
raises, sends nothing, just as it writes nothing. The body is fixed as JSON when you call deliver,
so what the receiver gets is exactly what was signed.
Limits: 100 deliveries per event, 500 per call, 256 KB per body. A subscription whose URL no
receiver could be at, or whose secret its scheme cannot sign with, is skipped; the call still
succeeds.
gateway worlds schema init ./x --connector github gives you a world whose issue and pull request routes notify registered
hooks.
See a session’s webhooks
Receive them on your machine
session listen forwards each delivery to a receiver you run locally, with its signed headers and
exact body:
0 and why it never answered) goes back to the world’s
request ledger, once per delivery.
A failed poll is retried until you press Ctrl-C. A session that ended stops
listen with exit
code 1.
Let the platform push them to a public URL
When your receiver is on the internet, the platform can deliver for you. Turn it on per project in Settings → General → World webhooks → Send webhooks to their receivers. Only project admins and owners can change it. While it is on, every running session of the project has its new deliveries posted to their subscriptions’ URLs within seconds, and each receiver’s answer is recorded in the world’s ledger.- Only
httpsURLs on port 443 receive. Private, loopback and other non-public addresses are refused. - Redirects are not followed. A receiver has 10 seconds to answer.
- Each delivery is sent at most once. A failure is recorded with its reason and not retried.
- A session opened while push is on is pushed from its first delivery. Sessions already running when you turn it on start from that moment, so nothing sent while push was off is replayed.
session listen and push can run together. The first answer to a delivery is the one kept.
Send a registered webhook somewhere else
The agent registers whatever URL it chooses. To have those deliveries arrive at your own receiver instead, open World settings → Webhook destinations and map the registered URL to a destination, or select the pencil next to a subscription in a session’s Webhooks section.- The mapping applies to every session of the world, including ones opened later.
- The world itself is unchanged: the agent still reads back the URL it registered, and the ledger records it.
- A destination follows the same rules as push:
httpson port 443, a public address, no credentials in the URL. - Push must be on for the project.
session listenalways sends to its own--forward-to.
Grade what the world sent
Deliveries are requests in the world’s ledger withvia webhook. The event type is the
operation; a receiver’s answer is a second entry whose operation ends in .answer.
"operation": "message.received.answer" to count what receivers answered.