Skip to main content

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, and docs/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.
There is no create/delete endpoint here — contacts are created automatically when a channel identity resolves (an inbound WhatsApp message, or the update_contact tool recording an email/phone), never directly through this API.

List Contacts

Paginated, filterable list of contacts in your organization.

Query Parameters

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[]
Capped at 50 conversations, most recently updated first.

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.
Each row is one contact. Columns, in order:
  • 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 the contacts:read scope.

Authentication

Query Parameters

Same as GET /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 the update_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).
A successful status change also appends a 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.