Skip to content

Contacts

Contacts represent WhatsApp users you communicate with. Each contact stores their phone number, profile information, and conversation history.

Retrieve a paginated list of contacts.

Terminal window
GET /api/contacts
ParameterTypeDescription
pageintegerPage number (default: 1)
limitintegerPage size, 1–100. Default 50.
searchstringSearch by profile name (case-insensitive) or phone number. Truncated at 1000 characters.
tagsstringComma-separated tags; matches contacts having ANY of them

Contacts are ordered by last_message_at DESC (nulls last), then created_at DESC.

{
"status": "success",
"data": {
"contacts": [
{
"id": "uuid",
"phone_number": "1234567890",
"name": "John",
"profile_name": "John",
"avatar_url": "https://...",
"status": "active",
"tags": ["vip", "billing"],
"metadata": { "custom_field": "value" },
"last_message_at": "2024-01-01T12:00:00Z",
"last_message_preview": "See you tomorrow!",
"unread_count": 2,
"assigned_user_id": "uuid",
"whatsapp_account": "Main Business",
"last_inbound_at": "2024-01-01T11:00:00Z",
"service_window_open": true,
"marketing_opt_out": false,
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T12:00:00Z"
}
],
"total": 100,
"page": 1,
"limit": 50
}
}
FieldTypeDescription
whatsapp_accountstringThe name of the WhatsApp account the contact belongs to (not a UUID)
assigned_user_iduuidThe agent the contact is assigned to, if any
unread_countintegerNumber of unread incoming messages
last_message_previewstringPreview text of the most recent message
service_window_openbooleanTrue if the customer messaged within the last 24 hours (last_inbound_at)
marketing_opt_outbooleanWhether the contact has opted out of marketing messages
statusstringAlways the literal "active" — contacts have no status column.
namestringMirrors profile_name; both hold the same value.

Retrieve a single contact by ID.

Terminal window
GET /api/contacts/{id}
{
"status": "success",
"data": {
"id": "uuid",
"phone_number": "1234567890",
"name": "John",
"profile_name": "John",
"avatar_url": "https://...",
"status": "active",
"tags": ["vip"],
"assigned_user_id": "uuid",
"whatsapp_account": "Main Business",
"metadata": {
"custom_field": "value"
},
"unread_count": 0,
"last_message_at": "2024-01-01T12:00:00Z",
"last_message_preview": "Thanks!",
"service_window_open": true,
"marketing_opt_out": false,
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T12:00:00Z"
}
}

Create a new contact (or restore a soft-deleted one with the same phone number). Requires contacts:write.

Terminal window
POST /api/contacts
{
"phone_number": "+1234567890",
"profile_name": "John Doe",
"whatsapp_account": "Main Business",
"tags": ["vip"],
"metadata": {
"custom_field": "value"
}
}
FieldTypeRequiredDescription
phone_numberstringYesContact phone number (a leading + is stripped)
profile_namestringNoDisplay name for the contact
whatsapp_accountstringNoName of the WhatsApp account to associate (not a UUID)
tagsstring[]NoTags to attach to the contact
metadataobjectNoFreeform custom data

Returns the created contact in the same shape as Get Contact.

Update an existing contact.

Terminal window
PUT /api/contacts/{id}

Only the fields you send are updated.

{
"profile_name": "John Smith",
"whatsapp_account": "Main Business",
"tags": ["vip", "billing"],
"metadata": {
"custom_field": "updated_value"
},
"assigned_user_id": "uuid",
"clear_assigned_agent": false
}
FieldTypeDescription
profile_namestringDisplay name
whatsapp_accountstringName of the associated WhatsApp account
tagsstring[]Replaces the contact's tags
metadataobjectReplaces the metadata object
assigned_user_iduuidAssign the contact to an agent
clear_assigned_agentbooleanSet true to unassign the contact (takes precedence over assigned_user_id)

Returns the full updated contact (same shape as Get Contact).

Delete a contact and all associated data.

Terminal window
DELETE /api/contacts/{id}
{
"status": "success",
"data": null
}

Assign a contact to a team member.

Terminal window
PUT /api/contacts/{id}/assign
{
"user_id": "uuid"
}

To unassign a contact, set user_id to null:

{
"user_id": null
}
{
"status": "success",
"data": {
"message": "Contact assigned successfully",
"assigned_user_id": "uuid"
}
}

Replace the full set of tags on a contact. Requires contacts:write.

Terminal window
PUT /api/contacts/{id}/tags
{
"tags": ["vip", "billing"]
}
{
"status": "success",
"data": {
"message": "Contact tags updated",
"tags": ["vip", "billing"]
}
}

