# 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](/api/events#send-events) has the whole event, and [Approvals](/concepts/approvals) explains which actions ask and who may answer.

> [!NOTE]
> A key answers only if it was made with **Can allow actions**. A worker's first use of a saved password, an email it sends, a press on a risky control in its browser, a public post, a payment and every card are always the person's, in the app. A key is refused with `403` for these, whatever it was made with.

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`.

- `type` (string): Always `approval`.

- `id` (approval id): The approval's id: `approval_` and 32 hex digits.

- `task_id` (task id, or null): The task the action belongs to. `null` when it belongs to none.

- `worker_id` (worker id, or null): The worker that asked. `null` when none did.

- `tool_request_id` (tool request id): The worker's request to use the tool: `tool_request_` and 32 hex digits.

- `tool_use_id` (event id): The `worker.tool_use` or `worker.mcp_tool_use` event that asks, in the form events use: `event_` and the same 32 hex digits as `tool_request_id`. A `user.approval` answers the approval by this id, and it never changes, even when the approval is asked again.

- `tool_name` (string): The tool's name. For an app, the app's own name for the tool. For an action on a Mac, one of `local.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.run` or `local.preview.open`. Reading one by id can also show `password.use`, `email.send` and `remote.browser.activate`.

- `input` (JSON): What 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's `name`.

- `status` (string): Where the approval stands. `waiting`: nobody has answered it, it has not run, and `expires_at` has 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 before `expires_at`, or the server took it back. `used`: it was allowed and the action ran. One of `waiting`, `allowed`, `denied`, `expired`, `used`.

- `created_at` (time): When the worker asked.

- `expires_at` (time): When 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_at` (time, or null): When it was allowed or denied. `null` before that.

```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": "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

- `limit` (integer): How many to answer, 1 to 100. The default is 20.
- `page` (string): The `next_page` of the answer before, to read the next page.
- `order` (string): `desc`, newest first (the default), or `asc`, oldest first. One of `desc`, `asc`.
- `created_at[gt]` (time): Only those asked after this time. `created_at[gte]`, `created_at[lt]` and `created_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).

### Example

```bash
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"
```

```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
}
```

### Errors

- 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.

## 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_id` (approval id, required): The approval, such as `approval_b3a1c5e7d9f24a6b8c0d1e2f3a4b5c6d`.

### Returns

The approval.

### Example

```bash
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"
```

```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"
}
```

### Errors

- 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.
