Timers & Scheduling
Causet supports delayed event emission via op: schedule in side effects rules. This covers common patterns like reminders, expiry checks, and time-windowed follow-ups. For longer or more complex schedules, use an external scheduler to submit intents to Causet at the right time.
op: schedule Syntax
side_effects:
rules:
- name: <rule_name>
when: <conditions>
then:
- op: schedule
event_type: <EVENT_TYPE>
delay_seconds: <integer>
payload:
<key>: <expression>Fields
| Field | Required | Description |
|---|---|---|
event_type | Yes | Event type to emit after the delay |
delay_seconds | Yes | Delay in seconds from intent processing time |
payload | No | Payload for the scheduled event |
Example: Show Reminder 24 Hours Before Event
side_effects:
rules:
- name: schedule_show_reminder
when:
- path: /reminder_enabled
op: eq
value: true
then:
- op: schedule
event_type: SHOW_REMINDER_SENT
delay_seconds: 86400
payload:
user_id: intent.user_id
show_id: intent.show_id
venue: intent.venueWhen the intent is processed, the runtime enqueues a SHOW_REMINDER_SENT event to fire 86400 seconds (24 hours) later. That event is committed to the ledger and can advance sagas or trigger further side effects.
Common Scheduling Patterns
Post-Purchase Follow-Up
side_effects:
rules:
- name: schedule_followup
when: {}
then:
- op: schedule
event_type: PURCHASE_FOLLOWUP_TRIGGERED
delay_seconds: 259200
payload:
user_id: intent.user_id
purchase_id: intent.purchase_idStale Session Expiry
side_effects:
rules:
- name: schedule_session_expiry
when:
- path: /session_active
op: eq
value: true
then:
- op: schedule
event_type: SESSION_EXPIRED
delay_seconds: 3600
payload:
session_id: intent.session_id
user_id: intent.user_idDeterminism
Scheduled events are deterministic: given the same entity state and intent payload, the same events will be scheduled. This matters for replay: if you replay the ledger, the rule logic will schedule the same events again. Ensure your rules guard against duplicate scheduling using entity state:
side_effects:
rules:
- name: schedule_reminder_if_not_already_scheduled
when:
- path: /reminder_scheduled
op: eq
value: false
then:
- op: schedule
event_type: SHOW_REMINDER_SENT
delay_seconds: 86400
payload:
user_id: intent.user_id
show_id: intent.show_idAnd in core, set the guard field when the schedule fires:
core:
rules:
- name: mark_reminder_scheduled
when: {}
then:
- op: set
path: /reminder_scheduled
value: trueLimitations
Relative Delays Only
delay_seconds is always relative to the time the intent is processed. There is no way to schedule at an absolute timestamp directly in the DSL.
If you need to fire at a specific time (e.g., 30 minutes before show start), calculate the delay in your client before submitting the intent, or use an external scheduler.
No Cancellation
Once a op: schedule fires, the scheduled event cannot be canceled. Use saga state to guard against processing stale scheduled events:
preflight:
rules:
- name: reminder_still_relevant
when:
- path: /status
op: neq
value: "cancelled"
error:
code: REMINDER_NOT_RELEVANT
message: "show was cancelled, ignore reminder"When the scheduled SHOW_REMINDER_SENT event fires, the action’s preflight rejects it if the show was cancelled in the interim. The event is still written to the ledger (preflight rejects the intent that would process it further), but no state change occurs.
External Scheduling for Complex Cases
For schedules that don’t fit delay_seconds:
- Absolute timestamps: external scheduler calculates remaining seconds, submits intent at the right time
- Recurring schedules: cron job or EventBridge rule → submit intent on each occurrence
- Very long delays (weeks, months): use a durable external scheduler like AWS EventBridge Scheduler, Google Cloud Scheduler, or a database-backed job queue
The entity in Causet doesn’t know or care that the trigger came from an external scheduler. It processes the intent the same as any other.
Checking Scheduled Event Status
Scheduled events appear in the ledger_events table when they fire. You can verify a scheduled event was committed:
SELECT event_id, event_type, entity_id, ts, payload
FROM ledger_events
WHERE event_type = 'SHOW_REMINDER_SENT'
AND payload->>'show_id' = 'show_xyz'
ORDER BY ts DESC
LIMIT 10;