Skip to main content

Documentation Index

Fetch the complete documentation index at: https://smithery.ai/docs/llms.txt

Use this file to discover all available pages before exploring further.

Triggers are in preview. Breaking changes may happen without notice.
Triggers let your app react to events from the services you’ve connected through Smithery — a Notion page update, a GitHub push, a Slack message — without the user being present. Each connection exposes a catalog of trigger types declared by the underlying MCP server. Your app subscribes through Smithery; the upstream MCP server delivers events as signed HTTPS webhooks directly to your endpoint. Smithery’s wire protocol tracks the MCP Events proposal. Only webhook delivery is supported today — events/poll and events/stream from the proposal are not implemented. Smithery proxies subscribe/unsubscribe calls through to the upstream MCP server (handling auth on your behalf). Once subscribed, events flow directly from the upstream MCP server to your endpoint — Smithery is not in the delivery path. Triggers are to events what tools are to actions:
ToolsTriggers
Synchronous action you invokeEvent that fires when something happens upstream
Request/responseSigned HTTPS POST to your URL
tools/list, tools/callevents/list, events/subscribe
Both are declared by the MCP server and live under the same connection.

Quick start

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.
  • An HTTPS endpoint that can receive webhooks.

1. Create a connection

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. Subscribe to a trigger

A single call carries what to listen for (name + params), where to deliver (delivery.url), and the Standard Webhooks secret used to sign deliveries (delivery.secret). Generate the secret on your side first:
WEBHOOK_SECRET="whsec_$(openssl rand -base64 32)"

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\" },
    \"delivery\": {
      \"url\": \"https://my-app.example.com/events\",
      \"secret\": \"$WEBHOOK_SECRET\"
    }
  }"
{
  "id": "sub_a3f1c8e2b0d49f7e",
  "refreshBefore": "2026-04-22T13:00:00.000Z"
}
The subscription is soft state with a mandatory TTL. Re-call this same endpoint with the same key (name, params, delivery.url) before refreshBefore to keep it alive — see Refreshing.

3. Handle events at your endpoint

Verify each delivery against the secret you supplied at subscribe time, then dispatch on event.name:
import { Webhook } from "standardwebhooks"

const WEBHOOK_SECRET = process.env.SMITHERY_WEBHOOK_SECRET!

app.post('/events', async (req, res) => {
  let event
  try {
    event = new Webhook(WEBHOOK_SECRET).verify(
      req.rawBody,
      req.headers as Record<string, string>,
    )
  } catch {
    return res.status(400).end()
  }

  if (event.name === 'page.updated') {
    await handlePageUpdated(event.data)
  }

  res.status(204).end()
})

Discovering triggers

# List trigger types on a connection
curl "https://smithery.run/my-app/notion/.triggers" \
  -H "Authorization: Bearer $SMITHERY_API_KEY"

# Get one trigger's schema
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" }
    }
  }
}

Subscribing

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" },
    "delivery": {
      "url": "https://my-app.example.com/events",
      "secret": "whsec_<base64 of 24-64 random bytes>"
    }
  }'
{
  "id": "sub_a3f1c8e2b0d49f7e",
  "refreshBefore": "2026-04-22T13:00:00.000Z"
}
FieldDescription
nameTrigger name from the catalog.
paramsTrigger-specific input matching the trigger’s inputSchema.
delivery.urlYour HTTPS webhook endpoint. The upstream MCP server POSTs events directly here.
delivery.secretStandard Webhooks secret you generate: whsec_ followed by base64 of 24–64 random bytes. The upstream MCP server signs each delivery with this.

Subscription identity

A subscription is keyed by (principal, delivery.url, name, params). Two calls with different params or a different delivery.url are different subscriptions. To watch two Notion workspaces, subscribe twice — once per workspace_id. To fan in events from many triggers (or many connections) into one endpoint, subscribe each trigger with the same delivery.url. Your receiver routes deliveries by the X-MCP-Subscription-Id header.

