Skip to content

Contacts

Download .md

The people you text and call: their phone number, email, name, company, attributes, tags and opt-outs, and the workspace’s custom contact fields. How-to: Keep contacts.

Generated from /openapi/v1.json; interactive version: API reference.

methodpathwhat it doessuccess
GET/v1/contactsList contacts, find the one with a phone number or email, search, or sync changes200
POST/v1/contactsCreate a contact201
GET/v1/contacts/{id}Read a contact200
PATCH/v1/contacts/{id}Change a contact200
DELETE/v1/contacts/{id}Delete a contact200
POST/v1/contacts/{id}/tagsTag a contact200
DELETE/v1/contacts/{id}/tags/{tag}Untag a contact200
POST/v1/contacts/{id}/dncOpt a contact out, or back in200
GET/v1/contacts/{id}/consent-eventsList a contact’s consent record200
POST/v1/contacts/{id}/consent-eventsRecord consent, or its withdrawal, by hand201
GET/v1/contacts/{id}/channelsList a contact’s phone numbers and emails200
POST/v1/contacts/{id}/channelsAdd a phone number or email to a contact200, 201
DELETE/v1/contacts/{id}/channels/{address}Remove a phone number or email from a contact200
GET/v1/contact-fieldsList the custom contact fields200

List contacts, find the one with a phone number or email, search, or sync changes.

Without a filter: every contact, newest first. With phone or email: the contacts that have it, the one it belongs to first (a number can be shared by two people); with both, the contacts that have both. With query: the contacts whose name, company, tags, notes, numbers or emails contain that text, newest first. tag, attribute_key / attribute_value and updated_after filter the list exactly, combine with each other and with query, and not with phone or email. With updated_after the order is oldest change first, by updated_at then id: a contact that changes while you page moves to the end and is answered again (keep the latest by id). Deleted contacts are left out, and a sync does not see deletions.

Parameters

nameintypedescription
limitqueryinteger
1–200
Page size, 1 to 200 (default 50).
afterquerystringThe previous page’s next_cursor.
phonequerystringE.164 (in a query string, send the + as %2B). Matches the number on any phone channel: SMS, calls, WhatsApp.
emailquerystringAn email (any case).
queryquerystringUp to 100 characters to search for, in any case: part of a name, a company, a tag, a number, an email or the contact’s notes. At least 3 once lower-cased; a Chinese name counts by its pinyin. Not with phone or email.
tagquerystring
1–64 chars
Only contacts that carry exactly this tag: case-sensitive, as stored (vip is not VIP or vip-lapsed). One tag per request; 1 to 64 characters after trimming.
attribute_keyquerystring
1–64 chars
Only contacts whose attributes has this key (1 to 64 characters). With attribute_value, only those whose value for it is that value.
attribute_valuequerystring
≤ 500 chars
With attribute_key: the value, as text, exactly as the contact object prints it (gold, 5, true). Up to 500 characters. Without attribute_key it is 400 invalid_request.
updated_afterquerystringISO 8601 time. Only contacts whose updated_at is at or after it, oldest change first (by updated_at, then id). For incremental sync: pass the largest updated_at you processed minus 2 minutes, page to the end, and upsert by id. updated_at moves when a contact’s fields, tags, attributes, numbers or emails (added, changed or removed) or opt-outs change.
include_totalquerystring
true, false
true adds total: how many contacts the list holds with these filters, all pages together (counted once, on this request). Default false.

Answers 200 with ContactPage. Errors use the error envelope; common codes: contacts, lists.

Create a contact.

Makes one contact with a phone, an email, or both. The phone is stored for SMS, calls and WhatsApp alike; that is not consent: opt-outs stay on the number, and dnc shows them. One contact per number: a phone or email another contact already has is 409 contact_exists, naming that contact.

Parameters

nameintypedescription
idempotency-keyheaderstring
≤ 255 chars
Send one with every create. A retry with the same key makes no second contact: it answers the first one as it is now. Rules: Idempotency.

