Heldrey rules documentation

Heldrey is a persistent, deterministic Google Calendar automation engine. A person states a recurring preference once; Heldrey validates it into a rule, previews it against the real calendar, applies it continuously, explains every action, and reverses changes safely. Google Calendar stays the system of record. No model runs when rules execute.

How a rule works

Conditions

Every condition is {"key", "operator", "value"}. Add "negate": true to invert one (Productive plan). Values are validated against the schema for the operator.

KeyMeaningOperatorsValuePlan
event.everyAlways true, so the rule acts on every event on the calendar.isnoneFree
titleThe event title.contains, not_contains, equals, starts_with, ends_with, contains_anytext / text_listFree
descriptionThe event description text.contains, not_contains, equals, starts_with, ends_with, contains_anytext / text_listFree
location.textThe location field as written on the event.contains, not_containstextFree
meeting.externalAt least one other attendee is outside your internal domains.is, is_notbooleanFree
meeting.internalEveryone else invited belongs to your internal domains.is, is_notbooleanFree
meeting.has_videoThe event has a recognized video link.is, is_notbooleanFree
meeting.has_physical_locationThe event has a usable physical location.is, is_notbooleanFree
attendees.just_meNo other attendees.is, is_notbooleanFree
attendees.one_on_oneExactly one other attendee.is, is_notbooleanFree
attendees.includesA particular person is invited.includes, excludesemail_listFree
attendees.domainSomeone from a given email domain is invited.includes_any, excludes_alldomain_listFree
attendees.countHow many other people are invited (rooms excluded).equals, greater_than, less_than, betweennumber / number_rangeFree
time.durationHow long the event lasts, in minutes.equals, greater_than, less_than, betweennumber / number_rangeFree
time.day_of_weekThe weekday the event starts on, in your timezone.inday_setFree
time.start_timeWhen the event starts, in your timezone.before, after, betweentime / time_rangeFree
time.is_all_dayThe event is an all-day entry.is, is_notbooleanFree
time.within_working_hoursThe event starts inside your configured working hours.is, is_notbooleanFree
meeting.fully_externalEvery other attendee is outside your internal domains.is, is_notbooleanFree
organizer.is_meYou are the organizer.is, is_notbooleanFree
organizer.emailThe organizer’s email address.is, is_nottextFree
organizer.domainThe organizer’s email domain.is, is_nottextFree
rsvp.mineYour response to the invitation.is, is_not, inenum / enum_list (needsAction, accepted, tentative, declined, not_invited)Free
meeting.structurePhysical, video, hybrid, or neither.is, is_not, inenum / enum_list (physical_only, video_only, hybrid, neither)Free
meeting.video_providerWhich video service the event uses.is, is_not, inenum / enum_list (google_meet, zoom, teams, webex, other)Free
location.savedThe event takes place at one of your saved locations.is, is_not, insaved_location / saved_location_listFree
location.is_effective_locationThe event is where you already are according to your schedule.is, is_notbooleanFree
location.physical_missingThe event has no usable physical location.is, is_notbooleanFree
location.unresolvedThe event has a location Heldrey could not place: it matches no saved location or alias, and the map provider has no one precise place for it.is, is_notbooleanFree
travel.requiredHeldrey currently plans travel to this event.is, is_notbooleanFree
event.typeRegular event, focus time, out of office, or working location.is, inenum / enum_list (default, focus_time, out_of_office, working_location)Free
event.availabilityWhether the event blocks time (busy) or not (free).isenum (busy, free)Free
recurrence.is_recurringThe event is an occurrence of a recurring series.is, is_notbooleanFree
recurrence.frequencyHow often the series repeats.is, is_not, inenum / enum_list (daily, weekly, monthly, yearly)Free
calendar.isThe calendar the event belongs to.is, is_notcalendarProductive
calendar.inThe event belongs to one of several calendars.incalendar_listProductive
calendar.is_primaryThe event is on your primary calendar.is, is_notbooleanProductive
account.isThe Google account the calendar belongs to.is, is_notaccountProductive

Value types: text and text_list are strings; number and duration are integers (minutes); time is "HH:MM"; time_range is {"min", "max"} in minutes from midnight; day_list is ISO weekdays 1 (Monday) to 7 (Sunday); saved_location takes {"name": "Office"} or {"default": true}; calendar takes {"primary": true}, {"name": "Work"}, or {"id": "..."}; domain_list and email_list are lowercase strings.

Actions

KeyMeaningParameters (defaults)Undo
event.colorSets the event color on your copy of the event.{"color":"blueberry"}restores the field
buffer.beforeHolds time right before the meeting. Travel there ends before it starts.{"minutes":15,"titleTemplate":"Buffer before {event_title}","color":null}removes the block
buffer.afterHolds time after the meeting. Travel away from it runs first.{"minutes":10,"titleTemplate":"Buffer after {event_title}","color":null}removes the block
event.reminderSets a single popup reminder before the event.{"minutesBefore":30}restores the field
block.prepAdds a named block before the event.{"minutes":30,"titleTemplate":"Prepare for {event_title}"}removes the block
block.followupAdds a named block after the event.{"minutes":15,"titleTemplate":"Follow up on {event_title}"}removes the block
travel.overrideSkip travel for this event, or require it even for a hybrid meeting.{"mode":"skip"}none
event.availabilityMarks the event as busy or free on your calendar.{"availability":"busy"}restores the field
event.visibilityMakes the event private or restores the calendar default.{"visibility":"private"}restores the field
attention.createMakes no calendar change; asks you to look at the event instead.{"message":"Add a location for this meeting","action":"open_in_calendar"}none

