Skip to main content
Aegra supports scheduled runs via a background cron scheduler. Cron jobs are stored in PostgreSQL and fired by a polling loop that runs inside every server instance.

Prerequisites

Enable the scheduler (it is on by default):
See the environment variables reference for all options.
Delivery semantics: at-least-once. When the scheduler claims a cron, the claim is held for CRON_CLAIM_DURATION_SECONDS. If a worker crashes between claiming and committing the run advance, another tick re-fires the cron once the claim expires. Make agent runs idempotent if duplicate firings are unacceptable.
Per-user cap. A single user can own at most CRON_MAX_PER_USER crons (default 100). Creating beyond the cap returns HTTP 429. Set CRON_MAX_PER_USER=0 to disable.
Sub-minute schedules. 6-field (seconds-first) schedules like */30 * * * * * are rejected unless CRON_ALLOW_SECONDS_SCHEDULE=true. They multiply scheduler load and DB writes per minute.

Creating a stateless cron

A stateless cron fires against a new thread on every occurrence:
Aegra accepts the graph ID directly in assistant_id for cron creation and resolves it to the default assistant for that graph at request time. By default, each stateless cron run deletes its ephemeral thread after the run completes, including the immediate first run that is triggered when the cron is created.
The snippets below assume you are inside an async def function with an initialized client.

Thread cleanup for stateless crons

Stateless crons create a fresh thread for every execution. Control what happens to that thread after the run finishes with on_run_completed:
  • "delete" (default) deletes the ephemeral thread automatically after the run completes.
  • "keep" preserves the thread so you can inspect its state or runs later.
When you keep stateless cron threads, you are responsible for cleaning them up. For long-lived deployments, use thread TTL policies or a separate cleanup job if you do not want preserved threads to accumulate.

Creating a thread-bound cron

Bind a cron to a specific thread so every firing continues the same conversation:

Cron expression format

Aegra uses croniter for schedule parsing.

Standard 5-field format

ExpressionMeaning
0 9 * * *Every day at 09:00
*/15 * * * *Every 15 minutes
0 9 * * 1Every Monday at 09:00
0 0 1 * *First day of every month at midnight

Seconds-level 6-field format

Prefix the standard expression with a seconds field for sub-minute scheduling:

Timezone support

By default schedules use UTC. Pass an IANA timezone name to fire in local time:
The server stores the timezone alongside the cron record and converts to UTC internally. Passing an invalid timezone name results in an HTTP 422 error.
When updating a cron’s schedule without changing its timezone, the existing timezone is preserved automatically.

Listing and searching

Updating a cron

Supported update fields: schedule, enabled, input, config, metadata, timezone.

Disabling and re-enabling

Disabled crons are ignored by the scheduler but remain in the database. You can also create a cron in a paused state by passing enabled=False at creation. In that case the immediate first run is skipped and the response is the persisted Cron instead of a Run.

Deleting a cron

Deletion is permanent. When a cron is bound to a thread and that thread is deleted, the cron is removed atomically as well (ON DELETE CASCADE).

Limitations

  • Scheduled runs skip per-request auth handlers. Creating a cron runs the full auth chain (crons.createassistants.readthreads.read/search), but each scheduled firing executes as a background daemon under the cron owner’s identity and does not re-dispatch @auth.on(...) handlers. Put per-run authorization logic in the graph itself if it must apply to scheduled runs.
  • Cron metadata is not copied onto fired runs. metadata set on a cron is stored on the cron record for search and filtering, but is not propagated to the runs the cron creates.
  • Delivery is at-least-once. Under a crash between firing and advancing the schedule, a run can fire more than once for a single occurrence. Make scheduled work idempotent.