Skip to Content
Polyant is open source under AGPL-3.0 — star us on GitHub.
Admin PanelScheduled Tasks

Scheduled Tasks

Scheduled tasks are per-instance jobs that fire on a schedule (or on demand) and run a prompt through the supervisor as if a user had sent a message. Use them for recurring digests, polling integrations that don’t push webhooks, periodic clean-up prompts, or one-shot reminders.

No top-level sidebar entry. Scheduled tasks are not exposed at /scheduled-tasks or anywhere in the global sidebar. They live only inside the instance detail page, under Triggers → Scheduled. To see all scheduled tasks across the system you have to open each instance in turn.

The UI lives inside the instance detail page, under Triggers → Scheduled. (Webhooks live next door under Triggers → Webhooks, runs history under Triggers → Runs.) Each row is one task with inline controls for enable/disable, edit, Run now, and delete.

What a task looks like

A task carries enough state to run autonomously and report back. The fields below mirror scheduled_tasks in packages/engine/src/scheduled-tasks/schema.ts.

FieldTypeNotes
namestringOperator-facing label.
descriptionstring (optional)Free-text reminder of what the task does.
promptstringThe user-message text sent to the supervisor when the task fires.
schedulediscriminated union (cron / interval / one-shot)See below.
enabledbooleanMaster switch. Disabled tasks never tick.
outboundChannelenum (optional)slack, telegram, whatsapp, or unset. Where the answer is sent.
outboundTargetstring (optional)Channel id or user id within outboundChannel.
keepHistorybooleanIf false, each run uses a fresh conversation id (no memory carry-over).
deleteAfterRunbooleanOne-shot tasks delete themselves after success.
maxRetriesint (default 3)Auto-disable after this many consecutive errors.

Schedule shape

type ScheduleConfig = | { type: "cron"; expression: string; timezone?: string } | { type: "interval"; everyMs: number; anchorAt?: string } | { type: "one-shot"; runAt: string };
  • Cron uses standard 5-field POSIX cron, optionally timezone-anchored.
  • Interval ticks every everyMs from anchorAt (or task creation time).
  • One-shot fires once at the given ISO timestamp. Combine with deleteAfterRun: true for fire-and-forget reminders.

Lifecycle

  1. The scheduler service ticks every 30 s (TICK_INTERVAL_MS = 30_000, packages/engine/src/scheduled-tasks/scheduler.service.ts:26).
  2. On each tick it claims up to MAX_CONCURRENT = 3 tasks whose nextRunAt is in the past, in priority order, and runs them in parallel.
  3. Each fire emits a cron event on the activity bus before the supervisor runs — useful as a heartbeat in the Activity feed.
  4. The supervisor processes the prompt exactly as if it were a user message. The conversation id is either reused (keepHistory: true) or freshly minted (keepHistory: false).
  5. The reply, if any, is delivered to outboundChannel/outboundTarget when configured. Otherwise it stays in the conversation for browsing.
  6. The run is recorded in scheduled_task_runs (run_id, started_at, completed_at, duration_ms, status, output, error, tool_calls, token_usage).
  7. On error, consecutive_errors increments. After maxRetries failures the task is auto-disabled and shows the error badge in the UI.

API

All endpoints are scoped to an instance — replace :slug with the instance slug.

MethodPathDescription
GET/api/instances/:slug/scheduled-tasksList tasks for the instance.
POST/api/instances/:slug/scheduled-tasksCreate a task.
GET/api/instances/:slug/scheduled-tasks/:idGet one task (full state).
PATCH/api/instances/:slug/scheduled-tasks/:idUpdate fields (name, prompt, schedule, enabled, …).
DELETE/api/instances/:slug/scheduled-tasks/:idRemove the task and cascade-delete its runs.
POST/api/instances/:slug/scheduled-tasks/:id/runTrigger an immediate execution (triggerType: manual).
GET/api/instances/:slug/scheduled-tasks/:id/runsPaginated run log for a single task (most recent first).
GET/api/instances/:slug/scheduled-tasks/runsCross-task global run log for the instance (no :id).

Worked example: daily digest at 09:00 Europe/Rome

  1. Open the instance detail page → Triggers → ScheduledNew task.
  2. Name: Morning digest.
  3. Schedule: cron, expression 0 9 * * *, timezone Europe/Rome.
  4. Prompt: “Search the last 24 hours of memories and summarise the three most important events. Be terse.”
  5. Outbound channel: slack, target: C012345678.
  6. Enable, save.

The next morning at 09:00 Rome time you’ll see one row in the Activity feed (cron), one tool call (searchMemory), one outbound (slack), and one entry in the Runs subtab.

Caveats

  • Tick granularity is 30 s. Don’t expect sub-minute accuracy.
  • Max concurrency is 3. A burst of due tasks queues; the rest wait for the next tick.
  • Engine restart resets nextRunAt for interval schedules. The scheduler recomputes the next fire from lastRunAt. Cron schedules are unaffected.
  • Auto-disable is per-task, not global. A flaky task won’t take down its neighbours.

See also: Activity for live visibility into firings, Activity Stream (concept) for the underlying bus.

Last updated on