API reference
Routines
Make, list, read, change, pause, run and archive routines, and read each run of a routine.
A routine is work that repeats by itself. It names a worker, the events each new task starts with, and what makes it run: a schedule, another start such as an email or an app event, or only your call. Each time it runs, the worker gets a task, so you read the work with the task routes. Routines explains what starts one and what each run records.
Each time a routine runs, or fails to, a routine run records it: the task it made, or why it made none. A routine belongs to one worker for good, and archiving a worker archives its routines. A workspace holds up to 1,000 routines, and a worker up to 50.
The routine object
Every route on this page but the run routes answers a routine, or a list of them. Times are RFC 3339 in UTC, to the millisecond. The runs of a routine are described under List routine runs.
Fields
typestringAlwaysroutine.idroutine idThe routine's id:routine_and 32 hex digits.workerobjectThe worker that gets a task each time the routine runs. A routine keeps its worker for good.Show its 3 fields
typestringAlwaysworker.idworker idThe worker.versionintegerThe worker version each task runs: the version the routine pins, or the worker's current version when it pins none.
namestringThe routine's name, 1 to 256 characters.descriptionstring or nullWhat the routine is for, up to 2,048 characters.metadataobjectYour own labels for the routine: up to 16 keys, each 1 to 64 characters, each with a string value of up to 512 characters.initial_eventsarray of objectsThe events each new task starts with, in order: one message or more, and at most one outcome. A routine made in the app reads as oneuser.message.Show its 5 fields
typestringuser.messageis a message each new task starts with.user.define_outcomeis what each task's work is checked against; send at most one.One of
user.message,user.define_outcomecontentarray of text blocksWithuser.message: one text block or more, each{ "type": "text", "text": "..." }. The messages' text, joined by blank lines, is the task's ask, up to 100,000 characters.descriptionstringWithuser.define_outcome: what done looks like.rubricobjectWithuser.define_outcome:{ "type": "text", "content": "..." }. Each line of the text is one of each task's Done Means: up to 8 lines of up to 200 characters.max_iterationsinteger or nullWithuser.define_outcome: how many check rounds before it gives up.
scheduleobject or nullWhen the routine runs by itself.nullwhen it runs only by hand or fromtriggers. A daily schedule made in the app reads as cron.Show its 5 fields
typestringAlwayscron.expressionstringA cron expression of five fields, such as0 8 * * 1-5(8:00 on weekdays), or an interval such as@every 2h, up to 256 characters. Runs are at least 5 minutes apart; a tighter expression is refused. A hosted plan may set a longer gap.timezonestringAn IANA time zone name, such asAmerica/Chicago, up to 64 characters.last_run_attime or nullWhen the routine last ran on its schedule.nullbefore the first time.upcoming_runs_atarray of timesThe next 5 exact times it will run, as planned. Each run starts a fixed moment later, up to 15 percent of the gap between runs (5 seconds to 9 minutes), the same for every run of one routine. Empty while the routine is paused or archived.
triggersarray of objectsThe other ways the routine starts, beside its schedule. Empty when it has none.Show its 4 fields
typestringwebhookstarts the routine when its webhook is called; a person makes the webhook's key in the app, because this API does not.emailstarts it when a message arrives at one of the workspace's inboxes.eventstarts it when an app reports an event. Each type may appear once.One of
webhook,email,eventinboxstringWithemail: the inbox's address, up to 254 characters. It must be one of the workspace's own.appstringWithevent: the app, 1 to 64 characters.eventstringWithevent: the app's name for the event, 1 to 120 characters.
budgetobject or nullThe most each task may cost.nullgives each task the default budget, $5.00.Show its 2 fields
typestringAlwayslimit.max_list_costmoneyThe most one task's work may cost at list price, from 1 cent to $1,000.Show its 2 fields
amountstringWhole US cents, as a string:"250"is $2.50.currencystringAlwaysUSD.
resourcesarray of objectsThe files, repositories and memory stores each task is given. A repository's token is never answered.Show its 8 fields
typestringThe kind of resource.One of
file,github_repository,memory_storefile_idfile idWithfile: a file you uploaded. List it once only.urlstringWithgithub_repository: ahttps://github.com/owner/repositoryaddress.checkoutobject or nullWithgithub_repository: what to check out,{ "type": "branch", "name" }or{ "type": "commit", "sha" }with a full 40-character SHA.memory_store_idmemory store idWithmemory_store: the store. A routine names at most 8, none archived or a worker's own.accessstringWithmemory_store:read_writeorread_only.One of
read_write,read_onlyinstructionsstring or nullWithmemory_store: how a task should use the store, up to 4,096 characters.mount_pathstring or nullAlwaysnull: a routine has no folder of its own. Each task it makes gets its own.
password_set_idsarray of password set idsThe password sets each task signs in with, in order. Unless the routine named others, the worker's own set, then the workspace's shared set.statusstringactive, orpausedwhen it does not start by itself. An archived routine readsactive, witharchived_atset.One of
active,pausedpaused_reasonobject or nullWhy the routine is paused.nullwhile it is not.Show its 2 fields
typestringmanualwhen a person paused it.errorwhen a start failed in a way that will not clear.One of
manual,errorerrorobjectWitherror: itstypeisworker_archived_error(the worker was archived),password_set_not_found_error(a set the routine names was deleted) orpassword_set_archived_error(a set it names was archived).
versionintegerCounts the changes made to the routine, starting at 1. Send it back asversionwhen you change the routine, and a change someone else made first is refused instead of overwritten.created_attimeWhen the routine was made.updated_attimeWhen anything about the routine last changed.archived_attime or nullWhen the routine was archived.nullwhile it is not.
{
"type": "routine",
"id": "routine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d",
"worker": {
"type": "worker",
"id": "worker_8c1f4e2a9b7d4f0e8a6c3b5d1e9f7a20",
"version": 1
},
"name": "Weekday opening email",
"description": null,
"metadata": {},
"initial_events": [
{
"type": "user.message",
"content": [
{
"type": "text",
"text": "Write three subject lines for today's email that says the shop opens at 7 am."
}
]
}
],
"schedule": {
"type": "cron",
"expression": "0 8 * * 1-5",
"timezone": "America/Chicago",
"last_run_at": "2026-10-23T13:03:12.408Z",
"upcoming_runs_at": [
"2026-10-26T13:00:00.000Z",
"2026-10-27T13:00:00.000Z",
"2026-10-28T13:00:00.000Z",
"2026-10-29T13:00:00.000Z",
"2026-10-30T13:00:00.000Z"
]
},
"triggers": [],
"budget": {
"type": "limit",
"max_list_cost": {
"amount": "300",
"currency": "USD"
}
},
"resources": [],
"password_set_ids": [
"password_set_597a64d52e7f4e84ac5d95929bf4cbae",
"password_set_37cbbff4bc744ede8217773f8a942448"
],
"status": "active",
"paused_reason": null,
"version": 3,
"created_at": "2026-10-20T15:12:40.206Z",
"updated_at": "2026-10-22T18:41:09.733Z",
"archived_at": null
}Make a routine
POST/routines
Make a routine for a worker. Say what each task starts with in initial_events, written as you would to a person, and when it runs in schedule, triggers or both. With neither, the routine runs only when you run it.
The routine is active at once, and its first run is planned from now. The same Idempotency-Key gives the same routine.
Headers
Idempotency-KeyUUIDSend one so a retry does the work once. A second call with the same key and the same content answers what the first call did. The same key with other content is409witherror_codeidempotency_conflict. Kept for 7 days.
Body
workerworker id or objectRequiredThe worker to give a task each time: its id, or{ "type": "worker", "id", "version" }to pin one version of it. It must be an active worker in the key's workspace.namestringRequiredThe routine's name, 1 to 256 characters.initial_eventsarray of objectsRequiredThe events each new task starts with, in order: 1 to 50. At least one is auser.message, and at most one is auser.define_outcome.Show its 5 fields
typestringRequireduser.messageis a message each new task starts with.user.define_outcomeis what each task's work is checked against; send at most one.One of
user.message,user.define_outcomecontentarray of text blocksRequiredWithuser.message: one text block or more, each{ "type": "text", "text": "..." }. The messages' text, joined by blank lines, is the task's ask, up to 100,000 characters.descriptionstringRequiredWithuser.define_outcome: what done looks like.rubricobjectRequiredWithuser.define_outcome:{ "type": "text", "content": "..." }. Each line of the text is one of each task's Done Means: up to 8 lines of up to 200 characters.max_iterationsinteger or nullWithuser.define_outcome: how many check rounds before it gives up.
descriptionstring or nullWhat the routine is for, up to 2,048 characters.metadataobjectYour own labels: up to 16 keys, each 1 to 64 characters, each with a string value of up to 512 characters.scheduleobject or nullWhen it runs by itself. A clock time that a time change skips is recorded as missed, and a repeated one runs once. A run missed while Little Worker was down runs once, and runs missed while the routine is paused are not made up.Show its 3 fields
typestringRequiredAlwayscron.expressionstringRequiredA cron expression of five fields, such as0 8 * * 1-5(8:00 on weekdays), or an interval such as@every 2h, up to 256 characters. Runs are at least 5 minutes apart; a tighter expression is refused. A hosted plan may set a longer gap.timezonestringRequiredAn IANA time zone name, such asAmerica/Chicago, up to 64 characters.
triggersarray of objectsOther ways it starts, beside the schedule.Show its 4 fields
typestringwebhookstarts the routine when its webhook is called; a person makes the webhook's key in the app, because this API does not.emailstarts it when a message arrives at one of the workspace's inboxes.eventstarts it when an app reports an event. Each type may appear once.One of
webhook,email,eventinboxstringWithemail: the inbox's address, up to 254 characters. It must be one of the workspace's own.appstringWithevent: the app, 1 to 64 characters.eventstringWithevent: the app's name for the event, 1 to 120 characters.
budgetobject or nullThe most each task may cost. Left out ornull, each task gets the default, $5.00.Show its 2 fields
typestringAlwayslimit.max_list_costmoneyThe most one task's work may cost at list price, from 1 cent to $1,000.Show its 2 fields
amountstringWhole US cents, as a string:"250"is $2.50.currencystringAlwaysUSD.
resourcesarray of objectsUp to 100 files, repositories and memory stores each task is given, in the form a task is given them in Give a task.Show its 8 fields
typestringRequiredThe kind of resource.One of
file,github_repository,memory_storefile_idfile idWithfile: a file you uploaded. List it once only.urlstringWithgithub_repository: ahttps://github.com/owner/repositoryaddress.authorization_tokenstringWithgithub_repository: a token that may clone it, 1 to 4,096 characters. It is kept sealed and never answered.checkoutobjectWithgithub_repository: what to check out,{ "type": "branch", "name" }or{ "type": "commit", "sha" }with a full 40-character SHA.memory_store_idmemory store idWithmemory_store: the store. A routine names at most 8, none archived or a worker's own.accessstringWithmemory_store:read_writeorread_only. Left out, it isread_write.One of
read_write,read_onlyinstructionsstringWithmemory_store: how a task should use the store, up to 4,096 characters.
password_set_idsarray of password set idsUp to 20 password sets each task signs in with, in place of the worker's own set and the shared set.
Returns
200 and the routine.
Errors
| Status | Type | When |
|---|---|---|
| 400 | invalid_request_error | A field is missing or not valid: an id of the wrong kind, a field the route does not take, a name over 256 characters, messages over 100,000 characters, an outcome rubric that is not 1 to 8 lines of up to 200 characters, a trigger type given twice, an Idempotency-Key that is not a UUID, or a list a task could not take (a file listed twice or expired, more than 8 memory stores, one listed twice, archived, or a worker's own). message says which. error_code is validation_error. |
| 400 | invalid_request_error | schedule.timezone is not a time zone (error_code invalid_timezone). Use a name like America/Chicago. |
| 400 | invalid_request_error | schedule.expression is not valid (Enter a valid schedule.), or runs more often than every 5 minutes (Routine schedules must be at least 5 minutes apart.). On a hosted plan with a longer gap, the message says how often a task may repeat. error_code is validation_error. |
| 401 | authentication_error | The key is missing, removed, or not a key. Send it in x-api-key. |
| 404 | not_found_error | The worker is not one of the key's (Worker not found.), the pinned version does not exist (Worker version not found.), an email trigger names an inbox that is not the workspace's, or a password set, file or memory store does not exist. |
| 409 | conflict_error | The worker is archived, or the limit is reached: 1,000 routines in the workspace, or 50 for the worker. Archive one first. A hosted plan may allow fewer. |
| 409 | conflict_error | The Idempotency-Key was already used with other content (error_code idempotency_conflict). Use a new key for new work. |
| 429 | rate_limit_error | The workspace made too many requests this minute. Wait the seconds in Retry-After, then try again. |
curl "https://core.thelittleworker.com/api/v1/routines" \
-H "x-api-key: $LITTLE_WORKER_API_KEY" \
-H "Little-Worker-Version: 2026-10-19" \
-H "Idempotency-Key: c3a5e7b9-1d2f-4a6c-8e0b-2d4f6a8c1e3b" \
-H "content-type: application/json" \
-d '{
"worker": "worker_8c1f4e2a9b7d4f0e8a6c3b5d1e9f7a20",
"name": "Weekday opening email",
"initial_events": [
{
"type": "user.message",
"content": [
{
"type": "text",
"text": "Write three subject lines for today'\''s email that says the shop opens at 7 am."
}
]
}
],
"schedule": {
"type": "cron",
"expression": "0 8 * * 1-5",
"timezone": "America/Chicago"
},
"budget": {
"type": "limit",
"max_list_cost": {
"amount": "300",
"currency": "USD"
}
}
}'const response = await fetch("https://core.thelittleworker.com/api/v1/routines", {
method: "POST",
headers: {
"x-api-key": process.env.LITTLE_WORKER_API_KEY,
"Little-Worker-Version": "2026-10-19",
"Idempotency-Key": "c3a5e7b9-1d2f-4a6c-8e0b-2d4f6a8c1e3b",
"content-type": "application/json",
},
body: JSON.stringify({
worker: "worker_8c1f4e2a9b7d4f0e8a6c3b5d1e9f7a20",
name: "Weekday opening email",
initial_events: [
{
type: "user.message",
content: [
{
type: "text",
text: "Write three subject lines for today's email that says the shop opens at 7 am.",
},
],
},
],
schedule: {
type: "cron",
expression: "0 8 * * 1-5",
timezone: "America/Chicago",
},
budget: {
type: "limit",
max_list_cost: {
amount: "300",
currency: "USD",
},
},
}),
})
const routine = await response.json()import os
import requests
response = requests.post(
"https://core.thelittleworker.com/api/v1/routines",
headers={
"x-api-key": os.environ["LITTLE_WORKER_API_KEY"],
"Little-Worker-Version": "2026-10-19",
"Idempotency-Key": "c3a5e7b9-1d2f-4a6c-8e0b-2d4f6a8c1e3b",
},
json={
"worker": "worker_8c1f4e2a9b7d4f0e8a6c3b5d1e9f7a20",
"name": "Weekday opening email",
"initial_events": [
{
"type": "user.message",
"content": [
{
"type": "text",
"text": "Write three subject lines for today's email that says the shop opens at 7 am.",
},
],
},
],
"schedule": {
"type": "cron",
"expression": "0 8 * * 1-5",
"timezone": "America/Chicago",
},
"budget": {
"type": "limit",
"max_list_cost": {
"amount": "300",
"currency": "USD",
},
},
},
)
routine = response.json(){
"type": "routine",
"id": "routine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d",
"worker": {
"type": "worker",
"id": "worker_8c1f4e2a9b7d4f0e8a6c3b5d1e9f7a20",
"version": 1
},
"name": "Weekday opening email",
"description": null,
"metadata": {},
"initial_events": [
{
"type": "user.message",
"content": [
{
"type": "text",
"text": "Write three subject lines for today's email that says the shop opens at 7 am."
}
]
}
],
"schedule": {
"type": "cron",
"expression": "0 8 * * 1-5",
"timezone": "America/Chicago",
"last_run_at": null,
"upcoming_runs_at": [
"2026-10-26T13:00:00.000Z",
"2026-10-27T13:00:00.000Z",
"2026-10-28T13:00:00.000Z",
"2026-10-29T13:00:00.000Z",
"2026-10-30T13:00:00.000Z"
]
},
"triggers": [],
"budget": {
"type": "limit",
"max_list_cost": {
"amount": "300",
"currency": "USD"
}
},
"resources": [],
"password_set_ids": [
"password_set_597a64d52e7f4e84ac5d95929bf4cbae",
"password_set_37cbbff4bc744ede8217773f8a942448"
],
"status": "active",
"paused_reason": null,
"version": 1,
"created_at": "2026-10-25T17:20:14.581Z",
"updated_at": "2026-10-25T17:20:14.581Z",
"archived_at": null
}List routines
GET/routines
List the routines of the key's workers, newest first. An archived routine is left out unless you ask for include_archived=true.
Query parameters
worker_idworker idOnly this worker's routines.include_archivedstringtruelists archived routines too.One of
true,falselimitintegerHow many to answer, 1 to 100. The default is 20.pagestringThenext_pageof the answer before, to read the next page.orderstringdesc, newest first (the default), orasc, oldest first.One of
desc,asccreated_at[gt]timeOnly those made after this time.created_at[gte],created_at[lt]andcreated_at[lte]work the same way.
Returns
A list: data holds the routines, and next_page is the page to send for the next ones (null on the last page).
Errors
| Status | Type | When |
|---|---|---|
| 400 | invalid_request_error | A filter is not valid, such as a worker_id of the wrong kind or a limit outside 1 to 100 (error_code validation_error), or page is not a page this list gave with the same order and created_at filters (error_code invalid_cursor). Read the first page again. |
| 401 | authentication_error | The key is missing, removed, or not a key. Send it in x-api-key. |
| 404 | not_found_error | worker_id names no worker of the key's (Worker not found.). |
| 429 | rate_limit_error | The workspace made too many requests this minute. Wait the seconds in Retry-After, then try again. |
curl "https://core.thelittleworker.com/api/v1/routines?worker_id=worker_8c1f4e2a9b7d4f0e8a6c3b5d1e9f7a20&limit=2" \
-H "x-api-key: $LITTLE_WORKER_API_KEY" \
-H "Little-Worker-Version: 2026-10-19"const response = await fetch("https://core.thelittleworker.com/api/v1/routines?worker_id=worker_8c1f4e2a9b7d4f0e8a6c3b5d1e9f7a20&limit=2", {
headers: {
"x-api-key": process.env.LITTLE_WORKER_API_KEY,
"Little-Worker-Version": "2026-10-19",
},
})
const routines = await response.json()import os
import requests
response = requests.get(
"https://core.thelittleworker.com/api/v1/routines",
headers={
"x-api-key": os.environ["LITTLE_WORKER_API_KEY"],
"Little-Worker-Version": "2026-10-19",
},
params={
"worker_id": "worker_8c1f4e2a9b7d4f0e8a6c3b5d1e9f7a20",
"limit": "2",
},
)
routines = response.json(){
"data": [
{
"type": "routine",
"id": "routine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d",
"worker": {
"type": "worker",
"id": "worker_8c1f4e2a9b7d4f0e8a6c3b5d1e9f7a20",
"version": 1
},
"name": "Weekday opening email",
"description": null,
"metadata": {},
"initial_events": [
{
"type": "user.message",
"content": [
{
"type": "text",
"text": "Write three subject lines for today's email that says the shop opens at 7 am."
}
]
}
],
"schedule": {
"type": "cron",
"expression": "0 8 * * 1-5",
"timezone": "America/Chicago",
"last_run_at": "2026-10-23T13:03:12.408Z",
"upcoming_runs_at": [
"2026-10-26T13:00:00.000Z",
"2026-10-27T13:00:00.000Z",
"2026-10-28T13:00:00.000Z",
"2026-10-29T13:00:00.000Z",
"2026-10-30T13:00:00.000Z"
]
},
"triggers": [],
"budget": {
"type": "limit",
"max_list_cost": {
"amount": "300",
"currency": "USD"
}
},
"resources": [],
"password_set_ids": [
"password_set_597a64d52e7f4e84ac5d95929bf4cbae",
"password_set_37cbbff4bc744ede8217773f8a942448"
],
"status": "active",
"paused_reason": null,
"version": 3,
"created_at": "2026-10-20T15:12:40.206Z",
"updated_at": "2026-10-22T18:41:09.733Z",
"archived_at": null
}
],
"next_page": null
}Read a routine
GET/routines/{routine_id}
Read one routine as it stands now, archived or not.
Path parameters
routine_idroutine idRequiredThe routine, such asroutine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d.
Returns
The routine.
Errors
| Status | Type | When |
|---|---|---|
| 400 | invalid_request_error | routine_id is not a routine id. error_code is validation_error. |
| 401 | authentication_error | The key is missing, removed, or not a key. Send it in x-api-key. |
| 404 | not_found_error | No routine of one of the key's workers has this id (Routine not found.). |
| 429 | rate_limit_error | The workspace made too many requests this minute. Wait the seconds in Retry-After, then try again. |
curl https://core.thelittleworker.com/api/v1/routines/routine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d \
-H "x-api-key: $LITTLE_WORKER_API_KEY" \
-H "Little-Worker-Version: 2026-10-19"const response = await fetch("https://core.thelittleworker.com/api/v1/routines/routine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d", {
headers: {
"x-api-key": process.env.LITTLE_WORKER_API_KEY,
"Little-Worker-Version": "2026-10-19",
},
})
const routine = await response.json()import os
import requests
response = requests.get(
"https://core.thelittleworker.com/api/v1/routines/routine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d",
headers={
"x-api-key": os.environ["LITTLE_WORKER_API_KEY"],
"Little-Worker-Version": "2026-10-19",
},
)
routine = response.json(){
"type": "routine",
"id": "routine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d",
"worker": {
"type": "worker",
"id": "worker_8c1f4e2a9b7d4f0e8a6c3b5d1e9f7a20",
"version": 1
},
"name": "Weekday opening email",
"description": null,
"metadata": {},
"initial_events": [
{
"type": "user.message",
"content": [
{
"type": "text",
"text": "Write three subject lines for today's email that says the shop opens at 7 am."
}
]
}
],
"schedule": {
"type": "cron",
"expression": "0 8 * * 1-5",
"timezone": "America/Chicago",
"last_run_at": "2026-10-23T13:03:12.408Z",
"upcoming_runs_at": [
"2026-10-26T13:00:00.000Z",
"2026-10-27T13:00:00.000Z",
"2026-10-28T13:00:00.000Z",
"2026-10-29T13:00:00.000Z",
"2026-10-30T13:00:00.000Z"
]
},
"triggers": [],
"budget": {
"type": "limit",
"max_list_cost": {
"amount": "300",
"currency": "USD"
}
},
"resources": [],
"password_set_ids": [
"password_set_597a64d52e7f4e84ac5d95929bf4cbae",
"password_set_37cbbff4bc744ede8217773f8a942448"
],
"status": "active",
"paused_reason": null,
"version": 3,
"created_at": "2026-10-20T15:12:40.206Z",
"updated_at": "2026-10-22T18:41:09.733Z",
"archived_at": null
}Change a routine
POST/routines/{routine_id}
Change a routine. A field left out stays as it was. A list you send replaces the routine's list in full, and metadata is merged key by key.
To change a routine only if nobody changed it since you read it, send its version. A routine changed since is refused with 409, and the error carries current_version. Without version, the last write wins.
Path parameters
routine_idroutine idRequiredThe routine, such asroutine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d.
Headers
Idempotency-KeyUUIDSend one so a retry does the work once. A second call with the same key and the same content answers what the first call did. The same key with other content is409witherror_codeidempotency_conflict. Kept for 7 days.
Body
versionintegerTheversionof the routine you read, 1 or more. The change is refused if the routine has changed since.workerworker id or objectThe routine's own worker, by its id or as{ "type": "worker", "id", "version" }to pin a version. An id alone makes the routine follow the worker's current version. Any other worker is refused: a routine keeps its worker.namestringA new name, 1 to 256 characters.descriptionstring or nullA new description, up to 2,048 characters.nullclears it.metadataobject or nullLabels to set. A key with a string value is set, a key set tonullis removed, and keys left out stay.nullfor the whole object clears every label.initial_eventsarray of objectsThe events each new task starts with. The list replaces the routine's, in full.Show its 5 fields
typestringRequireduser.messageis a message each new task starts with.user.define_outcomeis what each task's work is checked against; send at most one.One of
user.message,user.define_outcomecontentarray of text blocksRequiredWithuser.message: one text block or more, each{ "type": "text", "text": "..." }. The messages' text, joined by blank lines, is the task's ask, up to 100,000 characters.descriptionstringRequiredWithuser.define_outcome: what done looks like.rubricobjectRequiredWithuser.define_outcome:{ "type": "text", "content": "..." }. Each line of the text is one of each task's Done Means: up to 8 lines of up to 200 characters.max_iterationsinteger or nullWithuser.define_outcome: how many check rounds before it gives up.
scheduleobject or nullA new schedule.nullclears it, and the routine then runs only by hand or fromtriggers.Show its 3 fields
typestringRequiredAlwayscron.expressionstringRequiredA cron expression of five fields, such as0 8 * * 1-5(8:00 on weekdays), or an interval such as@every 2h, up to 256 characters. Runs are at least 5 minutes apart; a tighter expression is refused. A hosted plan may set a longer gap.timezonestringRequiredAn IANA time zone name, such asAmerica/Chicago, up to 64 characters.
triggersarray of objectsThe other starts beside the schedule. The list replaces the routine's, in full.Show its 4 fields
typestringwebhookstarts the routine when its webhook is called; a person makes the webhook's key in the app, because this API does not.emailstarts it when a message arrives at one of the workspace's inboxes.eventstarts it when an app reports an event. Each type may appear once.One of
webhook,email,eventinboxstringWithemail: the inbox's address, up to 254 characters. It must be one of the workspace's own.appstringWithevent: the app, 1 to 64 characters.eventstringWithevent: the app's name for the event, 1 to 120 characters.
budgetobject or nullA new budget for each task.nullgoes back to the default, $5.00.Show its 2 fields
typestringAlwayslimit.max_list_costmoneyThe most one task's work may cost at list price, from 1 cent to $1,000.Show its 2 fields
amountstringWhole US cents, as a string:"250"is $2.50.currencystringAlwaysUSD.
resourcesarray of objects or nullThe whole list of resources each task is given.[]ornullclears it.Show its 8 fields
typestringRequiredThe kind of resource.One of
file,github_repository,memory_storefile_idfile idWithfile: a file you uploaded. List it once only.urlstringWithgithub_repository: ahttps://github.com/owner/repositoryaddress.authorization_tokenstringWithgithub_repository: a token that may clone it, 1 to 4,096 characters. It is kept sealed and never answered.checkoutobjectWithgithub_repository: what to check out,{ "type": "branch", "name" }or{ "type": "commit", "sha" }with a full 40-character SHA.memory_store_idmemory store idWithmemory_store: the store. A routine names at most 8, none archived or a worker's own.accessstringWithmemory_store:read_writeorread_only. Left out, it isread_write.One of
read_write,read_onlyinstructionsstringWithmemory_store: how a task should use the store, up to 4,096 characters.
password_set_idsarray of password set ids or nullThe whole list of password sets each task signs in with, up to 20.nullgoes back to the worker's own set and the workspace's shared set, and[]names none.
Returns
200 and the routine, changed.
Errors
| Status | Type | When |
|---|---|---|
| 400 | invalid_request_error | routine_id is not a routine id. error_code is validation_error. |
| 400 | invalid_request_error | A field is missing or not valid: an id of the wrong kind, a field the route does not take, a name over 256 characters, messages over 100,000 characters, an outcome rubric that is not 1 to 8 lines of up to 200 characters, a trigger type given twice, an Idempotency-Key that is not a UUID, or a list a task could not take (a file listed twice or expired, more than 8 memory stores, one listed twice, archived, or a worker's own). message says which. error_code is validation_error. |
| 400 | invalid_request_error | schedule.timezone is not a time zone (error_code invalid_timezone). Use a name like America/Chicago. |
| 400 | invalid_request_error | schedule.expression is not valid (Enter a valid schedule.), or runs more often than every 5 minutes (Routine schedules must be at least 5 minutes apart.). On a hosted plan with a longer gap, the message says how often a task may repeat. error_code is validation_error. |
| 400 | invalid_request_error | worker names a worker other than the routine's own. Make a new routine for another worker. |
| 401 | authentication_error | The key is missing, removed, or not a key. Send it in x-api-key. |
| 404 | not_found_error | No routine of one of the key's workers has this id (Routine not found.), the pinned version does not exist, or a password set, file or memory store does not exist. |
| 409 | conflict_error | The routine is archived and cannot be changed. |
| 409 | conflict_error | The routine changed since the version you sent (error_code schedule_conflict). Nothing was changed. The error carries current_version: read the routine, apply your change to it, and send it again. |
| 409 | conflict_error | The Idempotency-Key was already used with other content (error_code idempotency_conflict). Use a new key for new work. |
| 429 | rate_limit_error | The workspace made too many requests this minute. Wait the seconds in Retry-After, then try again. |
curl "https://core.thelittleworker.com/api/v1/routines/routine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d" \
-H "x-api-key: $LITTLE_WORKER_API_KEY" \
-H "Little-Worker-Version: 2026-10-19" \
-H "Idempotency-Key: 5e7a9c1b-3d4f-4b6e-a0c2-4f6b8d0a2c5e" \
-H "content-type: application/json" \
-d '{
"version": 3,
"schedule": {
"type": "cron",
"expression": "0 9 * * 1-5",
"timezone": "America/Chicago"
}
}'const response = await fetch("https://core.thelittleworker.com/api/v1/routines/routine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d", {
method: "POST",
headers: {
"x-api-key": process.env.LITTLE_WORKER_API_KEY,
"Little-Worker-Version": "2026-10-19",
"Idempotency-Key": "5e7a9c1b-3d4f-4b6e-a0c2-4f6b8d0a2c5e",
"content-type": "application/json",
},
body: JSON.stringify({
version: 3,
schedule: {
type: "cron",
expression: "0 9 * * 1-5",
timezone: "America/Chicago",
},
}),
})
const routine = await response.json()import os
import requests
response = requests.post(
"https://core.thelittleworker.com/api/v1/routines/routine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d",
headers={
"x-api-key": os.environ["LITTLE_WORKER_API_KEY"],
"Little-Worker-Version": "2026-10-19",
"Idempotency-Key": "5e7a9c1b-3d4f-4b6e-a0c2-4f6b8d0a2c5e",
},
json={
"version": 3,
"schedule": {
"type": "cron",
"expression": "0 9 * * 1-5",
"timezone": "America/Chicago",
},
},
)
routine = response.json(){
"type": "routine",
"id": "routine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d",
"worker": {
"type": "worker",
"id": "worker_8c1f4e2a9b7d4f0e8a6c3b5d1e9f7a20",
"version": 1
},
"name": "Weekday opening email",
"description": null,
"metadata": {},
"initial_events": [
{
"type": "user.message",
"content": [
{
"type": "text",
"text": "Write three subject lines for today's email that says the shop opens at 7 am."
}
]
}
],
"schedule": {
"type": "cron",
"expression": "0 9 * * 1-5",
"timezone": "America/Chicago",
"last_run_at": "2026-10-23T13:03:12.408Z",
"upcoming_runs_at": [
"2026-10-26T14:00:00.000Z",
"2026-10-27T14:00:00.000Z",
"2026-10-28T14:00:00.000Z",
"2026-10-29T14:00:00.000Z",
"2026-10-30T14:00:00.000Z"
]
},
"triggers": [],
"budget": {
"type": "limit",
"max_list_cost": {
"amount": "300",
"currency": "USD"
}
},
"resources": [],
"password_set_ids": [
"password_set_597a64d52e7f4e84ac5d95929bf4cbae",
"password_set_37cbbff4bc744ede8217773f8a942448"
],
"status": "active",
"paused_reason": null,
"version": 4,
"created_at": "2026-10-20T15:12:40.206Z",
"updated_at": "2026-10-25T17:31:50.092Z",
"archived_at": null
}Pause a routine
POST/routines/{routine_id}/pause
Pause a routine. A paused routine starts by itself no more, from its schedule or any other start, but you can still run it. Its paused_reason is manual. Pausing a routine that is paused changes nothing.
Path parameters
routine_idroutine idRequiredThe routine, such asroutine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d.
Headers
Idempotency-KeyUUIDSend one so a retry does the work once. Kept for 7 days.
Returns
200 and the routine, paused.
Errors
| Status | Type | When |
|---|---|---|
| 400 | invalid_request_error | routine_id is not a routine id. error_code is validation_error. |
| 401 | authentication_error | The key is missing, removed, or not a key. Send it in x-api-key. |
| 404 | not_found_error | No routine of one of the key's workers has this id (Routine not found.). |
| 409 | conflict_error | The routine is archived and cannot be paused. |
| 429 | rate_limit_error | The workspace made too many requests this minute. Wait the seconds in Retry-After, then try again. |
curl "https://core.thelittleworker.com/api/v1/routines/routine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d/pause" \
-X POST \
-H "x-api-key: $LITTLE_WORKER_API_KEY" \
-H "Little-Worker-Version: 2026-10-19" \
-H "Idempotency-Key: 8b0d2f4a-6c1e-4e3a-9b5d-7f1a3c5e9b2d"const response = await fetch("https://core.thelittleworker.com/api/v1/routines/routine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d/pause", {
method: "POST",
headers: {
"x-api-key": process.env.LITTLE_WORKER_API_KEY,
"Little-Worker-Version": "2026-10-19",
"Idempotency-Key": "8b0d2f4a-6c1e-4e3a-9b5d-7f1a3c5e9b2d",
},
})
const routine = await response.json()import os
import requests
response = requests.post(
"https://core.thelittleworker.com/api/v1/routines/routine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d/pause",
headers={
"x-api-key": os.environ["LITTLE_WORKER_API_KEY"],
"Little-Worker-Version": "2026-10-19",
"Idempotency-Key": "8b0d2f4a-6c1e-4e3a-9b5d-7f1a3c5e9b2d",
},
)
routine = response.json(){
"type": "routine",
"id": "routine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d",
"worker": {
"type": "worker",
"id": "worker_8c1f4e2a9b7d4f0e8a6c3b5d1e9f7a20",
"version": 1
},
"name": "Weekday opening email",
"description": null,
"metadata": {},
"initial_events": [
{
"type": "user.message",
"content": [
{
"type": "text",
"text": "Write three subject lines for today's email that says the shop opens at 7 am."
}
]
}
],
"schedule": {
"type": "cron",
"expression": "0 8 * * 1-5",
"timezone": "America/Chicago",
"last_run_at": "2026-10-23T13:03:12.408Z",
"upcoming_runs_at": []
},
"triggers": [],
"budget": {
"type": "limit",
"max_list_cost": {
"amount": "300",
"currency": "USD"
}
},
"resources": [],
"password_set_ids": [
"password_set_597a64d52e7f4e84ac5d95929bf4cbae",
"password_set_37cbbff4bc744ede8217773f8a942448"
],
"status": "paused",
"paused_reason": {
"type": "manual"
},
"version": 4,
"created_at": "2026-10-20T15:12:40.206Z",
"updated_at": "2026-10-25T17:34:22.870Z",
"archived_at": null
}Unpause a routine
POST/routines/{routine_id}/unpause
Let a paused routine start by itself again. Its next run is planned from now, so the times it missed while paused are not made up. Unpausing a routine that is active changes nothing. A routine that names an archived password set cannot be unpaused: change its password_set_ids first.
Path parameters
routine_idroutine idRequiredThe routine, such asroutine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d.
Headers
Idempotency-KeyUUIDSend one so a retry does the work once. Kept for 7 days.
Returns
200 and the routine, active.
Errors
| Status | Type | When |
|---|---|---|
| 400 | invalid_request_error | routine_id is not a routine id. error_code is validation_error. |
| 400 | invalid_request_error | The routine names a password set that is archived (error_code validation_error). Change its password_set_ids, then unpause it. |
| 401 | authentication_error | The key is missing, removed, or not a key. Send it in x-api-key. |
| 404 | not_found_error | No routine of one of the key's workers has this id (Routine not found.). |
| 409 | conflict_error | The routine is archived and cannot be unpaused. |
| 429 | rate_limit_error | The workspace made too many requests this minute. Wait the seconds in Retry-After, then try again. |
curl "https://core.thelittleworker.com/api/v1/routines/routine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d/unpause" \
-X POST \
-H "x-api-key: $LITTLE_WORKER_API_KEY" \
-H "Little-Worker-Version: 2026-10-19" \
-H "Idempotency-Key: 1f3b5d7a-9c2e-4a4c-b6e8-0a2c4e6b8d1f"const response = await fetch("https://core.thelittleworker.com/api/v1/routines/routine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d/unpause", {
method: "POST",
headers: {
"x-api-key": process.env.LITTLE_WORKER_API_KEY,
"Little-Worker-Version": "2026-10-19",
"Idempotency-Key": "1f3b5d7a-9c2e-4a4c-b6e8-0a2c4e6b8d1f",
},
})
const routine = await response.json()import os
import requests
response = requests.post(
"https://core.thelittleworker.com/api/v1/routines/routine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d/unpause",
headers={
"x-api-key": os.environ["LITTLE_WORKER_API_KEY"],
"Little-Worker-Version": "2026-10-19",
"Idempotency-Key": "1f3b5d7a-9c2e-4a4c-b6e8-0a2c4e6b8d1f",
},
)
routine = response.json(){
"type": "routine",
"id": "routine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d",
"worker": {
"type": "worker",
"id": "worker_8c1f4e2a9b7d4f0e8a6c3b5d1e9f7a20",
"version": 1
},
"name": "Weekday opening email",
"description": null,
"metadata": {},
"initial_events": [
{
"type": "user.message",
"content": [
{
"type": "text",
"text": "Write three subject lines for today's email that says the shop opens at 7 am."
}
]
}
],
"schedule": {
"type": "cron",
"expression": "0 8 * * 1-5",
"timezone": "America/Chicago",
"last_run_at": "2026-10-23T13:03:12.408Z",
"upcoming_runs_at": [
"2026-10-26T13:00:00.000Z",
"2026-10-27T13:00:00.000Z",
"2026-10-28T13:00:00.000Z",
"2026-10-29T13:00:00.000Z",
"2026-10-30T13:00:00.000Z"
]
},
"triggers": [],
"budget": {
"type": "limit",
"max_list_cost": {
"amount": "300",
"currency": "USD"
}
},
"resources": [],
"password_set_ids": [
"password_set_597a64d52e7f4e84ac5d95929bf4cbae",
"password_set_37cbbff4bc744ede8217773f8a942448"
],
"status": "active",
"paused_reason": null,
"version": 5,
"created_at": "2026-10-20T15:12:40.206Z",
"updated_at": "2026-10-25T17:36:05.319Z",
"archived_at": null
}Run a routine
POST/routines/{routine_id}/run
Run a routine now, whatever its schedule says, and even while it is paused. The worker gets a task as the routine describes, and the answer is the routine run that recorded it. Its task_id is the task: read its work with the task routes.
A worker does one task at a time, so a worker already on a task refuses the run. Run it again when the worker is done.
Path parameters
routine_idroutine idRequiredThe routine, such asroutine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d.
Headers
Idempotency-KeyUUIDSend one so a retry does the work once. A second call with the same key and the same content answers what the first call did. The same key with other content is409witherror_codeidempotency_conflict. Kept for 7 days.
Body
expected_configuration_revisionintegerTheversionof the routine you expect, 1 or more. The run is refused if the routine changed since. Left out, the routine runs as it is now.
Returns
200 and the routine run it recorded.
typestringAlwaysroutine_run.idroutine run idThe run's id:routine_run_and 32 hex digits.routine_idroutine idThe routine.workerobjectThe worker that was to do the work.Show its 2 fields
typestringAlwaysworker.idworker idThe worker.
trigger_contextobjectWhat started it, in itstype.Show its 5 fields
typestringschedulewhen the schedule matched,manualwhen someone ran the routine, andwebhook,emailoreventfor the other starts.One of
schedule,manual,webhook,email,eventscheduled_attimeWithschedule: the instant the schedule matched.inboxstringWithemail: the inbox the message came to.appstring or nullWithevent: the app.nullfor a run from before it was recorded.eventstring or nullWithevent: the event.nullfor a run from before it was recorded.
task_idtask id or nullThe task the run made. Exactly one oftask_idanderroris set.errorobject or nullWhy the run made no task.nullwhen it made one.Show its 2 fields
typestringworker_archived_error: the worker was archived.worker_busy_error: the worker was busy with other work.nonexistent_local_time_error: the local time did not exist that day.more_than_one_hour_late_error: Little Worker was down for more than an hour past the scheduled time.routine_paused_error: the routine was paused.superseded_edit_error: an edit replaced the scheduled time.unknown_error: something else.One of
worker_archived_error,worker_busy_error,nonexistent_local_time_error,more_than_one_hour_late_error,routine_paused_error,superseded_edit_error,unknown_errormessagestringA sentence a person can read.
created_attimeWhen the run was recorded.
Errors
| Status | Type | When |
|---|---|---|
| 400 | invalid_request_error | routine_id is not a routine id. error_code is validation_error. |
| 400 | invalid_request_error | expected_configuration_revision is not a whole number of 1 or more (error_code validation_error). |
| 401 | authentication_error | The key is missing, removed, or not a key. Send it in x-api-key. |
| 404 | not_found_error | No routine of one of the key's workers has this id (Routine not found.). |
| 409 | conflict_error | The routine is archived and cannot be run. |
| 409 | conflict_error | The worker is archived or already working on a task. Run the routine again when the worker is done. Or the routine changed since the expected_configuration_revision you sent (error_code schedule_conflict), and nothing ran. |
| 409 | conflict_error | The Idempotency-Key was already used with other content (error_code idempotency_conflict). Use a new key for new work. |
| 429 | rate_limit_error | The workspace made too many requests this minute. Wait the seconds in Retry-After, then try again. |
curl "https://core.thelittleworker.com/api/v1/routines/routine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d/run" \
-H "x-api-key: $LITTLE_WORKER_API_KEY" \
-H "Little-Worker-Version: 2026-10-19" \
-H "Idempotency-Key: 4d6f8a0c-2b3e-4c5a-8d7f-9b1d3f5a7c0e" \
-H "content-type: application/json" \
-d '{
"expected_configuration_revision": 3
}'const response = await fetch("https://core.thelittleworker.com/api/v1/routines/routine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d/run", {
method: "POST",
headers: {
"x-api-key": process.env.LITTLE_WORKER_API_KEY,
"Little-Worker-Version": "2026-10-19",
"Idempotency-Key": "4d6f8a0c-2b3e-4c5a-8d7f-9b1d3f5a7c0e",
"content-type": "application/json",
},
body: JSON.stringify({
expected_configuration_revision: 3,
}),
})
const run = await response.json()import os
import requests
response = requests.post(
"https://core.thelittleworker.com/api/v1/routines/routine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d/run",
headers={
"x-api-key": os.environ["LITTLE_WORKER_API_KEY"],
"Little-Worker-Version": "2026-10-19",
"Idempotency-Key": "4d6f8a0c-2b3e-4c5a-8d7f-9b1d3f5a7c0e",
},
json={
"expected_configuration_revision": 3,
},
)
run = response.json(){
"type": "routine_run",
"id": "routine_run_2e4a6c8b0d1f4a3e9c5b7d9f1a3c5e7b",
"routine_id": "routine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d",
"worker": {
"type": "worker",
"id": "worker_8c1f4e2a9b7d4f0e8a6c3b5d1e9f7a20"
},
"trigger_context": {
"type": "manual"
},
"task_id": "task_01JB2M4Z8H6R3S5T7V9W1X0Y2A",
"error": null,
"created_at": "2026-10-25T17:38:47.215Z"
}Archive a routine
POST/routines/{routine_id}/archive
Archive a routine you no longer need. It stays readable, with its runs, but it never starts again, and it cannot be changed, paused or run. Archiving cannot be undone, and a routine cannot be deleted. Archiving an archived routine changes nothing.
Path parameters
routine_idroutine idRequiredThe routine, such asroutine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d.
Headers
Idempotency-KeyUUIDSend one so a retry does the work once. Kept for 7 days.
Returns
200 and the routine, with archived_at set.
Errors
| Status | Type | When |
|---|---|---|
| 400 | invalid_request_error | routine_id is not a routine id. error_code is validation_error. |
| 401 | authentication_error | The key is missing, removed, or not a key. Send it in x-api-key. |
| 404 | not_found_error | No routine of one of the key's workers has this id (Routine not found.). |
| 429 | rate_limit_error | The workspace made too many requests this minute. Wait the seconds in Retry-After, then try again. |
curl "https://core.thelittleworker.com/api/v1/routines/routine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d/archive" \
-X POST \
-H "x-api-key: $LITTLE_WORKER_API_KEY" \
-H "Little-Worker-Version: 2026-10-19" \
-H "Idempotency-Key: 6a8c0e2b-4d5f-4e7b-a9c1-1e3a5c7e9b0d"const response = await fetch("https://core.thelittleworker.com/api/v1/routines/routine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d/archive", {
method: "POST",
headers: {
"x-api-key": process.env.LITTLE_WORKER_API_KEY,
"Little-Worker-Version": "2026-10-19",
"Idempotency-Key": "6a8c0e2b-4d5f-4e7b-a9c1-1e3a5c7e9b0d",
},
})
const routine = await response.json()import os
import requests
response = requests.post(
"https://core.thelittleworker.com/api/v1/routines/routine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d/archive",
headers={
"x-api-key": os.environ["LITTLE_WORKER_API_KEY"],
"Little-Worker-Version": "2026-10-19",
"Idempotency-Key": "6a8c0e2b-4d5f-4e7b-a9c1-1e3a5c7e9b0d",
},
)
routine = response.json(){
"type": "routine",
"id": "routine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d",
"worker": {
"type": "worker",
"id": "worker_8c1f4e2a9b7d4f0e8a6c3b5d1e9f7a20",
"version": 1
},
"name": "Weekday opening email",
"description": null,
"metadata": {},
"initial_events": [
{
"type": "user.message",
"content": [
{
"type": "text",
"text": "Write three subject lines for today's email that says the shop opens at 7 am."
}
]
}
],
"schedule": {
"type": "cron",
"expression": "0 8 * * 1-5",
"timezone": "America/Chicago",
"last_run_at": "2026-10-23T13:03:12.408Z",
"upcoming_runs_at": []
},
"triggers": [],
"budget": {
"type": "limit",
"max_list_cost": {
"amount": "300",
"currency": "USD"
}
},
"resources": [],
"password_set_ids": [
"password_set_597a64d52e7f4e84ac5d95929bf4cbae",
"password_set_37cbbff4bc744ede8217773f8a942448"
],
"status": "active",
"paused_reason": null,
"version": 4,
"created_at": "2026-10-20T15:12:40.206Z",
"updated_at": "2026-10-25T17:40:31.447Z",
"archived_at": "2026-10-25T17:40:31.447Z"
}List routine runs
GET/routine_runs
List the runs of routines, newest first: every time a routine ran and made a task, and every time it was due and made none. A run of a routine that is not the key's is not listed. A run stays here after its routine is archived.
Query parameters
routine_idroutine idOnly this routine's runs.has_errorstringtruelists only runs that made no task.falselists only runs that made one.One of
true,falsetrigger_typestringOnly runs that something of this kind started.One of
schedule,manual,webhook,email,eventlimitintegerHow many to answer, 1 to 1,000. The default is 20.pagestringThenext_pageof the answer before, to read the next page.orderstringdesc, newest first (the default), orasc, oldest first.One of
desc,asccreated_at[gt]timeOnly those made after this time.created_at[gte],created_at[lt]andcreated_at[lte]work the same way.
Returns
A list: data holds the routine runs, and next_page is the page to send for the next ones (null on the last page). Each run has these fields.
typestringAlwaysroutine_run.idroutine run idThe run's id:routine_run_and 32 hex digits.routine_idroutine idThe routine.workerobjectThe worker that was to do the work.Show its 2 fields
typestringAlwaysworker.idworker idThe worker.
trigger_contextobjectWhat started it, in itstype.Show its 5 fields
typestringschedulewhen the schedule matched,manualwhen someone ran the routine, andwebhook,emailoreventfor the other starts.One of
schedule,manual,webhook,email,eventscheduled_attimeWithschedule: the instant the schedule matched.inboxstringWithemail: the inbox the message came to.appstring or nullWithevent: the app.nullfor a run from before it was recorded.eventstring or nullWithevent: the event.nullfor a run from before it was recorded.
task_idtask id or nullThe task the run made. Exactly one oftask_idanderroris set.errorobject or nullWhy the run made no task.nullwhen it made one.Show its 2 fields
typestringworker_archived_error: the worker was archived.worker_busy_error: the worker was busy with other work.nonexistent_local_time_error: the local time did not exist that day.more_than_one_hour_late_error: Little Worker was down for more than an hour past the scheduled time.routine_paused_error: the routine was paused.superseded_edit_error: an edit replaced the scheduled time.unknown_error: something else.One of
worker_archived_error,worker_busy_error,nonexistent_local_time_error,more_than_one_hour_late_error,routine_paused_error,superseded_edit_error,unknown_errormessagestringA sentence a person can read.
created_attimeWhen the run was recorded.
Errors
| Status | Type | When |
|---|---|---|
| 400 | invalid_request_error | A filter is not valid, such as a routine_id of the wrong kind, a trigger_type that is not one of the five, or a limit outside 1 to 1,000 (error_code validation_error), or page is not a page this list gave with the same order and created_at filters (error_code invalid_cursor). Read the first page again. |
| 401 | authentication_error | The key is missing, removed, or not a key. Send it in x-api-key. |
| 429 | rate_limit_error | The workspace made too many requests this minute. Wait the seconds in Retry-After, then try again. |
curl "https://core.thelittleworker.com/api/v1/routine_runs?routine_id=routine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d&limit=2" \
-H "x-api-key: $LITTLE_WORKER_API_KEY" \
-H "Little-Worker-Version: 2026-10-19"const response = await fetch("https://core.thelittleworker.com/api/v1/routine_runs?routine_id=routine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d&limit=2", {
headers: {
"x-api-key": process.env.LITTLE_WORKER_API_KEY,
"Little-Worker-Version": "2026-10-19",
},
})
const runs = await response.json()import os
import requests
response = requests.get(
"https://core.thelittleworker.com/api/v1/routine_runs",
headers={
"x-api-key": os.environ["LITTLE_WORKER_API_KEY"],
"Little-Worker-Version": "2026-10-19",
},
params={
"routine_id": "routine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d",
"limit": "2",
},
)
runs = response.json(){
"data": [
{
"type": "routine_run",
"id": "routine_run_2e4a6c8b0d1f4a3e9c5b7d9f1a3c5e7b",
"routine_id": "routine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d",
"worker": {
"type": "worker",
"id": "worker_8c1f4e2a9b7d4f0e8a6c3b5d1e9f7a20"
},
"trigger_context": {
"type": "manual"
},
"task_id": "task_01JB2M4Z8H6R3S5T7V9W1X0Y2A",
"error": null,
"created_at": "2026-10-25T17:38:47.215Z"
},
{
"type": "routine_run",
"id": "routine_run_9c7a5e3b1d2f4b6a8c0e2d4f6a8b1c3e",
"routine_id": "routine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d",
"worker": {
"type": "worker",
"id": "worker_8c1f4e2a9b7d4f0e8a6c3b5d1e9f7a20"
},
"trigger_context": {
"type": "schedule",
"scheduled_at": "2026-10-22T13:00:00.000Z"
},
"task_id": null,
"error": {
"type": "worker_busy_error",
"message": "The worker was busy with other work."
},
"created_at": "2026-10-22T13:00:04.771Z"
}
],
"next_page": null
}Read a routine run
GET/routine_runs/{routine_run_id}
Read one routine run.
Path parameters
routine_run_idroutine run idRequiredThe run, such asroutine_run_7a1c3e5b9d2f4e6a8c0b1d3f5e7a9c2b.
Returns
The routine run.
typestringAlwaysroutine_run.idroutine run idThe run's id:routine_run_and 32 hex digits.routine_idroutine idThe routine.workerobjectThe worker that was to do the work.Show its 2 fields
typestringAlwaysworker.idworker idThe worker.
trigger_contextobjectWhat started it, in itstype.Show its 5 fields
typestringschedulewhen the schedule matched,manualwhen someone ran the routine, andwebhook,emailoreventfor the other starts.One of
schedule,manual,webhook,email,eventscheduled_attimeWithschedule: the instant the schedule matched.inboxstringWithemail: the inbox the message came to.appstring or nullWithevent: the app.nullfor a run from before it was recorded.eventstring or nullWithevent: the event.nullfor a run from before it was recorded.
task_idtask id or nullThe task the run made. Exactly one oftask_idanderroris set.errorobject or nullWhy the run made no task.nullwhen it made one.Show its 2 fields
typestringworker_archived_error: the worker was archived.worker_busy_error: the worker was busy with other work.nonexistent_local_time_error: the local time did not exist that day.more_than_one_hour_late_error: Little Worker was down for more than an hour past the scheduled time.routine_paused_error: the routine was paused.superseded_edit_error: an edit replaced the scheduled time.unknown_error: something else.One of
worker_archived_error,worker_busy_error,nonexistent_local_time_error,more_than_one_hour_late_error,routine_paused_error,superseded_edit_error,unknown_errormessagestringA sentence a person can read.
created_attimeWhen the run was recorded.
Errors
| Status | Type | When |
|---|---|---|
| 400 | invalid_request_error | routine_run_id is not a routine run id. error_code is validation_error. |
| 401 | authentication_error | The key is missing, removed, or not a key. Send it in x-api-key. |
| 404 | not_found_error | No routine run in the key's workspace has this id (Routine run not found.). |
| 429 | rate_limit_error | The workspace made too many requests this minute. Wait the seconds in Retry-After, then try again. |
curl https://core.thelittleworker.com/api/v1/routine_runs/routine_run_7a1c3e5b9d2f4e6a8c0b1d3f5e7a9c2b \
-H "x-api-key: $LITTLE_WORKER_API_KEY" \
-H "Little-Worker-Version: 2026-10-19"const response = await fetch("https://core.thelittleworker.com/api/v1/routine_runs/routine_run_7a1c3e5b9d2f4e6a8c0b1d3f5e7a9c2b", {
headers: {
"x-api-key": process.env.LITTLE_WORKER_API_KEY,
"Little-Worker-Version": "2026-10-19",
},
})
const run = await response.json()import os
import requests
response = requests.get(
"https://core.thelittleworker.com/api/v1/routine_runs/routine_run_7a1c3e5b9d2f4e6a8c0b1d3f5e7a9c2b",
headers={
"x-api-key": os.environ["LITTLE_WORKER_API_KEY"],
"Little-Worker-Version": "2026-10-19",
},
)
run = response.json(){
"type": "routine_run",
"id": "routine_run_7a1c3e5b9d2f4e6a8c0b1d3f5e7a9c2b",
"routine_id": "routine_5d3b8a1e7c2f4a9b8e0d6c4a2f1b3e5d",
"worker": {
"type": "worker",
"id": "worker_8c1f4e2a9b7d4f0e8a6c3b5d1e9f7a20"
},
"trigger_context": {
"type": "schedule",
"scheduled_at": "2026-10-23T13:00:00.000Z"
},
"task_id": "task_01JB2K9X7F4N6P3Q8R5S1T0V2W",
"error": null,
"created_at": "2026-10-23T13:03:12.408Z"
}