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