Routines

A routine makes an agent act on its own, on a schedule. It binds the events to deliver on each fire to a target — create a fresh session per run, or send the turn into an existing long-lived session — and a cron schedule. Every fire records a routine run you can list, and can push webhooks.

Create one

bash
curl -X POST ${PUBLIC_API_BASE}/routines \
  -H "Authorization: Bearer ast_..." -H "Content-Type: application/json" \
  -d '{
    "name": "Weekly compliance scan",
    "target": { "type": "new_session", "agent": "agent_..." },
    "initial_events": [
      { "type": "user.message", "content": [{ "type": "text", "text": "Run the weekly compliance scan." }] }
    ],
    "schedule": { "type": "cron", "expression": "0 20 * * 5", "timezone": "America/New_York" }
  }'

The response echoes the routine with schedule.upcoming_runs_at — the next (up to 5) fire times — so you can confirm the cron before it runs. POST /v1/routines/{id}/run fires immediately, outside the schedule, to test it.

Two target modes

  • {"type": "new_session", "agent": "agent_...", "environment_id"?, "vault_ids"?} — each fire creates a fresh session (lazily provisioned) and delivers initial_events as its first turn. Sessions are ordinary sessions: they show up in lists, bill normally, and keep their history.
  • {"type": "session", "session_id": "sess_..."} — each fire sends initial_events as a turn into the same long-lived session, so the agent keeps its memory, files, and history across runs. If the session already has a turn in flight at fire time, the fire is skipped and recorded as a failed run (session_busy_error); nothing queues, and the next occurrence tries again.

Schedule semantics

5-field POSIX cron (minute hour day-of-month month day-of-week, day-of-week 0–7 with both 0 and 7 = Sunday) plus a required IANA timezone. Extended syntax (L W # ?, @daily, seconds fields) is rejected. Matching is literal wall-clock in the configured timezone: 0 20 * * * in America/New_York fires at 8 PM local year-round. On DST days, a time that doesn't exist fires at the first valid instant after the gap and a time that occurs twice fires twice — schedule outside 1–3 AM local (or in UTC) if that matters. Missed occurrences are never backfilled: any number of missed fires collapse into one at the next tick. Minimum granularity is one minute. schedule is nullable — a routine without one only fires manually.

Budgets

Pass the optional budget (same shape as a session budget — see the API reference) to bound each run separately:

  • new_session mode: the cap is copied onto each session the routine creates.
  • session mode: each fire grants a delta allowance — the target session's cap becomes consumed + budget at fire time (overwritten each run, never accumulated). The target must have been created with a budget.

Runs

Every fire attempt — scheduled or manual — records a run with exactly one of session_id or a typed error:

bash
curl "${PUBLIC_API_BASE}/routine-runs?routine=rtn_...&has_error=true" -H "Authorization: Bearer ast_..."

Unrecoverable errors (an archived or deleted agent/session/environment/vault) also pause the routine with the error mirrored in paused_reason, so you can fix the reference and unpause (which resumes from the next occurrence). Transient errors — insufficient_credits_error, rate_limited_error, session_busy_error — leave the routine active.

Lifecycle

POST .../pause suppresses scheduled fires (manual run still works); POST .../unpause resumes from the next occurrence; POST .../archive is terminal. Updates are omit-to-preserve: initial_events replaces wholesale, metadata is key-patched, schedule/budget clear with null.

Webhooks

Register an endpoint to be notified without polling:

bash
curl -X POST ${PUBLIC_API_BASE}/webhooks \
  -H "Authorization: Bearer ast_..." -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/hooks/agentsky", "events": ["routine_run.failed", "routine.paused"]}'

The create response is the only place the whsec_... signing secret appears — store it. Deliveries carry x-asteroids-event, x-asteroids-delivery, x-asteroids-timestamp, and x-asteroids-signature: v1=<hex hmac-sha256(secret, "<timestamp>.<body>")>; verify the signature, dedupe on the event id, and fetch the resource by data.id — payloads are thin and ordering is not guaranteed. Event types: routine.created/updated/paused/unpaused/archived and routine_run.started/succeeded/failed (scheduled fires only; manual runs emit none). Failed deliveries retry twice (5s, 30s); sustained failure disables the endpoint until you PATCH {"status": "active"}.