Request body: ContactCreate.

Answers 201 with Contact. Errors use the error envelope; common codes: contacts, lists.

Read a contact.

Parameters

nameintypedescription
idpathstring, requiredThe contact’s ct-… id or UUID.

Answers 200 with Contact. Errors use the error envelope; common codes: contacts, lists.

Change a contact.

Only the fields you send change. A new phone replaces the old number on every phone channel, and the old number passes to the next person who shares it, if anyone does. attributes merge: null removes a key. Tags and opt-outs have routes of their own.

Parameters

nameintypedescription
idpathstring, requiredThe contact’s ct-… id or UUID.

Request body: ContactUpdate.

Answers 200 with Contact. Errors use the error envelope; common codes: contacts, lists.

Delete a contact.

The contact disappears from /v1/contacts, lists and future campaigns; past messages, calls and campaign records still show it. A number or email another contact shares passes to that contact. Opt-outs stay on the number or email: a deleted contact never makes an opted-out number reachable.

Parameters

nameintypedescription
idpathstring, requiredThe contact’s ct-… id or UUID.

Answers 200 with the object below. Errors use the error envelope; common codes: contacts, lists.

fieldtypedescription
idstring, required
objectstring, required
always contact
deletedboolean, required
always true

Example: see Keep contacts.

Tag a contact.

Adds tags to the contact; a tag it already has stays once. A workflow that starts when a tag is applied starts for each new one. Answers the contact.

Parameters

nameintypedescription
idpathstring, requiredThe contact’s ct-… id or UUID.

Request body: ContactTagsAdd.

Answers 200 with Contact. Errors use the error envelope; common codes: contacts, lists.

Untag a contact.

Removes one tag (URL-encoded) from the contact. A tag it does not have changes nothing and answers the same. Answers the contact.

Parameters

nameintypedescription
idpathstring, requiredThe contact’s ct-… id or UUID.
tagpathstring, requiredThe tag, URL-encoded.

Answers 200 with Contact. Errors use the error envelope; common codes: contacts, lists.

Opt a contact out, or back in.

Opts every phone number of the contact out of texts, calls or WhatsApp, or its email out of email (status: opted_out), or back in (opted_in). Opt-outs are kept on the number or email, so they hold for anyone who has it. An opt-in never clears an opt-out the person sent themselves: that is 409 stopped_by_person, and nothing changes. All channels change, or none.

Parameters

nameintypedescription
idpathstring, requiredThe contact’s ct-… id or UUID.

Request body: ContactOptOutUpdate.

Answers 200 with ContactWithOptOut. Errors use the error envelope; common codes: contacts, lists.

List a contact’s consent record.

The consent events of the contact’s phone numbers and emails, newest first: the first-contact notice (notice_shown, with the text exactly as sent), consent deemed given, STOP (withdrawn) and START (restored), and events recorded by hand. Kept on the number or email, like opt-outs. A record, not a send rule: dnc decides what may be sent. Omni Octo records it; it does not decide whether a notice is legally sufficient.

Parameters

nameintypedescription
idpathstring, requiredThe contact’s ct-… id or UUID.
limitqueryinteger
1–200
How many, 1 to 200 (default 50), newest first.

Answers 200 with ConsentEventList. Errors use the error envelope; common codes: contacts, lists.

Record consent, or its withdrawal, by hand.

Adds an event to the contact’s consent record: recorded_by_operator (you have consent from elsewhere: a signed form, a call) or withdrawn. For channel sms, voice or whatsapp it is recorded on the contact’s phone number, for email on its email. withdrawn also opts that channel out, as POST /v1/contacts/{id}/dnc does (every number, or every email, of the contact), in the same transaction: both are saved, or neither. Opting back in is POST /v1/contacts/{id}/dnc (a STOP the person sent is cleared only by their START).

Parameters

nameintypedescription
idpathstring, requiredThe contact’s ct-… id or UUID.

