Skip to content

Scheduler and heartbeat

Scheduled jobs live in the agent's schedule list and appear in the dashboard's Scheduler tab, where you can also create, pause, edit and trigger them.

schedule: [
  {
    name: "Morning revenue check",
    cron: "0 9 * * 1-5",
    timezone: "America/Costa_Rica",
    message: "Check yesterday's revenue and flag anything unusual.",
  },
  {
    name: "Weekly deep analysis",
    cron: "0 7 * * 1",
    message: "Review last week's numbers in depth and write the report.",
    model: "olano:pro", // this job only; the agent's own model is unchanged
  },
]

A job is a message sent to the agent at a time you choose — so anything the agent can do in a conversation, it can do on a schedule. Use thread_id to keep a job's history in one continuous conversation.

A model per job. model (provider:model, for example olano:turbo or anthropic:claude-sonnet-4-6) runs that one job on a model other than the agent's configured one, so a cheap tier can do the nightly digest and a bigger one the weekly analysis on the same agent. Leave it out and the job follows the agent's model, including later changes to it. Each run is billed to the model that ran, and the run's conversation in the chat list shows which model it was; on a managed deployment a bigger tier spends credits faster. A model whose provider is not connected is refused when the job is saved, and a model that stops working later fails the run with that reason in the job's history rather than falling back silently. The same field exists on tasks in the Scheduler tab ("Model", with "Agent default" as the first choice) and on the scheduling tools an agent or AgentFather uses.

And a reasoning effort per job. reasoning_effort sets how much thinking the model does before answering on this job's runs, independently of the agent's own reasoning_effort: a nightly digest can run at low while the weekly analysis runs at high on the same agent. The value must be one of the levels the model the job runs on accepts (model when set, else the agent's configured model); the Scheduler tab's "Reasoning effort" field appears only for such a model and lists exactly that ladder, and every surface that saves a job refuses a level the model does not take, naming the ones it does. Leave it out to follow the agent's own setting. Higher levels cost more tokens and time per run; a level that stops being valid later (a model retired, a package changed) is dropped for that run with a log line rather than failing the job.

flowchart TB
    subgraph start["What starts the work"]
        direction LR
        S1["schedule list in agent.json5<br/>cron only"] ~~~ S2["Scheduler store<br/>the New task form or the agent itself,<br/>cron or one-time"] ~~~ HB["Heartbeat<br/>every 60 minutes by default,<br/>reads HEARTBEAT.md"]
    end
    start --> RUN["The agent runs, exactly as in a conversation:<br/>tools, files, approvals"]

Fan-out. With multi-user mode on, for_each_customer: true runs the job once per end user, and the message can use {customer_key}, {platform} and {user_id}. Use action instead of message to run a named script from allowed_actions.

Two places jobs live. The schedule list above is one of them; the other is the scheduler's own store, which is what the Scheduler tab's "New task" form writes and what an agent creates when it schedules something itself. The Scheduler tab merges both and badges each row, so nothing is hidden — but the difference matters when you go looking for what is triggering a job. A schedule entry is cron-only and lives in the agent's configuration; a stored task can also be one-time and can be handed to a different agent later.

Asking an agent about the schedule. Any agent can read and change its own scheduled work in chat, and AgentFather can do the same for every agent in the deployment — across both places — so "what wakes the finance agent at 3am?", "pause that job", or "schedule a Monday summary for the support agent" are all things you can simply ask for. Changing, creating or immediately running another agent's job always raises an approval card first, because it makes that agent start working on instructions you supplied. Ask to see a job's full instructions before approving an edit: a listing shows only its name, schedule and the first couple of lines.

Heartbeat is different and simpler: the agent wakes up periodically (default every 60 minutes) and reads HEARTBEAT.md in its workspace — a plain markdown file of standing instructions. It is the low-ceremony way to say "keep an eye on this" without writing a cron entry. Control it with the heartbeat block per agent, or heartbeat: in config.yaml for the fleet.