ObjectStackObjectStack

Time Relative Trigger

Time Relative Trigger protocol schemas

Time-Relative Trigger Protocol

A declarative trigger for time-relative business rules — "act on records whose date field is coming up (or overdue) relative to today" — without the author hand-writing a cron job + range query, and without the fragile date-equality-on-record-change anti-pattern (#1874).

The anti-pattern it replaces

Authors used to express "alert 60 days before end_date" as a record_change flow gated on end_date == daysFromNow(60). That predicate is only evaluated when the record happens to change, so it fires only if the record is edited on exactly that day — i.e. almost never, unattended. The robust alternative was a hand-written schedule flow that queries a date range every day, which every author re-implemented (contracts renewal_alert, hr document_expiring_soon, procurement po_overdue, …).

What this declares instead

A time_relative trigger sweeps an object on a schedule (daily by default) and launches the flow once per matching record, with that record in the automation context (so {record.<field>} interpolation and the start-node condition gate work exactly as they do for record-change flows). The descriptor is carried on the flow's start node as config.timeRelative.

@example T-minus renewal reminders (fires on the day a contract is 60/30/7 days out)

// flow start node
config: {
  timeRelative: {
    object: 'contracts',
    dateField: 'end_date',
    offsetDays: [60, 30, 7],
    filter: { status: 'active' },
  },
  // optional sweep cadence — defaults to daily at 08:00 UTC
  schedule: { type: 'cron', expression: '0 8 * * *' },
}

@example "Expiring soon" range (fires every day a document is within 30 days of expiry)

config: {
  timeRelative: { object: 'hr_document', dateField: 'expires_on', withinDays: 30 },
}

@example Overdue sweep (fires for POs up to 14 days past due)

config: {
  timeRelative: { object: 'purchase_order', dateField: 'due_date', withinDays: -14, filter: { status: 'open' } },
}

Source: packages/spec/src/automation/time-relative-trigger.zod.ts

TypeScript Usage

import { TimeRelativeTriggerSchema } from '@objectstack/spec/automation';
import type { TimeRelativeTrigger } from '@objectstack/spec/automation';

// Validate data
const result = TimeRelativeTriggerSchema.parse(data);

TimeRelativeTrigger

Properties

PropertyTypeRequiredDescription
objectstringObject (machine name) to sweep, e.g. "contracts".
dateFieldstringDate or datetime field evaluated relative to today, e.g. "end_date".
withinDaysintegeroptionalRange mode: fire while dateField is within N days of today. Positive = upcoming, negative = overdue lookback, 0 = today.
offsetDaysinteger[]optionalOffset mode: fire when dateField is exactly today + each offset (e.g. [60, 30, 7]).
filterRecord<string, any>optionalExtra ObjectQL where-map ANDed with the date window (e.g. { status: "active" }).
maxRecordsintegeroptionalMax records launched per sweep (default 1000). The sweep logs when it clamps.

On this page