Contacts
Contacts are org-scoped records of the people your agents have talked to, built from channel identity (a WhatsApp phone number, an email). One contact can span multiple agents and multiple conversations. See the Contacts guide for the concept, anddocs/architecture/CONTACT_IDENTITY.md in the repo for the full design.
All API requests require a valid JWT token in the
Authorization: Bearer <token> header. The API Gateway decodes the JWT and forwards auth context (user-id, organization-id, user-email, x-platform-role, x-org-role) as headers to downstream services.update_contact tool recording an email/phone), never directly through this API.
List Contacts
Paginated, filterable list of contacts in your organization.Query Parameters
string
Case-insensitive match against display name, phone, or email.
string
Filter to contacts with an identity on this channel. One of
whatsapp_business, web, widget, email. (whatsapp — the WAHA channel — is deliberately absent: it stores full JIDs rather than phone numbers, so it never produces a contact identity and the filter would always return zero rows.)string
Filter to contacts who have at least one conversation with this agent.
string
One of
new, engaged, qualified, closed.number
default:"20"
Max rows per page. Capped at 50.
number
default:"0"
Pagination offset.
Response (200)
object[]
number
Total matching contacts (for pagination), independent of
limit/offset.Get Contact
Full contact detail: the contact record, its field-write history (with provenance), and every channel identity tied to it.Path Parameters
string
required
Contact UUID.
Response (200)
object
The bare contact record —
id, organizationId, displayName, displayNameSource, primaryPhone, primaryEmail, sourceChannel, status, attributes, firstSeenAt, lastSeenAt. Note this is narrower than a list item: channels, conversationCount and agentCount are list-only aggregates and are not returned here.object[]
Newest first. Every write to this contact, applied or rejected.
object[]
A row with a non-null
rejected_reason was never applied to the contact — it records an attempt where the field’s value (an email or phone) already belonged to a different contact in the organization. Nothing was overwritten and nothing was merged; these rows are a work-list for a future contact-merge feature, which does not exist yet.Errors
error
Contact not found, or belongs to a different organization.
Get Contact Conversations
Every conversation this contact has had, across every agent — the view a single agent’s own conversation list can’t provide.Path Parameters
string
required
Contact UUID.
Response (200)
object[]
Errors
error
Contact not found, or belongs to a different organization.
Export Contacts
Streams every matching contact to a CSV file, ready for other CRMs and outbound tools. The response is chunked and has no row cap — large contact lists download completely.Query Parameters
string
Case-insensitive match against display name, phone, or email. Same semantics as List Contacts.
string
Filter to contacts with an identity on this channel. One of
whatsapp_business, web, widget, email.string
Filter to contacts who have at least one conversation with this agent.
string
One of
new, engaged, qualified, closed.string
Ignored unless the caller is a platform admin. Non-admin callers always export their own organization.
Response (200)
string
text/csv; charset=utf-8 (with a UTF-8 BOM).string
attachment; filename="contacts.csv".string
chunked — streamed, no Content-Length.- Base columns:
id,organizationId,displayName,displayNameSource,primaryPhone,primaryEmail,sourceChannel,status,firstSeenAt,lastSeenAt,organizationName,conversationCount,agentCount,channels,identities. - Attribute columns: every collected attribute key (e.g.
company,title, or any custom field), one column per key, sorted alphabetically.
Developer Key Export
The same CSV export for API-key integrations. Requires thecontacts:read scope.
Authentication
Query Parameters
Same asGET /api/contacts/export, except organizationId is not accepted — the export is always scoped to the key’s own organization.
Errors
error
Key is missing the
contacts:read scope — { "error": "insufficient_scope" }.Update Contact Status
Set a contact’s status — the only field this API lets you write directly (everything else is written by an agent via theupdate_contact tool, or resolved from channel identity).
Path Parameters
string
required
Contact UUID.
Request Body
string
required
One of
new, engaged, qualified, closed.Response (200)
object
The updated contact — the same bare contact record as
GET /contacts/:id (no list-only aggregates).fieldHistory row with source: "operator", attributing the change to the authenticated user.
Errors
error
status missing or not one of the four valid values.error
Contact not found, or belongs to a different organization.

