Contacts
Read-only access to your project’s contacts. Contacts represent the people who have talked to you across channels.
List contacts
List contacts (cursor-paginated).
Response
{
"results": [
{
"id": "7c4e9b02-1a55-4e88-b0d1-2f3a4b5c6d7e",
"display_name": "Jane Doe",
"notes": "VIP customer",
"metadata": { "plan": "gold" },
"is_blocked": false,
"created_at": "2026-06-01T09:00:00+00:00",
"updated_at": "2026-07-01T10:40:00+00:00"
}
],
"next_cursor": null,
"has_more": false
}Supports cursor and limit (default 50, max 100).
Get a contact
Retrieve a single contact by id.
Responses
{
"id": "7c4e9b02-1a55-4e88-b0d1-2f3a4b5c6d7e",
"display_name": "Jane Doe",
"notes": "VIP customer",
"metadata": { "plan": "gold" },
"is_blocked": false,
"created_at": "2026-06-01T09:00:00+00:00",
"updated_at": "2026-07-01T10:40:00+00:00"
}{ "detail": "contact_not_found" }The contact object
- id, display_name.
- notes — free-text operator notes.
- metadata — arbitrary JSON attached to the contact.
- is_blocked — whether the contact is blocked.
- created_at, updated_at — ISO-8601.
Create a contact
Create a contact and, optionally, the address it is reachable at. Requires the write scope.
curl -X POST https://chatonio.com/api/v1/contacts/ \
-H "Authorization: Bearer csk_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"display_name": "Jane Roe",
"identities": [
{ "channel_id": "your-email-channel-id", "address": "jane@acme.com" }
]
}'Identities are email-only
A contact is reachable through an identity — a channel plus the address the customer uses on it. Today you can only create one on an email channel, because an email address is something you can know in advance. Telegram, Viber and WhatsApp ids are issued by those platforms when the customer first writes in, and a web-chat identity is a browser session, so an address you invent for them would reach nobody. Those return 400 identity_not_supported_for_channel_type.
A contact with no identity is allowed — useful for mirroring your CRM — but it cannot be messaged, and it will not be matched when that person writes in, because incoming messages are matched on the identity. If the address already belongs to another contact you get 409 identity_already_exists naming it, rather than a second half-empty record.
Update a contact
PATCH /api/v1/contacts/{id}/ changes display_name, notes, metadata and tag_ids. Fields you leave out are untouched; tag_ids replaces the whole set, and [] clears it.
Tags
POST /api/v1/contacts/{id}/tags/ with {"tag_id": "…"} adds a tag, and DELETE /api/v1/contacts/{id}/tags/{tag_id}/ removes one. List the project’s tags with GET /api/v1/tags/. A tag from another project is rejected rather than ignored.
This contact’s other conversations
GET /api/v1/contacts/{id}/conversations/ returns every conversation this person has had, across all channels, newest first — useful for showing history next to a ticket in your own tooling.