Routines API
CRUD and lifecycle endpoints for scheduled routines — saved, recurring invocations of an existing Mise task. Routines build on the same FlowWorkflow.run pipeline as the regular run endpoints.
For a guided walkthrough — when to use a routine, picking a cadence, debugging — see Scheduling Routines.
Base URL
https://flows-api.jetty.io
Authentication
Authorization: Bearer $JETTY_API_TOKEN
Auth scoping mirrors the rest of the run API: routines belong to a (collection, task) pair, and an API key may only manage routines in the collection it is bound to. Cross-collection access returns 404, not 403, by design.
Endpoints
| Method | Path | Purpose |
|---|---|---|
POST | /api/v1/routines/{collection_name}/{task_name} | Create a routine |
GET | /api/v1/routines/{collection_name} | List routines in a collection |
GET | /api/v1/routines/{collection_name}/{task_name} | List routines for a task |
GET | /api/v1/routines/{collection_name}/{task_name}/{name} | Get a routine |
PATCH | /api/v1/routines/{collection_name}/{task_name}/{name} | Update cadence / overrides / paused |
DELETE | /api/v1/routines/{collection_name}/{task_name}/{name} | Delete |
POST | /api/v1/routines/{collection_name}/{task_name}/{name}/pause | Pause schedule |
POST | /api/v1/routines/{collection_name}/{task_name}/{name}/resume | Resume schedule |
POST | /api/v1/routines/{collection_name}/{task_name}/{name}/run-now | Fire immediately |
GET | /api/v1/routines/{collection_name}/{task_name}/{name}/runs | Recent trajectories |
GET | /api/v1/tasks/{collection_name}/{task_name}/init-params-schema | Helper: list overridable keys |
Schemas
The Pydantic shapes below are returned and accepted as JSON. Field types use TypeScript-style notation.
CadenceConfig
{
"type": "manual | hourly | daily | weekdays | weekly",
"hour_utc": "integer 0-23 | null",
"minute_utc": "integer 0-59 (default 0)",
"day_of_week": "mon | tue | wed | thu | fri | sat | sun | null"
}
| Field | Required when | Notes |
|---|---|---|
type | always | Picks one of five cadences |
hour_utc | daily, weekdays, weekly | Rejected on manual and hourly |
minute_utc | optional everywhere | Defaults to 0. On hourly, this is the minute-of-hour offset |
day_of_week | weekly | Three-letter lowercase token |
manual rejects every cadence param except type.
RoutineCreate
{
"name": "string (slug, unique per collection+task)",
"cadence": "CadenceConfig",
"init_params_overrides": "object (default {})",
"secret_params": "object (default {}, encrypted at rest)",
"webhook_url": "string | null",
"webhook_secret": "string | null",
"paused": "boolean (default false)"
}
init_params_overrides is merged on top of the task's existing workflow.init_params at fire time. Every key must already be declared on the task workflow — unknown keys produce a 400.
RoutineRead
RoutineCreate plus the server-managed fields:
{
"id": "integer",
"task_name": "string",
"collection_name": "string",
"last_run_at": "ISO 8601 datetime | null",
"next_run_at": "ISO 8601 datetime | null",
"overlap_policy": "string (default 'skip')"
}
next_run_at is null for paused routines and for manual routines.
Validation Rules
All validation is server-side and asserted by tests in PR 1.
- Unknown keys in
init_params_overrides(not present ontask.workflow.init_params) →400with the offending key list in the response body. daily,weekdays, andweeklyrequirehour_utc.weeklyadditionally requiresday_of_week.manualrejects any cadence param other thantype.- Auth scoping reuses the existing
AuthContext. API keys may only manage routines in the collection they are bound to.
Create
POST /api/v1/routines/{collection_name}/{task_name}
Request:
curl -X POST "https://flows-api.jetty.io/api/v1/routines/my-org/nl-to-sql-regression" \
-H "Authorization: Bearer $JETTY_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "morning-regression",
"cadence": {
"type": "weekdays",
"hour_utc": 9,
"minute_utc": 0
},
"init_params_overrides": {
"sample_size": 50
}
}'
Response — RoutineRead:
{
"id": 42,
"name": "morning-regression",
"task_name": "nl-to-sql-regression",
"collection_name": "my-org",
"cadence": {
"type": "weekdays",
"hour_utc": 9,
"minute_utc": 0,
"day_of_week": null
},
"init_params_overrides": {"sample_size": 50},
"secret_params": {},
"webhook_url": null,
"webhook_secret": null,
"paused": false,
"last_run_at": null,
"next_run_at": "2026-05-04T09:00:00Z",
"overlap_policy": "skip"
}
For manual routines the response includes next_run_at: null — manual routines exist only as a saved invocation preset, fired on demand via run-now.
List
GET /api/v1/routines/{collection_name} — collection-wide.
GET /api/v1/routines/{collection_name}/{task_name} — task-scoped.
curl -s "https://flows-api.jetty.io/api/v1/routines/my-org/nl-to-sql-regression" \
-H "Authorization: Bearer $JETTY_API_TOKEN"
Response — array of RoutineRead:
[
{
"id": 42,
"name": "morning-regression",
"task_name": "nl-to-sql-regression",
"collection_name": "my-org",
"cadence": {"type": "weekdays", "hour_utc": 9, "minute_utc": 0, "day_of_week": null},
"init_params_overrides": {"sample_size": 50},
"secret_params": {},
"webhook_url": null,
"webhook_secret": null,
"paused": false,
"last_run_at": "2026-05-02T09:00:11Z",
"next_run_at": "2026-05-05T09:00:00Z",
"overlap_policy": "skip"
}
]
Get
GET /api/v1/routines/{collection_name}/{task_name}/{name}
Returns a single RoutineRead. next_run_at and last_run_at are resolved live from the scheduler and may include warnings if the underlying task drifted (an override key was removed from the task workflow).
Update
PATCH /api/v1/routines/{collection_name}/{task_name}/{name}
Body is a partial RoutineCreate — any field omitted keeps its current value.
curl -X PATCH \
"https://flows-api.jetty.io/api/v1/routines/my-org/nl-to-sql-regression/morning-regression" \
-H "Authorization: Bearer $JETTY_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"cadence": {"type": "daily", "hour_utc": 13, "minute_utc": 30}}'
Response — updated RoutineRead. A run already in progress when the PATCH lands is not cancelled; the new spec applies to the next fire.
Delete
DELETE /api/v1/routines/{collection_name}/{task_name}/{name}
Removes the row and the underlying schedule (if any). Returns 204.
Pause / Resume
POST /api/v1/routines/{collection_name}/{task_name}/{name}/pause
POST /api/v1/routines/{collection_name}/{task_name}/{name}/resume
Both return the updated RoutineRead. Pausing leaves the row intact and stops the schedule from firing; the schedule handle is preserved so resume is a one-call round-trip.
Run Now
POST /api/v1/routines/{collection_name}/{task_name}/{name}/run-now
Fires the routine immediately, ignoring cadence. Returns the same WorkflowResponse as the regular run endpoints — including workflow_id and trajectory ID — so existing polling code works unchanged:
{
"workflow_id": "flow-...",
"trajectory_id": 9876,
"run_id": "..."
}
The trajectory is tagged with triggered_by_routine_id, identical to a scheduled fire.
List Runs
GET /api/v1/routines/{collection_name}/{task_name}/{name}/runs
Trajectories produced by this routine, newest first. Same shape as the generic trajectory list, server-side filtered to triggered_by_routine_id = <this routine>.
[
{
"trajectory_id": 9876,
"workflow_id": "flow-...",
"status": "completed",
"created_at": "2026-05-02T09:00:11Z"
}
]
Helper: init-params-schema
GET /api/v1/tasks/{collection_name}/{task_name}/init-params-schema
Lists the keys you may use in init_params_overrides, with their defaults and type hints. Single source of truth for client validation:
{
"keys": [
{"name": "model", "default": "gpt-4o", "type_hint": "string"},
{"name": "tenant_filter", "default": null, "type_hint": "string"},
{"name": "sample_size", "default": 10, "type_hint": "integer"}
]
}
Errors
| Status | Meaning |
|---|---|
400 | Validation failure — unknown override key, missing hour_utc for daily/weekdays/weekly, missing day_of_week for weekly, or extra cadence params on manual. |
401 | Missing or invalid bearer token. |
404 | Routine, task, or collection not visible to this API key (also returned for cross-collection access). |
409 | name already exists for this (collection, task) pair. |
Trajectory Tagging
Every trajectory produced by a routine fire — scheduled or run-now — is tagged with triggered_by_routine_id. This is how the runs endpoint above is implemented and how the Spot UI surfaces "runs of this routine."
Behind a Feature Flag
Routines ship behind ENABLE_ROUTINES=true on the Mise deployment. Until that flag is flipped on for your environment, every endpoint above returns 404.
Examples
Python (httpx)
import httpx
BASE = "https://flows-api.jetty.io"
TOKEN = "<your-jetty-api-token>"
COLLECTION = "my-org"
TASK = "nl-to-sql-regression"
headers = {"Authorization": f"Bearer {TOKEN}"}
with httpx.Client(base_url=BASE, headers=headers, timeout=30.0) as http:
# Discover overridable keys
schema = http.get(f"/api/v1/tasks/{COLLECTION}/{TASK}/init-params-schema").json()
print({k["name"]: k["default"] for k in schema["keys"]})
# Create
routine = http.post(
f"/api/v1/routines/{COLLECTION}/{TASK}",
json={
"name": "morning-regression",
"cadence": {"type": "weekdays", "hour_utc": 9, "minute_utc": 0},
"init_params_overrides": {"sample_size": 50},
},
).json()
# Fire on demand
fired = http.post(
f"/api/v1/routines/{COLLECTION}/{TASK}/{routine['name']}/run-now"
).json()
print(fired["workflow_id"])
TypeScript (fetch)
const base = 'https://flows-api.jetty.io';
const headers = {
Authorization: `Bearer ${process.env.JETTY_API_TOKEN}`,
'Content-Type': 'application/json',
};
await fetch(`${base}/api/v1/routines/my-org/nl-to-sql-regression`, {
method: 'POST',
headers,
body: JSON.stringify({
name: 'morning-regression',
cadence: { type: 'weekdays', hour_utc: 9, minute_utc: 0 },
init_params_overrides: { sample_size: 50 },
}),
});
See Also
- Scheduling Routines — Guided walkthrough, troubleshooting, and cadence comparison
- Chat Completions API — One-shot run endpoint counterpart
- Agentic Workflows — How
FlowWorkflow.runis wired