Skip to content
CalendarSync
IntegrationsPricingBlogAPI DocsFeatures
Sign InGet Started
Pro plan

CalendarSync API

A developer-friendly REST API to read your meetings, schedule events, manage syncs, and pull AI-generated notes. Same data your dashboard sees, accessible to your scripts, automations, and AI agents.

Get an API keyConnect to Claude (MCP) →Embed booking widget →

Authentication

Send your key as a Bearer token. Generate one in settings.

Authorization:
Bearer cs_live_…

Base URL

https://www.calendarsync.app

JSON in, JSON out. Routes live under /api/v1/….

Rate limits

100 requests / minute per API key. Watch X-RateLimit-Remaining in response headers. 429 means back off and retry.

Quick start

From API key to working response in 3 curl commands.

1. Sanity check

curl https://www.calendarsync.app/api/v1/status \
  -H "Authorization: Bearer cs_live_your_key"

2. List today's meetings

curl "https://www.calendarsync.app/api/v1/meetings?day=today" \
  -H "Authorization: Bearer cs_live_your_key"

3. Schedule something

curl -X POST https://www.calendarsync.app/api/v1/events \
  -H "Authorization: Bearer cs_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Quick sync",
    "start": "2026-04-26T07:00:00Z",
    "end": "2026-04-26T07:30:00Z",
    "attendees": [{"email": "friend@x.com"}]
  }'

Reference

AccountCalendarsSync pairsMeetingsCalendar eventsMeeting notesRemindersError codesInvited connections

Endpoints

Account

Quick health check on the account that owns the API key.

GET/api/v1/status

Account-wide sync health. Connection count, active/erroring sync pairs, recent errors.

Response

{
  "status": "healthy",
  "connections": 3,
  "sync_pairs": { "total": 3, "active": 3, "errors": 0 },
  "recent_errors_24h": 0,
  "timestamp": "2026-04-25T12:30:00.000Z"
}

Calendars

List the connected calendar accounts (Google, Microsoft, Apple).

GET/api/v1/connections

Returns every active calendar connection with provider, email, and last sync time. Use the id when scheduling events.

Response

{
  "data": [
    {
      "id": "abd156d6-...",
      "provider": "google",
      "email": "you@gmail.com",
      "calendar_id": "primary",
      "calendar_name": "you@gmail.com",
      "status": "active",
      "last_synced_at": "2026-04-25T12:30:00Z",
      "created_at": "2026-04-11T10:00:00Z"
    }
  ],
  "count": 1
}

Sync pairs

Inspect, trigger, and audit the calendar-to-calendar sync pairs you set up in the dashboard.

GET/api/v1/sync-pairs

List every sync pair with mode, direction, status, and the underlying connections on each side.

Response

{
  "data": [
    {
      "id": "de1168f5-...",
      "sync_mode": "mirror",
      "direction": "two_way",
      "is_active": true,
      "status": "idle",
      "last_synced_at": "2026-04-25T12:30:00Z",
      "connection_a": { "id": "...", "provider": "google", "email": "a@x.com" },
      "connection_b": { "id": "...", "provider": "google", "email": "b@y.com" }
    }
  ],
  "count": 1
}
POST/api/v1/sync-pairs/{id}/trigger

Run a sync for one pair right now. Useful when an instant-sync webhook was missed or you want to verify a specific change propagated.

Response

{
  "success": true,
  "data": {
    "events_created": 1,
    "events_updated": 0,
    "events_deleted": 0,
    "duration_ms": 4200,
    "trigger_type": "manual"
  }
}
GET/api/v1/sync-pairs/{id}/logs

Recent sync events for one pair. Supports ?limit (default 50, max 200) and ?offset.

Query params

  • limitMax rows (1-200). Default 50.
  • offsetSkip N rows. Default 0.

Response

{
  "data": [
    {
      "id": "log-...",
      "trigger_type": "webhook",
      "level": "info",
      "events_created": 1,
      "duration_ms": 843,
      "created_at": "2026-04-25T12:30:00Z"
    }
  ],
  "count": 1
}

