Skip to main content

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:

ReferenceCalendarUsable as
<accountId>:<calendarId>Google, Microsoft, or Applesource, target
hosted:<id>CalendarPipe hosted calendarsource, target
ics:<id>Subscribed ICS feedsource only
invitation:<email>Email invitation deliverytarget 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

  1. GET /api/v1/calendars — copy the id of the calendars you want.
  2. POST /api/v1/sync-rules/dry-run — see what your gate does to real events.
  3. POST /api/v1/sync-rules — create the rule, disabled.
  4. 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:

  • capabilities reports what the target calendar can actually do. Setting showAs: "outOfOffice" from a gate has no effect where capabilities.outOfOffice is false, and eventColor is likewise ignored where capabilities.eventColor is false — today that means anything other than Google.
  • sync is the rule's health. lastError and a rising errorCount mean syncs are failing; syncedThroughAt is 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 codeSource is visual, updating code discards 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:

CodeCause
402The rule limit for your plan is reached, or an invitation: target without the Pro feature.
404The rule does not exist, or a source/target reference is not a calendar you own.
422The gate function failed validation. details.errors lists what went wrong.