Skip to main content

Property Reference

This is the complete reference for all properties and methods available when writing gate functions. Consult this page while working in the Code Editor or reviewing code generated by the AI Generator.

GateEvent Properties

Every gate function receives a GateEvent object. It contains the event's core data plus computed convenience properties that simplify common checks.

Core Properties

These are the fundamental properties of every calendar event:

PropertyTypeDescriptionExample Value
titlestringEvent title/summary"Team Standup"
descriptionstring | nullEvent description/notes"Daily sync meeting"
locationstring | nullEvent location"Conference Room A"
start{ dateTime: string, timeZone: string | null }Event start time (ISO 8601){ dateTime: "2026-02-14T14:00:00Z", timeZone: "America/New_York" }
end{ dateTime: string, timeZone: string | null }Event end time (ISO 8601){ dateTime: "2026-02-14T15:00:00Z", timeZone: "America/New_York" }
isAllDaybooleanWhether the event is an all-day eventfalse
status'confirmed' | 'tentative' | 'cancelled'Event lifecycle status, set by the organizer for everyone (not your RSVP — see responseStatus)"confirmed"
responseStatus'accepted' | 'declined' | 'tentative' | 'needsAction' | 'organizer' | nullYour RSVP to the event (the connected calendar owner's response). null when you are not an attendee or the source calendar does not expose a response."needsAction"
showAs'free' | 'busy' | nullFree/busy status"busy"
attendeesArray<{ email: string, displayName?: string, status?: 'NEEDS-ACTION' | 'ACCEPTED' | 'DECLINED' | 'TENTATIVE' }>List of event attendees. Each attendee's status is their own RSVP (iCalendar PARTSTAT), present when the source calendar exposes it.[{ email: "alice@example.com", displayName: "Alice", status: "ACCEPTED" }]
organizer{ email: string, displayName?: string } | nullEvent organizer{ email: "bob@example.com", displayName: "Bob" }
recurrencestring[] | nullRecurrence rules (iCalendar RRULE format)["RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR"]
type'default' | 'outOfOffice'The kind of event"default"

The type property reflects the event kind as reported by the source calendar provider:

SourceValue
Google out-of-office event"outOfOffice"
Google focus time / working location / birthday / normal"default"
Outlook event shown as "Out of office""outOfOffice"
Outlook anything else"default"
Apple/CalDAV, ICS"default"

Filtering by RSVP status

responseStatus reflects your own response to the event (the response on the connected source calendar), not the responses of other attendees. Use it to keep invitations you haven't accepted off your synced calendar:

function gate(event: GateEvent): GateResult {
// Drop invitations you haven't accepted (declined or not yet responded)
if (
event.responseStatus === 'declined' ||
event.responseStatus === 'needsAction'
) {
return { pass: false, reason: 'Invitation not accepted' };
}
return { pass: true };
}

How each provider maps onto responseStatus:

responseStatusGoogleMicrosoftApple / CalDAV (PARTSTAT)
acceptedacceptedacceptedACCEPTED
declineddeclineddeclinedDECLINED
tentativetentativetentativelyAcceptedTENTATIVE
needsActionneedsActionnotRespondedNEEDS-ACTION
organizeryou organize itorganizeryou are the ORGANIZER
nullnot an attendeenone / not invitednot listed as an attendee
note

A null value means there is no RSVP to evaluate — for example a personal event you created with no other invitees. Account for null explicitly if your rule should treat un-invited events differently from accepted ones.

status vs responseStatus vs attendees[].status

There are three RSVP-adjacent fields, and it's easy to confuse them:

statusresponseStatusattendees[].status
DescribesThe event's own lifecycle stateYour RSVP to the eventEach invitee's RSVP
Set byThe organizer — the same for everyoneYou — yours aloneEach attendee individually
Answers"Is this event happening?""Am I going?""Who else is going?"
Valuesconfirmed | tentative | cancelledaccepted | declined | tentative | needsAction | organizer | null'NEEDS-ACTION' | 'ACCEPTED' | 'DECLINED' | 'TENTATIVE'

So a confirmed team meeting you were invited to but never answered is status: 'confirmed' and responseStatus: 'needsAction'.

Casing differs

responseStatus uses lowercase values ('accepted'), while attendees[].status uses the uppercase iCalendar PARTSTAT vocabulary ('ACCEPTED'). They describe the same idea from different vantage points — your own response vs. a specific attendee's — but responseStatus adds 'organizer'/null, and attendees[].status is omitted entirely when the source calendar doesn't expose it.

Watch out for 'tentative', which appears in both with different meanings:

  • status === 'tentative' — the event itself is tentatively scheduled (the organizer hasn't firmed it up).
  • responseStatus === 'tentative'you replied "maybe".

The two compose naturally — use status to drop cancelled events and responseStatus to drop invitations you haven't accepted:

function gate(event: GateEvent): GateResult {
if (event.status === 'cancelled') {
return { pass: false, reason: 'Event cancelled' };
}
if (
event.responseStatus === 'declined' ||
event.responseStatus === 'needsAction'
) {
return { pass: false, reason: 'Not accepted' };
}
return { pass: true };
}
Provider nuance

On Google the two are cleanly separated: status comes straight from the event and responseStatus from your own attendee entry. On Microsoft, status is derived (cancelled events, plus free/tentative showAs, are mapped to tentative), so responseStatus is the more reliable signal for "did I accept?".

Computed Convenience Properties

These are read-only properties computed from the core properties. They simplify common checks so you don't have to parse dates or count arrays yourself.

PropertyTypeDescriptionExample Value
startDatestringAlias for start.dateTime (ISO 8601)"2026-02-14T14:00:00Z"
endDatestringAlias for end.dateTime (ISO 8601)"2026-02-14T15:00:00Z"
durationMinutesnumberEvent duration in minutes60
dayOfWeeknumberDay of week (0 = Sunday, 6 = Saturday), in UTC1 (Monday)
hournumberHour of day the event starts (0-23), in UTC14 (2 PM UTC)
isWeekdaybooleanWhether the event falls on Monday-Friday, in UTCtrue
attendeeCountnumberNumber of attendees5

Computed properties are derived from core properties at sync time. For example, durationMinutes is calculated from start.dateTime and end.dateTime, and isWeekday is derived from dayOfWeek.

Times are evaluated in UTC

hour, dayOfWeek, and isWeekday are derived from the event's start time in UTC, not the event's (or your) local timezone. An event at 9 AM Pacific (UTC-7) has hour 16, and an event late on Sunday evening in a western timezone may already count as Monday. Shift your comparisons by your UTC offset — and remember the offset changes with daylight saving time. To work in the event's own timezone, parse start.dateTime together with start.timeZone yourself.

Helper Methods

GateEvent provides two helper methods for common pattern matching. Both perform case-insensitive matching.

MethodSignatureReturnsDescription
hasAttendee(pattern)(pattern: string) => booleanbooleanSearches attendee email addresses for the given pattern
matches(pattern)(pattern: string) => booleanbooleanSearches the event title and description for the given pattern

hasAttendee(pattern)

Performs a case-insensitive search across all attendee email addresses. Returns true if any attendee's email contains the pattern.

// Check for a specific attendee
event.hasAttendee('alice@example.com'); // exact email match

// Check for a domain
event.hasAttendee('@company.com'); // any attendee from company.com

// Case-insensitive
event.hasAttendee('Alice@Example.COM'); // matches "alice@example.com"

matches(pattern)

Performs a case-insensitive search across both the event title and description. Returns true if either field contains the pattern.

// Match by keyword
event.matches('standup'); // matches "Daily Standup", "STANDUP MEETING", etc.

// Matches in title or description
event.matches('confidential'); // true if title OR description contains "confidential"

Prefer matches() over manual event.title/event.description checks — it handles null descriptions and case-insensitivity for you.

GateResult Interface

The gate function must return a GateResult object (or a simple boolean). Here is the full interface:

interface GateResult {
pass: boolean;
reason?: string;
transform?: {
title?: string;
description?: string;
location?: string;
visibility?: 'default' | 'public' | 'private';
showAs?: 'free' | 'busy';
type?: 'default' | 'outOfOffice';
autoDecline?: boolean;
preBufferMinutes?: number;
postBufferMinutes?: number;
};
}

Fields

FieldTypeRequiredDescription
passbooleanYestrue = sync the event to the target calendar. false = block (skip) the event.
reasonstringNoHuman-readable explanation for the decision. Max 200 characters. Most useful when pass is false -- the reason appears in sync logs.
transformobjectNoOverride event properties on the synced copy. Only applies when pass is true.

Transform Fields

FieldTypeMax LengthDescription
titlestring500 charsReplace the event title on the synced copy
descriptionstring5,000 charsReplace the event description on the synced copy
locationstring500 charsReplace the event location on the synced copy. Set to an empty string to clear it.
visibility'default' | 'public' | 'private'--Change the event visibility
showAs'free' | 'busy'--Change the free/busy status
preBufferMinutesinteger0–60Start the synced event earlier by N minutes (pre-event buffer). Ignored for all-day events.
postBufferMinutesinteger0–60End the synced event later by N minutes (post-event buffer). Ignored for all-day events.
type'default' | 'outOfOffice'--Set the event kind in the destination calendar. See Out of office below.
autoDeclineboolean--Out of office only: auto-decline new conflicting invitations. See Out of office below.

Out of office

Set transform.type to "outOfOffice" to write the synced event as an Out of office event in the destination calendar.

  • Google: writes a real Out of office event — requires the primary calendar of a Google Workspace account.
  • Google personal accounts (@gmail.com), non-primary calendars, or all-day events: fall back to a busy event.
  • Outlook: shown as "Out of office" on any calendar (no Workspace requirement).
  • Apple/CalDAV, hosted calendars: falls back to a busy event.

Cannot be combined with showAs: "free" — out-of-office is always busy.

return { pass: true, transform: { type: 'outOfOffice' } };
Auto-decline new conflicting invitations (opt-in)

Set transform.autoDecline to true together with type: "outOfOffice" to have Google automatically decline new invitations that conflict with the synced Out of office event. Off by default.

  • Forward-looking only: from the moment the Out of office block syncs, new conflicting invitations are declined. Events already on the calendar are never touched.
  • Only effective where a real Out of office event is written — the primary calendar of a Google Workspace account. Everywhere else (Outlook, Apple/CalDAV, hosted, personal Google accounts, all-day events) the flag is ignored.
  • Inviters see Google's standard decline message.
return { pass: true, transform: { type: 'outOfOffice', autoDecline: true } };
tip

Transforms only modify the synced copy on the target calendar. The original event on the source calendar is never changed.

Return Types

The gate function can return any of these types:

Boolean

The simplest return type. true passes the event, false blocks it:

function gate(event: GateEvent): boolean {
return event.isWeekday;
}

GateResult

An object with pass, optional reason, and optional transform:

function gate(event: GateEvent): GateResult {
return {
pass: true,
reason: 'Synced normally',
transform: { title: `[Sync] ${event.title}` },
};
}

GateResult | boolean

The union type allows mixing both styles in the same function:

function gate(event: GateEvent): GateResult | boolean {
if (event.matches('confidential')) {
return { pass: false, reason: 'Blocked confidential event' };
}
return true; // pass everything else
}

Complete Example

This comprehensive gate function demonstrates multiple properties, methods, and transform options:

function gate(event: GateEvent): GateResult {
// Block confidential events entirely
if (event.matches('confidential') || event.matches('secret')) {
return { pass: false, reason: 'Blocked confidential event' };
}

// Block cancelled events
if (event.status === 'cancelled') {
return { pass: false, reason: 'Event is cancelled' };
}

// Redact personal events -- show the time block but hide details
if (event.matches('personal') || event.matches('private')) {
return {
pass: true,
transform: {
title: 'Personal Event',
description: '',
visibility: 'private',
},
};
}

// Mark solo events (no other attendees) as free
if (event.attendeeCount <= 1) {
return {
pass: true,
transform: { showAs: 'free' },
};
}

// Add prefix to large meetings (5+ attendees) for easy identification
if (event.attendeeCount >= 5) {
return {
pass: true,
transform: {
title: `[Team] ${event.title}`,
},
};
}

// Pass everything else unchanged
return { pass: true };
}

This example:

  • Uses matches() for keyword filtering (confidential, personal)
  • Checks status for cancelled events
  • Uses attendeeCount for attendee-based logic
  • Applies different transforms based on conditions
  • Returns a reason for blocked events
  • Falls through to a default pass for unmatched events
  • Code Editor — author and test functions with autocompletion.
  • Cookbook — recipes that use these properties.
  • Templates — common scenarios as one-click presets.