Scheduling
A schedule runs a task on a cron, a fixed interval, or once at a future moment. For most cases, pass schedule= when creating the task. This page covers everything else.
Cron schedules
Attach a schedule to an existing task with triggers.create():
from m8tes import M8tes
client = M8tes()
trigger = client.tasks.triggers.create(
task.id,
type="schedule",
cron="0 9 * * 1", # every Monday at 9am
timezone="America/New_York",
)
print(trigger.id, trigger.cron)Common cron patterns
Interval schedules
Pass interval_seconds instead of cron to run at a fixed interval:
trigger = client.tasks.triggers.create(
task.id,
type="schedule",
interval_seconds=3600, # every hour
)One-time runs
Pass run_at to run a task once at a future moment. It fires and then retires itself, so nothing is left behind:
trigger = client.tasks.triggers.create(
task.id,
type="schedule",
run_at="2027-07-20T09:00:00", # ISO 8601
timezone="America/New_York", # applied when run_at has no offset
)
print(trigger.run_at) # 2027-07-20T09:00:00-04:00cron, interval_seconds and run_at are mutually exclusive. run_at must be at least a few minutes ahead. To run something now, use tasks.run() instead.
Read the fire time back from trigger.run_at, not trigger.next_run: next_run is computed for cron schedules only.
Email notifications
By default, m8tes emails you when a scheduled run completes. Disable it per task:
client.tasks.update(task.id, email_notifications=False)email_notifications also works on tasks.create().
If a scheduled run fails, you get a failure email (after any automatic retries for transient errors finish). When several scheduled tasks fail in the same day, later failures are folded into one digest so a busy account is not spammed.
When schedules cannot start because the account needs a model plan or a paid plan, m8tes quiet-skips the fire (no failed run) and emails you at most once per outage. After three consecutive skips of that kind, the schedules are paused automatically and you get one more email with a link to fix it; they re-arm once the block clears.
Managing triggers
# list triggers; cron schedule triggers include next_run, the next fire
# time computed from the expression (null while paused, and null for
# interval schedules. Their phase lives in the scheduler)
triggers = client.tasks.triggers.list(task.id)
for t in triggers:
print(t.id, t.type, t.cron or t.interval_seconds or t.run_at, t.next_run)
# -> schedule_5 schedule 0 9 * * 1 2026-08-17T09:00:00Z
# pause and resume a schedule without losing it
client.tasks.triggers.update(task.id, trigger_id="schedule_5", enabled=False)
client.tasks.triggers.update(task.id, trigger_id="schedule_5", enabled=True)
# reshape a schedule in place, no delete + re-create
client.tasks.triggers.update(task.id, trigger_id="schedule_5", cron="0 18 * * 5", timezone="Europe/Copenhagen")
# a recurring schedule can become a one-off, and back
client.tasks.triggers.update(task.id, trigger_id="schedule_5", run_at="2027-07-20T09:00:00")
# delete a trigger
client.tasks.triggers.delete(task.id, trigger_id="schedule_5")Trigger ids are namespaced strings, not row numbers. schedule_5 and app_5 are different triggers, and the webhook and email ones are simply webhook and email. Pass back whatever list() gave you.
enabled also pauses and resumes app triggers. Webhook and email triggers are managed via the task webhook and agent email-inbox endpoints instead.
Next: Webhook Triggers · Email Inbox · Webhook Events