Request body: ConsentEventCreate.

Answers 201 with ConsentEvent. Errors use the error envelope; common codes: contacts, lists.

List a contact’s phone numbers and emails.

Every phone number and email of the contact, one item each (a number’s SMS, calls and WhatsApp are one item): phones before emails; in each, the primary one first (the one the contact’s phone or email shows), then oldest first. Each with its opt-outs and whether another contact shares it.

Parameters

nameintypedescription
idpathstring, requiredThe contact’s ct-… id or UUID.
limitqueryinteger
1–200
Page size, 1 to 200 (default 100).
afterquerystringThe previous page’s next_cursor.

Answers 200 with ContactChannelList. Errors use the error envelope; common codes: contacts, lists.

Add a phone number or email to a contact.

Adds one phone (for SMS, calls and WhatsApp) or one email beside the ones the contact has; PATCH phone would replace them all. One contact per number: one another contact has is 409 contact_exists, and nothing changes. Two people who share a number are set up in the dashboard.

Parameters

nameintypedescription
idpathstring, requiredThe contact’s ct-… id or UUID.

Request body: ContactChannelCreate.

Answers 200 with ContactChannel; 201 with ContactChannel. Errors use the error envelope; common codes: contacts, lists.

DELETE /v1/contacts/{id}/channels/{address}

Section titled “DELETE /v1/contacts/{id}/channels/{address}”

Remove a phone number or email from a contact.

address is the phone number in E.164 (URL-encode the + as %2B) or the email. A number goes from SMS, calls and WhatsApp at once. Its opt-outs stay on the number, and someone who shares it keeps it. A contact keeps at least one phone number or email.

Parameters

nameintypedescription
idpathstring, requiredThe contact’s ct-… id or UUID.
addresspathstring, requiredE.164 (%2B15555550100) or an email.

Answers 200 with the object below. Errors use the error envelope; common codes: contacts, lists.

fieldtypedescription
objectstring, required
always contact_channel
addressstring, required
deletedboolean, required
always true

Example: see Keep contacts.

List the custom contact fields.

The workspace’s custom contact fields, in the dashboard’s order: each one’s key is a key of a contact’s attributes and a campaign’s {{contact.<key>}}. Fields are defined in the dashboard; any key works in attributes, defined or not.

Parameters

nameintypedescription
limitqueryinteger
1–200
Page size, 1 to 200 (default 50).
afterquerystringThe previous page’s next_cursor.

Answers 200 with ContactFieldList. Errors use the error envelope; common codes: contacts, lists.

fieldtypedescription
objectstring, required
always list
dataarray of Contact, required
has_moreboolean, required
next_cursorstring or null, requiredPass as after for the next page; null on the last.
totalinteger
≥ 0
Only with include_total=true: how many contacts match these filters, all pages together, counted up to 100,000.
total_cappedboolean
always true
Present (true) when more than 100,000 contacts match: total is then 100000 and the real number is larger.

Example: see Keep contacts.

fieldtypedescription
idstring, requiredct-….
objectstring, required
always contact
uuidstring, required
namestring or null, required
phonestring or null, requiredE.164, or null for none.
emailstring or null, required
companystring or null, required
attributesmap of string to string, required
tagsarray of string, requiredThe contact’s tags, in the order they were added.
dncobject, requiredOpt-out state, kept on the number or email (not on the contact). Change it with POST /v1/contacts/{id}/dnc or in the dashboard; a person’s STOP is cleared only by their START.
dnc.smsboolean or null, requiredtrue when the contact’s phone number, for texts is opted out (by anyone who has it, on any record); null when the contact has none.
dnc.voiceboolean or null, requiredtrue when the contact’s phone number, for calls is opted out (by anyone who has it, on any record); null when the contact has none.
dnc.whatsappboolean or null, requiredtrue when the contact’s phone number, for WhatsApp is opted out (by anyone who has it, on any record); null when the contact has none.
dnc.emailboolean or null, requiredtrue when the contact’s email is opted out (by anyone who has it, on any record); null when the contact has none.
consentContactConsentStatus or null, required
consentsarray of ContactConsentStatus or null, requiredThe latest consent event of each of the contact’s numbers and emails, newest first (at most 20).
created_atstring or null, required
updated_atstring or null, requiredWhen the contact last changed: its fields, tags, phone numbers and emails (added, removed, made primary), or its opt-outs through /v1.

