Skip to main content
Return standard HTTP status codes. HunterAI’s behaviour for each is fixed and listed below.

Error body shape

On any error, return a JSON body with an error string. HunterAI reads it into the failure detail it records for the sync:
The body is optional — HunterAI falls back to the status code if it is absent or not JSON — but a clear message makes failures easy to diagnose. Only the first 300 characters are kept.

Status codes

“The entity fails” means that one entity (e.g. /leads) is marked failed for the run; entities that already committed stay committed, and the remaining entities still run. The overall sync is reported failed whenever any entity fails — including an optional one. The single exception is a 404 on an optional endpoint, which is not a failure at all. See Partial success.

401 Unauthorized

  • No retry — HunterAI stops the entity the instant it sees 401.
  • Nothing from an unauthenticated call is ingested.
  • Every entity in the run hits the same 401, so the whole sync ends failed.
What the business owner sees: the connection’s sync is marked failed with an authentication error. The fix is to update the token on the connection (see Authentication → Rotating) and re-run the sync.

404 Not Found

404 is how you declare an optional endpoint unimplemented.
Do not return 404 for an implemented endpoint that simply has no data — return 200 with { "data": [], "has_more": false } instead. A 404 on a required endpoint is read as “not implemented” and fails the entity.

429 and 5xx errors

429 and the three transient server codes — 502, 503, 504 — are retried up to 5 times before the entity fails. Every other 5xx is terminal on the first response.
  • 429 — HunterAI waits for the larger of your Retry-After header (seconds, floored to 1s) and an exponential backoff (1, 2, 4, 8, 16 s).
  • 502, 503, 504 — HunterAI backs off exponentially (1, 2, 4, 8, 16 s) and retries. A request that times out is retried the same way.
  • 500, 501, 505, and any other 5xxno retry. The entity fails the moment the response arrives, exactly like a hard 4xx.
Return 503 for a transient problem, not 500. 500 is the reflex code for “something broke”, but HunterAI treats it as permanent and fails the entity on the first attempt with no retry at all. A blip you expect to clear — a restarting process, a saturated pool, a brief lock — must be 503 (or 502/504) to be retried.
If the endpoint is still failing after the retries, that entity is marked failed. The remaining entities still run and keep what they commit, but the overall sync is reported failed.

Other 4xx — hard failure

400, 403, 422, and any other 4xx that is not 401, 404, or 429 fail the entity immediately with no retry. Reserve these for genuine client errors; do not use them for throttling (use 429) or transient issues (use 503).

Malformed records — null or missing id

A 200 page can still carry an unusable record. Every entity’s id is required and non-nullable (see each endpoint’s field table), because HunterAI keys the record on it. A record whose id is null or absent — and, for /events, a null or absent lead_id — is rejected, not stored.
Rejected record (null id)
  • No retry. This is a data error, not a transient one — retrying the same page would fail identically. The entity is marked failed the moment the bad record is seen.
  • No silent coercion. A null id is never turned into a placeholder. Earlier releases coerced it to the string "None", so two null-id records collided on the same key and one silently overwrote the other — that data-loss path is now closed.
  • Partial success still applies. Pages that already committed before the bad record stay committed, and other entities continue — but the overall sync is reported failed. See Partial success.
Do not send { "id": null } for a record that lacks a stable id. Either assign it a stable unique id or omit the record from the page. A null id fails the whole entity for that run, not just the one record.