Skip to main content
A world can notify the way its vendor does. When the agent registers a webhook through the world’s API (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 calls ctx.deliver with the event type, the body and, when the table declares one, the scope:
A tool sends the same way: 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.
The GitHub, Linear and Jamf templates already send their vendors’ webhooks: 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

This lists the subscriptions the session’s world holds: URL, events, scope, on or off, and how many deliveries each was sent. Secrets are never shown. The session’s page in the app shows the same list under Webhooks.

Receive them on your machine

session listen forwards each delivery to a receiver you run locally, with its signed headers and exact body:
Your receiver’s answer (its status, or 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.
Svix receivers refuse a delivery older than about five minutes. Use --replay only for deliveries that are still fresh.

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 https URLs 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: https on port 443, a public address, no credentials in the URL.
  • Push must be on for the project. session listen always sends to its own --forward-to.

Grade what the world sent

Deliveries are requests in the world’s ledger with via webhook. The event type is the operation; a receiver’s answer is a second entry whose operation ends in .answer.
A check reads them like any request:
This counts the deliveries the world sent, whether or not anyone was listening. Use "operation": "message.received.answer" to count what receivers answered.