Scheduling

Run agents, teams, and workflows on recurring schedules with persisted history and retry controls.

Recurring work such as daily briefs, queue triage, repository syncs, health checks, and reports should use the same runtime as on-demand runs. AgentOS stores schedules and run history in the platform database, invokes existing agent, team, or workflow endpoints, and lets agents manage schedules through SchedulerTools.

from agno.os import AgentOS

agent_os = AgentOS(
    agents=[agent],
    db=db,
    scheduler=True,
    scheduler_poll_interval=15,    # check for due jobs every N seconds
)

The scheduler runs inside the AgentOS process and polls agno_schedules every scheduler_poll_interval seconds. Keep at least one scheduler-enabled runtime running continuously. Due jobs retry failures up to each schedule's max_retries, and every attempt is persisted.

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

PatternHow
Agent ManagedSchedulerTools lets an agent create, list, inspect, delete, enable, disable, and review runs for schedules. Creating a schedule with an existing name updates it.
Manually RegisteredSchedules created in code, registered at startup.

Agent Managed

Give an agent SchedulerTools and it can schedule its own work via chat:

from agno.agent import Agent
from agno.tools.scheduler import SchedulerTools

agent = Agent(
    id="my-agent",
    model="openai:gpt-5.4",
    tools=[
        SchedulerTools(
            db=db,
            default_endpoint="/agents/my-agent/runs",
            default_method="POST",
            default_timezone="UTC",
        ),
    ],
)

# In Slack: "@MyAgent post a daily digest of open PRs at 9am ET"
# The agent calls SchedulerTools.create_schedule() with a cron expr.

The Scheduler Tools Agent example is a runnable version of this pattern.

Manually Registered

For schedules that should always exist (the daily digest, the hourly sync, the nightly cleanup), create them in your app's lifespan via ScheduleManager:

from contextlib import asynccontextmanager
from agno.scheduler import ScheduleManager

@asynccontextmanager
async def lifespan(app):
    manager = ScheduleManager(db=db)
    manager.create(
        name="daily_digest",
        cron="0 9 * * 1-5",                       # weekdays 9am
        endpoint="/agents/my-agent/runs",
        payload={"message": "Create the daily digest."},
        if_exists="update",                       # idempotent on restart
    )
    yield

agent_os = AgentOS(agents=[agent], db=db, scheduler=True, lifespan=lifespan)

if_exists="update" makes restarts idempotent by updating the existing schedule. Pass "skip" to preserve manually edited schedules or "raise" (the default) to surface accidental name collisions. This is the pattern Coda uses for daily digest and repo sync.

Schedule a workflow

Schedules invoke endpoints. Point a schedule at /workflows/<id>/runs when recurring work has multiple steps, branches, or review loops. See Workflow Automation and Workflows.

Schedule runs and observability

When a schedule fires, AgentOS:

  1. Looks up the schedule in agno_schedules and claims it through a database-backed lease.
  2. Calls the configured endpoint (POST /agents/<id>/runs, POST /teams/<id>/runs, or POST /workflows/<id>/runs) over HTTP via httpx.AsyncClient. AgentOS supplies an internal service credential, preserves the schedule owner, submits run endpoints with background=true and stream=false, and polls to a terminal or paused result. Scheduled component runs use the live published version.
  3. Records the schedule attempt in agno_schedule_runs with status, timings, the underlying run_id and session_id when returned, and any error. The target component persists its run according to its database configuration. Traces require tracing to be enabled.

Schedule runs are queryable from 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:

SELECT
    s.name,
    sr.status,
    sr.triggered_at,
    (sr.completed_at - sr.triggered_at) AS duration_s
FROM ai.agno_schedule_runs sr
JOIN ai.agno_schedules s ON s.id = sr.schedule_id
WHERE sr.created_at > extract(epoch from NOW() - INTERVAL '24 hours')::bigint
ORDER BY sr.created_at DESC;

The 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 through an atomic database-backed lease. The first replica to claim a due job runs it; the others skip while its lease is valid. Claims coordinate workers; they do not make external tool effects exactly-once. Make recurring actions safe to retry.

Deployment configuration can pin scheduler polling to a dedicated replica. See Scheduler for tuning details.