Example: see Create a contact.

phone, email, or both.

fieldtypedescription
phonestring
pattern ^\+[1-9]\d{6,14}$
E.164 (+14155550100). Stored for SMS, calls and WhatsApp alike (one contact per number); that is not consent: opt-outs stay on the number. A number another contact already has is 409 contact_exists.
emailstring
≤ 254 chars
An email another contact already has is 409 contact_exists.
namestring
≤ 200 chars
companystring
≤ 200 chars
attributesmap of string to string or number or boolean
values ≤ 500 chars
Custom fields, readable as {{contact.<key>}} in a campaign’s variables. Values are stored as text. Up to 20 keys (64 characters each), values up to 500 characters, 2048 bytes in all.
tagsarray of string
≤ 50 items; each 1–64 chars
Up to 50 tags, 64 characters each. A workflow that starts on a tag starts for these too.
{
"tags": [
"vip"
]
}

Only the fields you send change.

fieldtypedescription
phonestring or null
pattern ^\+[1-9]\d{6,14}$
A new number replaces the old one on every phone channel; null removes it. A number another contact has is 409 contact_exists, and nothing changes (the whole PATCH is one transaction). An opt-out on the old number stays on that number.
emailstring or null
≤ 254 chars
A new email replaces the old one; null removes it.
namestring or null
≤ 200 chars
companystring or null
≤ 200 chars
attributesmap of string to string or number or boolean or null
values ≤ 500 chars
Merged into the contact’s attributes: a value sets that key, null removes it, keys you leave out stay. Up to 20 keys (64 characters each), values up to 500 characters, 2048 bytes in all.

Example: see Keep contacts.

fieldtypedescription
tagsarray of string, required
1–50 items; each 1–64 chars
1 to 50 tags, each trimmed and 1 to 64 characters. A tag the contact already has stays once.
{
"tags": [
"vip"
]
}
fieldtypedescription
channelsarray of string, required
1–4 items; each sms, voice, whatsapp, email
Which channels: sms (texts), voice (calls) and whatsapp apply to every phone number of the contact, email to every email.
statusstring, required
opted_out, opted_in
opted_out: never text, call or email it there. opted_in: reachable again, unless the person sent STOP themselves (409 stopped_by_person).
reasonstring
≤ 500 chars
Why, for the opt-out history in the dashboard.
{
"channels": [
"sms"
],
"status": "opted_out",
"reason": "Asked on the phone not to be called"
}
fieldtypedescription
idstring, requiredct-….
objectstring, required
always contact
uuidstring, required
namestring or null, required
phonestring or null, requiredE.164, or null for none.
emailstring or null, required
companystring or null, required
attributesmap of string to string, required
tagsarray of string, requiredThe contact’s tags, in the order they were added.
dncobject, requiredOpt-out state, kept on the number or email (not on the contact). Change it with POST /v1/contacts/{id}/dnc or in the dashboard; a person’s STOP is cleared only by their START.
dnc.smsboolean or null, requiredtrue when the contact’s phone number, for texts is opted out (by anyone who has it, on any record); null when the contact has none.
dnc.voiceboolean or null, requiredtrue when the contact’s phone number, for calls is opted out (by anyone who has it, on any record); null when the contact has none.
dnc.whatsappboolean or null, requiredtrue when the contact’s phone number, for WhatsApp is opted out (by anyone who has it, on any record); null when the contact has none.
dnc.emailboolean or null, requiredtrue when the contact’s email is opted out (by anyone who has it, on any record); null when the contact has none.
consentContactConsentStatus or null, required
consentsarray of ContactConsentStatus or null, requiredThe latest consent event of each of the contact’s numbers and emails, newest first (at most 20).
created_atstring or null, required
updated_atstring or null, requiredWhen the contact last changed: its fields, tags, phone numbers and emails (added, removed, made primary), or its opt-outs through /v1.
opt_outobject, requiredWhat this request changed.
opt_out.statusstring, required
opted_out, opted_in
opt_out.appliedarray of string, required
each sms, voice, whatsapp, email
The channels now in status.
opt_out.kept_opted_outarray of string, required
each sms, voice, whatsapp, email
For an opt-in: channels that stay opted out, because another contact who has the same number sent STOP from it.

