> ## 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 /pipelines

> Required. Your pipelines with their stages nested inside. Stages carry the won/lost classification every lead metric depends on.

<Info>
  **Required.** `/pipelines` returns your pipelines, each with its stages nested in
  `stages[]`. HunterAI reads this one endpoint for both — there is no separate stages
  endpoint — and syncs it **before** `/leads` so each lead's won/lost can be resolved
  from its stage.
</Info>

## Purpose

A pipeline is one sales funnel; its stages are the steps a lead moves through.
HunterAI reads `/pipelines` to build the funnel shown in dashboards (stage order
and archived pipelines) and, most importantly, to learn which stages mean a lead
was **won** or **lost**. That classification is read from each stage's `kind` and
nothing on the lead itself, which is why pipelines and their stages sync before
leads.

## Request

`/pipelines` is **not incremental** — it receives no `updated_since` and is
fetched in full on every sync. Return your complete current list each time.

```http theme={null}
GET /api/hunterai/v1/pipelines?limit=100&page=1 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 pipelines per page. |
| `page`      | `1`, `2`, … | 1-based. Page until `has_more` is `false`.              |

## Response envelope

```json 200 OK theme={null}
{
  "data": [
    {
      "id": "pipe-sales",
      "name": "Sotuv",
      "is_archived": false,
      "sort": 1,
      "stages": [
        { "id": "stg-new",  "name": "Yangi",        "kind": "open", "sort": 1, "color": "#4A90D9" },
        { "id": "stg-nego", "name": "Muzokara",     "kind": "open", "sort": 2, "color": "#F5A623" },
        { "id": "stg-won",  "name": "Muvaffaqiyat", "kind": "won",  "sort": 3, "color": "#2FBF71" },
        { "id": "stg-lost", "name": "Yo'qotilgan",  "kind": "lost", "sort": 4, "color": "#D0021B" }
      ]
    },
    {
      "id": "pipe-resell",
      "name": "Qayta sotuv",
      "is_archived": true,
      "sort": 2,
      "stages": [
        { "id": "stg-contact", "name": "Aloqa",        "kind": "open", "sort": 1, "color": "#4A90D9" },
        { "id": "stg-won",     "name": "Muvaffaqiyat", "kind": "won",  "sort": 2, "color": "#2FBF71" },
        { "id": "stg-lost",    "name": "Yo'qotilgan",  "kind": "lost", "sort": 3, "color": "#D0021B" }
      ]
    }
  ],
  "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

### Pipeline 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 pipeline | `"pipe-sales"`                                                                                | The upsert key together with the connection. An absent or over-length `id` fails its page.                                                                                                                             |
| `name`        | string         | No       | Yes      | max 255 chars                                                                        | `"Sotuv"`                                                                                     | Silently truncated to 255 chars.                                                                                                                                                                                       |
| `is_archived` | boolean        | No       | No       | `true` or `false`                                                                    | `false`                                                                                       | Archived pipelines are still ingested. Defaults to `false` if omitted.                                                                                                                                                 |
| `sort`        | integer        | No       | No       | integer; a fractional or non-numeric value fails the page                            | `1`                                                                                           | `sort` **must** be an integer. Display order of the pipeline. A fractional or non-numeric value raises a validation error and fails the page; no minimum or maximum is enforced. Defaults to `0` when omitted or null. |
| `stages`      | array\<object> | Yes      | No       | array of stage objects (see [Stage fields](#stage-fields)); may be empty             | `[{ "id": "stg-won", "name": "Muvaffaqiyat", "kind": "won", "sort": 3, "color": "#2FBF71" }]` | A missing or `null` `stages` is rejected as an error and fails the page. An empty array is valid — a genuinely empty funnel — but its leads cannot be classified won or lost.                                          |

### Stage fields

Each object inside `stages[]`:

| Field   | Type    | Required | Nullable | Format / Constraint                                       | Example          | Notes                                                                                                                                                                                                                           |
| ------- | ------- | -------- | -------- | --------------------------------------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`    | string  | Yes      | No       | max 255 chars; stable; may repeat across pipelines        | `"stg-won"`      | Keyed together with the pipeline, so a shared stage id in two pipelines is stored as two rows. An absent or over-length `id` fails its page.                                                                                    |
| `name`  | string  | No       | Yes      | max 255 chars                                             | `"Muvaffaqiyat"` | Silently truncated to 255 chars.                                                                                                                                                                                                |
| `kind`  | string  | Yes      | No       | one of `open`, `won`, `lost` (matched case-insensitively) | `"won"`          | Sets every lead's won/lost. An unrecognised or missing value is rejected — see below.                                                                                                                                           |
| `sort`  | integer | No       | No       | integer; a fractional or non-numeric value fails the page | `3`              | `sort` **must** be an integer. Order of the stage within its pipeline. A fractional or non-numeric value raises a validation error and fails the page; no minimum or maximum is enforced. Defaults to `0` when omitted or null. |
| `color` | string  | No       | Yes      | max 20 chars                                              | `"#2FBF71"`      | Display color. Silently truncated to 20 chars.                                                                                                                                                                                  |

#### Stage kind classification

`kind` is how HunterAI knows which stage means a lead was **won** or **lost**. It
is the single highest-stakes field in this integration — every revenue, win-rate,
and conversion metric depends on it.

* Accepted values: exactly `open`, `won`, or `lost` (matched case-insensitively,
  so `Won` and `WON` are accepted).
* Any other value — or a missing `kind` — is **rejected as an error**. It fails
  the page and, because `/pipelines` is required, fails the sync. It is **not**
  silently classified as `open`.

<Warning>
  Because an unrecognised `kind` is a hard error, a typo like `"win"` or
  `"closed"` stops the sync instead of quietly mis-classifying deals — you find
  out immediately rather than discovering corrupted win-rate later. Emit exactly
  `open`, `won`, or `lost` for every stage. Leads read their won/lost from this
  value; see [`/leads`](/endpoints/leads).
</Warning>

### Timestamp format

This endpoint reads **no timestamp fields**. `/pipelines` is fetched in full every
sync and is not watermarked, so any `created_at` or `updated_at` you send on a
pipeline or stage is ignored.

## Fields the adapter ignores

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

* Any `created_at` / `updated_at` on a pipeline or stage — `/pipelines` is not
  incremental, so timestamps here are not read.
* `type` on a stage — HunterAI does not read it; won/lost comes from `kind`.
* Any other key not listed in the [Pipeline fields](#pipeline-fields) or
  [Stage fields](#stage-fields) tables — ignored.

## Identity & cross-references

* **Pipeline identity:** `id` is the stable primary key. HunterAI upserts by
  `(connection, id)`, so a pipeline's `id` **must never change**.
* **Stage identity:** a stage is keyed by `(connection, pipeline, id)`. The same
  stage `id` may appear in more than one pipeline and is stored once per pipeline,
  so always return each stage inside its owning pipeline's `stages[]`.
* **Referenced by leads:** each lead's `pipeline_id` and `stage_id` resolve here,
  and the stage's `kind` is what classifies that lead won/lost. See
  [`/leads`](/endpoints/leads). This dependency is why `/pipelines` syncs before
  `/leads`.

## Incremental sync

**This endpoint is not incremental.** `/pipelines` receives no `updated_since` and
is fetched in full on every sync — return your complete current list of pipelines,
each with its stages, every time. These collections are expected to be small (tens
to low hundreds of rows). See
[How sync works → Incremental sync](/concepts/how-sync-works#incremental-sync).

## Deletion behaviour

Pipelines and stages have **no tombstone** — there is no `is_deleted` on either.
Because the upsert only inserts and updates, a pipeline or stage you remove from
your response is **not** deleted from HunterAI; it simply stops being refreshed and
the historical row persists. Only leads support deletion, via `is_deleted` — see
[Limitations → Tombstones](/limitations#tombstones-apply-to-leads-only).

## Failure modes

A page of pipelines — and the stages flattened out of it — is validated and
written **as one transaction**. The consequences:

* **A single malformed pipeline or stage fails the whole page.** If any pipeline
  or stage on a page cannot be processed — a missing `id`, an over-length `id`, or
  a `kind` that is not `open`, `won`, or `lost` — none of the records on that page
  are saved. This is page-level, not per-record.
* **A failed page fails the sync.** Because `/pipelines` is required, a failed page
  takes both the pipelines and stages entities down and the overall sync is
  reported *failed*. Pages that already committed stay committed.
* **An unrecognised or missing stage `kind` is rejected**, not silently treated as
  `open` — it fails the page as above.
* **A missing or `null` `stages` on a pipeline is rejected** and fails the page. An
  empty `stages: []` is valid — a genuinely empty funnel — but its leads land with
  `is_won = is_lost = false` because there is no stage to classify them.
* **`429`, `5xx`, and request timeouts** are retried with backoff up to 5 times,
  then the entity fails; a timeout is retried the same way as a `5xx`. HunterAI's
  request rate limit is applied **per connection**.
* **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

<Warning>
  `/pipelines` is **required**. A `404` fails both the pipelines and stages
  entities and the sync is reported failed. Leads then have no stages to classify
  against, so every lead lands with `is_won = is_lost = false` — corrupting
  revenue, win-rate, and conversion metrics.
</Warning>