Meetings

List, read, update, and log meetings. Mirrors what you see on the dashboard's Meetings page.

GET/api/v1/meetings

List meetings with flexible filters. Each row includes attendees, event time, status, AI-generated summary, and structured action items when matched.

Query params

  • dayShortcut: today | tomorrow | yesterday (in your stored timezone). Overrides from/to.
  • fromISO timestamp lower bound (inclusive).
  • toISO timestamp upper bound (exclusive).
  • statusComma-separated list of statuses to include. Default: today,active. Possible: today, active, done, notes.
  • qFree-text search against meeting title and attendee emails.
  • limitMax rows (1-200). Default 50.

Response

{
  "meetings": [
    {
      "id": "mtg-...",
      "title": "Kickoff with Steffi",
      "status": "today",
      "event_date": "2026-04-26T09:00:00Z",
      "is_all_day": false,
      "attendees": [
        { "name": "Steffi Lim", "email": "steffi@x.com" }
      ],
      "meeting_summary": null,
      "action_items": [],
      "matched_transcript_id": null,
      "is_dismissed": false
    }
  ],
  "count": 1,
  "timezone": "Asia/Singapore",
  "window": { "from": "...", "to": "..." }
}
GET/api/v1/meetings/{id}

Full meeting record with notetaker-generated narrative and structured action items.

Response

{
  "meeting": {
    "id": "mtg-...",
    "title": "Kickoff with Steffi",
    "event_date": "2026-04-26T09:00:00Z",
    "meeting_summary": "Discussed scope, timelines, and onboarding...",
    "action_items": [
      {
        "text": "Send the proposal by Friday",
        "done": false,
        "owner": "Ryan Tan",
        "timestamp": "12:34"
      }
    ]
  }
}
PATCH/api/v1/meetings/{id}

Update one or more fields on a meeting. Use to mark a meeting done, dismiss, replace notes, or rewrite the action item list.

Request body

{
  "status": "done",
  "is_dismissed": false,
  "meeting_summary": "Updated remarks here.",
  "action_items": [
    { "text": "Email the deck", "done": false }
  ]
}

Response

{
  "meeting": {
    "id": "mtg-...",
    "status": "done",
    "is_dismissed": false,
    "updated_at": "2026-04-25T13:00:00Z"
  }
}
POST/api/v1/meetings

Log an ad-hoc or retrospective meeting (no calendar event behind it). Same as the dashboard's Log Meeting button.

Request body

{
  "title": "Quick chat with Iris",
  "event_date": "2026-04-25T08:30:00Z",
  "is_all_day": false,
  "attendees": [
    { "name": "Iris", "email": "iris@x.com" }
  ],
  "notes": "Aligned on next steps.",
  "status": "active"
}

Response

{
  "meeting": {
    "id": "mtg-...",
    "title": "Quick chat with Iris",
    "status": "active",
    "event_date": "2026-04-25T08:30:00Z",
    "meeting_summary": "Aligned on next steps.",
    "created_at": "2026-04-25T13:00:00Z"
  }
}

Calendar events

Schedule new events on a connected calendar. The dashboard's calendar provider (Google / Microsoft / Apple) handles the write.

POST/api/v1/events

Create a calendar event. Pick which calendar via connection_id (omit if you only have one active connection).

Request body

{
  "connection_id": "abd156d6-...",
  "title": "Catch-up with Steffi",
  "start": "2026-04-26T07:00:00Z",
  "end": "2026-04-26T07:30:00Z",
  "description": "30-min check-in.",
  "location": "Google Meet",
  "attendees": [
    { "email": "steffi@x.com", "displayName": "Steffi Lim" }
  ],
  "all_day": false
}

Response

{
  "event": {
    "id": "google-evt-id",
    "summary": "Catch-up with Steffi",
    "start": { "dateTime": "2026-04-26T07:00:00Z" },
    "end": { "dateTime": "2026-04-26T07:30:00Z" },
    "html_link": "https://calendar.google.com/...",
    "attendees": [{ "email": "steffi@x.com" }]
  },
  "connection": {
    "id": "abd156d6-...",
    "email": "you@gmail.com",
    "provider": "google"
  }
}

