Guide

Trigger Agents on Inbound Email with Webhooks

A cron loop that polls every minute means an agent reacts up to 60 seconds late and burns API calls finding nothing. A webhook flips that: the agent account notifies your endpoint the instant a message arrives, and the agent runs immediately. This guide registers a message.created webhook, receives it locally for development, verifies the x-nylas-signature so a forged payload can't trigger the agent, wires the handler to the agent loop, and adds a grant.expired hook so a broken account pages you instead of failing silently.

Written by Prem Keshari Senior SRE

VerifiedCLI 3.1.16 · Nylas managed · last tested June 8, 2026

What does triggering an agent on inbound email mean?

Triggering an agent on inbound email means the agent account pushes a notification to your endpoint the instant a message arrives, instead of the agent pulling the inbox on a timer. A webhook is that push: when mail lands on the agent's address, Nylas sends a message.created event to a URL you registered, and your handler runs the agent on that message. The agent reacts in real time, not on the next poll.

This is the event-driven counterpart to the cron loops in the support, lead, and invoice guides. Those loops call nylas email list --unread every minute; a webhook replaces the timer with a notification, so the agent wakes only when there's work and wakes as soon as Nylas delivers the event, typically within a second.

Webhook flow: mail arrives, message.created fires, the handler verifies the signature, then runs the agentMail arrives*.nylas.emailWebhook firesmessage.createdVerifyx-nylas-signatureRun agentprocess now

Why use webhooks instead of cron polling?

Polling and webhooks both get the agent its mail, but they trade off latency against simplicity. A 1-minute cron poll adds up to 60 seconds of delay and runs 1,440 times a day whether or not anything arrived. A webhook delivers in under a second and fires only on a real event, so a high-volume agent does far less idle work.

The cost is an endpoint: a webhook needs a URL that's reachable and verifies signatures, where a cron job needs neither. For a quiet inbox, polling is simpler and fine; for an agent that must respond fast — a support reply, a verification code, a time-sensitive alert — the webhook's sub-second latency is worth the endpoint. Many agents run both: a webhook for real-time reaction and a periodic poll as a safety net for any missed event.

How do I register a message.created webhook?

Register the webhook with nylas webhook create. It takes the --url of your endpoint and one or more --triggers; for inbound mail that's message.created. Add --notify to get an email if the webhook starts failing:

nylas webhook create \
  --url https://your-service.com/hooks/agent \
  --triggers message.created \
  --notify ops@yourcompany.com

The command returns a webhook id and a secret — store the secret, because it's what verifies that incoming payloads actually came from Nylas. List your registered webhooks any time with nylas webhook list --json. A single webhook covers every account in the application, so you register it once, not per agent.

How do I receive webhooks locally for development?

You can't point a webhook at localhost, so the CLI ships a receiver that tunnels a public URL to your machine. The nylas webhook server command starts a local endpoint, prints the events it receives, and lets you build the handler against real payloads before you deploy anything:

nylas webhook server --port 8080

Send the agent a test email and the message event prints in your terminal within a second. This is the fastest way to see the exact shape of a message.created payload — the message id, grant id, and metadata your handler will act on. For the full local-testing workflow, see testing email webhooks locally.

How do I verify the webhook signature?

Never act on an unverified webhook. Anyone who learns your endpoint URL can POST to it, so a handler that trusts the body will run the agent on forged input. Nylas signs every payload with an HMAC in the x-nylas-signature header, computed with your webhook secret. The nylas webhook verify command checks it against the exact raw body:

nylas webhook verify \
  --payload-file ./raw-body.json \
  --secret "$NYLAS_WEBHOOK_SECRET" \
  --signature "$X_NYLAS_SIGNATURE"

Verify against the exact bytes Nylas sent — don't reparse or reformat the JSON first, or the HMAC won't match. Reject anything that fails verification before the agent sees a single field. This is the input-validation boundary for the whole event-driven path: a verified payload is trusted, an unverified one is dropped, and the agent never runs on a forged message.

How does the webhook trigger the agent loop?

The handler is small: verify the signature, pull the message id from the payload, and run the same agent action your cron loop would have run. Because the event carries the message id, the handler fetches just that message instead of listing the whole inbox — one read, not a scan:

#!/usr/bin/env bash
set -euo pipefail

# raw_body = path to a file holding the exact raw request bytes;
# sig = the x-nylas-signature header value (both from your HTTP handler)
nylas webhook verify --payload-file "$raw_body" \
  --secret "$NYLAS_WEBHOOK_SECRET" --signature "$sig" >/dev/null

# Verified — extract the message id and act on just that message
msg_id=$(jq -re '.data.object.id' "$raw_body")
nylas email read "$msg_id" --json | run_agent   # your agent action

The handler pulls the id from the payload with jq. Swapping the cron loop's email list for a single email read on the event's message id is the efficiency win — the handler does exactly one message's worth of work. From here, the agent action is the same support, lead, or invoice logic from those guides, now triggered in real time.

How do I get paged when an account breaks?

Webhooks aren't only for mail. The grant.expired trigger fires when an account's grant goes invalid, which is exactly the failure the health-monitoring guide polls for — except a webhook tells you the moment it happens instead of on the next 5-minute check. Register it alongside the message hook:

nylas webhook create \
  --url https://your-service.com/hooks/grant-health \
  --triggers grant.expired \
  --notify oncall@yourcompany.com

Now a broken account pages you in real time rather than being discovered minutes later by a poll. Pair the event-driven alert with the scheduled probe in Monitor Agent Account Health — the webhook catches the moment of failure, and the poll is the backstop that catches anything the webhook missed.

Webhooks vs polling

ConcernCron pollingWebhooks
LatencyUp to the poll interval (e.g. 60s)Under a second
Idle workRuns every interval, often emptyFires only on a real event
InfrastructureA scheduler; no public endpointA reachable, signed endpoint
Best forQuiet inboxes, simple setupsReal-time, high-volume agents

The two aren't mutually exclusive, and the resilient pattern uses both: a webhook for sub-second reaction and a low-frequency poll as a safety net, so a missed or delayed event still gets picked up. That combination gives you real-time latency without trusting the network to be perfect.

Next steps