Trigger

A Trigger is one source node that watches an external service — Gmail, Slack, Jira, Notion and nine more — and fires a pipeline whenever it emits an event. n8n ships a separate node per service; HostWebhook ships one node with a service selector, so adding a new service is a dropdown option, not a new concept to learn.

Gmail: new email--▶Trigger--▶AI Node / Filter / anything

Services

Fourteen today — Gmail, Google Calendar, Google Drive, Google Sheets, Jira, Telegram, WhatsApp, Discord, Slack, RSS, Social Media, Mailchimp, Notion and Shopify. Same node, different service value.

How the event reaches us is not the same for all of them, and it is worth knowing which you are picking:

  • Push — the service calls us the moment something happens. Gmail (via Google Cloud Pub/Sub), Calendar, Drive, Sheets, Jira, Telegram, WhatsApp, Discord, Slack, Mailchimp and Shopify. Activating registers the subscription for you.
  • Polling — HostWebhook checks on a schedule, because the service offers no subscription we can register on your behalf. Notion, Social Media, and RSS when the feed has no WebSub hub. Activating starts the schedule.
Polling is not a shortcut we took. Notion, for instance, does have webhooks — but their subscriptions are created by hand in Notion's developer portal and verified by pasting a token back, so there is no API to register one per user. Zapier polls Notion for the same reason.
Every external service follows the same pattern: OAuth in, subscribe to events, receive webhooks. Making them separate node types would force the user to learn each one from scratch. With one node, the UX is: pick a service, pick a credential, configure filters, activate.

Gmail setup

Gmail inbound uses Google Cloud Pub/Sub — Google publishes a message every time the watched mailbox changes, HostWebhook picks up the push, fetches the new message via the Gmail API, and fires your pipeline with a structured payload.

1. Connect your Google account

Go to Settings › Credentials and connect a Google account with Full Gmail access. Under the hood this requests the gmail.modify + gmail.compose + gmail.send scopes so the trigger can read metadata, fetch full messages, and modify labels if downstream nodes need to.

A legacy Gmail credential with only gmail.send scope can't be used for triggers — it lacks read permissions. Re-connect with "Full Gmail access" when the picker prompts.

2. Create the trigger

Drop a Trigger on the canvas (red card in the Sources group). Open its detail page. Gmail is selected by default — pick the credential you just connected. Configure:

  • Labels — comma-separated label IDs to watch. Default INBOX. Use system labels INBOX, STARRED, IMPORTANT, SPAM, or a custom label ID like Label_12345. Use the Gmail listLabels operation on an emailAction to discover custom label IDs.
  • History type — which Gmail change to react to. Default messageAdded (new emails). Other options cover deletions and label changes.
  • Query filter — optional. Applied client-side after Gmail's history delta. Supports from:, to:, subject: prefixes and plain text tokens.

3. Activate the watch

Click Activate. HostWebhook calls Gmail's users.watch() registering the Pub/Sub topic. From that moment every email that matches your labels fires the pipeline in under 30 seconds. Google caps watches at 7 days — HostWebhook auto-renews daily before expiry so you don't have to.


Notion setup

Notion fires on pages added or edited — a new row in a database, or a change anywhere you shared with the connection.

1. Connect a workspace

Click Connect Notion and, on Notion's consent screen, tick the pages and databases this trigger should reach. Authorizing is not sharing: whatever you skip there stays invisible, and the pickers come back empty. You can add more later from Notion itself with ··· › Connections.

2. Say what fires it

  • Page added and Page updated — pick one or both. Nothing is ticked for you.
  • Where to watch — one database, or the whole workspace. One database is exact; the workspace mode is broader and less precise, because Notion's search cannot filter by date, only sort, so on a busy workspace with a long interval changes can fall off the end.
  • How often — a minute at the fastest. Notion allows about three requests per second per connection, shared with everything else you run against it.
  • On the first run — by default everything already in the database is ignored. Ask for a backfill only if you want the existing rows to fire.
