agno_schedules table). The scheduler runs in the same FastAPI process as your agents. No separate worker.
agno_schedules every scheduler_poll_interval seconds, fires due jobs, retries failures up to each schedule’s max_retries, and persists state.
The scheduler fires a due job by calling its endpoint over HTTP, against http://127.0.0.1:7777 by default. That matches the default serve() port. Set scheduler_base_url to match when you serve on a different host or port; otherwise schedules fire against the wrong URL.
Two ways to create schedules
Agent Managed
Give an agentSchedulerTools and it can schedule its own work via chat:
Manually Registered
For schedules that should always exist (the daily digest, the hourly sync, the nightly cleanup), create them in your app’s lifespan viaScheduleManager:
if_exists="update" makes the call idempotent. Re-running on restart updates the existing schedule rather than raising or duplicating. Pass "skip" if you want to leave manually-edited schedules alone, or "raise" (the default) to surface accidental name collisions. This is the pattern Coda uses for daily digest and repo sync.
Workflows for multi-step jobs
Schedules fire single endpoints. When the work is multi-step (research, then outline, then draft, then review), you reach for a workflow. Workflows are a separate primitive, but they’re the most common thing a schedule fires. A workflow is a typed pipeline whose steps run in order. UseParallel for concurrent steps, Loop for repetition, and Router to select one branch.
Loop.end_condition accepts a CEL expression string (as above) or a callable that takes the iteration’s step outputs and returns a bool. Condition is a separate primitive for if/else branching inside a workflow.
CEL string conditions need the optional
cel-python dependency: pip install cel-python or pip install 'agno[cel]'. Without it, the expression logs an error, evaluates to False, and the loop runs to max_iterations. The callable form has no extra dependency.POST /workflows/<id>/runs endpoint and can be scheduled. Session history is persisted when the workflow has a database. Traces are stored when AgentOS tracing is enabled.
For worked examples, see Demo OS.
Schedule runs and observability
When a schedule fires, AgentOS:- Looks up the schedule in
agno_schedulesand claims it via a row-level lease. - Calls the configured endpoint (
POST /agents/<id>/runsorPOST /workflows/<id>/runs) over HTTP viahttpx.AsyncClient. This is the same path an external caller would take, including auth headers. - Records the schedule attempt in
agno_schedule_runswith status, timings, the underlyingrun_idandsession_idwhen returned, and any error. The target component persists its run according to its database configuration. Traces require tracing to be enabled.
agno_schedule_runs. When the target component persists sessions and AgentOS tracing is enabled, the linked run also appears in session and trace views. This Postgres query lists runs fired in the last 24 hours:
ai. prefix is the schema PostgresDb creates its tables in by default (override with PostgresDb(db_schema=...)). Timestamps on schedule runs are stored as epoch seconds (BigInt). For the trace of a specific scheduled run, follow the run_id from agno_schedule_runs back to agno_traces. See Observability for the full data model.
Scheduler in HA
Every replica can run the scheduler loop safely on the backends that implement the scheduler’s claim methods (Postgres, SQLite, and MongoDB). Due schedules are claimed via a row-level lease (locked_by, locked_at on agno_schedules). The first replica to claim a due job runs it; the others skip. No leader election needed.
If you’d rather keep scheduler polling off your hot request path, pin it to a dedicated replica via deployment config. See Scheduler for tuning details.