> ## Documentation Index
> Fetch the complete documentation index at: https://docs.answeringagent.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Tasks

> Search, read, and update the follow-up tasks the AI creates from conversations and forms, and add notes to them.

A task is follow-up work for your team, such as a callback, a refund request, or a damage claim. The AI creates a task from a conversation or a website form submission when a person needs to act.

Task IDs name their source: `conversation-184532` came from conversation 184532, and `form-submission-45` came from form submission 45.

<Note>
  You can search tasks, read one, change its status or assignee, and add internal notes. To get each new task the moment it is created, use the [`task.created` webhook](/webhooks).
</Note>

## Search tasks

```
GET /tasks
```

[Try it in the playground](/api-reference/tasks/search-tasks).

Returns tasks, newest first.

<ParamField query="query" type="string">
  Matches the title, the description, or the customer's name, phone number, or email. Up to 100 characters.
</ParamField>

<ParamField query="status" type="string" default="all">
  One of:

  * `not_done`: `open`, `in_progress`, and `on_hold`.
  * `open`, `in_progress`, `on_hold`, or `done`: that status only.
  * `all`: every task.
</ParamField>

<ParamField query="assigned_to_me" type="boolean">
  `true` returns only tasks assigned to the key's owner.
</ParamField>

<ParamField query="range" type="string">
  Created within this window: `today`, `yesterday`, `7d`, `14d`, `30d`, `90d`, `365d`, `mtd`, or `ytd`. Every window starts at midnight in your team's timezone. See [time ranges](/api/overview#pages-and-time-ranges). Leave it out for all time.
</ParamField>

<ParamField query="limit" type="integer" default="10">
  Results per page, 1 to 50.
</ParamField>

<ParamField query="page" type="integer" default="1">
  Page number. Use `next_page` from the previous response.
