Skip to content

Errors and rate limits

Key-authenticated JSON endpoints return an ok boolean. Authentication, scope, routing, and validation errors usually use HTTP status codes; messaging and public booking have documented exceptions.

StatusMeaningTypical body
200Success, duplicate, or a domain/provider result that must be read from ok{"ok":true,...} or messaging {"ok":false,"reason":"..."}
402Message blocked by one of five billing reasons{"ok":false,"reason":"insufficient_credits",...}
403Unknown/revoked key, wrong scope, or unbound CTI agentforbidden, wrong_scope, no_bound_agent
404Unknown API route or inaccessible resourceunknown_route, not_found, no_lead
422Request validation failedbad_channel, bad_phone, invalid_list, malformed_json
429Fixed-window quota exceeded{"ok":false,"error":"rate_limited"}
500Database/insert failuredb_connect_failed, insert_failed

There is no common request ID, error object, Retry-After, or rate-limit response header today. Error identifiers are stable machine-readable strings, but endpoints are not wrapped in a version-negotiation layer.

SurfaceLimitBucket identity
/hooks/api_v1.php (all five routes combined)300/hourAPI key ID
/hooks/lead_intake.php120/hourAPI key ID
/hooks/kpi_ingest.php600/hourAPI key ID
/hooks/dni.php (all actions combined)3,600/hoursalted hash of IP
/book/api.php booking + reschedule10/hourraw IP

These are server-clock fixed windows aligned to Unix hours, not rolling windows. The increment happens before the limit check, and a request above the threshold remains counted. All three key-auth endpoint families use the same key:{id} bucket today, even though they compare it to different thresholds. For example, 121 earlier KPI/API calls can make the next lead-intake request exceed its 120 threshold. Treat the effective allowance as the strictest endpoint you need. All five api_v1.php routes also share one counter and the 300 threshold.

  • Retry 429 after the next hour boundary; use exponential backoff plus jitter if the exact reset is unknown.
  • Retry provider ingress only on 5xx. Duplicate provider identities are designed to be idempotent.
  • Use a stable operation_id when sending messages so retries do not create a second provider operation.
  • Do not retry deterministic 4xx until changing credentials, scope, route, or input.
  • Inspect both HTTP status and JSON ok/reason, especially for messages and booking.