Required.
/leads returns your leads. It is
incremental and syncs after
/pipelines, so each lead’s won/lost can be resolved from its stage.Purpose
A lead is one sales opportunity in your CRM. HunterAI reads/leads to compute
revenue, win-rate, conversion, and goal progress (from the lead’s value, its
stage, and its timestamps), traffic and source analytics (from source and the
utm fields), loss analysis (from loss_reason), and per-manager and
per-contact attribution. This is the highest-value endpoint: if it is empty,
almost every metric is empty.
Request
You must return the leads 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 leads on a later page to be skipped.
Response envelope
200 OK
data, has_more, page, limit) is the same on every endpoint
— see How sync works → The envelope.
Only data and has_more are read.
Fields
loss_reason needs no separate endpoint. Send the reason as free text on the
lead; HunterAI synthesizes a stable loss-reason record from the text and links
the lead to it. Identical text across leads maps to the same record.Timestamp format
created_at, updated_at, and closed_at accept a calendar date and time in
these concrete forms:
- With an explicit offset —
2025-07-02T16:45:00+05:00. - With a trailing
Zfor UTC —2025-07-02T16:45:00Z. - With no zone at all —
2025-07-02T16:45:00, which is read as UTC. - The date and time may be separated by a
Tor by a single space (2025-07-02 16:45:00). - A bare date —
2025-07-02— is accepted; the time becomes00:00UTC.
null for that
field, and no error is raised — the lead is still ingested.
Fields the adapter ignores
Sending any of these has no effect — do not spend effort populating them:currency— dropped.priceis stored as a plain number with no currency attached, so a connection is effectively single-currency. See Limitations → Currency.contact_idsbeyond the first element — onlycontact_ids[0]is kept; the rest are ignored.is_won/is_lost— there is no such field on a lead. Won/lost is derived from the lead’s stagekind, never from the lead itself.- Any other key not listed in the Fields table — ignored.
Identity & cross-references
- Identity:
idis the stable primary key. HunterAI upserts by(connection, id), soidmust never change for the same lead — a changedidis ingested as a brand-new lead. pipeline_id→ resolves against/pipelines.stage_id→ resolves against the stages nested in/pipelines. The stage’skindis what makes a lead won or lost — akindoutsideopen,won, orlostis an error at/pipelines, not something the lead can override.responsible_user_id→ resolves against/users.contact_ids[0]→ resolves against/contacts.
Incremental sync
/leads is incremental. HunterAI sends updated_since; return only leads whose
updated_at is greater than or equal to it (inclusive — re-returning the
boundary lead is harmless, since the upsert is idempotent).
updated_atdrives the per-entity watermark — the largestupdated_atHunterAI has committed. The next run resumes from there.- You must return leads ordered by
updated_atascending, so the watermark can advance page by page without skipping an older lead on a later page. updated_atmust change on every modification of a lead — a stage move, an owner change, a field edit, or a soft-delete. A change that does not advanceupdated_atfalls at or below the watermark and is never pulled again.
Deletion behaviour
Leads are the only entity that supports a tombstone. Setis_deleted: true to
soft-delete a lead; it defaults to false. A lead that simply stops appearing in
your responses is not deleted — the upsert only inserts and updates, never
removes a row. See
Limitations → Tombstones.
Failure modes
A page of leads (up to 100 records) is validated and written as one transaction. The consequences of that:- A single malformed lead fails the whole page. If any lead on a page cannot
be processed — a missing
id, a non-numericprice— none of the ~100 leads on that page are saved. This is page-level, not per-record. - A failed page fails the sync. Because
/leadsis required, a page that fails takes the leads entity down with it, and the overall sync is reported failed. Pages that already committed stay committed, and the next run resumes from the watermark. - A lead whose stage was not synced cannot be classified. It lands with
is_won = is_lost = false; this is counted and surfaced in the run summary, not silent. Always return every stage a lead can reference on/pipelines. - An unparseable timestamp does not fail anything — the field becomes
nulland the lead is still ingested. See the consequence forupdated_atabove. 429responses are retried with backoff up to 5 times, then the entity fails. HunterAI applies its request rate limit per connection, so two connections to the same base URL are throttled independently.5xxresponses and request timeouts are retried with exponential backoff up to 5 times; a request that times out is retried the same way as a5xx. Persistent failure fails the entity.- 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.