LanternDocs

Scheduling

Run an agent on a cron expression. Schedules are their own resource, so one agent can have many, each with its own input and timezone. Manage them at /v1/schedules.

In plain terms"Every weekday at 9am, summarize my inbox," with nobody clicking a button. A cron expression is five fields: minute, hour, day of month, month, weekday. Scheduled runs appear in the dashboard like any other run.

Cron expressions

Five fields: minute, hour, day of month, month, day of week. Each field accepts *, lists, ranges and */N steps:

# ┌─── minute (0-59)
# │ ┌─── hour (0-23)
# │ │ ┌─── day of month (1-31)
# │ │ │ ┌─── month (1-12)
# │ │ │ │ ┌─── day of week (0-7, Sun=0 or 7)
# │ │ │ │ │
# * * * * *

0 9 * * 1-5     # every weekday at 9:00 AM (in the schedule timezone)
*/15 * * * *    # every 15 minutes
0 0 1 * *       # first day of every month at midnight
Day-of-month and day-of-week are ANDed. Classic Vixie cron fires on either when both are set. Lantern requires both to match, so 0 9 1-7 * 1 fires on the first Monday of the month. If you port a crontab that relied on the OR behavior, rewrite it as two schedules. The search for the next fire time gives up after two years.

Schedule API

Create a schedule

POST /v1/schedules
Content-Type: application/json

{
  "agentName": "research-agent",
  "cronExpr": "0 9 * * 1-5",
  "timezone": "America/New_York",
  "inputTemplate": { "mode": "daily-digest" },
  "deliveryEmail": "me@example.com",   // optional: email the result
  "enabled": true
}

Response: 201 Created
{
  "id": "sched-uuid",
  "agentName": "research-agent",
  "cronExpr": "0 9 * * 1-5",
  "inputTemplate": { "mode": "daily-digest" },
  "deliveryEmail": "me@example.com",
  "enabled": true,
  "nextFireAt": "2026-07-28T13:00:00Z"
}

List schedules

GET /v1/schedules

Response: 200 OK — bare array
[
  { "id": "sched-uuid", "agentName": "...", "cronExpr": "...", "enabled": true, ... }
]

Update a schedule

PUT /v1/schedules/{id}
Content-Type: application/json

{ "enabled": false }   // pause. Also accepts cronExpr, deliveryEmail, timezone

Delete a schedule

DELETE /v1/schedules/{id}

Response: 204 No Content

Timezone handling

timezone is an optional IANA name (an invalid one returns 400). Cron then matches the schedule's local time. Resolution order: the schedule's zone, then LANTERN_DEFAULT_TIMEZONE, then UTC. With neither set, 0 9 * * * fires at 09:00 UTC.

The deployment default (not a per-schedule zone) also sets the budget day boundary: daily limits roll at local midnight in that zone.

Surfaces trigger runs too. Inbound messages on a channel start runs. They are not schedules but follow the same run lifecycle. See Surfaces.