A page counts as added while its created and edited timestamps still match. Notion keeps both to the minute, so a page created and tweaked within the same minute arrives once, as added.

What it sends

Properties arrive flattened, so a status is {{payload.properties.Status}} and not a walk through Notion's type wrappers. The untouched object is still there under raw for anything the flattening does not cover.

{
  "eventType": "page_added",
  "id": "24f8a1b2-c3d4-4e5f-8091-a2b3c4d5e6f7",
  "title": "Fix the Google login",
  "url": "https://www.notion.so/24f8a1b2...",
  "createdTime": "2026-08-07T12:00:00.000Z",
  "lastEditedTime": "2026-08-07T12:00:00.000Z",
  "parentId": "...",
  "properties": {
    "Status": "Not started",
    "Due": "2026-08-14",
    "Owner": ["Ariel"],
    "Done": false,
    "Points": 3,
    "Tags": ["backend", "urgente"]
  },
  "raw": { "object": "page", "...": "..." }
}

Payload shape

Every email fires the pipeline with a structured payload. Identical shape to what the gmail_getMessage emailAction operation returns, so the same downstream nodes work regardless of how the message is fetched.

{
  "_meta": { "iterable": false, "count": 1 },
  "id": "18f4a...",
  "threadId": "18f4a...",
  "labelIds": ["INBOX", "UNREAD", "Label_123"],
  "snippet": "Hey team, quick update on...",
  "historyId": "987654",
  "internalDate": "2026-04-18T15:30:00.000Z",
  "from": "Alice <[email protected]>",
  "to": "[email protected]",
  "cc": null,
  "subject": "Q1 review",
  "messageIdHeader": "<CAF...>",
  "references": "<CA...> <CB...>",
  "inReplyTo": "<CA...>",
  "body": "<p>Hey team...</p>",
  "bodyText": "Hey team...",
  "bodyHtml": "<p>Hey team...</p>",
  "size": 12480
}

Common downstream patterns

Classify + route

Trigger → AI Node (intent: "classify as billing / support / sales") → Conditional → per-category action. Works great for inboxes that receive mixed mail.

AI-assisted reply

Trigger → Gmail with aiEnabled: true + intent "draft a polite reply thanking the sender and suggest next steps". The AI uses the full Gmail toolkit and creates a draft the user reviews before sending.

Auto-label

Trigger → Filter (check subject contains "invoice") → Email Action with operation: addLabel. No AI, zero per-event cost, runs server-side in milliseconds.


Plan tier

  • Free — 3 triggers
  • Pro — 6 triggers
  • Enterprise — unlimited

One pool for every service. Gmail, Calendar, Drive, Sheets, Jira, Slack, Discord, Telegram, WhatsApp and RSS all draw from the same count — the number above is the total, with no per-service exceptions.


Limits to know

  • Every inbound email counts against your monthlyEventLimit.
  • Gmail API rate limit: 250 quota units per user per second. History queries cost 5 units — bursty inboxes may trigger backoff.
  • Watch caps at 7 days. Auto-renewal runs at 01:00 UTC daily; if the cron misses 7 days in a row the watch expires silently.
  • A single Gmail account can only have one active watch at a time. Creating two active triggers against the same credential fails at activation.

Self-hosted: server env

If you run HostWebhook on your own Railway, these env vars must be set on the API service for Gmail triggers to activate:

GMAIL_PUBSUB_TOPIC=projects/<your-project>/topics/<topic-name>
GMAIL_PUSH_AUDIENCE=https://api.yourdomain.com/api/triggers/gmail/push
GMAIL_PUSH_SERVICE_ACCOUNT=pubsub-gmail-pusher@<your-project>.iam.gserviceaccount.com

Plus in Google Cloud Console: (1) create a Pub/Sub topic, (2) grant pubsub.publisher to [email protected], and (3) create a push subscription pointing at the API URL with a service account for JWT signing.