Chatqus CRM API

Contacts

Server

Contact management


List contacts

GET
https://api-chat.misindo.id
/api/contacts

List contacts › query Parameters

search
​string

Search by name, phone, or email (case-insensitive)

tag
​string

Filter by tag (JSONB containment)

page
​integer · min: 1
Default: 1
per_page
​integer · min: 1 · max: 100
Default: 20

List contacts › Responses

200

Paginated contacts. Note this endpoint wraps its rows in a pagination envelope; it does NOT return a bare array.

Pagination envelope returned by `GET /api/contacts`. Mirrors the server's `PaginatedResponse<Contact>` — see `list_contacts` in `crates/api-gateway/src/routes/contacts.rs`.
​Contact[] · required
total
​integer · int64 · required

Matching contacts across all pages

page
​integer · int64 · required

1-based page number

per_page
​integer · int64 · required

Page size actually applied, clamped to 100

total_pages
​integer · int64 · required

Import contacts from a CSV file

POST
https://api-chat.misindo.id
/api/contacts/import

Multipart upload (field name file). Recognized columns (English or Indonesian header names): name, phone_number, email, channel_source, tags. A headerless file is read in that column order. Phone numbers are normalized to bare digits; rows whose phone or email already exists are skipped. Maximum 5000 data rows per request. Requires contacts.manage permission.

Import contacts from a CSV file › Request Body

file
​string · binary · required

CSV file (UTF-8)

Import contacts from a CSV file › Responses

Import summary

imported
​integer

Newly created contacts

skipped
​integer

Rows whose phone/email already existed

errors
​string[]

Per-row failures, "Row N — reason"


Preview a CSV before importing (wizard step 1)

POST
https://api-chat.misindo.id
/api/contacts/import/preview

Parses raw CSV content and returns headers, up to 5 sample rows, and the total data-row count. Requires contacts.manage permission.

Preview a CSV before importing (wizard step 1) › Request Body

csv_content
​string · required

Raw CSV text

Preview a CSV before importing (wizard step 1) › Responses

200

Parsed preview

headers
​string[]
sample_rows
​array[]
total_rows
​integer

Import contacts from CSV with explicit column mapping (wizard step 2)

POST
https://api-chat.misindo.id
/api/contacts/import/execute

column_mapping maps CSV header names to contact fields (name, phone_number, email, channel, tags). Rows with a phone number upsert on it; email-only rows match existing contacts case-insensitively by email. Enforces the tenant contact quota for newly created contacts. Maximum 5000 data rows per request. Requires contacts.manage permission.

Import contacts from CSV with explicit column mapping (wizard step 2) › Request Body

csv_content
​string · required

Raw CSV text (first row must be the header)

​object · required

CSV header name → contact field

file_name
​string

Original file name, echoed into import history

Import contacts from CSV with explicit column mapping (wizard step 2) › Responses

Import summary

imported
​integer

Newly created contacts

updated
​integer

Existing contacts refreshed by upsert

skipped
​integer

Rows without phone or email

errors
​string[]

Per-row failures, "Row N — reason"


Get a contact by ID

GET
https://api-chat.misindo.id
/api/contacts/{id}

Get a contact by ID › path Parameters

id
​string · uuid · required

Get a contact by ID › Responses

Contact details

id
​string · uuid · required
channel_source
​string · required

whatsapp, instagram, or email

tags
​string[] · required

JSON array of tag strings

created_at
​string · date-time · required
updated_at
​string · date-time · required
phone_number
​string
name
​string
email
​string · email

Update a contact

PATCH
https://api-chat.misindo.id
/api/contacts/{id}

Update name, email, or tags. At least one field required.

Update a contact › path Parameters

id
​string · uuid · required

Update a contact › Request Body

At least one field required.
name
​string
email
​string · email
tags
​string[]

Update a contact › Responses

200

Updated contact

id
​string · uuid · required
channel_source
​string · required

whatsapp, instagram, or email

tags
​string[] · required

JSON array of tag strings

created_at
​string · date-time · required
updated_at
​string · date-time · required
phone_number
​string
name
​string
email
​string · email

List conversations for a contact

GET
https://api-chat.misindo.id
/api/contacts/{id}/conversations

List conversations for a contact › path Parameters

id
​string · uuid · required

List conversations for a contact › query Parameters

page
​integer
Default: 1
per_page
​integer
Default: 20

List conversations for a contact › Responses

200

Conversations for this contact

id
​string · uuid · required
contact_id
​string · uuid · required
status
​ConversationStatus · enum · required

The complete set of conversation states. Mirrors ConversationStatus in crates/omni-common/src/models/conversation.rs. pending and expired were removed by migrations/20260326000039_simplify_conversation_statuses.sql (pending → open, expired → resolved) and snoozed was added by migrations/20260519000012_conversation_snooze.sql. Passing a removed value as a status filter is rejected with 400 before the handler runs, because the query struct deserializes straight into this enum. A snoozed conversation is hidden from the inbox until snoozed_until passes, at which point it reopens automatically.

Enum values:
open
snoozed
resolved
last_message_at
​string · date-time · required
unread_count
​integer · required
created_at
​string · date-time · required
updated_at
​string · date-time · required
contact_channel
​string · required

whatsapp, instagram, or email

assigned_agent_id
​string · uuid
assigned_agent_name
​string

Full name of the assigned agent (null when unassigned)

contact_name
​string
contact_phone
​string
​Assignee[]

All co-assignees (human agents and/or AI agent) attached to this conversation via the conversation_assignees junction, ordered by assignment time. assigned_agent_id remains the denormalized primary.