Example: see Keep contacts.

The latest consent event across the contact’s phone numbers and emails (the first-contact notice, consent deemed given, STOP / START, or recorded by hand), or null when there is none. Omni Octo records it; it does not decide whether it is legally sufficient. History: GET /v1/contacts/{id}/consent-events.

fieldtypedescription
statusstring, required
notice_shown, notice_unconfirmed, deemed_given, withdrawn, restored, recorded
notice_shown (the first-contact notice went out), notice_unconfirmed (a notice send whose outcome is unknown), deemed_given (consent deemed given: the person wrote again after the notice, or the notice went out, per the agent’s setting), withdrawn (STOP, or recorded by hand), restored (START after a STOP), recorded (recorded by hand). A record, not a send rule: opt-outs (dnc) decide what may be sent.
atstring, requiredWhen (ISO 8601).
channelstring, required
sms, voice, whatsapp, email
notice_versioninteger or null, requiredThe version of the agent’s notice the event is about; null for a STOP / START or a hand-recorded event.
agentstring or null, requiredThe agent (ag-…) whose notice it was, or null.

Example: see Keep contacts.

fieldtypedescription
objectstring, required
always list
dataarray of ConsentEvent, required
has_moreboolean, required
next_cursorstring or null, requiredPass as after for the next page; null on the last.

Example: see Keep contacts.

fieldtypedescription
idstring, requiredThe event’s id (UUID).
objectstring, required
always consent_event
channelstring, required
sms, voice, whatsapp, email
addressstring, requiredThe phone number (E.164) or email it is about.
kindstring, required
notice_shown, deemed_given, withdrawn, restored, recorded_by_operator
What happened.
basisstring, required
first_contact_notice, webchat_prechat, opt_out_keyword, operator, api
Why the event exists.
statusstring or null, required
notice_shown, notice_unconfirmed, deemed_given, withdrawn, restored, recorded
The status this event puts the number in: notice_shown (the first-contact notice went out), notice_unconfirmed (a notice send whose outcome is unknown), deemed_given (consent deemed given: the person wrote again after the notice, or the notice went out, per the agent’s setting), withdrawn (STOP, or recorded by hand), restored (START after a STOP), recorded (recorded by hand). A record, not a send rule: opt-outs (dnc) decide what may be sent. null for a notice send that failed (it shows nothing).
delivery_statusstring or null, required
pending, sent, unconfirmed, failed
Only on notice_shown: whether the notice went out.
agentstring or null, requiredag-…, or null.
notice_versioninteger or null, required
notice_textstring or null, requiredThe notice exactly as sent (a snapshot: a later edit never changes it).
notice_sha256string or null, requiredsha256 (hex) of notice_text.
notestring or null, required
sourcestring or null, requiredwebhook, dashboard, api or octo.
occurred_atstring, required

Example: see Keep contacts.