Mark all incoming messages from a contact as read (clears the unread badge). Used by the chat view when the conversation is open.

Terminal window
POST /api/contacts/{id}/mark-read
{
"status": "success",
"data": { "status": "ok" }
}

The metadata field is a freeform JSON object that can hold any structured data. It is displayed in the Contact Info panel alongside tags and session data.

Different value types are rendered differently in the panel:

TypeDisplay
String, numberKey-value row
BooleanYes / No badge
ObjectCollapsible section with key-value rows
Array of objectsCollapsible table with column headers
Array of primitivesInline badges
{
"metadata": {
"plan": "premium",
"age": 30,
"active": true,
"address": {
"city": "Mumbai",
"state": "Maharashtra",
"zip": "400001"
},
"orders": [
{ "id": "ORD-001", "amount": 1500, "status": "delivered" },
{ "id": "ORD-002", "amount": 2300, "status": "pending" }
],
"interests": ["fitness", "tech", "travel"]
}
}

This renders as:

  • General section — plan, age, and active as key-value rows (boolean shown as a badge)
  • Address section — collapsible key-value pairs for city, state, zip
  • Orders section — collapsible table with Id, Amount, Status columns
  • Interests section — inline badges: fitness, tech, travel

The panel supports one level of nesting. Top-level keys are organized as follows:

Top-level valueRendered as
Primitive (string, number, boolean)Row in the General section
ObjectIts own collapsible section with key-value rows
Array of objectsIts own collapsible section with a table
Array of primitivesIts own collapsible section with badges

Values inside a nested object or array are always displayed as flat text. If a nested object contains another object, the inner value is shown as a raw JSON string. For example:

{
"metadata": {
"address": {
"city": "Mumbai",
"location": { "lat": 19.07, "lng": 72.87 }
}
}
}

Here city displays as Mumbai, but location displays as {"lat":19.07,"lng":72.87}.

Retrieve chatbot session data for a contact, including collected variables and panel configuration.

Terminal window
GET /api/contacts/{id}/session-data
{
"status": "success",
"data": {
"session_id": "uuid",
"flow_id": "uuid",
"flow_name": "Customer Support Flow",
"session_data": {
"customer_name": "John Doe",
"customer_email": "john@example.com",
"order_id": "ORD-12345",
"order_status": "shipped"
},
"panel_config": {
"sections": [
{
"id": "customer",
"label": "Customer Info",
"columns": 1,
"collapsible": true,
"default_collapsed": false,
"order": 1,
"fields": [
{"key": "customer_name", "label": "Name", "order": 1},
{"key": "customer_email", "label": "Email", "order": 2}
]
},
{
"id": "order",
"label": "Order Details",
"columns": 2,
"collapsible": true,
"default_collapsed": true,
"order": 2,
"fields": [
{"key": "order_id", "label": "Order ID", "order": 1},
{"key": "order_status", "label": "Status", "order": 2}
]
}
]
}
}
}
FieldTypeDescription
session_iduuidThe chatbot session ID
flow_iduuidThe flow that collected the data
flow_namestringName of the flow
session_dataobjectKey-value pairs of collected variables
panel_configobjectPanel display configuration from the flow

Contacts are the scope for conversation messages and internal notes.

Messages are sent and retrieved under the contact resource. See the Messages API for full request/response details.

MethodPathDescription
GET/api/contacts/{id}/messagesList messages for a contact (paginated)
POST/api/contacts/{id}/messagesSend a message to the contact
POST/api/contacts/{id}/messages/{message_id}/reactionAdd or remove an emoji reaction on a message

Internal, contact-scoped notes visible to agents (never sent to the customer). Requires chat:read to list and chat:write to modify.

MethodPathDescription
GET/api/contacts/{id}/notesList notes for a contact (cursor-paginated, oldest first)
POST/api/contacts/{id}/notesCreate a note
PUT/api/contacts/{id}/notes/{note_id}Update a note
DELETE/api/contacts/{id}/notes/{note_id}Delete a note
{
"content": "Customer prefers email follow-up."
}
{
"status": "success",
"data": {
"id": "uuid",
"contact_id": "uuid",
"created_by_id": "uuid",
"created_by_name": "Admin User",
"content": "Customer prefers email follow-up.",
"created_at": "2024-01-01T12:00:00Z",
"updated_at": "2024-01-01T12:00:00Z"
}
}

The list endpoint returns { "notes": [...], "total": <n>, "has_more": <bool> }.