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.
Authentication
Send your key as a Bearer token. Generate one in settings.
Bearer cs_live_…
Base URL
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"}]
}'Endpoints
Account
Quick health check on the account that owns the API key.
/api/v1/statusAccount-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).
/api/v1/connectionsReturns 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.
/api/v1/sync-pairsList 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
}/api/v1/sync-pairs/{id}/triggerRun 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"
}
}/api/v1/sync-pairs/{id}/logsRecent 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.
/api/v1/meetingsList 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": "..." }
}/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"
}
]
}
}/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"
}
}/api/v1/meetingsLog 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.
/api/v1/eventsCreate 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.
/api/v1/pull-notesPull 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.
/api/v1/remindersList 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
}/api/v1/remindersCreate 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
| Status | Meaning |
|---|---|
| 400 | Malformed request body or missing required field |
| 401 | Missing or invalid API key |
| 403 | Key valid but the account isn't on Pro, or the resource isn't owned by you |
| 404 | Resource not found |
| 409 | Conflict (e.g. user already attributed) |
| 429 | Rate limited. Back off until X-RateLimit-Reset. |
| 500 | Server 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 →