Routines
Scheduled Chats. A cron fires a fresh Chat on a cadence - a weekly refresh, a daily pipeline pulse, a standing competitive sweep - with no human in the loop.
Put a recurring question on a schedule — /routines is scheduled Chats. A Routine is a cron that fires a Chat on a cadence: each occurrence starts one turn of Master — the platform's general-purpose agent — in a fresh Chat named "{Routine name} — {date}". Use Routines for standing work — refreshing a living document every Monday, a weekly competitive sweep, a daily pipeline pulse — anything you would otherwise re-ask by hand.
The model
The cron fires
One occurrence of the schedule, in the timezone you set.
your cadence
A fresh Chat opens
Never appended to an old one - history is one Chat per occurrence.
named by date
One Master turn runs
on_questionis alwaysnone, so a routine can never park waiting on a person.headless
Outputs commit
A living document lands as a new version; promotion stays human-gated.
verified write gate
cron occurrence
│
▼
new Chat "Weekly pipeline refresh — 2026-07-13"
│
▼
one Master turn, headless:
- prompt = the routine's prompt (plus the doc's prior version, when declared)
- config = the routine's persisted config (write_outputs, actions_allowed, …)
- on_question is ALWAYS "none": ask_a_human returns no_human_in_loop immediately;
a routine can never park waiting on a personEvery fire is a normal Chat run: watch it over the same SSE stream / read handles, find it in the Chats list, and read any document it committed in Living Knowledge. A routine that maintains a document commits through the verified write gate (see Outputs below) — promotion to the current version stays human-gated unless every verification check passes.
Create a routine
curl -X POST https://app.amdahl.ai/api/platform/v1/routines \
-H "X-API-Key: $AMDAHL_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Weekly pipeline refresh",
"prompt": "Refresh the pipeline health report from current warehouse data.",
"cron": "0 9 * * 1",
"timezone": "America/New_York",
"config": {
"write_outputs": true,
"agents": ["pipeline-analyst", "win-loss-analyst"]
}
}'Body fields
| Field | Required | What it does |
|---|---|---|
name | yes | Human label. Each fired Session is named "{name} — {date}". |
prompt | yes | The Chat input every fire starts the Master turn with. |
cron | yes | Cron expression, validated before the routine is stored. |
timezone | no | IANA timezone the cron evaluates in. Default UTC. |
agent | no | LEGACY single-agent pin. Prefer config.agents; passing both rejects. |
config | no | Per-fire Chat config (below). |
enabled | no | Default true. |
Config
The persisted subset of the Chat config, applied on every fire. on_question is not configurable — routines are headless, so a human ask always fails fast with no_human_in_loop.
| Key | Default | Notes |
|---|---|---|
agents | — | Who the routine employs: "all" (plain Master, unrestricted delegation), one ref (the Master turn runs AS that agent), or several refs (the Master may delegate ONLY to that roster; other agents and inline prompts are refused). Unset behaves like the legacy agent pin. |
write_outputs | true | Routines usually exist to refresh a document; the write gate still verifies. |
write_memory | false | Long-term memory commits. |
actions_allowed | (absent = all) | Outbound actions each fired run may invoke. Absent = every cataloged action is allowed (the default); pass a list to narrow, or [] to disable actions entirely. |
external_search | false | Market fan-outs. |
include_divergence | false | Divergence-map fusion on external reads. |
depth | deep | quick / standard / deep. Unset fires at deep, same as a Chat. |
There is no evidence knob — citations and the query refs behind every data-backed block are unconditional on every fired run.
The earlier
output_slug/full_regendoc-target knobs are no longer writable — the agent picks its own document slug viaoutputs.write_doc. Existing routines that carry them keep their behavior (prior-version injection, wrote-no-doc detection) until their config is next replaced.
Manage routines
| Verb | What it does |
|---|---|
GET /routines | List (filter ?enabled=, paginate ?limit=&offset=). |
GET /routines/:id | One routine, including the last fire's Session + run ids. |
PATCH /routines/:id | Patch name / prompt / cron / timezone / agent / config / enabled. |
DELETE /routines/:id | Remove it. Sessions and documents from earlier fires are untouched. |
POST /routines/:id/run-now | Fire immediately, off-cadence. Returns the Session + run handles. |
run-now returns the same { chat_id, run_id, stream_url, read_url, resume_url } envelope a Chat START does, so you can watch the fire live.
Outputs: the document commit gate
A routine that refreshes a living document commits it through outputs.write_doc — the Master's only document-write path — never a raw knowledge-base upload. The gate enforces, in order:
- Run policy — the run must have been started with
write_outputs: true(the routine's config controls this per fire). - Degraded-read halt — if a theme read (
data.cluster_search) FAILED during the run, the commit is blocked entirely. A failed read is not an absence of themes; shipping would silently overwrite the last good version. - Verified promotion — every stated digit is checked against the cells returned by the document's bound query pack (
outputs.run_bound_queries, executed earlier in the same run). Auto-promotion to the current version is refused when no pack ran, when any digit fails verification, or when the run's pinned agent is tenant-authored — the new version then lands proposed and a human promotes it in Living Knowledge, which is the normal, safe outcome.
Documents keep their identity across every writer (routine fires, manual uploads): the slug resolves deterministically to the same per-workspace version family.
Reliability semantics
- Fresh Session per fire. A routine never appends into an old Session; history is one Session per occurrence.
- Missed occurrences self-heal. A fire skipped across a deploy/restart is caught up once by the scheduler's reconcile pass (never once per skipped period).
- No double-fires. A cron tick and a catch-up for the same occurrence collapse to one run.
- A failed fire is retried by the cadence. If starting the Master turn fails, nothing is recorded and the reconcile catch-up re-fires it; there is no hot retry loop.
- Roster fires re-validate. Every agent in
config.agentsre-resolves at fire time; a routine pointing at a deleted agent skips the fire loudly instead of running with a partial roster.
Over MCP
Routines ride the Amdahl MCP server's agents tool (one tool covers the Chat lifecycle, the agent library, and its routines): list_routines, get_routine, create_routine (name + prompt + cron, optional timezone / agent / config / enabled), update_routine, delete_routine, and run_routine_now — the last returns the same Chat/run handles a start_chat does, so an MCP agent can fire a routine and watch it via the same tool's chat_status action. Routine reads are also MCP resources: routine://list and routine://<id>. Scopes: routines:read / routines:write (both on the customer-agent key bundle; existing keys were backfilled).
Scopes
| Scope | Grants | Default role |
|---|---|---|
routines:read | GET /routines, GET /routines/:id | viewer |
routines:write | create / update / delete / run-now | editor |
outputs:write | outputs.write_doc, outputs.run_bound_queries (agent gate) | editor |