Skip to content
Skip to main content

Get a webhook for new email

A cron job that lists messages every minute wastes most of its calls finding nothing new, and it still leaves you up to 60 seconds behind. Gmail makes that worse: the Gmail API caps per-project quota, so polling many mailboxes drains it fast. A message.created webhook flips the model. Nylas pushes you the new message within seconds of it landing, and the same subscription covers Gmail and Outlook without separate Pub/Sub topics or Graph subscriptions.

This recipe shows the three pieces you need: subscribe to the trigger, pass the challenge handshake, and handle the notification when mail arrives. For the wider catalog of message, calendar, and engagement triggers, see real-time webhooks.

Create a webhook destination with POST /v3/webhooks/ and set trigger_types to ["message.created"]. That one trigger fires for every new message on any connected account, so a single subscription spans all 6 providers including Gmail and Outlook. The request takes your HTTPS webhook_url, a description, and optional notification_email_addresses for failure alerts.

The samples below register one endpoint for new-email notifications. The webhook_url must be a public HTTPS URL, since Nylas calls it from the internet within 10 seconds to verify it. Swap message.created for the full trigger array if you need more events on the same endpoint.

Before any notification arrives, the API confirms you own the endpoint with a handshake. The first time you create the webhook or set it back to active, Nylas sends a GET request carrying a challenge query parameter, and your handler has 10 seconds to echo that exact value back in a 200 OK body. Return only the raw value: no quotes, no JSON wrapper, no chunked encoding. Get any of those wrong and the endpoint is marked failed on the first try, with no retry.

The handler below answers the GET challenge and accepts POST notifications on the same route. A passing handshake is what generates your webhook_secret, the key you’ll need for signature checks later.

Once verified, Nylas POSTs a JSON notification each time mail arrives. The message.created payload nests the message under data.object, which carries the id and grant_id you need (application_id sits on data). Acknowledge with a 200 OK inside 10 seconds first, then fetch the full message with GET /v3/grants/{grant_id}/messages/{message_id} to read the subject, sender, and body. Acknowledging before you fetch keeps a slow downstream call from timing out the 10-second webhook window.

The handler below pulls the IDs from the payload and requests the full message. Returning the 200 OK before the fetch keeps you under the timeout even when the message read is slow.

The message.created trigger has three delivery variants, and your handler should treat all of them as “new mail arrived.” A standard notification includes the message body inline. If the payload would exceed the 1 MB limit, Nylas strips the body and appends .truncated to the type, so you re-fetch with a Get Message request. If you subscribe to message.created.cleaned and have Clean Conversations configured, the body arrives as cleaned markdown instead of raw HTML. You don’t subscribe to .truncated separately, but your code needs a branch for it.

Gmail delivery is faster when you wire up Pub/Sub. For Google grants with gmail.readonly or gmail.labels scopes, Google Pub/Sub feeds push events instead of polling, which shaves seconds off message.created latency. Outlook accounts get comparable speed through Microsoft Graph subscriptions that Nylas manages for you, so neither provider needs you to renew anything.

Two delivery properties shape your handler design. The API guarantees at-least-once delivery, so the same message.created can arrive twice, often because Google and Microsoft Graph send upserts. Dedupe on the message id before you act, and read verify webhook signatures to confirm each payload is genuine using the X-Nylas-Signature HMAC. Ordering isn’t guaranteed either: a message.created and a later message.updated for the same message can land out of order, so trust the fetched message state over the sequence of notifications. The full failure and retry behavior lives in Using webhooks with Nylas.