Tell your agent when a webhook fails
The Expose MCP server supports MCP Events. An agent subscribes to
request.completed on one of your tunnels, and Expose sends it a
signed webhook for every request that matches, such as a Stripe webhook your app answered with a
500. The agent hears about the failure instead of asking for the latest requests again and again.
{
"eventId": "evt_01k9x3m7c2…",
"name": "request.completed",
"timestamp": "2026-11-11T09:41:12Z",
"data": {
"tunnel": "shop.eu-1.sharedwithexpose.com",
"request_id": "…",
"started_at": "2026-11-11T09:41:12Z",
"method": "POST",
"path": "/stripe/webhook",
"status": 500,
"duration_ms": 112,
"request_content_type": "application/json",
"response_content_type": "text/html"
},
"cursor": null
}
How it works
-
1. Ask your agent
"Tell me when a Stripe webhook to shop.eu-1.sharedwithexpose.com fails." It subscribes with the tunnel's public host and the filters it needs.
-
2. Expose watches the tunnel
The Expose server that carries your tunnel reports its requests about once a second, only while somebody subscribes to it.
-
3. Your agent gets the event
A signed webhook with the method, path, status and duration of the request. Headers and bodies stay with Expose.
-
4. It looks closer
With the event's
request_id,expose.get_requestreturns the headers and body, for up to 30 minutes.
Filters
The same filters as expose.list_requests. Filter on status error when only failures matter.
- tunnel
- Required. The tunnel's public host, such as shop.eu-1.sharedwithexpose.com. The subscription keeps watching when the tunnel reconnects on the same host.
- method
- Only requests with this HTTP method, such as POST.
- status
- A code like 404, a class like 5xx, or error for every 4xx, 5xx and request that got no answer.
- path
- Only requests whose path or query contains this text, such as /stripe.
Subscribing
Clients send events/subscribe with their own webhook URL and secret. Your agent does this for you; this is what it sends.
{
"method": "events/subscribe",
"params": {
"name": "request.completed",
"arguments": {
"tunnel": "shop.eu-1.sharedwithexpose.com",
"path": "/stripe",
"status": "error"
},
"delivery": {
"mode": "webhook",
"url": "https://agent.example.com/hooks/expose",
"secret": "whsec_…"
}
}
}
Delivery and limits
- Webhook delivery
- Events are signed with Standard Webhooks, and the webhook-id is the event id, so receivers can drop duplicates. Failed deliveries are retried over about 14 minutes.
- Verified callbacks
- Before the first delivery to a URL, it has to echo a signed challenge. Callbacks must be https on public addresses, and redirects aren't followed.
- A day at a time
- Subscriptions last up to 24 hours. Subscribing again refreshes one, can rotate its secret and reports how deliveries went.
- Rate limited
- Up to 30 events a minute per subscription, so a busy tunnel can't flood your agent; the next refresh says when events were dropped. Up to 50 subscriptions per account.
Which agents
MCP Events is a draft extension of the Model Context Protocol (SEP-3415), and Expose delivers events by webhook. ChatGPT subscribes to them today: connect the Expose app and ask it to watch a tunnel. Other clients that implement webhook delivery of the draft subscribe the same way.
Coding agents such as Claude Code, Codex and Amp don't receive MCP Events. They use
expose.list_requests with the same filters whenever you ask
them to check.
What leaves Expose
- A summary, not the request. Events carry the method, path, status, duration and content types. Secret query values in the path are redacted.
- Only your tunnels. A request reaches your subscriptions only when the tunnel was opened with your Expose token.
- Only to your agent. Events go to the webhook URL your client subscribed with, for ChatGPT one of OpenAI's.
- Your call. Disconnecting an app on your profile page ends its subscriptions.