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

# GET /tasks

> Optional. Manager tasks — the basis for discipline metrics. Incremental on updated_at; return 404 if you don't implement it and the sync still succeeds.

<Info>
  **Optional.** `/tasks` returns manager tasks — the to-dos your managers own. If
  you do not implement it, return `404` — HunterAI skips it and the sync still
  succeeds. It is [incremental](/concepts/how-sync-works#incremental-sync).
</Info>

## Purpose

A task is one to-do a manager owns in your CRM — a follow-up call, a document to
send, a meeting to hold. HunterAI reads `/tasks` to compute manager-discipline
metrics: overdue tasks, completion rate, and follow-up cadence. A task is linked
to the lead or contact it concerns, so those metrics can be attributed per manager
and per record.

## Request

```http theme={null}
GET /api/hunterai/v1/tasks?limit=100&page=1&updated_since=2025-07-01T00:00:00+05:00 HTTP/1.1
Host: crm.example.uz
Authorization: Bearer <token>
Accept: application/json
```

| Query param     | Value       | Notes                                                        |
| --------------- | ----------- | ------------------------------------------------------------ |
| `limit`         | `100`       | Always sent. Return up to this many tasks per page.          |
| `page`          | `1`, `2`, … | 1-based. Page until `has_more` is `false`.                   |
| `updated_since` | timestamp   | Return tasks with `updated_at >= updated_since` (inclusive). |

You **must** return tasks ordered by `updated_at` **ascending**. HunterAI advances
its watermark page by page and relies on that order; an out-of-order page can cause
older tasks on a later page to be skipped.

## Response envelope

```json 200 OK theme={null}
{
  "data": [
    {
      "id": "t-3001",
      "lead_id": "L-9001",
      "contact_id": null,
      "responsible_user_id": "u-1001",
      "text": "Shartnomani yuborish",
      "is_completed": false,
      "due_at": "2025-07-05T09:00:00+05:00",
      "completed_at": null,
      "updated_at": "2025-07-02T16:50:00+05:00"
    },
    {
      "id": "t-3002",
      "lead_id": null,
      "contact_id": "c-5003",
      "responsible_user_id": "u-1002",
      "text": "Qayta qo'ng'iroq",
      "is_completed": true,
      "due_at": "2025-07-04T09:00:00+05:00",
      "completed_at": "2025-07-04T08:30:00+05:00",
      "updated_at": "2025-07-04T08:30:00+05:00"
    }
  ],
  "has_more": false,
  "page": 1,
  "limit": 100
}
```

The envelope (`data`, `has_more`, `page`, `limit`) is the same on every endpoint
— see [How sync works → The envelope](/concepts/how-sync-works#the-envelope).
Only `data` and `has_more` are read.

## Fields

| Field                 | Type    | Required | Nullable | Format / Constraint                                                              | Example                       | Notes                                                                                                                  |
| --------------------- | ------- | -------- | -------- | -------------------------------------------------------------------------------- | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `id`                  | string  | Yes      | No       | max 255 chars; stable and unique per connection; never changes for the same task | `"t-3001"`                    | The upsert key together with the connection. An absent or over-length `id` fails its page.                             |
| `lead_id`             | string  | No       | Yes      | max 255 chars; must match an `id` from [`/leads`](/endpoints/leads)              | `"L-9001"`                    | Links the task to a lead. **Wins when both `lead_id` and `contact_id` are present** — see [Association](#association). |
| `contact_id`          | string  | No       | Yes      | max 255 chars; must match an `id` from [`/contacts`](/endpoints/contacts)        | `"c-5003"`                    | Links the task to a contact. Used **only when `lead_id` is absent** — see [Association](#association).                 |
| `responsible_user_id` | string  | No       | Yes      | must match an `id` from [`/users`](/endpoints/users)                             | `"u-1001"`                    | The owning manager; drives per-manager discipline metrics.                                                             |
| `text`                | string  | No       | Yes      | no length limit (stored as text)                                                 | `"Shartnomani yuborish"`      | Task description. Not truncated.                                                                                       |
| `is_completed`        | boolean | No       | No       | `true` or `false`                                                                | `false`                       | Whether the task is done. Defaults to `false` when omitted.                                                            |
| `due_at`              | string  | No       | Yes      | timestamp (see [Timestamp format](#timestamp-format))                            | `"2025-07-05T09:00:00+05:00"` | When the task is due; the basis for overdue metrics. An unparseable value becomes `null`.                              |
| `completed_at`        | string  | No       | Yes      | timestamp (see [Timestamp format](#timestamp-format))                            | `"2025-07-04T08:30:00+05:00"` | When the task was completed. An unparseable value becomes `null`.                                                      |
| `updated_at`          | string  | Yes      | No       | timestamp (see [Timestamp format](#timestamp-format))                            | `"2025-07-02T16:50:00+05:00"` | Drives `updated_since` and the watermark. Must be present, parseable, and must advance on every modification.          |

### Association

A task is linked to **at most one** entity, resolved in this exact order:

1. **If `lead_id` is present, the task is linked to that lead.** `lead_id` wins even
   when `contact_id` is also present — `contact_id` is then ignored.
2. **If `lead_id` is absent and `contact_id` is present, the task is linked to that
   contact.**
3. **If neither is present, the task is accepted and ingested, but linked to
   nothing.**

<Warning>
  **A task linked to nothing is invisible in every report.** It is ingested without
  error, but with no lead and no contact it attaches to no manager's book of work
  and appears in no discipline metric — so it is almost never what you want. Send a
  `lead_id` (preferred) or a `contact_id` on every task.

  **This differs from [`/calls`](/endpoints/calls#association):** a call with
  neither `lead_id` nor `contact_id` is **rejected** and fails the calls entity,
  whereas an unassociated task is accepted and merely orphaned. Two similar
  endpoints, opposite behaviour — do not assume one from the other.
</Warning>

### Timestamp format

`due_at`, `completed_at`, and `updated_at` accept a calendar date and time in these
concrete forms:

* With an explicit offset — `2025-07-05T09:00:00+05:00`.
* With a trailing `Z` for UTC — `2025-07-05T09:00:00Z`.
* With no zone at all — `2025-07-05T09:00:00`, which is read as UTC.
* The date and time may be separated by a `T` or by a single space
  (`2025-07-05 09:00:00`).
* A bare date — `2025-07-05` — is accepted; the time becomes `00:00` UTC.

Prefer the explicit-offset form. **An unparseable value becomes `null` for that
field, and no error is raised** — the task is still ingested. A task's own
`created_at` is **not** a timestamp field here; it is ignored entirely (see
[Fields the adapter ignores](#fields-the-adapter-ignores)).

<Warning>
  **Consequence when this hits `updated_at`:** a `null` `updated_at` carries no
  modification time, so the task cannot advance the
  [watermark](/concepts/how-sync-works#ordering-and-the-watermark) and is not
  selected by any later `updated_since` filter. In effect the task — and every
  future change to it — drops out of incremental sync. Keep `updated_at` a valid
  timestamp that advances on every change.
</Warning>

## Fields the adapter ignores

Sending any of these has **no effect** — do not spend effort populating them:

* `created_at` — **ignored entirely.** A task has no creation-time column; only
  `updated_at`, `due_at`, and `completed_at` are read. See
  [Limitations → Fields dropped entirely](/limitations#fields-dropped-entirely).
* `task_type`, `duration`, `result` — not part of this spec; dropped.
* Any other key not listed in the [Fields](#fields) table — tasks retain no raw
  copy, so anything unlisted is dropped entirely.

## Identity & cross-references

* **Identity:** `id` is the stable primary key. HunterAI upserts by
  `(connection, id)`, so a task's `id` **must never change** — a changed `id` is
  ingested as a brand-new task.
* `lead_id` → resolves against [`/leads`](/endpoints/leads).
* `contact_id` → resolves against [`/contacts`](/endpoints/contacts).
* `responsible_user_id` → resolves against [`/users`](/endpoints/users).
* Association between the task and a lead or contact follows the
  [Association](#association) rule above.

## Incremental sync

`/tasks` is incremental. HunterAI sends `updated_since`; return only tasks whose
`updated_at` **is greater than or equal to** it (inclusive — re-returning the
boundary task is harmless, since the upsert is idempotent).

* `updated_at` drives the per-entity **watermark** — the largest `updated_at`
  HunterAI has committed. The next run resumes from there.
* You **must** return tasks ordered by `updated_at` **ascending**, so the watermark
  can advance page by page without skipping an older task on a later page.
* `updated_at` **must change on every modification** of a task — completion, a due-
  date change, a reassignment, or a text edit. A change that does not advance
  `updated_at` falls at or below the watermark and is never pulled again.

See [How sync works](/concepts/how-sync-works#incremental-sync) for the shared
model.

## Deletion behaviour

Tasks have **no tombstone** — there is no `is_deleted` on a task. Because the
upsert only inserts and updates, a task you delete in your CRM, or simply stop
returning, is **not** removed from HunterAI; its historical row persists. Marking a
task `is_completed: true` records completion but does not delete it. Only leads
support deletion, via `is_deleted` — see
[Limitations → Tombstones](/limitations#tombstones-apply-to-leads-only).

## Failure modes

A page of tasks (up to 100 records) is validated and written **as one
transaction**. The consequences:

* **A single malformed task fails the whole page.** If any task on a page cannot be
  processed — a missing `id`, or an over-length `id` — none of the tasks on that
  page are saved. This is page-level, not per-record.
* **A failed page fails the tasks entity, not the whole sync.** Because `/tasks` is
  **optional**, a page that fails takes the tasks entity down and reports it failed,
  but the overall sync still succeeds — unlike a required entity, whose failure
  fails the sync. Pages that already committed stay committed, and the next run
  resumes from the watermark.
* **A task with neither `lead_id` nor `contact_id` does *not* fail.** It is accepted
  and ingested, just linked to nothing and therefore invisible in every report. This
  is the opposite of [`/calls`](/endpoints/calls#failure-modes), where an
  unassociated call fails the page.
* **An unparseable timestamp does not fail anything** — the field becomes `null` and
  the task is still ingested. See the consequence for `updated_at` above.
* **`429`, `5xx`, and request timeouts** are retried with exponential backoff up to
  5 times, then the entity fails; a timeout is retried the same way as a `5xx`.
  HunterAI applies its request rate limit **per connection**, so two connections to
  the same base URL are throttled independently.
* **A repeated or unbounded page** — a page whose IDs match the previous page, or a
  run past the page ceiling — fails the entity. See
  [How sync works → Paging](/concepts/how-sync-works#paging).

## If this endpoint is missing

<Check>
  `/tasks` is **optional**. Returning `404` skips it cleanly — the entity reports
  success with zero records and the overall sync still succeeds.
</Check>

Without `/tasks`, HunterAI has no manager-discipline metrics — overdue tasks,
completion rate, and follow-up cadence are all empty. Leads, contacts, calls, and
attribution are unaffected.
