---
title: "openemail.tasks"
description: "Every method in this namespace: its signature, its parameters, what it returns and an example."
url: "https://openemail.uk/docs/python/reference/tasks"
area: "Python"
category: "Reference"
---

# openemail.tasks

Every method in this namespace: its signature, its parameters, what it returns and an example.

## Methods

Instructions the assistant runs by itself, on a schedule, when mail arrives or when asked: create a task, change, pause, resume and delete it, run it now, hand the assistant a one-off job, follow each run step by step, and approve or deny what a run asks before it acts.

### `tasks.list()`

List the tasks of the workspace, a page at a time

```python
def list(
    *,
    limit: int | None = None,
    cursor: str | None = None,
    status: TaskStatus | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> Page[TaskResource]
```

Returns one page of tasks, the most recently changed first. `list_all` collects every page and `iterate` walks them lazily. A task is an instruction in plain words that the assistant runs by itself: on a schedule, when mail arrives, or only when asked. Each one says what starts it in `trigger`, how much it may do without asking in `autonomy`, when it runs next in `nextRunAt`, how its last run ended in `lastRunStatus` and how many approval requests wait for an answer in `pendingApprovals`.

Give `status=` to read only the tasks that are `active` or only the ones that are `paused`. `pausedReason` says why a paused task stopped: `user` when somebody paused it, `failures` after 5 failed runs in a row, and `plan_limit` when the plan no longer covers it.

Scopes: `tasks:read`, `threads:read`, `emails:read`.

**Parameters**

- `limit` (`int`): Page size, from 1 to 100. The server defaults to 25.
- `cursor` (`str`): The `nextCursor` of the previous page, passed back as it came. Leave it out for the first page.
- `status` (`TaskStatus`): Only tasks in this state: `active` or `paused`. Left out, both are listed.
- `api_key` (`str`): Overrides the client API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `OpenEmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`Page[TaskResource]`, a dict with `items`, `hasMore` and `nextCursor`. Each item has `id`, `name`, `instruction`, `status`, `pausedReason`, `trigger`, `autonomy`, `toolkits`, `model`, `notify`, `maxRunsPerDay`, `maxSteps`, `nextRunAt`, `heldUntil`, `lastRunAt`, `lastRunStatus`, `runCount`, `pendingApprovals`, `chatId`, `createdAt` and `updatedAt`.

**Example**

```python
from openemail import openemail

page = openemail.tasks.list(status='active')

for task in page['items']:
    print(task['name'], task['trigger']['kind'], task['nextRunAt'], task['pendingApprovals'])
