Polling a calendar API every few minutes to spot new meetings is slow, wasteful, and a fast way to burn through rate limits. The native push systems make it worse: Google wants you to register and renew watch channels, and Microsoft Graph wants per-resource subscriptions you renew every few days. A multi-provider app ends up running two separate systems just to learn that someone moved a meeting.
Nylas webhooks collapse that into one push channel. You subscribe a single HTTPS endpoint to the calendar and event triggers you care about, and the same event.updated handler fires whether the change happened in Google Calendar or Outlook. For the general webhook setup that also covers email and grant events, see Get real-time updates with webhooks.
Subscribe to calendar and event changes
Section titled “Subscribe to calendar and event changes”A calendar webhook is a push subscription: you register an HTTPS URL and a list of trigger_types, and Nylas sends an HTTP POST with a JSON body each time a matching change occurs. The six calendar triggers cover both event lifecycle and calendar lifecycle, and one subscription can carry all of them across every connected provider.
Create the subscription with a single request to POST /v3/webhooks/. Pass your endpoint as webhook_url and the triggers in trigger_types. The notification_email_addresses field is optional and tells Nylas where to email you if the endpoint starts failing. Subscribing to all six triggers covers every calendar and event change.
curl --request POST \ --url 'https://api.us.nylas.com/v3/webhooks/' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer <NYLAS_API_KEY>' \ --data-raw '{ "trigger_types": [ "event.created", "event.updated", "event.deleted", "calendar.created", "calendar.updated", "calendar.deleted" ], "webhook_url": "https://yourapp.com/webhooks/nylas", "description": "Calendar and event sync", "notification_email_addresses": ["[email protected]"] }'import Nylas, { WebhookTriggers } from "nylas";
const nylas = new Nylas({ apiKey: "<NYLAS_API_KEY>" });
const webhook = await nylas.webhooks.create({ requestBody: { triggerTypes: [ WebhookTriggers.EventCreated, WebhookTriggers.EventUpdated, WebhookTriggers.EventDeleted, WebhookTriggers.CalendarCreated, WebhookTriggers.CalendarUpdated, WebhookTriggers.CalendarDeleted, ], webhookUrl: "https://yourapp.com/webhooks/nylas", description: "Calendar and event sync", },});
console.log("Webhook created:", webhook);from nylas import Clientfrom nylas.models.webhooks import WebhookTriggers
nylas = Client("<NYLAS_API_KEY>")
webhook = nylas.webhooks.create( request_body={ "trigger_types": [ WebhookTriggers.EVENT_CREATED, WebhookTriggers.EVENT_UPDATED, WebhookTriggers.EVENT_DELETED, WebhookTriggers.CALENDAR_CREATED, WebhookTriggers.CALENDAR_UPDATED, WebhookTriggers.CALENDAR_DELETED, ], "webhook_url": "https://yourapp.com/webhooks/nylas", "description": "Calendar and event sync", })
print(webhook)Subscribe to calendar events from the terminal
Section titled “Subscribe to calendar events from the terminal”The Nylas CLI registers a calendar webhook in one command: nylas webhook create --triggers event.created,event.updated,event.deleted subscribes your URL to event changes, so you get a push the moment an event is created, updated, or deleted instead of polling each calendar.
nylas webhook create --url https://yourapp.example.com/webhooks/nylas --triggers event.created,event.updated,event.deletedRun nylas webhook triggers --category event to see the event triggers, or --category calendar for calendar-object changes like calendar.created. Your endpoint must acknowledge each delivery with 200 OK within 10 seconds, or the notification is retried. See the webhook create command reference.
Verify the webhook challenge
Section titled “Verify the webhook challenge”Before Nylas delivers any notification, it confirms you own the endpoint with a one-time handshake. The first time you create a webhook, or set an existing one back to active, the API sends a GET request to your webhook_url with a single challenge query parameter. Your endpoint has 10 seconds to answer.
Your handler must return the exact value of challenge in the body of a 200 OK response, with no quotes, JSON wrapping, or extra characters. Returning anything else fails verification and the webhook stays inactive. The snippet below echoes the parameter straight back.
app.get("/webhooks/nylas", (req, res) => { res.status(200).send(req.query.challenge);});@app.get("/webhooks/nylas")def verify(challenge: str): return Response(content=challenge, media_type="text/plain", status_code=200)A successful handshake generates your webhook_secret, which you use to verify every notification that follows. The challenge handshake details cover the full verification flow.
Handle event notifications
Section titled “Handle event notifications”After verification, Nylas POSTs a notification to your endpoint each time a subscribed trigger fires. The payload identifies the change by type and includes the affected object’s id and grant_id. For an event.updated notification, that’s the event ID and the calendar it belongs to, so your handler knows exactly what changed without re-listing the calendar.
The notification carries identifiers, not always the complete object. The reliable pattern is to read the object ID from the payload, then make one GET request for the full event, since that response always reflects current provider state. Always answer with a 200 OK within 10 seconds, then process asynchronously.
@app.post("/webhooks/nylas")async def handle(request: Request): payload = await request.json() if payload["type"] == "event.updated": data = payload["data"]["object"] event = nylas.events.find( identifier=data["grant_id"], event_id=data["id"], query_params={"calendar_id": data["calendar_id"]}, ) sync_to_database(event) return Response(status_code=200)How do I sync Outlook calendar events in real time?
Section titled “How do I sync Outlook calendar events in real time?”Subscribe one webhook to event.created, event.updated, and event.deleted, and the same endpoint receives changes from Outlook and Microsoft 365 within seconds of an edit. You skip the Microsoft Graph per-resource subscription model entirely: there is no subscription to create per mailbox and nothing to renew on a timer. The subscription you created above already covers connected Microsoft accounts.
Graph’s own push model is why teams reach for an alternative on Outlook. A Graph subscription targets one resource and expires after at most 4,230 minutes, just under 3 days, so you run a renewal job that sends a PATCH before each expiry and recreates any subscription that lapses across every mailbox you watch. The webhook here has no expiration to manage and fans out across providers from a single registration, so adding a second Outlook tenant or a Google account changes nothing in your handler. Microsoft does sometimes batch edits into more than one event.updated for the same change, so key on the event id and apply the latest state, the idempotent-handler rule covered below. For the full tradeoff between calling Graph directly and a unified call, see the Microsoft Graph API alternative guide.
Things to know about calendar webhooks
Section titled “Things to know about calendar webhooks”Calendar webhooks behave consistently across providers, but a few details affect how you build and debug them. There are six calendar-related triggers in total, and you subscribe to any subset of them on a single endpoint.
The full trigger list:
| Trigger | Fires when |
|---|---|
event.created | A new event is added to a calendar |
event.updated | An existing event changes |
event.deleted | An event is removed |
calendar.created | A new calendar is added |
calendar.updated | A calendar’s metadata changes |
calendar.deleted | A calendar is removed |
Webhooks beat polling on latency. A subscription pushes a notification within seconds of the change, while a polling loop checking every 5 minutes averages 2.5 minutes of lag and wastes one request per cycle whether anything changed or not. Across a few thousand users, that gap is the difference between a live calendar and a stale one.
Calendar uses standard webhooks, not Pub/Sub. Gmail real-time email sync can route through Google Cloud Pub/Sub, but calendar and event triggers always arrive over the standard Nylas webhook channel described here. You don’t need a Pub/Sub topic or any Google Cloud setup to receive event.created.
Verify the signature on every notification. Each delivery carries an X-Nylas-Signature header: a hex-encoded HMAC-SHA256 of the raw request body, signed with your webhook_secret. Recompute the HMAC over the unmodified body and compare before trusting the data. The webhook security guide walks through the check, including the case where the body arrives compressed.
Sync timing differs by provider. Google and Microsoft both deliver calendar changes quickly, but Microsoft occasionally batches updates, so a single edit can produce more than one event.updated for the same event. Treat your handler as idempotent: key on the event id and apply the latest state rather than assuming one notification per change.
Retries are automatic. If your endpoint doesn’t return a 200 OK within 10 seconds, Nylas retries with backoff and marks the endpoint failing, then failed, after repeated misses. Respond fast and do the heavy work in a queue. See Get real-time updates with webhooks for the retry and failure rules.
What’s next
Section titled “What’s next”- Get real-time updates with webhooks for the unified setup across email, calendar, and grant events
- Using webhooks with Nylas for the full setup, verification, and failure handling
- Secure a webhook for signature verification on every notification
- Checking calendar availability to combine real-time sync with free/busy lookups