Sophon Docs
Features

Cron & Scheduled Jobs

Schedule recurring tasks — agent runs, webhook triggers, workflow invocations — with standard cron expressions.

Cron jobs are how Sophon does things on a schedule without your involvement. "Every weekday at 9am, have the morning-briefing agent summarize my calendar." "Every hour, check the build status." "Once at 15:00 today, remind me to call Ada." The scheduler handles all of it, powered by Quartz.NET.

Cron is separate from workflows. Workflows are reusable named automations; cron is pure scheduling. The two compose: a cron job can trigger a workflow, or an agent, or a webhook, or a custom action.

Types of jobs

TypeFiresUse case
RecurringCron expression (0 9 * * MON)Daily briefings, hourly checks, weekly reports
One-shotAt a specific datetime"Remind me at 15:00"
IntervalEvery N minutes/hours"Every 15 minutes, check the queue"

Creating a job

From the Dashboard

Settings → Cron Jobs → New. Fill in:

  • Name
  • Schedule type (recurring / one-shot / interval)
  • Expression (0 9 * * MON or a datetime or an interval)
  • Action type: Agent (pick an agent + message), Workflow (pick a workflow), Webhook (POST to a URL), Custom (inline code — C# or Python)
  • Timezone (default: your user setting)
  • Enabled / disabled

Click Save. If the action is ≥ Medium risk (e.g., the agent message triggers a workflow that sends email), the job enters a pending approval state until you confirm.

From the CLI

sophon cron list
sophon cron add --name morning-briefing \
  --schedule "0 9 * * MON-FRI" \
  --action agent --agent sophon --message "summarize my day"
sophon cron trigger morning-briefing        # run once now
sophon cron pause morning-briefing
sophon cron resume morning-briefing
sophon cron delete morning-briefing
sophon cron history morning-briefing        # execution log

Cron expressions

Sophon uses standard 5-field Unix cron expressions (optionally 6-field with seconds):

┌───────────── minute        (0 - 59)
│ ┌─────────── hour          (0 - 23)
│ │ ┌───────── day of month  (1 - 31)
│ │ │ ┌─────── month         (1 - 12 or JAN-DEC)
│ │ │ │ ┌───── day of week   (0 - 7 or SUN-SAT, 0 and 7 = Sunday)
│ │ │ │ │
* * * * *

Examples:

ExpressionMeans
0 9 * * MON-FRIEvery weekday at 09:00
*/15 * * * *Every 15 minutes
0 0 1 * *Midnight on the 1st of each month
0 8,20 * * *Daily at 08:00 and 20:00
0 22 * * SUNSunday at 22:00

Action types

Agent run

Send a message to a specific agent on a schedule. The agent processes it like any other chat message — through the full orchestration pipeline with tool calls, memory, approvals, everything.

Good for periodic check-ins: "summarize yesterday's activity", "check my inbox and flag urgent items."

Workflow

Trigger a workflow. Cron just calls workflow.trigger at the scheduled time. The workflow does the actual work.

Good for complex multi-step automations you've already built in the workflow builder.

Webhook

POST to an arbitrary URL at the scheduled time. Configurable headers and body; optional HMAC-SHA256 signature.

Good for integrating with external systems: "every hour, ping Zapier to run my scenario."

Custom action

Inline code — C# (Roslyn) or Python (CPython) — executed in the skills sandbox. Useful for one-off logic that doesn't deserve a full workflow or skill.

# At 09:00, compute budget remaining and message user
from sophon import budget, chat

remaining = budget.remaining_daily()
if remaining < 1000:
    chat.send(user="me", text=f"Warning: only {remaining} tokens left today.")

Execution history

Every cron fire is logged:

  • Job ID, name
  • Fire time (scheduled and actual)
  • Status (success, failed, misfire, cancelled)
  • Duration
  • Output (truncated) or error message

Dashboard → Settings → Cron Jobs → <job> → History shows the log. Useful for debugging "it didn't run" mysteries — look for misfire (the scheduler was down when it should have fired) vs failed (it ran but the action errored).

Scheduling semantics

  • Misfires — if the Gateway was down when a job should have fired, the scheduler handles it per your misfire policy:
    • DoNothing (default) — skip, log as misfire
    • FireNow — run immediately on next startup
    • IgnoreMisfires — treat as if it fired on time
  • Concurrency — by default, a job running from a prior fire blocks the next fire. Configurable per job via AllowConcurrent.
  • Clock drift — Sophon uses the server clock. For multi-instance deployments (Enterprise), make sure all Gateways sync against NTP; otherwise you may see duplicate fires.

Exponential backoff on errors

Cron jobs that fail are retried with exponential backoff — 1 min, 5 min, 15 min, 1 h, then give up. This prevents a misbehaving job from flooding logs and external services. You can disable retries per job if the action is idempotent and you'd rather fail fast.

Deterministic staggering

If you have many jobs scheduled for the same minute (e.g., 50 jobs at 0 9 * * *), Quartz staggers them over the minute based on job ID hash — no thundering herd.

Approval gates

Cron jobs that invoke Medium+ risk actions are gated at creation time, not on every fire. You approve once ("yes, this agent can send emails on my behalf daily at 9am"), and subsequent fires don't prompt.

Emergency override: disable a job (Dashboard or sophon cron pause) instead of rejecting each fire.

Data flow and variables

Cron jobs can bind variables from the schedule context into the action:

VariableMeaning
$cron.fireTimeWhen this fire was scheduled
$cron.actualFireTimeWhen it actually ran
$cron.previousFireTimeWhen the previous fire ran
$cron.nextFireTimeWhen the next fire is scheduled
$cron.jobNameThe job's name
$cron.userThe owning user

Use these in agent messages, workflow inputs, or webhook bodies — e.g., "Summarize activity since {{$cron.previousFireTime}}".

Limits and gotchas

  • Timezones are per-job. A job at 0 9 * * * in Europe/Tirana won't drift around DST; Quartz handles it correctly. Just make sure the job has the right timezone set.
  • One-shot jobs get deleted after they fire. They disappear from Settings → Cron Jobs. Check History if you need to confirm it ran.
  • Personal tier has a 100-job cap. Pro / Enterprise is unbounded.
  • Custom code jobs run in the sandbox — no host filesystem access, network allowlist applies.
  • Don't schedule jobs more frequent than 1 minute apart. Quartz supports it, but you'll hit concurrency and token cost problems fast.

REST API

GET    /api/cron
POST   /api/cron
GET    /api/cron/{id}
PATCH  /api/cron/{id}
DELETE /api/cron/{id}
POST   /api/cron/{id}/trigger
POST   /api/cron/{id}/pause
POST   /api/cron/{id}/resume
GET    /api/cron/{id}/history

Where to go next

  • Workflows — what cron usually triggers
  • Tasks — cron fires show up in the task ledger
  • Webhooks — cron-driven outbound webhooks