Refreshing

Webhook subscriptions expire on TTL. Re-call subscribe with the same key before refreshBefore to reset it. If you stop refreshing, the subscription expires and the upstream MCP server stops delivering — no explicit unsubscribe is required.
# Refresh — same call as the original subscribe
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" },
    "delivery": {
      "url": "https://my-app.example.com/events",
      "secret": "whsec_<same-or-rotated-secret>"
    }
  }'
Supplying a new delivery.secret on refresh rotates the signing key.

Receiving events

The upstream MCP server POSTs each event directly to your subscription’s delivery.url:
POST https://my-app.example.com/events
Content-Type: application/json
webhook-id: evt_01HW...
webhook-timestamp: 1739980800
webhook-signature: v1,<base64 HMAC-SHA256(secret, "id.timestamp.body")>
X-MCP-Subscription-Id: sub_a3f1c8e2b0d49f7e

{
  "eventId": "evt_01HW...",
  "name": "page.updated",
  "timestamp": "2026-04-22T12:00:00.000Z",
  "data": {
    "page_id": "...",
    "updated_at": "2026-04-22T12:00:00.000Z"
  }
}
FieldDescription
eventIdStable event identifier; matches the webhook-id header. Use for deduplication.
nameTrigger name, e.g. page.updated.
timestampISO 8601 timestamp from the upstream event.
dataPayload matching the trigger’s payloadSchema.

Signature verification

The upstream MCP server signs every delivery with the Standard Webhooks HMAC profile, using the delivery.secret you supplied at subscribe time.
HeaderValue
webhook-idUnique event identifier; dedupe on this.
webhook-timestampUnix seconds; reject deliveries older than ~5 minutes.
webhook-signaturev1,<base64 HMAC-SHA256(secret, "webhook-id.webhook-timestamp.body")>.
X-MCP-Subscription-IdSubscription id. Use it to look up the correct secret before parsing the body.
Verify against the raw request body, not a re-serialized JSON object. Off-the-shelf Standard Webhooks libraries (e.g. Svix) work without modification. During secret rotation, webhook-signature may carry multiple space-delimited v1, signatures; compliant verifiers accept the delivery if any one verifies.

Delivery semantics

  • At-least-once — dedupe on webhook-id.
  • The upstream MCP server retries failures with exponential backoff and gives up on persistent non-retryable errors.
  • Your handler should be idempotent and return 2xx only after the event is durably accepted.

Unsubscribing

Unsubscribing is eager cleanup — subscriptions also expire naturally on TTL if you stop refreshing. Both forms supply the subscription key (name, params, delivery.url); the id is not accepted as input.
curl -X DELETE "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" },
    "delivery": { "url": "https://my-app.example.com/events" }
  }'
Unsubscribing also deregisters any upstream webhook the trigger had registered.

API reference

All paths are relative to https://smithery.run. Requests require Authorization: Bearer $SMITHERY_API_KEY.

REST endpoints

MethodPathBodyPurpose
GET/{ns}/{conn}/.triggersList trigger types on a connection.
GET/{ns}/{conn}/.triggers/{name}Get a trigger’s schema.
POST/{ns}/{conn}/.triggers/{name}{ params, delivery: { url, secret } }Subscribe or refresh. Returns { id, refreshBefore }.
DELETE/{ns}/{conn}/.triggers/{name}{ params, delivery: { url } }Unsubscribe.

MCP methods

Available on any connection advertising the ai.smithery/events extension during initialize. Method and field names track the MCP Events proposal.
MethodParamsResult
ai.smithery/events/list{ events: [{ name, description, delivery, inputSchema, payloadSchema }] }
ai.smithery/events/subscribe{ name, params, delivery: { mode: "webhook", url, secret } }{ id, refreshBefore }
ai.smithery/events/unsubscribe{ name, params, delivery: { url } }{}

Learn more