Skip to main content
These are constraints of the canonical model that ingests your data. Each is deliberate; each has a consequence you should design around. None of them is a bug — they are the shape of v1.0.

Currency is not stored

A lead’s price is stored as a plain integer with no currency. The currency field you send is dropped from the mapped columns (it survives only in the raw copy, which analytics do not read).
Consequence: a connection is effectively single-currency. If your leads mix currencies, HunterAI sums them as one unit — a 1000 USD deal and a 1000 UZS deal both count as 1000. Revenue totals, averages, and goal progress will be wrong.Design around it: send price in one consistent currency per connection. If you operate in several currencies and need correct totals, convert to a single reporting currency before sending, or raise a multi-currency requirement with HunterAI.

Only the first contact on a lead is kept

contact_ids is an array, but HunterAI keeps only the first element. The rest are dropped.
Consequence: a multi-contact deal is linked to exactly one contact. Contact-level analytics see only the primary.Design around it: order contact_ids so the most representative contact is first.

Tombstones apply to leads only

The only delete signal HunterAI honours is a lead’s is_deleted: true. There is no soft-delete for contacts, users, tasks, or calls — those entities have no deleted flag in the canonical model.
Consequence: a contact, task, or call you delete in your CRM is still ingested as active. HunterAI also never removes a row that simply stops appearing in your responses — the upsert only inserts and updates, never deletes.Design around it: the only deletion you can propagate is a lead via is_deleted. For everything else, plan for records to persist in HunterAI once synced.

Stage kind must be exactly open, won, or lost

The stage kind on /pipelines must be one of open, won, or lost (matched case-insensitively). 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.
Consequence: an unrecognised kind is a hard error, not a silent fallback. A stage that should be “won” but is spelled "win" stops the sync outright — you find out immediately rather than discovering corrupted win-rate and revenue later.Design around it: validate that every stage emits exactly open, won, or lost before you request a connection.

Won/lost depends on the stage being synced

A lead’s won/lost is resolved from its stage’s kind, looked up among stages already synced in the same run.
Consequence: if a lead references a pipeline/stage that was not synced (for example a partial sync of only leads, or a lead pointing at a stage you did not return on /pipelines), HunterAI cannot classify it. The lead lands with is_won = is_lost = false. This is counted and surfaced in the run summary — it is not silent — but the classification is wrong until the pipelines/stages are present.Design around it: always return every stage a lead can reference on /pipelines, and let full syncs run all entities.

Fields dropped entirely

Some fields you can send are discarded because the canonical model has no column for them: Additionally, contacts, users, pipelines, stages, and tasks retain no raw copy — any field on those entities not listed in the reference is dropped. Only leads, events, and calls keep your full raw payload for future reprocessing.

Field truncation limits

Over-length text is silently truncated to fit — it never rejects the record. Maximum accepted lengths:

Timestamp handling

  • Accepted formats: exactly the set every endpoint page documents — an explicit offset, a trailing Z, no zone at all (read as UTC), a T or a single space between date and time, or a bare date (time becomes 00:00 UTC). See /leads → Timestamp format for the canonical list; it is identical on every endpoint page. Prefer the explicit-offset form.
  • Unparseable value: becomes null for that field. The record is still ingested — a single bad timestamp does not reject the record or fail the page.
Consequence: a null updated_at means the record cannot participate in the watermark — an incremental sync may not pick it up correctly. Keep updated_at valid and always advancing on every change.

Managers are not linked to HunterAI logins

CRM managers are ingested and used for attribution, but for a custom source they are not automatically turned into HunterAI logins. Per-manager dashboards, scoping, and coaching are unavailable until invitation-based linking ships. This is a deliberate security decision — see Go live → What does not work yet.