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

# Changelog

> Version history of the HunterAI CRM integration spec.

<Update label="v1.0.3" description="2026-07-24">
  **Internal consistency pass — the Limitations and How-sync-works pages now match the
  endpoint reference exactly. No change to the adapter contract; these corrections remove
  documentation that contradicted the endpoint pages.**

  * **Stage `kind` — Limitations now matches `/pipelines`.** An unrecognised or missing
    stage `kind` is **rejected as an error** (it fails the page, and because `/pipelines`
    is required, the sync) — **not** silently classified as `open`. The Limitations page
    had still described the old silent-fallback behaviour.
  * **Partial success — optional-entity failure is contained.**
    [How sync works](/concepts/how-sync-works#partial-success) now states that the overall
    sync is marked *failed* only when a **required** entity fails; an **optional** entity
    (`/tasks`, `/calls`, `/events`) failing takes down only that entity and the sync still
    succeeds. The section previously said any entity failure failed the sync.
  * **Page-level atomicity stated on the sync page.** How sync works now spells out that a
    page is written as one transaction, so **one malformed record fails its whole page**
    (page-level, not per-record) — matching every endpoint page.
  * **Timestamp formats aligned.** The Limitations timestamp section now defers to the
    endpoint pages' [Timestamp format](/endpoints/leads#timestamp-format) list instead of
    a narrower one, so every page accepts the same forms (explicit offset, trailing `Z`,
    no zone, `T`-or-space separator, bare date).
  * **Redaction.** The internal repo path was removed from the introduction, leaving the
    reference to the production adapter (writing conventions).
</Update>

<Update label="v1.0.2" description="2026-07-24">
  **The three optional endpoints brought to the reference standard, with several
  field requirements now stated precisely.**

  * **`/events`, `/tasks`, `/calls`** rewritten to the full endpoint-page standard:
    complete §4 field tables (type, required, nullability, constraint, example,
    notes), full response envelopes with paging, and explicit identity,
    incremental, deletion, and failure-mode sections.
  * **Optional-entity failure is contained.** For all three, a page that fails
    (a malformed record) fails **only that entity** and reports it failed; because
    the endpoint is optional, the overall sync still succeeds. A required entity's
    page failure still fails the whole sync.
  * **`/events` — `lead_id` is hard-required.** An event without a `lead_id` fails
    its page and thus the events entity. `type` is **never null** — an absent value
    becomes an empty string (truncated to 100 chars). `value_before` / `value_after`
    accept a scalar, object, or array: a scalar is wrapped as `{ "value": <scalar> }`,
    an object or array is stored unchanged. The watermark **prefers `updated_at` and
    falls back to `created_at`**; `updated_at` is read for the watermark only and is
    not stored.
  * **`/tasks` — association rule stated unambiguously.** `lead_id` wins when both
    `lead_id` and `contact_id` are present; `contact_id` is used **only when
    `lead_id` is absent**; a task with **neither is accepted but linked to nothing**
    and is invisible in every report. A task's own `created_at` is **ignored
    entirely** (no column) and is now listed under fields the adapter ignores.
  * **`/calls` — stricter than `/tasks`, by design.** A call with **neither
    `lead_id` nor `contact_id` is rejected** and fails its page — the opposite of a
    task, which tolerates it; the asymmetry is now called out on both pages.
    `duration_seconds` **must be an integer**; a non-numeric value fails the page —
    stricter than `price`, which tolerates a numeric string (a known inconsistency).
    `direction` accepts `in` or `out`; any other value, `null`, or an omitted value
    silently becomes `out`.
</Update>

<Update label="v1.0.1" description="2026-07-24">
  **Field-spec corrections and two endpoint pages brought to the reference standard.**

  * **`/pipelines` — `sort` must be an integer.** Both the pipeline-level and
    stage-level `sort` **must** be an integer; a fractional or non-numeric value now
    raises a validation error and fails the page. No minimum or maximum is enforced,
    and an omitted or `null` value still defaults to `0`.
  * **`/pipelines` — `stages` is now required and non-nullable.** A missing or `null`
    `stages` is rejected as an error and fails the page (an intended contract; the
    adapter tolerates it today). An empty `stages: []` stays valid — a genuinely empty
    funnel — but its leads cannot be classified won or lost.
  * **`/leads` — `id` limit stated.** The lead `id` is documented as `max 255 chars`,
    matching `/pipelines`; an over-length `id` fails its page. No behaviour change —
    the limit was always enforced by the shared identifier column.
  * **`/users` and `/contacts`** rewritten to the full endpoint-page standard: complete
    field tables (types, required/optional, nullability, constraints), full response
    envelopes, and explicit identity, incremental, deletion, and failure-mode sections.
    `/users` is not incremental and drops `phone`; `/contacts` is incremental on
    `updated_at` and carries **no deletion signal at all**. `custom_fields` on a contact
    must be a JSON object — a non-object value is silently stored as `null`.
</Update>

<Update label="v1.0" description="2026-07-24">
  **First stable release, backed by a working adapter.**

  The custom-CRM adapter is implemented. Its field tables — types, required/optional,
  nullability, and truncation limits — follow the field-table standard and are verified
  against that implementation. A small number of conformance requirements state the
  **intended** contract for adapter behaviours scheduled to change, and read as the
  target rather than today's behaviour: a numeric `price` (a non-numeric value is
  rejected, not coerced to `0`); a required, always-advancing `updated_at`; ascending
  `updated_at` result ordering; page-atomic ingestion (one malformed record fails its
  whole page, and for a required entity the whole sync); request timeouts retried like
  `5xx`; and per-connection rate limiting.

  * **Endpoints** — four required (`/pipelines`, `/users`, `/contacts`, `/leads`) and three
    optional (`/events`, `/tasks`, `/calls`), all `GET`, all read-only.
  * **Authentication** — long-lived read-only Bearer token, encrypted at rest. OAuth 2.0
    documented as available on request, not implemented.
  * **Sync model** — one-directional pull; initial full sync, then incremental via
    `updated_since` with a per-entity watermark; `has_more` paging; idempotent upsert.
  * **Reference** — full field tables with types, required/optional, and truncation limits
    for every entity.
  * **Limitations** — documented in full, each with its consequence: currency not stored,
    first-contact-only, lead-only tombstones, strict stage `kind`, dropped fields,
    truncation, timestamp handling, and managers not yet linked to logins.
  * **Onboarding** — the real HunterAI connection endpoints (`/api/v1/crm/custom/…`) and
    the test-connection call.

  This release **supersedes** the earlier v0.9 draft. No prior published draft remains.
</Update>

<Update label="v0.9 (draft, superseded)" description="Pre-release">
  Internal draft written before the adapter existed. Superseded in full by v1.0, which is
  the first version backed by a running implementation. Do not rely on any v0.9 artifact.
</Update>
