Skip to main content

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

MethodPathPurpose
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}/pausePause schedule
POST/api/v1/routines/{collection_name}/{task_name}/{name}/resumeResume schedule
POST/api/v1/routines/{collection_name}/{task_name}/{name}/run-nowFire immediately
GET/api/v1/routines/{collection_name}/{task_name}/{name}/runsRecent trajectories
GET/api/v1/tasks/{collection_name}/{task_name}/init-params-schemaHelper: 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"
}
FieldRequired whenNotes
typealwaysPicks one of five cadences
hour_utcdaily, weekdays, weeklyRejected on manual and hourly
minute_utcoptional everywhereDefaults to 0. On hourly, this is the minute-of-hour offset
day_of_weekweeklyThree-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.

  1. Unknown keys in init_params_overrides (not present on task.workflow.init_params) → 400 with the offending key list in the response body.
  2. daily, weekdays, and weekly require hour_utc.
  3. weekly additionally requires day_of_week.
  4. manual rejects any cadence param other than type.
  5. 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

StatusMeaning
400Validation failure — unknown override key, missing hour_utc for daily/weekdays/weekly, missing day_of_week for weekly, or extra cadence params on manual.
401Missing or invalid bearer token.
404Routine, task, or collection not visible to this API key (also returned for cross-collection access).
409name 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