Sync Rules
Pro plan. The REST API requires a Pro subscription. View plans.
A sync rule copies events from one calendar to another, passing each event through a gate function that decides whether it syncs and what it looks like on the other side. The full lifecycle is available over the API — you never have to open the dashboard to build, test, or change a rule.
Endpoints, parameters, and response shapes are in the API Reference. This page is the complement to it: which calendar references are valid on which side, the order the calls go in, and what a write actually causes downstream. None of that is expressible in an OpenAPI document.
Addressing calendars
Source and target are single strings — the same id values GET /api/v1/calendars returns. Both are typed as strings, but they are not interchangeable:
| Reference | Calendar | Usable as |
|---|---|---|
<accountId>:<calendarId> | Google, Microsoft, or Apple | source, target |
hosted:<id> | CalendarPipe hosted calendar | source, target |
ics:<id> | Subscribed ICS feed | source only |
invitation:<email> | Email invitation delivery | target only |
ICS feeds are read-only, so they can only be a source. invitation:<email> delivers events as calendar invitations instead of writing to a calendar, and requires the Pro invitation feature.
The order to do it in
GET /api/v1/calendars— copy theidof the calendars you want.POST /api/v1/sync-rules/dry-run— see what your gate does to real events.POST /api/v1/sync-rules— create the rule, disabled.PATCH /api/v1/sync-rules/{id}with{"enabled": true}— start syncing.
Testing gate code before you commit to it
POST /api/v1/sync-rules/dry-run runs gate code against real events and returns what each one would become. Nothing is written, and no rule has to exist yet — pass a source reference to test a calendar directly, or ruleId to use an existing rule's source.
Check eventsSource before trusting the result. When real events cannot be read, the run falls back to built-in samples and says so: eventsSource becomes "sample" and fallbackReason explains why — no_source (neither source nor ruleId given), no_events (the calendar had none in range), or fetch_failed (the provider could not be reached). A clean sample run says nothing about your real calendar.
Creating a rule
Rules are created disabled unless you pass enabled: true, so you can inspect one before it starts syncing. Omitting code — or sending null — gives the default pass-through gate.
Gate code is executed once before it is saved. Code that throws is rejected with 422 and the messages in details.errors, so a broken gate never reaches your calendars.
What a rule tells you
Two fields on a rule are worth reading before you write code against it:
capabilitiesreports what the target calendar can actually do. SettingshowAs: "outOfOffice"from a gate has no effect wherecapabilities.outOfOfficeisfalse, andeventColoris likewise ignored wherecapabilities.eventColorisfalse— today that means anything other than Google.syncis the rule's health.lastErrorand a risingerrorCountmean syncs are failing;syncedThroughAtis how far into the future the rule has actually covered.
Updating a rule
Send any combination of name, code, enabled, and eventColor. Two consequences are worth knowing before you send a code update:
- It does not re-apply the gate to events already synced. A synced event is only rewritten when it changes at the source, so your new gate reaches existing copies gradually — not at once. To apply it to everything the rule covers, follow the update with a re-sync.
- It converts a visual-builder rule to code. If
codeSourceisvisual, updatingcodediscards the visual configuration and the dashboard shows the rule as code from then on. Read the rule first if you need to keep it.
eventColor forces a re-sync. Unlike a code update, changing the colour re-writes the copies already on the calendar rather than only future ones — a colour is not part of the source event, so nothing else would ever bring the existing ones into line. Pass null to clear it.
Source and target cannot be changed. Re-pointing a live rule would orphan every event it already wrote to the old target. Delete the rule and create a new one instead.
Enabling and disabling
Enabling a rule that has never synced sets its window to start from now, so the first run does not pull in your entire calendar history.
Forcing a re-sync
POST /api/v1/sync-rules/{id}/resync clears the rule's sync cursors and the record of what it has already written, so the next scheduled run re-processes every event in the window against the current gate. This is how you apply a changed gate to events that already synced.
It returns 202 — the work happens on that next run, not during the request. The rule's window is also reset to start from now.
Deleting a rule
DELETE /api/v1/sync-rules/{id} returns 204, and deleting a rule that is already gone is still 204. For an invitation-delivery rule, cancellations are sent to the invitee before the rule is removed.
Errors
Beyond the standard error codes:
| Code | Cause |
|---|---|
402 | The rule limit for your plan is reached, or an invitation: target without the Pro feature. |
404 | The rule does not exist, or a source/target reference is not a calendar you own. |
422 | The gate function failed validation. details.errors lists what went wrong. |