Triggers are in draft. Breaking change may happen without notice.
Triggers let your app react to events from the services you’ve connected through Smithery. With Triggers, you can listen to events that happen on various services, such as when a Notion page is updated, or when a commit is pushed to GitHub. Each connection exposes a catalog of trigger types declared by the service; your app activates the ones it needs and receives events as signed HTTP webhooks.
Triggers are to events what tools are to actions:
| Tools | Triggers |
|---|
| Synchronous action you invoke | Event that fires when something happens upstream |
| Request/response | Signed HTTP POST to your URL |
tools/list, tools/call | events/list, events/subscribe |
Both are declared by the MCP server and live under the same connection.
Getting started
This walkthrough wires up a single Notion connection to deliver page.updated events to your app. You’ll need:
- A Smithery namespace (e.g.
my-app) — created on the dashboard or via Connect.
- A
SMITHERY_API_KEY from your namespace settings.
1. Create a connection
Create a connection to Notion with the id notion — this id becomes the stable URL segment for the rest of this walkthrough. mcpUrl is the MCP server URL from the Notion server page on Smithery.
curl -X PUT "https://smithery.run/my-app/notion" \
-H "Authorization: Bearer $SMITHERY_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "mcpUrl": "https://server.smithery.ai/notion" }'
2. Register a subscription URL
A subscription tells Smithery where to deliver events. Scope it to the notion connection so only Notion events land here.
curl -X POST "https://smithery.run/my-app/notion/.subscriptions" \
-H "Authorization: Bearer $SMITHERY_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "url": "https://my-app.example.com/events" }'
{
"id": "sub_01HW...",
"url": "https://my-app.example.com/events",
"secret": "whsec_..." // shown only once — store securely
}
3. Create a trigger
curl -X POST "https://smithery.run/my-app/notion/.triggers/page.updated" \
-H "Authorization: Bearer $SMITHERY_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "params": { "workspace_id": "w_123" } }'
4. Handle events at your URL
Store the secret returned in step 2 as SMITHERY_WEBHOOK_SECRET in your app, then verify incoming deliveries:
import { Webhook } from 'standardwebhooks'
app.post('/events', async (req, res) => {
const wh = new Webhook(process.env.SMITHERY_WEBHOOK_SECRET!)
const event = wh.verify(req.rawBody, req.headers)
if (event.name === 'page.updated') {
await handlePageUpdated(event.data)
}
res.status(204).end()
})
Fan in from multiple connections
Once you have triggers on more than one connection, register a namespace-scoped subscription to receive events from all of them at a single URL:
curl -X POST "https://smithery.run/my-app/.subscriptions" \
-H "Authorization: Bearer $SMITHERY_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "url": "https://my-app.example.com/events" }'
Route on event.connection_id to tell sources apart.
Discovering triggers
# List available trigger types on a connection
curl "https://smithery.run/my-app/notion/.triggers" \
-H "Authorization: Bearer $SMITHERY_API_KEY"
# Get the schema for a specific trigger
curl "https://smithery.run/my-app/notion/.triggers/page.updated" \
-H "Authorization: Bearer $SMITHERY_API_KEY"
{
"name": "page.updated",
"description": "Fires when a page in the watched workspace is updated.",
"delivery": ["webhook"],
"inputSchema": {
"type": "object",
"properties": {
"workspace_id": { "type": "string" }
},
"required": ["workspace_id"]
},
"payloadSchema": {
"type": "object",
"properties": {
"page_id": { "type": "string" },
"updated_at": { "type": "string", "format": "date-time" }
}
}
}
From an active MCP session, call ai.smithery/events/list:{
"jsonrpc": "2.0",
"id": 1,
"method": "ai.smithery/events/list"
}
The response mirrors the REST payload — a catalog of { name, description, delivery, inputSchema, payloadSchema }. Field names match the MCP committee’s events proposal so migration stays simple.
Creating a trigger
curl -X POST "https://smithery.run/my-app/notion/.triggers/page.updated" \
-H "Authorization: Bearer $SMITHERY_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "params": { "workspace_id": "w_123" } }'
{
"id": "trg_01HW...",
"name": "page.updated",
"connection_id": "notion",
"params": { "workspace_id": "w_123" },
"created_at": "2026-04-22T12:00:00.000Z"
}
Most apps should use the REST path above. The MCP path is a raw pass-through to the server’s events/subscribe method — you generate the subscription id and supply your own delivery URL. Use it when you’re already in an active MCP session and want a single wire protocol.
{
"jsonrpc": "2.0",
"id": 2,
"method": "ai.smithery/events/subscribe",
"params": {
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"name": "page.updated",
"params": { "workspace_id": "w_123" },
"delivery": {
"mode": "webhook",
"url": "https://my-app.example.com/events"
}
}
}
Returns {}.
params are the trigger-specific inputs defined by the trigger’s inputSchema (see Discovering triggers above). The same trigger name can have multiple instances (e.g. watch two different workspaces) — each call creates a new instance with its own id.
Subscriptions
A subscription tells Smithery where to deliver events. Create one per destination URL:
- Connection-scoped (
/{namespace}/{connectionId}/.subscriptions) — receives events from one connection. Recommended starting point.
- Namespace-scoped (
/{namespace}/.subscriptions) — fans in events from every connection. Route on event.connection_id.
Each POST returns a subscription id and a one-time secret used to verify webhook signatures.
Receiving events
Smithery delivers events as HTTP POST to your subscription URL. The envelope matches the MCP events proposal’s webhook body shape:
{
"id": "trg_01HW...",
"name": "page.updated",
"timestamp": "2026-04-22T12:00:00.000Z",
"data": {
"page_id": "...",
"updated_at": "2026-04-22T12:00:00.000Z"
},
"connection_id": "notion",
"namespace": "my-app"
}
| Field | Description |
|---|
id | Trigger id this event belongs to. |
name | Trigger name, e.g. page.updated. |
timestamp | ISO 8601 timestamp from the upstream event. |
data | Event payload, matching the trigger’s payloadSchema. |
connection_id, namespace | Routing metadata — useful when a subscription fans in events from multiple connections. |
Dedupe on the webhook-id delivery header (see below).
Signature verification
Smithery signs every delivery using the Standard Webhooks specification. Your endpoint receives three headers:
| Header | Value |
|---|
webhook-id | Unique message identifier |
webhook-timestamp | Unix seconds of delivery |
webhook-signature | v1,<base64 HMAC-SHA256> |
The signed payload is {id}.{timestamp}.{body}, HMAC’d with the subscription secret.
Use any Standard Webhooks library to verify — SDKs exist for most languages.
Delivery semantics
- At-least-once — dedupe on the
webhook-id header.
- Smithery retries
5xx, 408, and 429 responses and network failures with exponential backoff (currently up to 4 attempts over ~20 seconds). 4xx responses outside those codes are treated as terminal.
- Your handler should be idempotent and return a
2xx status once the event is durably accepted.
Deleting a trigger
curl -X DELETE "https://smithery.run/my-app/notion/.triggers/page.updated/trg_01HW..." \
-H "Authorization: Bearer $SMITHERY_API_KEY"
{
"jsonrpc": "2.0",
"id": 3,
"method": "ai.smithery/events/unsubscribe",
"params": {
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"delivery": { "url": "https://my-app.example.com/events" }
}
}
Deleting a trigger also deregisters its upstream webhook.
API reference
REST endpoints
All paths are relative to https://smithery.run. Requests require Authorization: Bearer $SMITHERY_API_KEY.
| Method | Path | Purpose |
|---|
GET | /{ns}/{conn}/.triggers | List trigger types on a connection |
GET | /{ns}/{conn}/.triggers/{name} | Get trigger type schema |
POST | /{ns}/{conn}/.triggers/{name} | Create a trigger instance — body { params } |
GET | /{ns}/{conn}/.triggers/{name}/{id} | Get trigger instance detail |
DELETE | /{ns}/{conn}/.triggers/{name}/{id} | Delete a trigger instance |
POST | /{ns}/.subscriptions | Create a namespace-wide subscription — body { url } |
GET | /{ns}/.subscriptions | List namespace subscriptions |
DELETE | /{ns}/.subscriptions/{id} | Delete a namespace subscription |
POST | /{ns}/{conn}/.subscriptions | Create a connection-scoped subscription |
GET | /{ns}/{conn}/.subscriptions | List connection-scoped subscriptions |
DELETE | /{ns}/{conn}/.subscriptions/{id} | Delete a connection-scoped subscription |
MCP methods
Available on any server advertising the ai.smithery/events extension during initialize. Field and method names track the MCP committee’s events proposal so migration to the standardized events/* namespace will be a prefix drop.
| Method | Params | Result |
|---|
ai.smithery/events/list | — | { events: [{ name, description, delivery, inputSchema, payloadSchema }] } |
ai.smithery/events/subscribe | { id, name, params, delivery: { mode, url } } | {} |
ai.smithery/events/unsubscribe | { id, delivery: { url } } | {} |
| Header | Value |
|---|
webhook-id | Unique message identifier — dedupe on this |
webhook-timestamp | Unix seconds |
webhook-signature | v1,<base64 HMAC-SHA256> over {id}.{timestamp}.{body} using the subscription secret |
Learn more