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

# Data model

> What HunterAI derives from each entity, and how your source IDs, timestamps, and custom fields land in the canonical model.

HunterAI maps every source into one **canonical, source-blind** model. Once ingested,
nothing downstream — dashboards, funnels, AI coaching — knows whether a row came from
amoCRM or your custom CRM. This page explains what each entity becomes and the handful of
conventions that apply everywhere.

## What HunterAI derives from each entity

| Entity                 | Powers                                                                                 |
| ---------------------- | -------------------------------------------------------------------------------------- |
| **Pipelines & stages** | Funnel structure; the won/lost classification every deal metric depends on.            |
| **Users**              | The roster of CRM managers, and per-manager attribution of deals, tasks, and calls.    |
| **Contacts**           | The people behind deals; contact-level context for analytics.                          |
| **Leads**              | Deals — the core of revenue, conversion, win-rate, and goal-progress analytics.        |
| **Events**             | Lead history: stage-change timelines, activity reconstruction.                         |
| **Tasks**              | Manager discipline metrics — overdue, completion, follow-up cadence.                   |
| **Calls**              | Call volume and connect-rate (a call with `duration_seconds > 0` counts as connected). |

## Conventions that apply everywhere

### IDs are strings

Every `id` you return — and every cross-reference like `pipeline_id`, `stage_id`,
`responsible_user_id`, `contact_ids`, `lead_id` — is coerced to a **string** on ingest. Use
whatever ID scheme you like: numeric, UUID, or prefixed slugs (`pipe-sales`, `stg-won`).
The only hard rule: an ID must be **stable** for the life of the record, because it is half
of the `(connection, id)` upsert key.

<Warning>
  If a record's `id` changes between syncs, HunterAI treats it as a **new** record and the
  old one is orphaned. Never recycle or renumber IDs.
</Warning>

### Cross-references are by ID, never nested objects

A lead points at its pipeline, stage, owner, and contact by **ID**. HunterAI resolves the
relationships on its side. You do not embed the full pipeline object inside a lead — you
send `pipeline_id`, `stage_id`, `responsible_user_id`, and `contact_ids`.

### Timestamps are ISO-8601 strings

All timestamps are **ISO-8601** strings. Both of these are accepted:

```
2025-07-24T09:30:00Z          # UTC, trailing Z
2025-07-24T09:30:00+05:00     # explicit offset (Uzbekistan is UTC+5)
```

* A timestamp with no timezone is interpreted as **UTC**.
* An **unparseable** timestamp becomes `null` for that field — it does **not** reject the
  record or fail the page. A `null` `updated_at`, however, means the record cannot
  participate in the watermark, so keep your timestamps valid.
* `updated_at` is load-bearing: it is both the `updated_since` filter field and the
  watermark. See [How sync works](/concepts/how-sync-works#ordering-and-the-watermark).

### Custom fields

`/contacts` and `/leads` accept a flat `custom_fields` object:

```json theme={null}
"custom_fields": { "telegram": "@aziz", "region": "Toshkent" }
```

HunterAI stores it as a list of `{ "name": …, "value": … }` pairs. Values may be any JSON
type. Custom fields are retained but are **not** interpreted by analytics in v1.0.

### String fields are truncated, not rejected

Text fields have maximum accepted lengths. A value longer than the limit is **silently
truncated** to fit — it never rejects the record. The exact limit is listed on each
[endpoint page](/endpoints/pipelines) and collected in
[Limitations → Truncation](/limitations#field-truncation-limits).

### Raw payloads

For **leads**, **events**, and **calls**, HunterAI keeps your full raw JSON alongside the
mapped columns (useful for later reprocessing). **Contacts, users, pipelines, stages, and
tasks do not retain a raw copy** — any field on those entities that this spec does not map
is dropped. Don't rely on sending extra fields on those entities; send what the reference
documents.

## Tenancy

A HunterAI business can hold more than one connection. Every ingested row is stamped with
the **connection** it came from, and that stamp is applied by HunterAI — never trusted from
your payload. Your data is isolated per connection; two connections never see each other's
rows.