Only one action per conflict domain applies per rule (for example one color). A block title may use {event_title}; a title carrying any other {…} is refused, and the refusal names the placeholders the title can use.

Operators

OperatorReads as
isis
is_notis not
containscontains
not_containsdoes not contain
equalsequals
starts_withstarts with
ends_withends with
contains_anycontains any of
includesincludes
excludesexcludes
includes_anyincludes any from
excludes_allexcludes all from
greater_thanis greater than
less_thanis less than
betweenis between
beforeis before
afteris after
inis one of

Colors

Google Calendar event colors by name: lavender (Lavender), sage (Sage), grape (Grape), flamingo (Flamingo), banana (Banana), tangerine (Tangerine), peacock (Peacock), graphite (Graphite), blueberry (Blueberry), basil (Basil), tomato (Tomato). "purple" is accepted as an alias for grape. Any other color is written as a hex, such as #039be5.

Limits

Plans and limits

PlanPriceGoogle accountsCalendarsActive rulesGroups, "not", stopping later rulesCalendar syncUndoActivityTeam
Free$0113 (+1 per qualified referral)nonosingle30 daysno
Productive$20/month510unlimitedyesyessingle and batch365 daysno
Team$70/month for 5 users510unlimitedyesyessingle and batch365 daystemplates, membership, seats

Referral program

Instruction packages

An instruction package is JSON that matches the schema at https://app.heldrey.com/schemas/instructions/v1.json. It carries "heldrey": "1.0", an optional package_id (so the same package is recognized if imported twice), and up to 20 items. Item types: custom_rule, invite_hold, travel_time, calendar_sync. References to calendars and locations are portable ({"primary": true}, {"name": "..."}, {"default": true}); Heldrey asks the person to map anything ambiguous and never guesses.

{
  "heldrey": "1.0",
  "package_id": "interviews-breathing-room",
  "title": "Breathing room around interviews",
  "generated_by": "An AI assistant",
  "items": [
    {
      "type": "custom_rule",
      "name": "Breathing room around interviews",
      "when": {
        "all": [
          {
            "key": "title",
            "operator": "contains",
            "value": "Interview"
          }
        ]
      },
      "then": [
        {
          "key": "buffer.before",
          "params": {
            "minutes": 15
          }
        },
        {
          "key": "buffer.after",
          "params": {
            "minutes": 10
          }
        }
      ],
      "enabled": true
    },
    {
      "type": "custom_rule",
      "name": "Color external meetings",
      "calendar": {
        "primary": true
      },
      "when": {
        "all": [
          {
            "key": "meeting.external",
            "operator": "is",
            "value": true
          },
          {
            "any": [
              {
                "key": "meeting.has_video",
                "operator": "is",
                "value": true
              },
              {
                "key": "time.duration",
                "operator": "greater_than",
                "value": 45
              }
            ]
          }
        ]
      },
      "then": [
        {
          "key": "event.color",
          "params": {
            "color": "tomato"
          }
        }
      ],
      "enabled": false
    }
  ]
}
{
  "heldrey": "1.0",
  "package_id": "holds-and-travel",
  "items": [
    {
      "type": "invite_hold",
      "enabled": true,
      "title_template": "Pending: {event_title}",
      "color": "graphite"
    },
    {
      "type": "travel_time",
      "enabled": true,
      "hybrid_mode": "assume_virtual",
      "extra_minutes": 10
    },
    {
      "type": "travel_time",
      "enabled": true,
      "calendars": [
        {
          "name": "Personal"
        }
      ],
      "hybrid_mode": "assume_in_person",
      "extra_minutes": 5
    },
    {
      "type": "custom_rule",
      "name": "Skip travel for the weekly review",
      "when": {
        "all": [
          {
            "key": "title",
            "operator": "equals",
            "value": "Weekly review"
          }
        ]
      },
      "then": [
        {
          "key": "travel.override",
          "params": {
            "mode": "skip"
          }
        }
      ]
    }
  ]
}
{
  "heldrey": "1.0",
  "package_id": "work-to-personal",
  "items": [
    {
      "type": "calendar_sync",
      "name": "Work → Personal",
      "source": {
        "name": "Work"
      },
      "target": {
        "name": "Personal"
      },
      "title_mode": "hidden",
      "masked_title": "Busy (work)",
      "days": [
        1,
        2,
        3,
        4,
        5
      ],
      "between": {
        "start": "08:00",
        "end": "19:00"
      },
      "enabled": true
    }
  ]
}

MCP server