Both advertise
"events": { "listChanged": false } in server/discover. The edge answers events/* itself, so subscribing works while your computer is asleep. Events are produced on your computer, so they arrive once it’s back online.
Events
Agents server
Vendor bot connector
On a bot connector,
bot_id is always that bot. Passing a different bot_id is refused with -32012. Filter arguments are optional strings; an event is delivered when its data has every value you passed. events/list returns each event’s inputSchema and payloadSchema.
Subscribe
- Callback URL:
https://only, no credentials, no private, loopback, link-local or carrier-grade NAT addresses. The address is checked again on every delivery, and we connect to exactly the address we checked. Redirects are not followed. - Secret: you choose it.
whsec_plus base64 of 24 to 64 bytes. - Idempotent: the same app, server, URL, event name and arguments always give the same
id. Subscribing again refreshes it. - TTL: leave out
ttlMsfor 24 hours. Anything you ask for is clamped to between 1 minute and 7 days;ttlMs: null(no expiry) gets 7 days. Subscribe again beforerefreshBefore, or deliveries stop. - Secret rotation: refresh with a new
secret. For the next hour, deliveries are signed with both the new and the old key (two space-separated signatures), so you can switch over without dropping events. - Replay: user events are available for seven-day catch-up. Cursors are scoped to the authenticated subscription, including its filters and target. Initial subscribe starts at the current position; a refresh with a saved cursor queues matching retained events.
truncated: truereports matching history older than that window. Cursors stay behind earlier recoverable deliveries, so out-of-order delivery cannot silently skip them. - Limit: 50 live subscriptions per app per server (
-32013).
Verification challenge
Before a new subscription goes live (and again when you change the secret, or after a day), we POST a signed challenge to your URL:2xx with the same value:
events/subscribe fails with -32015 CallbackEndpointError and data.reason set to connection_refused, timeout, tls_error, http_4xx, http_5xx or challenge_failed.
Deliveries
- Signed with Standard Webhooks: base64 HMAC-SHA256 of
"<webhook-id>.<webhook-timestamp>.<raw body>"keyed with your decoded secret. Reject timestamps more than five minutes old. webhook-idequalseventIdand stays the same on every retry. Deduplicate on it.- Any
2xxwithin 10 seconds is delivered. Other answers and network errors are retried with backoff (30 s, 2 min, 10 min, 30 min, 1 h, then every 2 h) for 24 hours. 410 Goneends the subscription.413drops that event without retrying. Bodies are never larger than 256 KiB.
When access ends
If you remove the app’s approval in Allternit (or revoke a vendor bot CLI key), or stop one subscription in Connected apps, the subscriptions end at once, and each callback gets one signed message:events/unsubscribe with the same name, arguments and URL ends one subscription. It always answers {}.
Seeing and ending subscriptions
Owners see every app’s subscriptions in Settings › Connected apps (Desktop, ai.allternit.com, the phone layout and the m.allternit.com app). Under each approved app it lists the events it gets in plain words, when each was last sent, the last delivery error, and Stop for one subscription. See Connected apps. Subscriptions a vendor agent made with anallternit-bot CLI key are listed under that key instead (GET /api/v1/vendor-bots/:id/cli-keys, see allternit-bot CLI), and stop the same way.
Stopping one ends it at once and its callback gets the same signed terminated message, with "reason": "ended_by_owner". The app is still approved, so it can subscribe again; removing the approval ends all of them.
The routes behind it (cloud API, Clerk session or compute-scoped API key; 503 mcp_edge_not_configured until the edge is on):
Errors
For runtime developers: forwarding events to the cloud
Events start in each runtime’sbot_events ledger. A runtime forwards them to POST https://api.allternit.com/api/v1/runtime/events, signed with its device credential the same way cloud-api signs relayed requests to it:
- Up to 100 events and 1 MiB per request.
id(1–128 characters) is the idempotency key for that runtime; resending it answersduplicate. - Ledger names map onto the event list above (
channel.message.receivedbecomesmessage.received,agent.approval.requestedbecomesapproval.requested,run.completedbecomesagent.run.completed). Other types are answeredignored, so a forwarder can send its whole outbox. Outbound echoes (own: true) are ignored. bot_idandthread_idare copied intodata.datamust be an object of at most 250 KiB.- The answer lists one result per event, in order:
{"results":[{"id","status","eventId"?}],"accepted","duplicates","ignored","rejected"}withstatusone ofaccepted,duplicate,ignored,too_large,invalid. Advance the cursor past every result; on5xxresend the same batch.401means the signature, clock or device credential is wrong.
The other direction: apps that send events to Allternit
Allternit is also an MCP Events client. When a server you connected as a connector advertisesevents, a bot can subscribe to its events and wake on them: the runtime subscribes with a webhook URL on api.allternit.com, which verifies each Standard Webhooks delivery and relays it to your computer. See Wake a bot from a connected app’s events and the Connector events API.
Related
- Connected apps
- Wake a bot from a connected app’s events
- MCP Servers API
- Platform API webhooks, which can use the same Standard Webhooks signatures
ChatGPT interoperability
OpenAI’s MCP Events integration uses protocol2026-07-28, webhook delivery and callback verification. It works in Work web chats, desktop Work chats with Cloud selected, and dots. Rescan a plugin after changing its event catalog.
ChatGPT does not support polling, streaming, or the draft’s gap / terminated control notifications. Allternit stops delivery on revocation regardless of whether the client understands a termination notification. A 2xx acknowledges receipt; task execution and output are asynchronous. Test discovery, filtered subscriptions, refresh/replay, real task output, unsubscribe, revocation and duplicate deliveries before describing a plugin workflow as verified.