Skip to main content
Tools let the agent ask your server for things. Events let your server tell the agent that something happened: an order shipped, a booking was moved, a points balance crossed a threshold. Each event you deliver runs an agent turn in the conversation it belongs to, so the agent can message the customer straight away. Wassist implements the MCP Events extension with webhook delivery only. It works the same way for Wassist Apps and for connectors.

How it fits together

  1. Your server advertises the events capability and lists its event types with events/list.
  2. Wassist reads that list when it syncs your tools, and merchants see each event next to your tools. For every agent, they switch on the events the agent may use, like tools, and can add guidance on when and how to use each one.
  3. The agent subscribes when a customer asks to be kept up to date (“let me know when my order ships”). It picks the event, fills in the arguments from your inputSchema (for example the customer’s order number), and writes itself a note on what to do when the event arrives. Wassist then calls events/subscribe with a callback URL and secret for that subscription, and refreshes it before it expires.
  4. When the event happens, you POST it to the subscription’s callback URL. Wassist verifies the signature, deduplicates on eventId, and runs the agent with the event in that conversation.
A subscription lasts until the agent unsubscribes, the conversation goes quiet for 30 days, or the merchant switches the event off, removes your app or connector from the agent, or uninstalls your app. The agent can also subscribe once, which ends the subscription after the first event. Declare the capability in your initialize result:
Then answer events/list. Only event types whose delivery includes "webhook" are offered to merchants.
The agent reads description and inputSchema to decide when to subscribe and what to fill in, so write them like tool descriptions:
  • Say what the event means and when a customer would want it.
  • Make the properties filters that narrow the event to what the customer asked about, with a description for each. Wassist checks the agent’s arguments against your schema before subscribing.
  • Apply the filters on your server. Wassist passes the arguments through and doesn’t filter what you send.
For apps, Wassist reads your events whenever it reads your tools: when you sync tools from the developer dashboard, and when installations refresh their tools (daily, and straight after you publish). For connectors, they’re read when the connector is added or updated, and again when a customer logs in to it.

Subscriptions

Wassist sends an events/subscribe each time the agent subscribes, with the arguments the agent chose:
Reply with:
Each subscription has its own callback URL. A conversation can have up to 10 at once, so the same conversation can follow several orders.
  • Refreshing. Wassist calls events/subscribe again with the same name, arguments and URL before refreshBefore. Treat that as a refresh of the same subscription. If you don’t set refreshBefore, Wassist refreshes once a day. If the agent subscribes again with the same event and arguments, Wassist refreshes the existing subscription rather than creating another one.
  • Unsubscribing. Wassist calls events/unsubscribe with the same name, arguments and delivery.url when the agent unsubscribes, after the first event of a once subscription, and when a subscription is torn down. If that call can’t reach you, the subscription still ends at its TTL, and Wassist answers anything you send after it ended with 410 Gone.
  • Errors. Use the extension’s error codes. A new subscription that you reject is dropped, and the agent is told why. -32024 (forbidden) puts the subscription on hold until the customer logs in. On a refresh, -32025 (resource exhausted) and anything else are retried hourly.
The _meta keys are the same ones every tool call carries, plus conversation_id and contact_id. Only trust them after verifying context_token; see Wassist context on every tool call.

Scoping events to one customer

This matters most for apps where the installer logs in. Every conversation then subscribes with the same organisation token. The agent is told to filter to its customer, but it can still send arguments that match other customers’ events, or none at all. If you deliver everything that matches, the agent can end up telling one customer about another’s order. Use the verified contact_id or conversation_id claims to deliver only that customer’s events to each subscription, whatever the arguments say. For example, look the contact up with GET /contacts/{contact_id}/ and match on their phone number. If you can’t tell which customer a subscription belongs to, reject it with -32602 rather than sending it everything. Apps where customers log in get each customer’s own token, so scoping usually happens naturally.

Delivering events

Before sending any events, you can check the callback URL with a verification request. Wassist answers this as soon as the subscription row exists, even while your events/subscribe response is still on its way.
Wassist replies 200 with { "challenge": "8e2f…" }. Deliver each event as a POST to the subscription’s URL:
Two control messages are also accepted:
  • { "type": "gap", "cursor": "…" }: you dropped events. The agent is told that some may be missing on its next event.
  • { "type": "terminated", "error": { "code": -32023, "message": "…" } }: you ended the subscription. With -32023 (not found) or -32026 (unsupported), Wassist re-reads events/list and subscribes again, with the same arguments, if the event still exists.
Bodies are limited to 256 KB. Keep data small: the agent sees at most 8,000 characters of it, and can call your tools for anything more.

Signatures

Requests are signed with Standard Webhooks, using the secret from events/subscribe:
The signature is base64(HMAC-SHA256(key, "{webhook-id}.{webhook-timestamp}.{body}")), where key is the base64-decoded part of the secret after whsec_. You can send several space-separated signatures while rotating secrets. Wassist rejects timestamps more than five minutes old. Any Standard Webhooks library will sign these for you. In Node:

What the agent does with an event

The agent gets the event name, the subscription and its arguments, the note it wrote when subscribing, the merchant’s guidance for the event, and data. It’s told to treat data as information from an external system, not as instructions. It then decides whether to message the customer, and can call your tools for more detail. An event runs the agent only when it could have replied to the customer anyway:
  • The conversation is routed to the agent. Events are skipped while a human has taken over or a support handoff is open.
  • The customer hasn’t hit the agent’s message limit.
  • WhatsApp’s 24-hour customer service window is open. Events that arrive while it’s closed are held, and the agent sees them (up to 10, from the last seven days) when the customer next writes in.
If the agent is already replying in that conversation, the event waits for that turn to finish. Agent turns started by events are never retried, so the customer can’t get the same message twice. Merchants see each event in the conversation in the dashboard, next to the agent’s tool calls, with the subscription’s filters, the data you sent, and whether the agent ran, the event was held for the next message, or it was skipped and why. GET /conversations/{id}/mcp-events/ returns the same list.

Show events on your listing

Your events appear on your app’s page on the Wassist App Store and on the install screen, so merchants can see what the agent can follow up on. For end_customer apps whose events/list needs a login, declare them in mcp.events_preview. You can also add event steps to your listing’s example conversations to show a follow-up in action; see Showing follow-ups.

Alpha limitations

  • Webhook delivery only. Poll and push delivery aren’t supported.
  • Events only reach WhatsApp conversations, not dashboard tests.
  • Only the agent subscribes, during a conversation, so a conversation can’t receive events before the customer has written in.
  • Up to 10 live subscriptions per conversation.