Meeting notes

Force a fresh Fireflies pull. Useful right after a call to attach the transcript without waiting for the 15-min cron.

POST/api/v1/pull-notes

Pull recent notetaker transcripts and match them to your meetings. Returns which tasks got matched.

Request body

{
  "force": false,
  "window_days": 7
}

Response

{
  "matched": 1,
  "total_tasks": 8,
  "total_transcripts": 3,
  "matches": [
    {
      "taskId": "mtg-...",
      "taskTitle": "Kickoff with Steffi",
      "transcriptTitle": "Kickoff with Steffi"
    }
  ],
  "skipped_reason": null
}

Reminders

Per-pair meeting reminders. Send WhatsApp/email nudges before events on a synced pair.

GET/api/v1/reminders

List configured reminders across the user's sync pairs.

Response

{
  "data": [
    {
      "id": "rem-...",
      "sync_pair_id": "de1168f5-...",
      "minutes_before": 15,
      "message_template": "Heads up: {title} starts in 15 min.",
      "is_active": true,
      "created_at": "2026-04-20T10:00:00Z"
    }
  ],
  "count": 1
}
POST/api/v1/reminders

Create a reminder for a specific sync pair.

Request body

{
  "sync_pair_id": "de1168f5-...",
  "minutes_before": 15,
  "message_template": "Heads up: {title} starts in 15 min."
}

Response

{
  "data": {
    "id": "rem-...",
    "sync_pair_id": "de1168f5-...",
    "minutes_before": 15,
    "is_active": true
  }
}

Error codes

StatusMeaning
400Malformed request body or missing required field
401Missing or invalid API key
403Key valid but the account isn't on Pro, or the resource isn't owned by you
404Resource not found
409Conflict (e.g. user already attributed)
429Rate limited. Back off until X-RateLimit-Reset.
500Server error. Retry with backoff.

Invited connections

Pro accounts can invite other people to connect their own calendar. Managed from the dashboard, not the API.

How it works

From Dashboard → Calendars, click “Invite someone”. You get a single-use link that expires in 7 days; optionally we email it for you. The invitee opens the link, picks what to share, and connects Google or Outlook. Their calendar then appears in your connection list with an “Invited” badge and can be used in sync pairs and booking links like any other.

What the invitee sees

Who is asking (your name and email), a Busy only / Full details toggle (default Busy only), and one Connect button per provider. No CalendarSync account is created for them. After connecting they receive an email with a private manage link where they can change the share setting or disconnect in one click. Lost the link? The original invite page can resend it to the email on file.

Privacy

The share setting belongs to the invitee and you cannot override it. Busy only is a hard ceiling: events sourced from that calendar are always written as blockers and show as free/busy in availability, even inside a mirror pair. Either side can disconnect at any time; the other side is notified by email.

Billing

SGD 2 per invited connection per month, always monthly. If your own Pro plan is monthly, it is added as a quantity line to that subscription. If your Pro plan is annual, it runs as a separate monthly subscription on the same card, so you are never charged a year up front for someone who may only be connected for a few weeks. Charged only when the invitee actually connects, prorated for the current period, removed when they disconnect. Calendars you connect yourself are not counted.

Next: connect this to Claude

The CalendarSync MCP server wraps every endpoint above as a tool your AI assistant can call directly. Ask it about your schedule, get notes, log meetings, and book events with plain English.

MCP setup guide →
CalendarSync

Keep all your calendars in sync. Share scheduling links. Never get double-booked again.

Product

  • Integrations
  • Pricing
  • Features

Resources

  • API Reference
  • Blog
  • Partner Program
  • Compare Alternatives
  • Sign In

Legal

  • Privacy Policy
  • Terms of Service

© 2026 CalendarSync. All rights reserved.

Fazier badge

Built for people who live in their calendars.