fieldtypedescription
kindstring, required
recorded_by_operator, withdrawn
recorded_by_operator: you have consent from elsewhere (a signed form, a call). withdrawn: the person withdrew it; this also opts that channel out (as POST /v1/contacts/{id}/dnc).
channelstring, required
sms, voice, whatsapp, email
Which of the contact’s addresses: sms, voice and whatsapp mean its phone number, email its email.
notestring
≤ 500 chars
Why, or where the consent came from.
{
"kind": "recorded_by_operator",
"channel": "sms",
"note": "Signed the clinic's paper form on 2026-10-30"
}
fieldtypedescription
objectstring, required
always list
dataarray of ContactChannel, required
has_moreboolean, required
next_cursorstring or null, requiredPass as after for the next page; null on the last.
{
"object": "list",
"data": [
{
"object": "contact_channel",
"type": "phone",
"address": "+15555550100",
"channels": [
"sms"
],
"primary": false,
"shared": false,
"dnc": {
"sms": null,
"voice": null,
"whatsapp": null,
"email": null
},
"created_at": null
}
],
"has_more": false,
"next_cursor": null
}
fieldtypedescription
objectstring, required
always contact_channel
typestring, required
phone, email
addressstring, requiredE.164, or the email.
channelsarray of string, required
each sms, voice, whatsapp, email
The channels the contact has it on. A phone added here has all three.
primaryboolean, requiredThe contact’s primary phone number or email: the one its phone (or email) shows. A contact has one primary phone number and one primary email.
sharedboolean, requiredAnother contact has it too (two people set up in the dashboard as sharing it).
dncobject, requiredThe opt-outs kept on this number or email.
dnc.smsboolean or null, requiredOpted out of texts; null when it does not apply.
dnc.voiceboolean or null, requiredOpted out of calls; null when it does not apply.
dnc.whatsappboolean or null, requiredOpted out of WhatsApp; null when it does not apply.
dnc.emailboolean or null, requiredOpted out of email; null when it does not apply.
created_atstring or null, required
{
"object": "contact_channel",
"type": "phone",
"address": "+15555550100",
"channels": [
"sms"
],
"primary": false,
"shared": false,
"dnc": {
"sms": null,
"voice": null,
"whatsapp": null,
"email": null
},
"created_at": null
}

phone or email, one per request.

fieldtypedescription
phonestring
pattern ^\+[1-9]\d{6,14}$
E.164 (+15555550100). Stored for SMS, calls and WhatsApp alike. A number another contact has is 409 contact_exists.
emailstring
≤ 254 chars
An email another contact has is 409 contact_exists.
primarybooleantrue makes it the contact’s primary phone number (or email): the one its phone (or email) shows, and the one a campaign uses. The other kind’s primary stays as it is.

Example: see Keep contacts.

fieldtypedescription
objectstring, required
always list
dataarray of ContactField, required
has_moreboolean, required
next_cursorstring or null, requiredPass as after for the next page; null on the last.
{
"object": "list",
"data": [
{
"id": "fd-0000000001",
"object": "contact_field",
"key": "tier",
"name": "Tier",
"type": "text",
"options": [],
"position": 1,
"agent_access": "hidden",
"agent_role": false
}
],
"has_more": false,
"next_cursor": null
}
fieldtypedescription
idstring, requiredfd-….
objectstring, required
always contact_field
keystring, requiredThe key in a contact’s attributes, and in a campaign’s {{contact.<key>}}.
namestring, requiredThe label the dashboard shows.
typestring, required
text, number, date, boolean, select, url
What the dashboard lets a person enter. /v1 stores every value as text.
optionsarray of string or null, requiredA select field’s choices; null for other types.
positioninteger, requiredIts place in the dashboard’s order.
agent_accessstring, required
hidden, read, read_write
Whether the business’s AI agent sees this field’s value when it talks to an identified contact. Set in the dashboard. read_write: the agent can also update the value after the contact confirms it in a later message (WhatsApp and SMS; select, number and boolean fields).
agent_roleboolean, requiredThis select field is the contact’s role, which the agent can follow role-specific instructions for. At most one per workspace.
{
"id": "fd-0000000001",
"object": "contact_field",
"key": "tier",
"name": "Tier",
"type": "text",
"options": [],
"position": 1,
"agent_access": "hidden",
"agent_role": false
}