Routines
Routines run an app op on its own, on a timer, with no chat open.
What you can do
Run an op on a timer. A Routine invokes one of an app’s declared ops on its own, hourly, daily, or weekly, without a chat open and without the owner doing anything. Use it for a poll, a refresh, a daily check, or any op that should just keep running.
Up to three per app, twenty-five per account. Each app can carry up to 3 Routines; each account, up to 25 across every app. Both caps are fixed today, not configurable.
Stops itself after repeated failures. A Routine that fails 5 times in a row disables itself and emails the owner. Re-enabling it resets the failure count and starts the clock over.
Technical
A Routine invokes its target op with empty input ({}) on the schedule you set. It runs as the app itself, the same as any other server-side call — it does not count as the owner opening the app.
Copy this prompt for your agent
Set up a Routine for my Charming app. Read
https://charm.ing/docs/capabilities/routines.md first. Work out
which op should run on its own and how often, then create the
Routine and tell me when it'll first run.
How an agent performs this job
Use create_routine to schedule a declared op, list_routines to see the caller’s Routines and their run state, update_routine to change the interval or enable/disable one, and delete_routine to stop and remove one. The matching HTTP routes are POST /api/v1/apps/{appId}/routines (create), GET /api/v1/routines (list), PATCH /api/v1/routines/{routineId} (update), and DELETE /api/v1/routines/{routineId} (delete) — session-gated, and behaviorally identical to their MCP twins.
A worked example:
create_routine({ app_id: "<uuid>", op: "refreshFeed", interval: "hourly" })
→ Created Routine routine_<uuid>: runs `refreshFeed` hourly on <app>.
list_routines()
→ routine_<uuid> — `refreshFeed` on app <uuid>, hourly
If refreshFeed then fails on 5 ticks in a row, the Routine disables itself (enabled: false, disabled_reason: "auto"), the owner gets an email with a ready-to-paste fix prompt, and next_run_at reads null until it’s re-enabled with update_routine({ routine_id, enabled: true }).
The contract
interval is a closed enum: hourly, daily, or weekly — never raw cron syntax. The target op must accept empty input; an op with any required input field cannot be scheduled. create_routine and its HTTP twin reject a second Routine on the same (app, op) pair.
list_routines and every other Routines response return a routine object with id, app_id, op, interval, and seven run-state fields:
| Field | Meaning |
|---|---|
enabled |
false once the Routine is owner-disabled or auto-disabled |
disabled_reason |
"owner", "auto", or null when enabled |
next_run_at |
Next scheduled run (ISO 8601), or null while disabled |
last_run_at |
Last tick’s timestamp, or null before the first run |
last_outcome |
Last tick’s result, or null before the first run |
last_error |
{ kind, message } for the last failure, or null |
consecutive_failures |
Resets to 0 on any success or on re-enable |
Four error kinds are specific to Routines:
| Error kind | Fires when |
|---|---|
routine_limit_exceeded |
The per-app (3) or per-user (25) cap is already met |
op_requires_input |
The target op declares a required input field, so it can’t run with empty input |
duplicate_routine |
A Routine already exists for this (app, op) pair |
invalid_interval |
Internal guard for the interval-to-cron mapping; not reachable through normal hourly/daily/weekly input |
The generated MCP reference and REST API reference define the full request and response shapes.
Limits, access, and deletion
- Caps. 3 Routines per app, 25 per account, hourly is the shortest interval available. See Limits.
- Access. Creating, updating, and deleting a Routine requires the same
app:writeaccess as editing the app itself. See Privacy and sharing. - Deletion. Deleting the app or the account cascades to its Routines; there’s no separate cleanup step.
- Not a Routine tick. An unattended tick never counts as the owner using the app — it doesn’t affect the app’s activity history the way an owner’s own call would.
Related
- Privacy and sharing: a Routine runs with the same
app:writegate as editing the app - Limits: the 3-per-app / 25-per-user caps and hourly floor
- How Charming works
- Docs home
- llms-full.txt