Google Calendar push notifications use watch channels. You register a channel per watched resource, store channel IDs, renew them before they expire, and make follow-up API calls because the native notification tells you that something changed, not always what changed. That’s fine for a Google-only product, but it becomes brittle once you add Outlook or Exchange.
Nylas gives you one webhook destination for Google Calendar, Microsoft, iCloud, and Exchange. You subscribe to event triggers once, then your app receives the same payload shape whenever a Google event changes.
Subscribe to Google Calendar event changes
Section titled “Subscribe to Google Calendar event changes”Create a webhook destination with the event triggers your sync pipeline needs. For most Google Calendar sync jobs, subscribe to all three event lifecycle triggers.
curl --request POST \ --url 'https://api.us.nylas.com/v3/webhooks/' \ --header 'Authorization: Bearer <NYLAS_API_KEY>' \ --header 'Content-Type: application/json' \ --data '{ "trigger_types": [ "event.created", "event.updated", "event.deleted" ], "webhook_url": "https://app.example.com/webhooks/nylas", "description": "Google Calendar event sync", "notification_email_addresses": ["[email protected]"] }'import Nylas, { WebhookTriggers } from "nylas";
const nylas = new Nylas({ apiKey: process.env.NYLAS_API_KEY });
const webhook = await nylas.webhooks.create({ requestBody: { triggerTypes: [ WebhookTriggers.EventCreated, WebhookTriggers.EventUpdated, WebhookTriggers.EventDeleted, ], webhookUrl: "https://app.example.com/webhooks/nylas", description: "Google Calendar event sync", },});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, ], "webhook_url": "https://app.example.com/webhooks/nylas", "description": "Google Calendar event sync", })This subscription isn’t limited to Google. If the same Nylas application also has Microsoft or Exchange grants, the same destination can receive their calendar events too.
Verify the webhook challenge
Section titled “Verify the webhook challenge”When you create or reactivate a webhook, Nylas sends a GET request to your webhook_url with a challenge query parameter. Return that exact value as plain text within 10 seconds.
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)After verification succeeds, Nylas stores a webhook_secret for the destination. Use it to verify the X-Nylas-Signature header on every notification.
Handle event notifications
Section titled “Handle event notifications”A Google Calendar event notification identifies the changed grant, calendar, event ID, and trigger type. Respond with 200 OK, then process the event in a queue or background job.
@app.post("/webhooks/nylas")async def handle_nylas_webhook(request: Request): payload = await request.json()
if payload["type"] in ["event.created", "event.updated", "event.deleted"]: event = payload["data"]["object"] upsert_sync_job( grant_id=event["grant_id"], calendar_id=event["calendar_id"], event_id=event["id"], trigger=payload["type"], )
return Response(status_code=200)For created and updated events, fetch the full event if your local copy needs fields that aren’t in the notification payload. For deleted events, mark the local row deleted by ID instead of trying to read the event again.
Nylas webhooks vs Google watch channels
Section titled “Nylas webhooks vs Google watch channels”| Concern | Google Calendar watch channels | Nylas webhooks |
|---|---|---|
| Setup | Register a watch channel for the resource you monitor | Create one webhook destination with event triggers |
| Renewal | Channels expire and need renewal | Managed by Nylas |
| Providers | Google only | Google, Microsoft, iCloud, and Exchange |
| Payload model | Notification that a resource changed | Unified event notification with object identifiers |
| Security | Check Google channel headers | Verify X-Nylas-Signature with the webhook secret |
If you only support Google Calendar and already own the native sync pipeline, watch channels work. If your product needs multiple calendar providers, a unified webhook removes the renewal and provider-branching code from your app.
Things to know about Google Calendar webhooks
Section titled “Things to know about Google Calendar webhooks”Webhooks start after the grant exists. Nylas doesn’t send notifications for events that happened before the user connected their Google account.
Use grant.expired too. Calendar sync stops if the grant expires. Add the grant.expired trigger to your webhook destination or create a separate destination for auth health.
Expect duplicate or out-of-order delivery. Network retries can deliver the same notification more than once. Key your sync work by grant_id, calendar_id, event_id, and trigger type, then apply the latest provider state.
Recurring events need special handling. Moving one occurrence can send an update for the parent and occurrence metadata. Store master_event_id from expanded instances so your app can connect the change to the series.
Google Pub/Sub is for Gmail sync, not calendar webhooks. Calendar events use standard Nylas webhook destinations. You don’t need to create a Google Cloud Pub/Sub topic to receive event.updated notifications.
What’s next
Section titled “What’s next”- Sync calendar events with webhooks for the provider-agnostic setup
- Get real-time updates with webhooks for email, calendar, grant, and Notetaker triggers
- Verify webhook signatures before processing payloads
- Google Calendar recurring events for recurrence-specific sync behavior
- Webhook notification schemas for every payload shape