</ParamField>

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://answeringagent.com/api/v1/tasks?status=not_done&range=30d&limit=2" \
    -H "X-API-KEY: $ANSWERING_AGENT_API_KEY"
  ```

  ```javascript JavaScript theme={null}
  const params = new URLSearchParams({ status: 'not_done', range: '30d', limit: '2' });
  const res = await fetch(`https://answeringagent.com/api/v1/tasks?${params}`, {
    headers: { 'X-API-KEY': process.env.ANSWERING_AGENT_API_KEY },
  });
  if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
  const { tasks, next_page } = await res.json();
  ```
</CodeGroup>

```json theme={null}
{
  "total_matching": 37,
  "page": 1,
  "next_page": 2,
  "tasks": [
    {
      "id": "conversation-184532",
      "url": "https://answeringagent.com/dashboard/conversations/184532",
      "title": "Cancel monthly membership",
      "status": "open",
      "assignee": { "id": 9034, "name": "Priya Shah" },
      "category": "cancellation",
      "priority": "high",
      "created_at": "2026-10-05T14:21:07.000Z",
      "customer": { "name": "Dana Ruiz", "phone": "+15125550118", "email": "dana.ruiz@example.com" },
      "conversation": {
        "id": 184532,
        "url": "https://answeringagent.com/dashboard/conversations/184532",
        "channel": "phone",
        "phone_line": { "id": 2181, "name": "Westside Wash", "phone_number": "+15125550142" }
      },
      "form": null
    },
    {
      "id": "form-submission-45",
      "url": "https://answeringagent.com/dashboard/forms/submissions/45",
      "title": "Fleet account inquiry",
      "status": "in_progress",
      "assignee": null,
      "category": "form",
      "priority": "medium",
      "created_at": "2026-10-04T19:10:44.000Z",
      "customer": { "name": "Marcus Webb", "phone": "+15125550163", "email": "marcus@webbfleet.example.com" },
      "conversation": null,
      "form": { "title": "Fleet accounts" }
    }
  ]
}
```

### Task fields

<ResponseField name="id" type="string">
  The task ID, such as `conversation-184532` or `form-submission-45`.
</ResponseField>

<ResponseField name="url" type="string">
  The task's conversation or form submission in the dashboard.
</ResponseField>

<ResponseField name="title" type="string | null">
  A short title. Up to 200 characters.
</ResponseField>

<ResponseField name="status" type="string">
  `open`, `in_progress`, `on_hold`, or `done`. Search and updates take the same values.
</ResponseField>

<ResponseField name="assignee" type="object | null">
  The assigned team member's `id` and `name`, or `null` when nobody is assigned. Send the `id` as `assigned_to` or `expected_assigned_to` in an update. It matches `members[].id` in [`GET /account`](/api/account).
</ResponseField>

<ResponseField name="category" type="string | null">
  The category key of the task's conversation, such as `cancellation`, from the same list as a [conversation's `category`](/api/conversations#conversation-fields). A form task has `form`.
</ResponseField>

<ResponseField name="priority" type="string">
  `low`, `medium`, `high`, or `urgent`, or `normal` when none was set.
</ResponseField>

<ResponseField name="created_at" type="string | null">
  When the source conversation started or the form was submitted, as an ISO 8601 UTC timestamp. Despite the name, it is not the time the AI created the task, which comes later. Search with `range` uses this time too.
</ResponseField>

<ResponseField name="customer" type="object">
  The customer's `name`, `phone`, and `email`. A conversation task takes them from the conversation's contact, and a form task from the form submission. Any of them can be `null`.
</ResponseField>

<ResponseField name="conversation" type="object | null">
  The source conversation: `id`, `url`, `channel`, and `phone_line` (`id`, `name`, `phone_number`). `null` for form tasks.
</ResponseField>

<ResponseField name="form" type="object | null">
  The source form's `title`. `null` for conversation tasks.
</ResponseField>

## Get a task

```
GET /tasks/{task_id}
```

[Try it in the playground](/api-reference/tasks/get-a-task).

Returns one task with every [task field](#task-fields), plus its full `description`.

<ParamField path="task_id" type="string" required>
  A task ID from search, such as `conversation-184532`.
</ParamField>

<CodeGroup>
  ```bash cURL theme={null}
  curl https://answeringagent.com/api/v1/tasks/conversation-184532 \
    -H "X-API-KEY: $ANSWERING_AGENT_API_KEY"
  ```

  ```javascript JavaScript theme={null}
  const res = await fetch('https://answeringagent.com/api/v1/tasks/conversation-184532', {
    headers: { 'X-API-KEY': process.env.ANSWERING_AGENT_API_KEY },
  });
  if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
  const task = await res.json();
  ```
</CodeGroup>

```json theme={null}
{
  "id": "conversation-184532",
  "url": "https://answeringagent.com/dashboard/conversations/184532",
  "title": "Cancel monthly membership",
  "status": "open",
  "assignee": { "id": 9034, "name": "Priya Shah" },
  "category": "cancellation",
  "priority": "high",
  "created_at": "2026-10-05T14:21:07.000Z",
  "customer": { "name": "Dana Ruiz", "phone": "+15125550118", "email": "dana.ruiz@example.com" },
  "conversation": {
    "id": 184532,
    "url": "https://answeringagent.com/dashboard/conversations/184532",
    "channel": "phone",
    "phone_line": { "id": 2181, "name": "Westside Wash", "phone_number": "+15125550142" }
  },
  "form": null,
  "description": "Dana Ruiz wants to cancel her Unlimited membership before the charge on the 12th. She is moving out of state and declined the free-month offer. Confirm the cancellation in the POS and reply by text."
}
```

<ResponseField name="description" type="string | null">
  The full task description. Up to 5,000 characters.
</ResponseField>

A task ID that does not exist, or that the key's owner cannot see, returns `404`. So does a task on a repeat conversation that was merged into an earlier one. Its `error` names the task to use instead, such as `This conversation was merged into conversation-184519. Use that task.`

## Update a task

```
PATCH /tasks/{task_id}
```

[Try it in the playground](/api-reference/tasks/update-a-task).

Changes a task's status, its assignee, or both. It works like making the same change in the dashboard. The assignee gets the usual notification, and the task's activity shows the change as made by the person who created the key.

<ParamField path="task_id" type="string" required>
  A task ID from search, such as `conversation-184532`.
</ParamField>

<ParamField body="status" type="string">
  `open`, `in_progress`, `on_hold`, or `done`.
</ParamField>

<ParamField body="assigned_to" type="integer | null">
  A team member's `id`, from a task's `assignee.id` or `members[].id` in [`GET /account`](/api/account), or `null` to unassign.
</ParamField>

<ParamField body="expected_status" type="string">
  Optional. `open`, `in_progress`, `on_hold`, or `done`. Apply the change only if the task still has this status. Otherwise the API returns `409` and changes nothing. A task that never had a status counts as `open`. Use it so a sync does not overwrite a change someone just made in the dashboard.
</ParamField>

<ParamField body="expected_assigned_to" type="integer | null">
  Optional. Apply the change only if the task still has this assignee. Use `null` to require that nobody is assigned.
</ParamField>

Send `status`, `assigned_to`, or both. Any other key in the body returns `400`.

<Warning>
  Setting `status` to `in_progress`, `on_hold`, or `done` without `assigned_to` can assign the task to the person who created the key, the same as a click in the dashboard. That happens when the person has **Self assign** turned on in the team's member settings, which is the default. To keep the current assignee, send their ID in `assigned_to`.
</Warning>

This example marks Dana Ruiz's task done for Priya Shah (`9034` in the [`/account` example](/api/account)), but only if nobody changed its status since your sync last read it:

<CodeGroup>
  ```bash cURL theme={null}
  curl -X PATCH https://answeringagent.com/api/v1/tasks/conversation-184532 \
    -H "X-API-KEY: $ANSWERING_AGENT_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{ "status": "done", "assigned_to": 9034, "expected_status": "open" }'
  ```

  ```javascript JavaScript theme={null}
  const res = await fetch('https://answeringagent.com/api/v1/tasks/conversation-184532', {
    method: 'PATCH',
    headers: {
      'X-API-KEY': process.env.ANSWERING_AGENT_API_KEY,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ status: 'done', assigned_to: 9034, expected_status: 'open' }),
  });
  if (res.status === 409) {
    // Someone changed the task in the dashboard. Read it again before you retry.
  } else if (!res.ok) {
    throw new Error(`${res.status}: ${await res.text()}`);
  }
  ```
</CodeGroup>

The response is the updated task, in the same shape as [Get a task](#get-a-task). Here it has `"status": "done"` and `"assignee": { "id": 9034, "name": "Priya Shah" }`.

The change can take the task out of what the key can see. For example, a manager who sees only some phone lines reassigns a task they could see only because it was theirs. The change is still saved, and the body is `{"id": "conversation-184532", "status": "open", "assignee": {"id": 9040, "name": "Jordan Lee"}, "visible": false}`.

| Status | Meaning |
| - | - |
| `200` | Updated. The body is the task, or `id`, `status`, `assignee`, and `"visible": false` when the key can no longer see it. |
| `400` | The body is invalid, has an unknown key, or has neither `status` nor `assigned_to`. |
| `403` | The key's creator cannot change this task. |
| `404` | `"error": "That task is not available."` The task does not exist, or the key cannot see it. For a repeat conversation that was merged into an earlier one, the `error` names the task to update instead. |
| `409` | `"error": "The task changed. Its status or assignee no longer matches expected_status or expected_assigned_to."` Nothing changed. |
| `422` | The dashboard would refuse the same change. The `error` gives the reason, such as `The assigned user must be a member of the team.` or `This task has unmet completion requirements.` |

If your team requires a note before a task can be marked done, [add a note](#add-a-note) first, then send `"status": "done"`. Without the note, the update returns `422` with `This task has unmet completion requirements.`

## Add a note

```
POST /tasks/{task_id}/notes
```

[Try it in the playground](/api-reference/tasks/add-a-note-to-a-task).

Adds an internal note to a conversation task, the same as typing a note on the task in the dashboard. Only your team sees the note. Answering Agent does not contact the customer. The note shows in the task's activity with the person who created the key as its author, and the audit trail records that it came through the API.

A note counts as work by a person on the conversation, the same as a note typed in the dashboard. It never reopens a task that is done.

<ParamField path="task_id" type="string" required>
  A conversation task ID, such as `conversation-184532`. Form tasks do not take notes, so a `form-submission-` ID returns `400`.
</ParamField>

<ParamField body="content" type="string" required>
  The note text. 1 to 5,000 characters after leading and trailing spaces are removed. An @mention stays plain text and notifies nobody.
</ParamField>

Send only `content`. Any other key in the body, including `task_id`, returns `400`.

<Warning>
  Each request adds a note. If you send the same request twice, for example when you retry after a timeout, the task gets two notes.
</Warning>

This example records that Dana Ruiz's membership was cancelled:

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://answeringagent.com/api/v1/tasks/conversation-184532/notes \
    -H "X-API-KEY: $ANSWERING_AGENT_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{ "content": "Called Dana back. Membership cancelled in the POS before the charge on the 12th." }'
  ```

  ```javascript JavaScript theme={null}
  const res = await fetch('https://answeringagent.com/api/v1/tasks/conversation-184532/notes', {
    method: 'POST',
    headers: {
      'X-API-KEY': process.env.ANSWERING_AGENT_API_KEY,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      content: 'Called Dana back. Membership cancelled in the POS before the charge on the 12th.',
    }),
  });
  if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
  const note = await res.json();
  ```
</CodeGroup>

```json theme={null}
{
  "id": 552190,
  "content": "Called Dana back. Membership cancelled in the POS before the charge on the 12th.",
  "author": { "id": 9021, "name": "Sam Ortiz" },
  "created_at": "2026-10-06T15:02:11.000Z"
}
```

<ResponseField name="id" type="integer">
  The note ID.
</ResponseField>

<ResponseField name="content" type="string">
  The note text.
</ResponseField>

<ResponseField name="author" type="object | null">
  The person who created the key, as `id` and `name`. `null` only when that person has no name on their profile.
</ResponseField>

<ResponseField name="created_at" type="string | null">
  When the note was added, as an ISO 8601 UTC timestamp.
</ResponseField>

| Status | Meaning |
| - | - |
| `201` | Added. The body is the note. |
| `400` | The body is invalid or has an unknown key, `content` is empty or longer than 5,000 characters, or the task is a form task. |
| `403` | The key's creator cannot add notes to this task. |
| `404` | `"error": "That task is not available."` The task does not exist, or the key cannot see it. For a repeat conversation that was merged into an earlier one, the `error` names the task to use instead. |
| `422` | The dashboard would refuse the same note. The `error` gives the reason. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.