```

**Notes**

- Reading tasks needs `threads:read` and `emails:read` beside `tasks:read`.
- A task works across every address of the workspace. A key or an app limited to particular addresses or domains is refused with 422 `capability_unsupported`, and an app acting for a member whose role does not reach every address with 403 `owner_only`.
- The order follows `updatedAt`, which moves when a task changes or starts a run. A workspace holds at most 100 tasks, so `limit=100` reads them all in one request and no task can move between pages.

Also available in: API [`GET /tasks`](https://openemail.uk/docs/api/reference/tasks#get-tasks); TypeScript [`tasks.list()`](https://openemail.uk/docs/sdk/reference/tasks#list); Ruby [`tasks.list`](https://openemail.uk/docs/ruby/reference/tasks#list); PHP [`tasks->list`](https://openemail.uk/docs/php/reference/tasks#list); Go [`Tasks.List`](https://openemail.uk/docs/go/reference/tasks#list); Java [`tasks().list`](https://openemail.uk/docs/java/reference/tasks#list); C# [`Tasks.ListAsync`](https://openemail.uk/docs/csharp/reference/tasks#list); CLI [`openemail tasks list`](https://openemail.uk/docs/cli/reference/tasks#tasks-list).

### `tasks.list_all()`

Collect every task into one list

```python
def list_all(
    *,
    limit: int | None = None,
    cursor: str | None = None,
    status: TaskStatus | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> builtins.list[TaskResource]
```

Walks every page of `list` and returns all tasks, the most recently changed first. One request per page. Give `status=` to collect only the active tasks or only the paused ones.

Scopes: `tasks:read`, `threads:read`, `emails:read`.

**Parameters**

- `limit` (`int`): Page size for each request, from 1 to 100. The server defaults to 25.
- `cursor` (`str`): Starts the walk after this cursor instead of the first page.
- `status` (`TaskStatus`): Only tasks in this state: `active` or `paused`. Left out, both are collected.
- `api_key` (`str`): Overrides the client API key for every page of this walk.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `OpenEmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`list[TaskResource]` holding every task.

**Example**

```python
from openemail import openemail

tasks = openemail.tasks.list_all(limit=100)

waiting = [task for task in tasks if task['pendingApprovals'] > 0]

for task in waiting:
    print(task['name'], task['pendingApprovals'])
```

**Notes**

- If any page fails, the call raises and the tasks already fetched are discarded.
- A key or an app limited to particular addresses or domains is refused with 422 `capability_unsupported`, and an app acting for a member whose role does not reach every address with 403 `owner_only`.

Also available in: API [`GET /tasks`](https://openemail.uk/docs/api/reference/tasks#get-tasks); TypeScript [`tasks.listAll()`](https://openemail.uk/docs/sdk/reference/tasks#listAll); Ruby [`tasks.list_all`](https://openemail.uk/docs/ruby/reference/tasks#listAll); PHP [`tasks->listAll`](https://openemail.uk/docs/php/reference/tasks#listAll); Go [`Tasks.ListAll`](https://openemail.uk/docs/go/reference/tasks#listAll); Java [`tasks().listAll`](https://openemail.uk/docs/java/reference/tasks#listAll); C# [`Tasks.ListAllAsync`](https://openemail.uk/docs/csharp/reference/tasks#listAll).

### `tasks.iterate()`

Stream the tasks one at a time

```python
def iterate(
    *,
    limit: int | None = None,
    cursor: str | None = None,
    status: TaskStatus | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> Iterator[TaskResource]
```

Returns a generator that yields tasks individually, the most recently changed first, and requests the next page only once the current one is drained. Nothing is fetched until you consume it, and breaking out of the loop stops the requests.

Scopes: `tasks:read`, `threads:read`, `emails:read`.

**Parameters**

- `limit` (`int`): Page size for each request, from 1 to 100. The server defaults to 25.
- `cursor` (`str`): Starts the walk after this cursor instead of the first page.
- `status` (`TaskStatus`): Only tasks in this state: `active` or `paused`. Left out, both are walked.
- `api_key` (`str`): Overrides the client API key for every page of this walk.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `OpenEmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`Iterator[TaskResource]`, a generator yielding one task per step.

**Example**

```python
from openemail import openemail

for task in openemail.tasks.iterate(status='paused'):
    print(task['name'], 'was paused by', task['pausedReason'])
```

**Notes**

- The generator is lazy, so an abandoned loop costs only the pages you consumed.
- A key or an app limited to particular addresses or domains is refused with 422 `capability_unsupported`, and an app acting for a member whose role does not reach every address with 403 `owner_only`.

Also available in: API [`GET /tasks`](https://openemail.uk/docs/api/reference/tasks#get-tasks); TypeScript [`tasks.iterate()`](https://openemail.uk/docs/sdk/reference/tasks#iterate); Ruby [`tasks.iterate`](https://openemail.uk/docs/ruby/reference/tasks#iterate); PHP [`tasks->iterate`](https://openemail.uk/docs/php/reference/tasks#iterate); Go [`Tasks.Iterate`](https://openemail.uk/docs/go/reference/tasks#iterate); Java [`tasks().iterate`](https://openemail.uk/docs/java/reference/tasks#iterate); C# [`Tasks.IterateAsync`](https://openemail.uk/docs/csharp/reference/tasks#iterate).

### `tasks.get()`

Read one task by id

```python
def get(
    id: str,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> TaskResource
```

Looks up a single task and returns it in the same shape as a row of `list`: its instruction, its trigger with the schedule or the mail filter, how much it may do alone, its toolkits and limits, when it runs next and how the last run ended.

`nextRunAt` is set for an active `schedule` task and `None` for the other kinds and while paused. `heldUntil` is set when the workspace ran out of AI actions for the day: the task starts nothing until then. `pendingApprovals` counts the approval requests of its runs that still wait for an answer.

Scopes: `tasks:read`, `threads:read`, `emails:read`.

**Parameters**

- `id` (`str`, required): Task id such as `tsk_5c1e9a7b3d2f48e6a0b4c7d1`, as `list` returns it.
- `api_key` (`str`): Overrides the client API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `OpenEmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`TaskResource` with `id`, `name`, `instruction`, `status`, `pausedReason`, `trigger`, `autonomy`, `toolkits`, `model`, `notify`, `maxRunsPerDay`, `maxSteps`, `nextRunAt`, `heldUntil`, `lastRunAt`, `lastRunStatus`, `runCount`, `pendingApprovals`, `chatId`, `createdAt` and `updatedAt`.

**Example**

```python
from openemail import openemail

task = openemail.tasks.get('tsk_5c1e9a7b3d2f48e6a0b4c7d1')

print(task['name'], task['status'], task['trigger']['kind'], task['nextRunAt'])
```

**Notes**

- A missing task raises `OpenEmailApiError` with `is_not_found` set to `True` and code `task_not_found`.
- A key or an app limited to particular addresses or domains is refused with 422 `capability_unsupported`, and an app acting for a member whose role does not reach every address with 403 `owner_only`.

Also available in: API [`GET /tasks/{id}`](https://openemail.uk/docs/api/reference/tasks#get-tasks-id); TypeScript [`tasks.get()`](https://openemail.uk/docs/sdk/reference/tasks#get); Ruby [`tasks.get`](https://openemail.uk/docs/ruby/reference/tasks#get); PHP [`tasks->get`](https://openemail.uk/docs/php/reference/tasks#get); Go [`Tasks.Get`](https://openemail.uk/docs/go/reference/tasks#get); Java [`tasks().get`](https://openemail.uk/docs/java/reference/tasks#get); C# [`Tasks.GetAsync`](https://openemail.uk/docs/csharp/reference/tasks#get); CLI [`openemail tasks get`](https://openemail.uk/docs/cli/reference/tasks#tasks-get).

### `tasks.create()`

Save a task for the assistant to run by itself

```python
def create(
    body: TaskCreate,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> TaskResource
```

Saves an instruction for the assistant with what starts it and how much it may do alone, and returns the task as it is stored. `instruction` says what to do in plain words, as you would tell a person. It is the only instruction a run follows: text inside emails is read, never obeyed.

`trigger.kind` says what starts a run. A `schedule` task needs `trigger.schedule` and gets its first run time at once, in `nextRunAt`. A `mail` task starts with the next message that reaches an inbox and matches `trigger.mail`, or with every message when no filter is sent. Mail from an address of the workspace itself never starts one. A `manual` task waits for `run`.

`autonomy` limits what a run does without asking. `ask` sends every change to a person to approve. `organize`, the default, lets the run label, archive, bin, snooze, draft, take notes and keep contacts by itself, and email the addresses of the workspace. `reply` also lets it answer an email without asking, as long as it adds no recipients. Anything else waits as an approval request, which `list_approvals` reads. A run never reaches billing, members, roles, domains, rules, keys or webhooks.

A task acts with what its caller holds, so the call needs more than `tasks:write`: `threads:read`, `threads:write`, `emails:read`, `emails:send` and `labels:write`, and the read and write scopes of every toolkit the task names.

Scopes: `tasks:write`, `threads:read`, `threads:write`, `emails:read`, `emails:send`, `labels:write`.

**Parameters**

- `body['name']` (`str`, required): What the task is called in the app, trimmed, 1 to 120 characters.
- `body['instruction']` (`str`, required): What the assistant should do, in plain words, trimmed, 1 to 8,000 characters.
- `body['trigger']['kind']` (`TaskTriggerKind`, required): `schedule` runs on a clock, `mail` runs when a message arrives, and `manual` runs only when started with `run`.
- `body['trigger']['schedule']['cadence']` (`TaskCadence`, required): Needed for a `schedule` task and ignored for the other kinds. `hourly` runs every `everyHours` hours at `minute`, `daily` runs at `hour`:`minute` on each of `weekdays`, and `monthly` runs at `hour`:`minute` on `monthDay`.
- `body['trigger']['schedule']['everyHours']` (`int`): For `hourly`: how many hours lie between runs, from 1 to 12, counted from midnight in the time zone. Defaults to 1.
- `body['trigger']['schedule']['hour']` (`int`): The hour of the day, 0 to 23, in the time zone of the schedule. Defaults to 8.
- `body['trigger']['schedule']['minute']` (`int`): The minute of the hour, 0 to 59. Defaults to 0.
- `body['trigger']['schedule']['weekdays']` (`list[int]`): For `daily`: the days it runs, 0 for Sunday to 6 for Saturday. Left out, it runs every day.
- `body['trigger']['schedule']['monthDay']` (`int`): For `monthly`: the day of the month, 1 to 28. Defaults to 1.
- `body['trigger']['schedule']['timezone']` (`str`): An IANA time zone such as `Europe/Berlin`. The schedule follows its clock changes. Defaults to `UTC`.
- `body['trigger']['mail']['addresses']` (`list[str]`): For a `mail` task: start only for mail delivered to one of these addresses of the workspace, at most 25. Left out or empty, every address counts.
- `body['trigger']['mail']['fromContains']` (`str | None`): For a `mail` task: start only when the sender address contains this text, whatever the case, up to 200 characters.
- `body['trigger']['mail']['subjectContains']` (`str | None`): For a `mail` task: start only when the subject contains this text, whatever the case, up to 200 characters.
- `body['autonomy']` (`TaskAutonomy`): How much a run may do without asking: `ask`, `organize` or `reply`. Defaults to `organize`.
- `body['toolkits']` (`list[TaskToolkit]`): Extra groups of tools a run may use beyond reading and organising mail: `contacts`, `audiences`, `templates`, `tracking`, `translate`, `files`, `notes`, `drafts`, `sent`, `insights`, `subscriptions`, `calendar`, `knowledge` and `web`. Left out, `drafts`, `notes` and `knowledge`. An empty list gives it none.
- `body['model']` (`TaskModel`): `standard` is the everyday model. `advanced` reasons better over long jobs and costs ten AI actions where the standard one costs one. Defaults to `standard`.
- `body['notify']` (`TaskNotifyMode`): `attention` notifies the workspace owner when a run waits for approval or fails, `always` also when it finishes, and `never` stays silent. Defaults to `attention`.
- `body['maxRunsPerDay']` (`int`): The most runs the task may start in any 24 hours, however often its trigger fires, from 1 to 500. Defaults to 50.
- `body['maxSteps']` (`int`): The most model steps one run may take before it has to write its report, from 1 to 60. Defaults to 20.
- `body['paused']` (`bool`): `True` creates the task paused, so it does not run until `resume`. A paused task does not count against the active tasks of the plan.
- `api_key` (`str`): Overrides the client API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `OpenEmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`TaskResource` with the new `id`, `status` `active`, or `paused` when you asked for that, the trigger as it is stored with every default filled in, and `nextRunAt` set for a schedule.

**Example**

```python
from openemail import openemail

task = openemail.tasks.create(
    {
        'name': 'Label invoices',
        'instruction': 'When an invoice arrives, label it Invoices and add a note with the amount and the due date.',
        'trigger': {'kind': 'mail', 'mail': {'subjectContains': 'invoice'}},
        'autonomy': 'organize',
    }
)

print(task['id'], task['status'], task['trigger']['kind'])
```

**Notes**

- Each toolkit brings its own scopes: `contacts` needs `contacts:read` and `contacts:write`, `audiences` needs `audiences:read`, `audiences:write` and `contacts:read`, and `templates`, `files`, `drafts`, `calendar` and `knowledge` need their read and write scope. The default toolkits therefore need `drafts:read`, `drafts:write`, `knowledge:read` and `knowledge:write`. A missing scope is a 403 `insufficient_scope`.
- A workspace runs 1 active task on Free, 5 on Starter and 25 on Business, with no cap on Enterprise, and holds at most 100 tasks, paused ones included. One more active task than the plan covers is a 403 `task_active_limit_reached`, and a task past the 100 a 403 `task_limit_reached`.
- A `schedule` trigger without its schedule, a time zone that is not known and a toolkit a task may not use are 422 `invalid_task`. A value of the wrong type or length is 422 `invalid_parameter` with `param` naming it, and a key the body does not take is 422 `unknown_parameter`.
- A task works across every address of the workspace. A key or an app limited to particular addresses or domains is refused with 422 `capability_unsupported`, and an app acting for a member whose role does not reach every address with 403 `owner_only`.
- Not retried automatically, and nothing deduplicates by name, so a retry after a lost response can leave two tasks. List them and delete the spare.

Also available in: API [`POST /tasks`](https://openemail.uk/docs/api/reference/tasks#post-tasks); TypeScript [`tasks.create()`](https://openemail.uk/docs/sdk/reference/tasks#create); Ruby [`tasks.create`](https://openemail.uk/docs/ruby/reference/tasks#create); PHP [`tasks->create`](https://openemail.uk/docs/php/reference/tasks#create); Go [`Tasks.Create`](https://openemail.uk/docs/go/reference/tasks#create); Java [`tasks().create`](https://openemail.uk/docs/java/reference/tasks#create); C# [`Tasks.CreateAsync`](https://openemail.uk/docs/csharp/reference/tasks#create); CLI [`openemail tasks create`](https://openemail.uk/docs/cli/reference/tasks#tasks-create).

### `tasks.update()`

Change what a task does, what starts it or how much it may do alone

```python
def update(
    id: str,
    patch: TaskPatch,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> TaskResource
```

Changes a task and returns it as it is stored. Send only what changes: a field left out stays as it is, and a patch with nothing in it is a 422.

A new `trigger` replaces the old one whole. Send its `kind` and, for a schedule, the whole `schedule` again, because what the new trigger leaves out goes back to its default, not to what the task had. A schedule change moves `nextRunAt` of an active task.

Runs already started keep the settings they began with, and later runs follow the new ones. It neither pauses nor resumes a task: `pause` and `resume` do that. Like `create`, the call needs the scopes the task acts with, counted from the toolkits it has after the change.

Scopes: `tasks:write`, `threads:read`, `threads:write`, `emails:read`, `emails:send`, `labels:write`.

**Parameters**

- `id` (`str`, required): Task id such as `tsk_5c1e9a7b3d2f48e6a0b4c7d1`, as `list` returns it.
- `patch['name']` (`str`): New name, trimmed, 1 to 120 characters. Left out, the name stays.
- `patch['instruction']` (`str`): New instruction in plain words, trimmed, 1 to 8,000 characters. Left out, the instruction stays.
- `patch['trigger']['kind']` (`TaskTriggerKind`, required): Needed whenever any part of `trigger` is sent: `schedule`, `mail` or `manual`.
- `patch['trigger']['schedule']['cadence']` (`TaskCadence`, required): Needed for a `schedule` trigger: `hourly`, `daily` or `monthly`.
- `patch['trigger']['schedule']['everyHours']` (`int`): For `hourly`: how many hours lie between runs, from 1 to 12, counted from midnight in the time zone. Defaults to 1.
- `patch['trigger']['schedule']['hour']` (`int`): The hour of the day, 0 to 23, in the time zone of the schedule. Defaults to 8.
- `patch['trigger']['schedule']['minute']` (`int`): The minute of the hour, 0 to 59. Defaults to 0.
- `patch['trigger']['schedule']['weekdays']` (`list[int]`): For `daily`: the days it runs, 0 for Sunday to 6 for Saturday. Left out, it runs every day.
- `patch['trigger']['schedule']['monthDay']` (`int`): For `monthly`: the day of the month, 1 to 28. Defaults to 1.
- `patch['trigger']['schedule']['timezone']` (`str`): An IANA time zone such as `Europe/Berlin`. Defaults to `UTC`.
- `patch['trigger']['mail']['addresses']` (`list[str]`): For a `mail` trigger: start only for mail delivered to one of these addresses of the workspace, at most 25. Left out or empty, every address counts.
- `patch['trigger']['mail']['fromContains']` (`str | None`): For a `mail` trigger: start only when the sender address contains this text, whatever the case, up to 200 characters.
- `patch['trigger']['mail']['subjectContains']` (`str | None`): For a `mail` trigger: start only when the subject contains this text, whatever the case, up to 200 characters.
- `patch['autonomy']` (`TaskAutonomy`): How much a run may do without asking: `ask`, `organize` or `reply`. Left out, it stays.
- `patch['toolkits']` (`list[TaskToolkit]`): The toolkits a run may use from now on, replacing the list the task had. An empty list gives it none. Left out, they stay.
- `patch['model']` (`TaskModel`): `standard` or `advanced`, which costs ten AI actions where the standard one costs one. Left out, it stays.
- `patch['notify']` (`TaskNotifyMode`): When the workspace owner is notified: `attention`, `always` or `never`. Left out, it stays.
- `patch['maxRunsPerDay']` (`int`): The most runs the task may start in any 24 hours, from 1 to 500. Left out, it stays.
- `patch['maxSteps']` (`int`): The most model steps one run may take, from 1 to 60. Left out, it stays.
- `api_key` (`str`): Overrides the client API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `OpenEmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`TaskResource` with the unchanged `id` and `status`, and everything else as it is now.

**Example**

```python
from openemail import openemail

task = openemail.tasks.update(
    'tsk_5c1e9a7b3d2f48e6a0b4c7d1',
    {
        'trigger': {
            'kind': 'schedule',
            'schedule': {
                'cadence': 'daily',
                'hour': 8,
                'weekdays': [1, 2, 3, 4, 5],
                'timezone': 'Europe/Berlin',
            },
        }
    },
)

print(task['trigger']['schedule'], task['nextRunAt'])
```

**Notes**

- A missing task is a 404 `task_not_found`. An empty patch or a value of the wrong type or length is 422 `invalid_parameter`, a key the patch does not take 422 `unknown_parameter`, and a trigger without its schedule, a time zone that is not known or a toolkit a task may not use 422 `invalid_task`.
- The key or app needs the read and write scopes of every toolkit the task has after the change, or the call is a 403 `insufficient_scope`.
- A key or an app limited to particular addresses or domains is refused with 422 `capability_unsupported`, and an app acting for a member whose role does not reach every address with 403 `owner_only`.
- The SDK retries this call after a network failure, since the same patch sent twice leaves the same task.

Also available in: API [`PATCH /tasks/{id}`](https://openemail.uk/docs/api/reference/tasks#patch-tasks-id); TypeScript [`tasks.update()`](https://openemail.uk/docs/sdk/reference/tasks#update); Ruby [`tasks.update`](https://openemail.uk/docs/ruby/reference/tasks#update); PHP [`tasks->update`](https://openemail.uk/docs/php/reference/tasks#update); Go [`Tasks.Update`](https://openemail.uk/docs/go/reference/tasks#update); Java [`tasks().update`](https://openemail.uk/docs/java/reference/tasks#update); C# [`Tasks.UpdateAsync`](https://openemail.uk/docs/csharp/reference/tasks#update); CLI [`openemail tasks update`](https://openemail.uk/docs/cli/reference/tasks#tasks-update).

### `tasks.delete()`

Delete a task with every run it made

```python
def delete(
    id: str,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> DeletedTaskResource
```

Deletes a task for good, with every run it made and their records. A run in flight is stopped and its open approval requests are closed. What the runs already did in the mailbox stays done.

There is no undo. To stop a task and keep its history, use `pause`.

Scopes: `tasks:write`.

**Parameters**

- `id` (`str`, required): Task id such as `tsk_5c1e9a7b3d2f48e6a0b4c7d1`, as `list` returns it.
- `api_key` (`str`): Overrides the client API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `OpenEmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`DeletedTaskResource`, a dict with `object` set to `task`, the `id` and `deleted` set to `True`.

**Example**

```python
from openemail import openemail

removed = openemail.tasks.delete('tsk_5c1e9a7b3d2f48e6a0b4c7d1')

print(removed['id'], removed['deleted'])
```

**Notes**

- The SDK does not retry a delete. A 404 `task_not_found` on your own second attempt after a lost response means the first one worked.
- A key or an app limited to particular addresses or domains is refused with 422 `capability_unsupported`, and an app acting for a member whose role does not reach every address with 403 `owner_only`.

Also available in: API [`DELETE /tasks/{id}`](https://openemail.uk/docs/api/reference/tasks#delete-tasks-id); TypeScript [`tasks.delete()`](https://openemail.uk/docs/sdk/reference/tasks#delete); Ruby [`tasks.delete`](https://openemail.uk/docs/ruby/reference/tasks#delete); PHP [`tasks->delete`](https://openemail.uk/docs/php/reference/tasks#delete); Go [`Tasks.Delete`](https://openemail.uk/docs/go/reference/tasks#delete); Java [`tasks().delete`](https://openemail.uk/docs/java/reference/tasks#delete); C# [`Tasks.DeleteAsync`](https://openemail.uk/docs/csharp/reference/tasks#delete); CLI [`openemail tasks delete`](https://openemail.uk/docs/cli/reference/tasks#tasks-delete).

### `tasks.pause()`

Stop a task from starting new runs

```python
def pause(
    id: str,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> TaskResource
```

Stops a task from starting new runs: its schedule no longer fires and arriving mail no longer starts it. A run already going carries on, and its approval requests can still be answered. `run` still starts a paused task by hand. Pausing a paused task leaves it paused. It takes no body.

Scopes: `tasks:write`.

**Parameters**

- `id` (`str`, required): Task id such as `tsk_5c1e9a7b3d2f48e6a0b4c7d1`, as `list` returns it.
- `api_key` (`str`): Overrides the client API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `OpenEmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`TaskResource` with `status` `paused`, `pausedReason` `user` and `nextRunAt` `None`.

**Example**

```python
from openemail import openemail

task = openemail.tasks.pause('tsk_5c1e9a7b3d2f48e6a0b4c7d1')

print(task['status'], task['pausedReason'])
```

**Notes**

- A missing task is a 404 `task_not_found`.
- To stop a run that is already going, use `cancel_run`.
- A key or an app limited to particular addresses or domains is refused with 422 `capability_unsupported`, and an app acting for a member whose role does not reach every address with 403 `owner_only`.
- Retried automatically on network failure and retryable statuses, since pausing twice leaves the same paused task.

Also available in: API [`POST /tasks/{id}/pause`](https://openemail.uk/docs/api/reference/tasks#post-tasks-id-pause); TypeScript [`tasks.pause()`](https://openemail.uk/docs/sdk/reference/tasks#pause); Ruby [`tasks.pause`](https://openemail.uk/docs/ruby/reference/tasks#pause); PHP [`tasks->pause`](https://openemail.uk/docs/php/reference/tasks#pause); Go [`Tasks.Pause`](https://openemail.uk/docs/go/reference/tasks#pause); Java [`tasks().pause`](https://openemail.uk/docs/java/reference/tasks#pause); C# [`Tasks.PauseAsync`](https://openemail.uk/docs/csharp/reference/tasks#pause); CLI [`openemail tasks pause`](https://openemail.uk/docs/cli/reference/tasks#tasks-pause).

### `tasks.resume()`

Turn a paused task back on

```python
def resume(
    id: str,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> TaskResource
```

Turns a paused task back on, whoever or whatever paused it: a person, 5 failed runs in a row or the plan. It clears the failure count and `heldUntil`, and gives a `schedule` task its next run time. A schedule picks up at its next time, and the runs it missed while paused are not made up. It takes no body.

A task acts with what its caller holds, so the call needs the same scopes as `create`: `threads:read`, `threads:write`, `emails:read`, `emails:send` and `labels:write` beside `tasks:write`, and the read and write scopes of every toolkit the task names.

Scopes: `tasks:write`, `threads:read`, `threads:write`, `emails:read`, `emails:send`, `labels:write`.

**Parameters**

- `id` (`str`, required): Task id such as `tsk_5c1e9a7b3d2f48e6a0b4c7d1`, as `list` returns it.
- `api_key` (`str`): Overrides the client API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `OpenEmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`TaskResource` with `status` `active`, `pausedReason` and `heldUntil` `None` and, for a schedule, `nextRunAt` set.

**Example**

```python
from openemail import openemail

task = openemail.tasks.resume('tsk_5c1e9a7b3d2f48e6a0b4c7d1')

print(task['status'], task['nextRunAt'])
```

**Notes**

- A plan that covers no more active tasks answers 403 `task_active_limit_reached`: 1 on Free, 5 on Starter, 25 on Business and no cap on Enterprise. A missing task is a 404 `task_not_found`.
- A key or an app without the scopes of a toolkit the task names is refused with 403 `insufficient_scope`.
- A key or an app limited to particular addresses or domains is refused with 422 `capability_unsupported`, and an app acting for a member whose role does not reach every address with 403 `owner_only`.
- Retried automatically on network failure and retryable statuses, since resuming twice leaves the same active task.

Also available in: API [`POST /tasks/{id}/resume`](https://openemail.uk/docs/api/reference/tasks#post-tasks-id-resume); TypeScript [`tasks.resume()`](https://openemail.uk/docs/sdk/reference/tasks#resume); Ruby [`tasks.resume`](https://openemail.uk/docs/ruby/reference/tasks#resume); PHP [`tasks->resume`](https://openemail.uk/docs/php/reference/tasks#resume); Go [`Tasks.Resume`](https://openemail.uk/docs/go/reference/tasks#resume); Java [`tasks().resume`](https://openemail.uk/docs/java/reference/tasks#resume); C# [`Tasks.ResumeAsync`](https://openemail.uk/docs/csharp/reference/tasks#resume); CLI [`openemail tasks resume`](https://openemail.uk/docs/cli/reference/tasks#tasks-resume).

### `tasks.run()`

Start a run of a task now

```python
def run(
    id: str,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> TaskRunDetailResource
```

Starts a run of a task at once, whatever its trigger and whether or not it is paused, and returns the run as it was queued. The run works in the background. Read it again with `get_run` to follow it until `status` is `done`, `failed` or `cancelled`. While it is `waiting`, an approval request is open, which `list_approvals` reads.

The run uses the instruction and the settings the task has now, and counts towards its `maxRunsPerDay`. It takes no body. A task acts with what its caller holds, so the call needs the same scopes as `create`, counted from the toolkits of the task.

Scopes: `tasks:write`, `threads:read`, `threads:write`, `emails:read`, `emails:send`, `labels:write`.

**Parameters**

- `id` (`str`, required): Task id such as `tsk_5c1e9a7b3d2f48e6a0b4c7d1`, as `list` returns it.
- `api_key` (`str`): Overrides the client API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `OpenEmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`TaskRunDetailResource` with a new `id`, `status` `queued`, `trigger` `manual` and the `taskId` and `taskName` of the task. `transcript` and `approvals` fill in as the run works.

**Example**

```python
from openemail import openemail

queued = openemail.tasks.run('tsk_5c1e9a7b3d2f48e6a0b4c7d1')

run = openemail.tasks.get_run(queued['id'])

print(run['status'], run['result'])
```

**Notes**

- A task that has started `maxRunsPerDay` runs in the last 24 hours answers 409 `task_run_limit_reached`. A missing task is a 404 `task_not_found`.
- A key or an app without the scopes of a toolkit the task names is refused with 403 `insufficient_scope`.
- A key or an app limited to particular addresses or domains is refused with 422 `capability_unsupported`, and an app acting for a member whose role does not reach every address with 403 `owner_only`.
- Not retried automatically: every call starts another run, so a retry after a lost response can start two. Read `list_runs` with `task_id=` before you try again.

Also available in: API [`POST /tasks/{id}/run`](https://openemail.uk/docs/api/reference/tasks#post-tasks-id-run); TypeScript [`tasks.run()`](https://openemail.uk/docs/sdk/reference/tasks#run); Ruby [`tasks.run`](https://openemail.uk/docs/ruby/reference/tasks#run); PHP [`tasks->run`](https://openemail.uk/docs/php/reference/tasks#run); Go [`Tasks.Run`](https://openemail.uk/docs/go/reference/tasks#run); Java [`tasks().run`](https://openemail.uk/docs/java/reference/tasks#run); C# [`Tasks.RunAsync`](https://openemail.uk/docs/csharp/reference/tasks#run); CLI [`openemail tasks run`](https://openemail.uk/docs/cli/reference/tasks#tasks-run).

### `tasks.list_runs()`

List task runs, a page at a time

```python
def list_runs(
    *,
    limit: int | None = None,
    cursor: str | None = None,
    task_id: str | None = None,
    status: TaskRunStatus | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> Page[TaskRunResource]
```

Returns one page of runs, newest first: every run in the workspace, or the runs of one task with `task_id=`. `list_all_runs` collects every page and `iterate_runs` walks them lazily. A run is one execution. It belongs to a task, named in `taskId` and `taskName`, or it is a one-off job started with `start_run`, where both are `None`.

`status` is `queued` until a runner picks the run up, `running` while it works, `waiting` while an approval request is open, then `done`, `failed` or `cancelled`. `result` is the report the run wrote when it finished, and `error` says why a failed run failed. `steps`, `toolCalls` and `actions` count how often the model was asked what to do next, how many tool calls ran and how many AI actions the run has cost the workspace. `get_run` adds the record of each step.

Runs are kept for 90 days.

Scopes: `tasks:read`, `threads:read`, `emails:read`.

**Parameters**

- `limit` (`int`): Page size, from 1 to 100. The server defaults to 25.
- `cursor` (`str`): The `nextCursor` of the previous page, passed back as it came. Leave it out for the first page.
- `task_id` (`str`): Only the runs of this task, by its id. Left out, the runs of every task and the one-off runs are listed.
- `status` (`TaskRunStatus`): Only runs in this state: `queued`, `running`, `waiting`, `done`, `failed` or `cancelled`.
- `api_key` (`str`): Overrides the client API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `OpenEmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`Page[TaskRunResource]`, a dict with `items`, `hasMore` and `nextCursor`. Each item has `id`, `taskId`, `taskName`, `trigger`, `threadId`, `status`, `instruction`, `autonomy`, `model`, `steps`, `toolCalls`, `actions`, `result`, `error`, `errorDetail`, `pendingApprovals`, `chatId`, `createdAt`, `updatedAt`, `startedAt` and `finishedAt`.

**Example**

```python
from openemail import openemail

page = openemail.tasks.list_runs(task_id='tsk_5c1e9a7b3d2f48e6a0b4c7d1', status='failed')

for run in page['items']:
    print(run['id'], run['createdAt'], run['error'], run['errorDetail'])
```

**Notes**

- `trigger` says what started the run: `schedule`, `mail` or `manual`. A run that mail started names the conversation in `threadId`.
- `error` is `ai_limit` when the workspace ran out of AI actions for the day, `ai_unavailable` when AI is not set up, and `attempts` or `no_progress` when the run kept failing or stood still. `errorDetail` adds a sentence about it.
- A key or an app limited to particular addresses or domains is refused with 422 `capability_unsupported`, and an app acting for a member whose role does not reach every address with 403 `owner_only`.
- The cursor is opaque and holds where the last row sat in this order, so a run that starts between pages never breaks the walk.

Also available in: API [`GET /task-runs`](https://openemail.uk/docs/api/reference/tasks#get-task-runs); TypeScript [`tasks.listRuns()`](https://openemail.uk/docs/sdk/reference/tasks#listRuns); Ruby [`tasks.list_runs`](https://openemail.uk/docs/ruby/reference/tasks#listRuns); PHP [`tasks->listRuns`](https://openemail.uk/docs/php/reference/tasks#listRuns); Go [`Tasks.ListRuns`](https://openemail.uk/docs/go/reference/tasks#listRuns); Java [`tasks().listRuns`](https://openemail.uk/docs/java/reference/tasks#listRuns); C# [`Tasks.ListRunsAsync`](https://openemail.uk/docs/csharp/reference/tasks#listRuns); CLI [`openemail tasks list-runs`](https://openemail.uk/docs/cli/reference/tasks#tasks-list-runs).

### `tasks.list_all_runs()`

Collect every task run into one list

```python
def list_all_runs(
    *,
    limit: int | None = None,
    cursor: str | None = None,
    task_id: str | None = None,
    status: TaskRunStatus | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> builtins.list[TaskRunResource]
```

Follows `nextCursor` from page to page and returns every run that matches, newest first, in the shape `list_runs` returns. `limit=` sets the page size of each request, not the total. A busy workspace keeps a great many runs for its 90 days, so give `task_id=` or `status=`, or prefer `iterate_runs` when you do not need them all in memory.

Scopes: `tasks:read`, `threads:read`, `emails:read`.

**Parameters**

- `limit` (`int`): Page size for each request, from 1 to 100. The server defaults to 25.
- `cursor` (`str`): Starts the walk after this cursor instead of the first page.
- `task_id` (`str`): Only the runs of this task, by its id.
- `status` (`TaskRunStatus`): Only runs in this state: `queued`, `running`, `waiting`, `done`, `failed` or `cancelled`.
- `api_key` (`str`): Overrides the client API key for every page of this walk.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `OpenEmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`list[TaskRunResource]` holding every matching run across all pages.

**Example**

```python
from openemail import openemail

runs = openemail.tasks.list_all_runs(task_id='tsk_5c1e9a7b3d2f48e6a0b4c7d1', limit=100)

actions = sum(run['actions'] for run in runs)

print(f'{len(runs)} runs cost {actions} AI actions')
```

**Notes**

- If any page fails, the call raises and the runs already fetched are discarded.
- A key or an app limited to particular addresses or domains is refused with 422 `capability_unsupported`, and an app acting for a member whose role does not reach every address with 403 `owner_only`.

Also available in: API [`GET /task-runs`](https://openemail.uk/docs/api/reference/tasks#get-task-runs); TypeScript [`tasks.listAllRuns()`](https://openemail.uk/docs/sdk/reference/tasks#listAllRuns); Ruby [`tasks.list_all_runs`](https://openemail.uk/docs/ruby/reference/tasks#listAllRuns); PHP [`tasks->listAllRuns`](https://openemail.uk/docs/php/reference/tasks#listAllRuns); Go [`Tasks.ListAllRuns`](https://openemail.uk/docs/go/reference/tasks#listAllRuns); Java [`tasks().listAllRuns`](https://openemail.uk/docs/java/reference/tasks#listAllRuns); C# [`Tasks.ListAllRunsAsync`](https://openemail.uk/docs/csharp/reference/tasks#listAllRuns).

### `tasks.iterate_runs()`

Stream the task runs one at a time

```python
def iterate_runs(
    *,
    limit: int | None = None,
    cursor: str | None = None,
    task_id: str | None = None,
    status: TaskRunStatus | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> Iterator[TaskRunResource]
```

Returns a generator that yields runs one by one, newest first, and requests the next page only once the current one is drained. Nothing is fetched until you consume it, and breaking out of the loop stops the requests.

Scopes: `tasks:read`, `threads:read`, `emails:read`.

**Parameters**

- `limit` (`int`): Page size for each request, from 1 to 100. The server defaults to 25.
- `cursor` (`str`): Starts the walk after this cursor instead of the first page.
- `task_id` (`str`): Only the runs of this task, by its id.
- `status` (`TaskRunStatus`): Only runs in this state: `queued`, `running`, `waiting`, `done`, `failed` or `cancelled`.
- `api_key` (`str`): Overrides the client API key for every page of this walk.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `OpenEmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`Iterator[TaskRunResource]`, a generator yielding one run per step.

**Example**

```python
from openemail import openemail

for run in openemail.tasks.iterate_runs(status='waiting'):
    print(run['id'], run['taskName'], 'waits for', run['pendingApprovals'], 'answers')
```

**Notes**

- The generator is lazy, so an abandoned loop costs only the pages you consumed.
- A key or an app limited to particular addresses or domains is refused with 422 `capability_unsupported`, and an app acting for a member whose role does not reach every address with 403 `owner_only`.

Also available in: API [`GET /task-runs`](https://openemail.uk/docs/api/reference/tasks#get-task-runs); TypeScript [`tasks.iterateRuns()`](https://openemail.uk/docs/sdk/reference/tasks#iterateRuns); Ruby [`tasks.iterate_runs`](https://openemail.uk/docs/ruby/reference/tasks#iterateRuns); PHP [`tasks->iterateRuns`](https://openemail.uk/docs/php/reference/tasks#iterateRuns); Go [`Tasks.IterateRuns`](https://openemail.uk/docs/go/reference/tasks#iterateRuns); Java [`tasks().iterateRuns`](https://openemail.uk/docs/java/reference/tasks#iterateRuns); C# [`Tasks.IterateRunsAsync`](https://openemail.uk/docs/csharp/reference/tasks#iterateRuns).

### `tasks.get_run()`

Read one run with every step it took

```python
def get_run(
    id: str,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> TaskRunDetailResource
```

Returns one run with everything it did: the fields of a row of `list_runs`, the record of the run in `transcript`, in order, and its approval requests in `approvals`, newest first. `steps` stays the count it is in `list_runs`.

The entry of `transcript` with `role` `user` holds the instruction the run started with. Each entry with `role` `assistant` is one step: the `text` the assistant wrote and `calls`, the tools it called at that step. A call names its `tool` as the MCP server lists it, with the `args` it was called with, `done`, which is `False` while the call waits for approval or has not run yet, and `result`, what the tool answered.

A run is `queued`, then `running`, and `waiting` while an approval request is open. Read it again until it is `done`, `failed` or `cancelled`. `result` then holds the report it wrote.

Scopes: `tasks:read`, `threads:read`, `emails:read`.

**Parameters**

- `id` (`str`, required): Run id such as `run_2b8d4f6a1c3e5079b6d8f0a2`, as `list_runs` returns it.
- `api_key` (`str`): Overrides the client API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `OpenEmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`TaskRunDetailResource`: the fields of `TaskRunResource`, plus `transcript`, a list of steps with `role`, `text`, `at` and `calls`, and `approvals`, a list of `TaskApprovalResource`.

**Example**

```python
from openemail import openemail

run = openemail.tasks.get_run('run_2b8d4f6a1c3e5079b6d8f0a2')

for step in run['transcript']:
    for call in step['calls']:
        print(step['at'], call['tool'], call['done'])

print(run['status'], run['result'])
```

**Notes**

- A missing run raises `OpenEmailApiError` with `is_not_found` set to `True` and code `task_run_not_found`. Runs are kept for 90 days, so an older one is missing too.
- A key or an app limited to particular addresses or domains is refused with 422 `capability_unsupported`, and an app acting for a member whose role does not reach every address with 403 `owner_only`.
- Read only, so the SDK retries it after a network failure like any other read.

Also available in: API [`GET /task-runs/{id}`](https://openemail.uk/docs/api/reference/tasks#get-task-runs-id); TypeScript [`tasks.getRun()`](https://openemail.uk/docs/sdk/reference/tasks#getRun); Ruby [`tasks.get_run`](https://openemail.uk/docs/ruby/reference/tasks#getRun); PHP [`tasks->getRun`](https://openemail.uk/docs/php/reference/tasks#getRun); Go [`Tasks.GetRun`](https://openemail.uk/docs/go/reference/tasks#getRun); Java [`tasks().getRun`](https://openemail.uk/docs/java/reference/tasks#getRun); C# [`Tasks.GetRunAsync`](https://openemail.uk/docs/csharp/reference/tasks#getRun); CLI [`openemail tasks get-run`](https://openemail.uk/docs/cli/reference/tasks#tasks-get-run).

### `tasks.start_run()`

Hand the assistant a one-off job to do in the background

```python
def start_run(
    body: TaskRunStart,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> TaskRunDetailResource
```

Hands the assistant one job to do in the background, with no task saved: a long tidy-up, a report, or anything that should not wait on an open chat. It returns the run as it was queued. Follow it with `get_run`, and answer what it asks with `approve` or `deny`.

`instruction` is the whole job, in plain words. `threadId` hands the run one conversation as its starting point. `autonomy`, `toolkits`, `model` and `maxSteps` mean what they mean on a task and take the same defaults. The workspace owner is notified when the run waits for approval or fails.

A workspace starts at most 100 of these in any 24 hours. A run acts with what its caller holds, so the call needs the same scopes as `create`: `threads:read`, `threads:write`, `emails:read`, `emails:send` and `labels:write` beside `tasks:write`, and the read and write scopes of every toolkit it names.

Scopes: `tasks:write`, `threads:read`, `threads:write`, `emails:read`, `emails:send`, `labels:write`.

**Parameters**

- `body['instruction']` (`str`, required): What the assistant should do, in plain words, trimmed, 1 to 8,000 characters. It is the only instruction the run follows: text inside emails is read, never obeyed.
- `body['autonomy']` (`TaskAutonomy`): How much the run may do without asking: `ask`, `organize` or `reply`. Defaults to `organize`.
- `body['toolkits']` (`list[TaskToolkit]`): Extra groups of tools the run may use beyond reading and organising mail, such as `contacts`, `calendar`, `knowledge` or `web`. Left out, `drafts`, `notes` and `knowledge`. An empty list gives it none.
- `body['model']` (`TaskModel`): `standard` is the everyday model. `advanced` reasons better over long jobs and costs ten AI actions where the standard one costs one. Defaults to `standard`.
- `body['maxSteps']` (`int`): The most model steps the run may take before it has to write its report, from 1 to 60. Defaults to 20.
- `body['threadId']` (`str`): A conversation to hand the run as its starting point, by the id `threads.list` returns.
- `api_key` (`str`): Overrides the client API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `OpenEmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`TaskRunDetailResource` with a new `id`, `status` `queued`, `trigger` `manual`, and `taskId` and `taskName` `None`. `transcript` and `approvals` fill in as the run works.

**Example**

```python
from openemail import openemail

queued = openemail.tasks.start_run(
    {
        'instruction': 'Find every invoice that arrived this month and label it Invoices.',
        'autonomy': 'organize',
        'maxSteps': 30,
    }
)

print(queued['id'], queued['status'])
```

**Notes**

- Past 100 one-off runs in 24 hours the call answers 409 `task_run_limit_reached`.
- A toolkit a run may not use is 422 `invalid_task`, a value of the wrong type or length 422 `invalid_parameter` with `param` naming it, and a key the body does not take 422 `unknown_parameter`. A missing scope of a toolkit is a 403 `insufficient_scope`.
- A key or an app limited to particular addresses or domains is refused with 422 `capability_unsupported`, and an app acting for a member whose role does not reach every address with 403 `owner_only`.
- Not retried automatically: every call starts another run, so a retry after a lost response can start two. Read `list_runs` before you try again.

Also available in: API [`POST /task-runs`](https://openemail.uk/docs/api/reference/tasks#post-task-runs); TypeScript [`tasks.startRun()`](https://openemail.uk/docs/sdk/reference/tasks#startRun); Ruby [`tasks.start_run`](https://openemail.uk/docs/ruby/reference/tasks#startRun); PHP [`tasks->startRun`](https://openemail.uk/docs/php/reference/tasks#startRun); Go [`Tasks.StartRun`](https://openemail.uk/docs/go/reference/tasks#startRun); Java [`tasks().startRun`](https://openemail.uk/docs/java/reference/tasks#startRun); C# [`Tasks.StartRunAsync`](https://openemail.uk/docs/csharp/reference/tasks#startRun); CLI [`openemail tasks start-run`](https://openemail.uk/docs/cli/reference/tasks#tasks-start-run).

### `tasks.cancel_run()`

Stop a run that has not ended

```python
def cancel_run(
    id: str,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> TaskRunDetailResource
```

Stops a run that is queued, running or waiting for approval, and closes its open approval requests, which become `cancelled`. What the run already did stays done: nothing is rolled back. The task it belongs to is left as it is and runs again when its trigger next fires. It takes no body.

Scopes: `tasks:write`.

**Parameters**

- `id` (`str`, required): Run id such as `run_2b8d4f6a1c3e5079b6d8f0a2`, as `list_runs` returns it.
- `api_key` (`str`): Overrides the client API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `OpenEmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`TaskRunDetailResource` with `status` `cancelled` and `finishedAt` set, and `transcript` showing how far it got.

**Example**

```python
from openemail import openemail

run = openemail.tasks.cancel_run('run_2b8d4f6a1c3e5079b6d8f0a2')

print(run['status'], run['finishedAt'], run['toolCalls'])
```

**Notes**

- A run that has already ended answers 409 `task_run_finished`, and a missing one 404 `task_run_not_found`.
- To keep a task from starting more runs, use `pause`.
- A key or an app limited to particular addresses or domains is refused with 422 `capability_unsupported`, and an app acting for a member whose role does not reach every address with 403 `owner_only`.
- Not retried automatically. A 409 `task_run_finished` on your own second attempt after a lost response means the first one worked or the run ended by itself, and `get_run` says which.

Also available in: API [`POST /task-runs/{id}/cancel`](https://openemail.uk/docs/api/reference/tasks#post-task-runs-id-cancel); TypeScript [`tasks.cancelRun()`](https://openemail.uk/docs/sdk/reference/tasks#cancelRun); Ruby [`tasks.cancel_run`](https://openemail.uk/docs/ruby/reference/tasks#cancelRun); PHP [`tasks->cancelRun`](https://openemail.uk/docs/php/reference/tasks#cancelRun); Go [`Tasks.CancelRun`](https://openemail.uk/docs/go/reference/tasks#cancelRun); Java [`tasks().cancelRun`](https://openemail.uk/docs/java/reference/tasks#cancelRun); C# [`Tasks.CancelRunAsync`](https://openemail.uk/docs/csharp/reference/tasks#cancelRun); CLI [`openemail tasks cancel-run`](https://openemail.uk/docs/cli/reference/tasks#tasks-cancel-run).

### `tasks.list_approvals()`

List what runs are waiting for a person to allow

```python
def list_approvals(
    *,
    limit: int | None = None,
    cursor: str | None = None,
    status: TaskApprovalStatus | None = None,
    run_id: str | None = None,
    task_id: str | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> Page[TaskApprovalResource]
```

Returns one page of approval requests, newest first. An approval request is a tool call a run stopped at because it may not make it alone: `tool` names the tool, `summary` says in one line what would happen, and `args` holds the exact arguments it would be called with. A reply shows the recipients worked out from the message it answers.

Without `status=` the list holds only the requests still `pending`. Give `status=` to read the ones that were `approved`, `denied`, `expired` or `cancelled`, and `run_id=` or `task_id=` to read those of one run or of one task. `list_all_approvals` collects every page and `iterate_approvals` walks them lazily.

A request waits 7 days. Then it expires and the run carries on without it. Answer one with `approve` or `deny`.

Scopes: `tasks:read`, `threads:read`, `emails:read`.

**Parameters**

- `limit` (`int`): Page size, from 1 to 100. The server defaults to 25.
- `cursor` (`str`): The `nextCursor` of the previous page, passed back as it came. Leave it out for the first page.
- `status` (`TaskApprovalStatus`): Only requests in this state: `pending`, `approved`, `denied`, `expired` or `cancelled`. Left out, the list holds the ones still pending.
- `run_id` (`str`): Only the requests of this run, by its id.
- `task_id` (`str`): Only the requests of the runs of this task, by its id.
- `api_key` (`str`): Overrides the client API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `OpenEmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`Page[TaskApprovalResource]`, a dict with `items`, `hasMore` and `nextCursor`. Each item has `id`, `runId`, `taskId`, `taskName`, `tool`, `summary`, `args`, `status`, `createdAt`, `expiresAt` and `decidedAt`.

**Example**

```python
from openemail import openemail

page = openemail.tasks.list_approvals()

for approval in page['items']:
    print(approval['id'], approval['taskName'], approval['summary'], approval['expiresAt'])
```

**Notes**

- No value of `status=` means the requests still pending. Read each other state in turn to see the rest.
- `taskId` and `taskName` are `None` on a request of a one-off run.
- A key or an app limited to particular addresses or domains is refused with 422 `capability_unsupported`, and an app acting for a member whose role does not reach every address with 403 `owner_only`.

Also available in: API [`GET /task-approvals`](https://openemail.uk/docs/api/reference/tasks#get-task-approvals); TypeScript [`tasks.listApprovals()`](https://openemail.uk/docs/sdk/reference/tasks#listApprovals); Ruby [`tasks.list_approvals`](https://openemail.uk/docs/ruby/reference/tasks#listApprovals); PHP [`tasks->listApprovals`](https://openemail.uk/docs/php/reference/tasks#listApprovals); Go [`Tasks.ListApprovals`](https://openemail.uk/docs/go/reference/tasks#listApprovals); Java [`tasks().listApprovals`](https://openemail.uk/docs/java/reference/tasks#listApprovals); C# [`Tasks.ListApprovalsAsync`](https://openemail.uk/docs/csharp/reference/tasks#listApprovals); CLI [`openemail tasks list-approvals`](https://openemail.uk/docs/cli/reference/tasks#tasks-list-approvals).

### `tasks.list_all_approvals()`

Collect every approval request into one list

```python
def list_all_approvals(
    *,
    limit: int | None = None,
    cursor: str | None = None,
    status: TaskApprovalStatus | None = None,
    run_id: str | None = None,
    task_id: str | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> builtins.list[TaskApprovalResource]
```

Walks every page of `list_approvals` and returns all the requests that match, newest first. One request per page. Without `status=` it collects only the requests still `pending`, which is the list a person has to answer.

Scopes: `tasks:read`, `threads:read`, `emails:read`.

**Parameters**

- `limit` (`int`): Page size for each request, from 1 to 100. The server defaults to 25.
- `cursor` (`str`): Starts the walk after this cursor instead of the first page.
- `status` (`TaskApprovalStatus`): Only requests in this state: `pending`, `approved`, `denied`, `expired` or `cancelled`. Left out, the ones still pending are collected.
- `run_id` (`str`): Only the requests of this run, by its id.
- `task_id` (`str`): Only the requests of the runs of this task, by its id.
- `api_key` (`str`): Overrides the client API key for every page of this walk.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `OpenEmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`list[TaskApprovalResource]` holding every matching request.

**Example**

```python
from openemail import openemail

pending = openemail.tasks.list_all_approvals(run_id='run_2b8d4f6a1c3e5079b6d8f0a2')

for approval in pending:
    print(approval['tool'], approval['summary'])
```

**Notes**

- If any page fails, the call raises and the requests already fetched are discarded.
- A key or an app limited to particular addresses or domains is refused with 422 `capability_unsupported`, and an app acting for a member whose role does not reach every address with 403 `owner_only`.

Also available in: API [`GET /task-approvals`](https://openemail.uk/docs/api/reference/tasks#get-task-approvals); TypeScript [`tasks.listAllApprovals()`](https://openemail.uk/docs/sdk/reference/tasks#listAllApprovals); Ruby [`tasks.list_all_approvals`](https://openemail.uk/docs/ruby/reference/tasks#listAllApprovals); PHP [`tasks->listAllApprovals`](https://openemail.uk/docs/php/reference/tasks#listAllApprovals); Go [`Tasks.ListAllApprovals`](https://openemail.uk/docs/go/reference/tasks#listAllApprovals); Java [`tasks().listAllApprovals`](https://openemail.uk/docs/java/reference/tasks#listAllApprovals); C# [`Tasks.ListAllApprovalsAsync`](https://openemail.uk/docs/csharp/reference/tasks#listAllApprovals).

### `tasks.iterate_approvals()`

Stream the approval requests one at a time

```python
def iterate_approvals(
    *,
    limit: int | None = None,
    cursor: str | None = None,
    status: TaskApprovalStatus | None = None,
    run_id: str | None = None,
    task_id: str | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> Iterator[TaskApprovalResource]
```

Returns a generator that yields approval requests one by one, newest first, and requests the next page only once the current one is drained. Without `status=` it yields only the requests still `pending`. Nothing is fetched until you consume it, and breaking out of the loop stops the requests.

Scopes: `tasks:read`, `threads:read`, `emails:read`.

**Parameters**

- `limit` (`int`): Page size for each request, from 1 to 100. The server defaults to 25.
- `cursor` (`str`): Starts the walk after this cursor instead of the first page.
- `status` (`TaskApprovalStatus`): Only requests in this state: `pending`, `approved`, `denied`, `expired` or `cancelled`. Left out, the ones still pending are walked.
- `run_id` (`str`): Only the requests of this run, by its id.
- `task_id` (`str`): Only the requests of the runs of this task, by its id.
- `api_key` (`str`): Overrides the client API key for every page of this walk.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `OpenEmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`Iterator[TaskApprovalResource]`, a generator yielding one approval request per step.

**Example**

```python
from openemail import openemail

for approval in openemail.tasks.iterate_approvals(task_id='tsk_5c1e9a7b3d2f48e6a0b4c7d1'):
    print(approval['id'], approval['tool'], approval['summary'])
```

**Notes**

- The generator is lazy, so an abandoned loop costs only the pages you consumed.
- A key or an app limited to particular addresses or domains is refused with 422 `capability_unsupported`, and an app acting for a member whose role does not reach every address with 403 `owner_only`.

Also available in: API [`GET /task-approvals`](https://openemail.uk/docs/api/reference/tasks#get-task-approvals); TypeScript [`tasks.iterateApprovals()`](https://openemail.uk/docs/sdk/reference/tasks#iterateApprovals); Ruby [`tasks.iterate_approvals`](https://openemail.uk/docs/ruby/reference/tasks#iterateApprovals); PHP [`tasks->iterateApprovals`](https://openemail.uk/docs/php/reference/tasks#iterateApprovals); Go [`Tasks.IterateApprovals`](https://openemail.uk/docs/go/reference/tasks#iterateApprovals); Java [`tasks().iterateApprovals`](https://openemail.uk/docs/java/reference/tasks#iterateApprovals); C# [`Tasks.IterateApprovalsAsync`](https://openemail.uk/docs/csharp/reference/tasks#iterateApprovals).

### `tasks.get_approval()`

Read one approval request by id

```python
def get_approval(
    id: str,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> TaskApprovalResource
```

Looks up a single approval request and returns it in the same shape as a row of `list_approvals`: the run that asked and its task, the `tool` the run wants to call, the one-line `summary`, the exact `args` and its `status`. `args` are what will be done once it is approved, unless `approve` replaces the subject or the words of a send or a reply.

Scopes: `tasks:read`, `threads:read`, `emails:read`.

**Parameters**

- `id` (`str`, required): Approval request id such as `apr_9f3a1c5e7b2d4608a1c3e5f7`, as `list_approvals` returns it.
- `api_key` (`str`): Overrides the client API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `OpenEmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`TaskApprovalResource` with `id`, `runId`, `taskId`, `taskName`, `tool`, `summary`, `args`, `status`, `createdAt`, `expiresAt` and `decidedAt`.

**Example**

```python
from openemail import openemail

approval = openemail.tasks.get_approval('apr_9f3a1c5e7b2d4608a1c3e5f7')

print(approval['tool'], approval['summary'], approval['args'])
```

**Notes**

- A missing request raises `OpenEmailApiError` with `is_not_found` set to `True` and code `task_approval_not_found`.
- `taskId` and `taskName` are `None` on a request of a one-off run.
- A key or an app limited to particular addresses or domains is refused with 422 `capability_unsupported`, and an app acting for a member whose role does not reach every address with 403 `owner_only`.

Also available in: API [`GET /task-approvals/{id}`](https://openemail.uk/docs/api/reference/tasks#get-task-approvals-id); TypeScript [`tasks.getApproval()`](https://openemail.uk/docs/sdk/reference/tasks#getApproval); Ruby [`tasks.get_approval`](https://openemail.uk/docs/ruby/reference/tasks#getApproval); PHP [`tasks->getApproval`](https://openemail.uk/docs/php/reference/tasks#getApproval); Go [`Tasks.GetApproval`](https://openemail.uk/docs/go/reference/tasks#getApproval); Java [`tasks().getApproval`](https://openemail.uk/docs/java/reference/tasks#getApproval); C# [`Tasks.GetApprovalAsync`](https://openemail.uk/docs/csharp/reference/tasks#getApproval); CLI [`openemail tasks get-approval`](https://openemail.uk/docs/cli/reference/tasks#tasks-get-approval).

### `tasks.approve()`

Let a run do what it asked for

```python
def approve(
    id: str,
    body: TaskApprovalEdits | None = None,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> TaskApprovalResource
```

Approves a pending request, so the run makes the tool call with the arguments the request shows. For a send or a reply, `subject` and `body` replace what the run wrote, and the answer shows the changed `args`. For any other tool they are ignored. Call it with no body to approve the request as it stands.

The run picks up again once every request of its step is answered, so a run that asked for three things waits for three answers. A task acts with what its caller holds, so approving needs the scopes the run works with: the same as `create`, counted from the toolkits of its task.

Scopes: `tasks:write`, `threads:read`, `threads:write`, `emails:read`, `emails:send`, `labels:write`.

**Parameters**

- `id` (`str`, required): Approval request id such as `apr_9f3a1c5e7b2d4608a1c3e5f7`, as `list_approvals` returns it.
- `body['subject']` (`str`): For a send or a reply: the subject to use instead of the one the run wrote, trimmed, 1 to 998 characters.
- `body['body']` (`str`): For a send or a reply: the words to send instead of the ones the run wrote, trimmed, 1 to 100,000 characters.
- `api_key` (`str`): Overrides the client API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `OpenEmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`TaskApprovalResource` with `status` `approved`, `decidedAt` set and `args` as the tool will be called with them.

**Example**

```python
from openemail import openemail

approval = openemail.tasks.approve(
    'apr_9f3a1c5e7b2d4608a1c3e5f7', {'subject': 'Your refund is on its way'}
)

print(approval['status'], approval['decidedAt'])
```

**Notes**

- A request that was already answered, has expired, or whose run has ended answers 409 `task_approval_decided`. A missing one is a 404 `task_approval_not_found`.
- There is no undo once the run has acted, so read `args` first.
- A key or an app without the scopes of a toolkit the task names is refused with 403 `insufficient_scope`, and a key the body does not take is 422 `unknown_parameter`.
- A key or an app limited to particular addresses or domains is refused with 422 `capability_unsupported`, and an app acting for a member whose role does not reach every address with 403 `owner_only`.
- The SDK does not retry it. A 409 `task_approval_decided` on your own second attempt after a lost response means the first one worked or somebody else answered, and `get_approval` says how.

Also available in: API [`POST /task-approvals/{id}/approve`](https://openemail.uk/docs/api/reference/tasks#post-task-approvals-id-approve); TypeScript [`tasks.approve()`](https://openemail.uk/docs/sdk/reference/tasks#approve); Ruby [`tasks.approve`](https://openemail.uk/docs/ruby/reference/tasks#approve); PHP [`tasks->approve`](https://openemail.uk/docs/php/reference/tasks#approve); Go [`Tasks.Approve`](https://openemail.uk/docs/go/reference/tasks#approve); Java [`tasks().approve`](https://openemail.uk/docs/java/reference/tasks#approve); C# [`Tasks.ApproveAsync`](https://openemail.uk/docs/csharp/reference/tasks#approve); CLI [`openemail tasks approve`](https://openemail.uk/docs/cli/reference/tasks#tasks-approve).

### `tasks.deny()`

Refuse what a run asked for

```python
def deny(
    id: str,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> TaskApprovalResource
```

Denies a pending request. The run is told, does not try that call again, and carries on with the rest of its task once every request of its step is answered. Nothing the request described is done. It takes no body.

Scopes: `tasks:write`.

**Parameters**

- `id` (`str`, required): Approval request id such as `apr_9f3a1c5e7b2d4608a1c3e5f7`, as `list_approvals` returns it.
- `api_key` (`str`): Overrides the client API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `OpenEmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`TaskApprovalResource` with `status` `denied` and `decidedAt` set.

**Example**

```python
from openemail import openemail

approval = openemail.tasks.deny('apr_9f3a1c5e7b2d4608a1c3e5f7')

print(approval['status'], approval['tool'])
```

**Notes**

- A request that was already answered, has expired, or whose run has ended answers 409 `task_approval_decided`. A missing one is a 404 `task_approval_not_found`.
- To stop the whole run instead of one request, use `cancel_run`.
- A key or an app limited to particular addresses or domains is refused with 422 `capability_unsupported`, and an app acting for a member whose role does not reach every address with 403 `owner_only`.
- The SDK does not retry it. A 409 `task_approval_decided` on your own second attempt after a lost response means the first one worked or somebody else answered, and `get_approval` says how.

Also available in: API [`POST /task-approvals/{id}/deny`](https://openemail.uk/docs/api/reference/tasks#post-task-approvals-id-deny); TypeScript [`tasks.deny()`](https://openemail.uk/docs/sdk/reference/tasks#deny); Ruby [`tasks.deny`](https://openemail.uk/docs/ruby/reference/tasks#deny); PHP [`tasks->deny`](https://openemail.uk/docs/php/reference/tasks#deny); Go [`Tasks.Deny`](https://openemail.uk/docs/go/reference/tasks#deny); Java [`tasks().deny`](https://openemail.uk/docs/java/reference/tasks#deny); C# [`Tasks.DenyAsync`](https://openemail.uk/docs/csharp/reference/tasks#deny); CLI [`openemail tasks deny`](https://openemail.uk/docs/cli/reference/tasks#tasks-deny).
