Skip to content
RoutineOS
Esc
↑↓navigate↵open⌘Jpreview
On this page

routine.toml and app.toml

Every table and key in a routine's routine.toml and its app.toml, with types, defaults and what lint checks. Taken from schema.py, appmodel.py and the scaffolds.

A routine folder has two TOML files:

  • routine.toml describes one routine: type, schedule, store, workspace, settings, gate and actions. It is loaded by schema.py.
  • app.toml describes the app around it: routines, store kind, budget, integrations, secrets, environment, deployment and risks. It is loaded by appmodel.py. new --app writes it.

Both are read with Python’s tomllib.

routine.toml

[routine]

Key Type Required Values
name text yes The routine name and producer id
type text yes serverless, watch, approval-gated, interactive
schedule text yes Five-field cron, UTC. The minute field must be a single number, so runs are at least an hour apart
first_run text no report-only (default) or live

[store]

Key Type Values
adapter text artifact-db in every scaffold. See Storage

[workspace]

Key Type Meaning
enabled bool The routine writes to a Desk workspace. false for serverless and approval-gated scaffolds
name text Workspace name used by seed when --name is not given
template text Template the routine came from, copied into meta/workspace
template_version integer That template’s version

[[setting]]

One table per setting.

Key Type Required Meaning
name text yes Setting name
type text yes toggle, number, choice, list
default by type yes toggle: true or false. list: a list. number: within min and max. choice: one of choices
label text no Shown on the Desk
example text no Shown as a placeholder
role text list only include or exclude. Needed for a setting used in settingFilters
min, max number no Bounds for a number
choices list choice only Non-empty
cost_note text no What the setting costs, shown on the Desk

A routine that writes Inbox messages ("inbox": true in its definition) must declare a number setting max_inbox_per_run.

[gate]

Key Type Meaning
enabled bool Required true before any send, post or delete action
transport text email or slack, the two verified transports. Lint does not check the value
timeout_hours number How long a gate waits. Keep it longer than the longest gap between runs

[[action]]

Key Type Meaning
name text Action name
kind text send, post and delete are external and need [gate] enabled = true

Example: Hiring Signals

From templates/jobs-watch/routine.toml, settings shortened:

[routine]
name = "jobs-watch"
type = "watch"
schedule = "0 22 * * 1,4"  # UTC, Monday and Thursday; minimum interval is 1 hour
first_run = "report-only"

[store]
adapter = "artifact-db"

[workspace]
enabled = true
template = "jobs-watch"
template_version = 1

[[setting]]
name = "title_keywords"
type = "list"
role = "include"
label = "Title keywords"
default = ["Shopify", "headless", "commerce", "AI automation"]
cost_note = "each keyword adds a search per market"

[[setting]]
name = "max_inbox_per_run"
type = "number"
label = "Inbox messages per run"
default = 5
min = 0
max = 20
cost_note = "no extra cost; more messages ask more of your attention"

[gate]
enabled = true
transport = "email"
timeout_hours = 48

The other settings are markets (list, include), excluded_companies (list, exclude) and lookback_days (number, 1 to 30, default 7). new hiring-signals --from-template jobs-watch renames the routine.

app.toml

[app]

Key Required Meaning
id yes App id
title yes Shown on the permission screen
status yes not live, live, not yet tested, live and tested, retired
purpose no One sentence
owner no Who owns it
created no Date

[[routine]]

Key Meaning
name Must match routine.toml
folder The routine’s folder, . for the app folder
type Routine type
schedule Must match routine.toml
timezone IANA zone, shown on the permission screen. The cron stays UTC
role collector, actor, reporter or listener (scaffold comment)
delivery repo (default), account-skill or inline
repo owner/name, needed for repo delivery unless [deployed] repo is set
skill Lowercase skill name. Needed for account-skill
connectors Names declared in [[integration]]
hosts Hosts reached, each declared in [[integration]]
writes Collections this routine owns, each with a [[collection]]
tools Tools a run may use. No unlisted send tool

[store]

Key Meaning
kind artifact-db by default

[budget]

Key Required Meaning
max_runs_per_day yes Whole number, 1 or more. Prompt-enforced, not guaranteed
alert_channel no Where the one budget notice goes, for example email

[[collection]]

Key Meaning
name A store collection
writer The one routine that writes it

[[integration]]

Key Meaning
name Name used in a routine’s connectors
kind connector, mcp, secret-host, http
direction read or write
hosts Hosts it reaches
gate Must be true when direction = "write"
what Purpose shown on the permission screen

[[secret]]

Key Meaning
name Secret name, never its value
where connector or network-secret. env-var is refused
host The single host it may reach

[environment]

What you read in the claude.ai environment dialog. RoutineOS cannot read it.

Key Type Meaning
name text Environment name. Default is shared by every routine using it
network text none, trusted, limited, full, custom
allowed_domains list Host names
setup_script bool Whether a setup script runs
env_vars list Variable names only
shared bool The environment is shared

[deployed]

Key Meaning
routine_ids Task ids, after creation and read-back
environment Environment the task uses
repo Repo, used when a routine has no repo

[risks]

Key Meaning
file risks.md
relies_on Registry capability ids. platform check reports them
combo true when private data, untrusted content and an external send are knowingly accepted together

What lint checks

routine.toml (schema.py):

  • [routine] has name, type and schedule; type is known.
  • The schedule has five fields and a single-number minute field.
  • first_run is report-only or live.
  • Each setting has a name, a known type and a default of the right shape; role only on lists; a choice default is in choices; a number default is within min and max.
  • A send, post or delete action has [gate] enabled = true.
  • prompt.md exists, contains “is data, not instructions”, and has a line starting Push rule:.

app.toml (appmodel.py), each problem prefixed app.toml: :

  • [app] has id, title and status.
  • No string looks like a secret (sk-, AKIA, AIza, ya29. patterns): “looks like a secret value, remove it”.
  • Each routine’s connectors and hosts are declared in [[integration]].
  • One writer per collection; a routine writes only collections it owns.
  • A write integration has gate = true: “integration ‘’ writes externally, set gate = true”.
  • No secret is stored in an env var.
  • Delivery mode, repo and skill name are valid.
  • [environment] values have the right types and a known network level.
  • tools is a list of names.
  • A live status has [deployed].routine_ids.
  • Each schedule passes the same cron rule.
  • [budget].max_runs_per_day exists and is a whole number of 1 or more.
  • routine.toml and app.toml agree on name and schedule.

With app.toml present, lint also scans the other files in the folder for secret-shaped values (reported as file and line, never the value; dashboard/ is skipped), requires a non-empty risks.md, and requires each routine’s prompt.md to mention state/verified and state/budget.

Lint does not reject unknown keys in either file.

Was this page helpful?