API reference
Approvals
List the approvals that wait for an answer and read one, then answer it with a key made to allow actions.
An approval is a worker asking before it takes an action that asks first: an app action your settings ask about, or an action on a Mac. The task stops with requires_action and waits until someone answers.
These two routes read approvals: the list of those that wait for an answer, and any one by its id. They do not answer. To answer, send a user.approval event to the approval's task with the approval's tool_use_id and a result, allow or deny. Send events has the whole event, and Approvals explains which actions ask and who may answer.
A task that waits on an approval is idle with a stop_reason of requires_action, and state.needs is allow. The event_ids of the stop are the tool_use_id of each approval it waits on. An approval answers once. A key cannot choose to remember an answer, so it can never loosen a setting.
A built-in tool that the worker's own tools settings ask about waits in the same way, and its worker.tool_use event is in event_ids, but it has no approval object on these routes.
The approval object
An approval is one action a worker asked to take, and what became of the question. Times are RFC 3339 in UTC, to the millisecond. A step that is asked again gets a new approval, and the same tool_use_id.
Fields
typestringAlwaysapproval.idapproval idThe approval's id:approval_and 32 hex digits.task_idtask id or nullThe task the action belongs to.nullwhen it belongs to none.worker_idworker id or nullThe worker that asked.nullwhen none did.tool_request_idtool request idThe worker's request to use the tool:tool_request_and 32 hex digits.tool_use_idevent idTheworker.tool_useorworker.mcp_tool_useevent that asks, in the form events use:event_and the same 32 hex digits astool_request_id. Auser.approvalanswers the approval by this id, and it never changes, even when the approval is asked again.tool_namestringThe tool's name. For an app, the app's own name for the tool. For an action on a Mac, one oflocal.command.execute,local.command.input,local.file.read,local.file.write,local.file.list,local.screen.screenshot,local.screen.click,local.screen.keyPress,local.codex.runorlocal.preview.open. Reading one by id can also showpassword.use,email.sendandremote.browser.activate.inputJSONWhat the worker sent the tool. An app's input is shown as sent, with any saved password masked. A Mac action's command, arguments and environment are never shown, so its input is{}. A password's first use shows only the password'sname.statusstringWhere the approval stands.waiting: nobody has answered it, it has not run, andexpires_athas not passed.allowed: a person or a key allowed it, and the action has not run yet.denied: a person or a key did not allow it, and the action does not run.expired: nobody answered beforeexpires_at, or the server took it back.used: it was allowed and the action ran.One of
waiting,allowed,denied,expired,usedcreated_attimeWhen the worker asked.expires_attimeWhen the approval stops waiting. An app action waits 24 hours, and an action on a Mac waits 5 minutes. A Mac action that nobody answers by then does not run. An app action answered late is asked again under a new approval, and still runs.decided_attime or nullWhen it was allowed or denied.nullbefore that.
{
"type": "approval",
"id": "approval_b3a1c5e7d9f24a6b8c0d1e2f3a4b5c6d",
"task_id": "task_01JA8Z6Q4M3T9V2K7X5R1B0C8D",
"worker_id": "worker_8c1f4e2a9b7d4f0e8a6c3b5d1e9f7a20",
"tool_request_id": "tool_request_9e8d7c6b5a4f4e3dac1b0a9f8e7d6c5b",
"tool_use_id": "event_9e8d7c6b5a4f4e3dac1b0a9f8e7d6c5b",
"tool_name": "schedule_campaign",
"input": {
"list": "Customers",
"send_at": "2026-10-26T07:00:00Z"
},
"status": "waiting",
"created_at": "2026-10-25T16:05:02.114Z",
"expires_at": "2026-10-26T16:05:02.114Z",
"decided_at": null
}List approvals
GET/approvals
List the approvals that wait for an answer your key's person may give, across all their tasks, newest first. Only approvals with the status waiting are listed. One that was allowed, denied, used, expired or taken back leaves the list, so read it by id to see its later status.
The list leaves out the kinds only the person answers, on their cards in the app: the first use of a saved password (tool_name password.use), an email the worker sends (email.send), a press in the worker's hosted browser (remote.browser.activate), and a refund. It also leaves out a built-in tool step that the worker's tools settings ask about.
On a shared workspace, the list shows only the approvals your key's person may answer.
Query parameters
limitintegerHow 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 asked after this time.created_at[gte],created_at[lt]andcreated_at[lte]work the same way.
Returns
A list: data holds the approvals, 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 query value is not valid, such as a limit outside 1 to 100 or a time that is not RFC 3339 (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/approvals?limit=20" \
-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/approvals?limit=20", {
headers: {
"x-api-key": process.env.LITTLE_WORKER_API_KEY,
"Little-Worker-Version": "2026-10-19",
},
})
const approvals = await response.json()import os
import requests
response = requests.get(
"https://core.thelittleworker.com/api/v1/approvals",
headers={
"x-api-key": os.environ["LITTLE_WORKER_API_KEY"],
"Little-Worker-Version": "2026-10-19",
},
params={
"limit": "20",
},
)
approvals = response.json(){
"data": [
{
"type": "approval",
"id": "approval_b3a1c5e7d9f24a6b8c0d1e2f3a4b5c6d",
"task_id": "task_01JA8Z6Q4M3T9V2K7X5R1B0C8D",
"worker_id": "worker_8c1f4e2a9b7d4f0e8a6c3b5d1e9f7a20",
"tool_request_id": "tool_request_9e8d7c6b5a4f4e3dac1b0a9f8e7d6c5b",
"tool_use_id": "event_9e8d7c6b5a4f4e3dac1b0a9f8e7d6c5b",
"tool_name": "schedule_campaign",
"input": {
"list": "Customers",
"send_at": "2026-10-26T07:00:00Z"
},
"status": "waiting",
"created_at": "2026-10-25T16:05:02.114Z",
"expires_at": "2026-10-26T16:05:02.114Z",
"decided_at": null
}
],
"next_page": null
}Read an approval
GET/approvals/{approval_id}
Read one approval, in any status. Read it after you answered to see whether the action ran: allowed until it does, then used.
Unlike the list, this shows a password's first use (its input holds only the password's name), an email send, and a hosted browser press. The person still answers those in the app. A refund is answered only on its card in the app, and reading it is 404.
Path parameters
approval_idapproval idRequiredThe approval, such asapproval_b3a1c5e7d9f24a6b8c0d1e2f3a4b5c6d.
Returns
The approval.
Errors
| Status | Type | When |
|---|---|---|
| 400 | invalid_request_error | approval_id is not an approval id (error_code validation_error). |
| 401 | authentication_error | The key is missing, removed, or not a key. Send it in x-api-key. |
| 403 | permission_error | On a shared workspace, the approval belongs to a task that another person started (error_code not_task_starter), or to a Mac that another person paired (not_allowlisted). |
| 404 | not_found_error | No approval in the key's workspace has this id, or it is a refund or a built-in tool step, which these routes do not show (Approval 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/approvals/approval_b3a1c5e7d9f24a6b8c0d1e2f3a4b5c6d \
-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/approvals/approval_b3a1c5e7d9f24a6b8c0d1e2f3a4b5c6d", {
headers: {
"x-api-key": process.env.LITTLE_WORKER_API_KEY,
"Little-Worker-Version": "2026-10-19",
},
})
const approval = await response.json()import os
import requests
response = requests.get(
"https://core.thelittleworker.com/api/v1/approvals/approval_b3a1c5e7d9f24a6b8c0d1e2f3a4b5c6d",
headers={
"x-api-key": os.environ["LITTLE_WORKER_API_KEY"],
"Little-Worker-Version": "2026-10-19",
},
)
approval = response.json(){
"type": "approval",
"id": "approval_b3a1c5e7d9f24a6b8c0d1e2f3a4b5c6d",
"task_id": "task_01JA8Z6Q4M3T9V2K7X5R1B0C8D",
"worker_id": "worker_8c1f4e2a9b7d4f0e8a6c3b5d1e9f7a20",
"tool_request_id": "tool_request_9e8d7c6b5a4f4e3dac1b0a9f8e7d6c5b",
"tool_use_id": "event_9e8d7c6b5a4f4e3dac1b0a9f8e7d6c5b",
"tool_name": "schedule_campaign",
"input": {
"list": "Customers",
"send_at": "2026-10-26T07:00:00Z"
},
"status": "used",
"created_at": "2026-10-25T16:05:02.114Z",
"expires_at": "2026-10-26T16:05:02.114Z",
"decided_at": "2026-10-25T16:06:11.540Z"
}