Skip to main content

Contacts

Contacts are the people your campaigns call. Each contact carries tags for campaign targeting, a preferred language, and an arbitrary metadata object for lead data (cart contents, product interest, budget signals, etc.).

The public API uses the path /v1/contacts. The internal API uses /api/voice/customers — these are the same records.

List contacts​

GET /v1/contacts
Scope: voice:contacts:read

Query parameters:

ParamTypeNotes
searchstringSearches name and phone
tagsstring (repeatable)?tags=Region:West&tags=Segment:Lapsed
limitintegerDefault 50, max 100
offsetintegerDefault 0

Response:

{
"contacts": [
{
"uuid": "cst_c1d2e3",
"name": "Jordan Ellis",
"phone": "+14155550192",
"email": "[email protected]",
"tags": ["Region:West", "Segment:Lapsed"],
"preferred_language": "en",
"metadata": {
"product_interest": "Standing Desk Pro",
"cart_value": 850,
"lead_type": "cart_abandon"
},
"total_calls": 2,
"last_contact": "2026-05-12T14:34:00Z",
"created_at": "2026-05-01T00:00:00Z"
}
],
"total": 1482
}

Create contact​

POST /v1/contacts — Rate limit: 60/minute
Scope: voice:contacts:write

Request body:

{
"name": "Jordan Ellis",
"phone": "+14155550192",
"email": "[email protected]",
"tags": ["Region:West", "Segment:Lapsed"],
"preferred_language": "en",
"metadata": {
"product_interest": "Standing Desk Pro",
"cart_value": 850,
"lead_type": "cart_abandon"
}
}
FieldTypeRequiredNotes
namestring✓
phonestring✓E.164 format; upsert key
emailstring—
tagsstring[]—Used for campaign targeting
preferred_languagestring—hi, en, hi-en, ta, te, kn, mr, bn, gu, ml, pa
metadataobject—Arbitrary JSON; carries lead data
overwriteboolean—Default false. If true, upserts on duplicate phone; if false, returns 409 Conflict

Update contact​

PATCH /v1/contacts/{contact_id} — Rate limit: 60/minute
Scope: voice:contacts:write

All fields optional. metadata is merged, not replaced — pass only the keys you want to change.

{
"tags": ["Region:West", "Priority:High"],
"metadata": { "cart_value": 920 }
}

Delete contact​

DELETE /v1/contacts/{contact_id}
Scope: voice:contacts:write


Bulk upsert contacts​

POST /v1/contacts/bulk — Rate limit: 10/minute
Scope: voice:contacts:write

Upserts a batch of up to 1,000 contacts by phone number. Designed for daily lead-list pushes from external CRMs.

Request body:

{
"contacts": [
{
"name": "Jordan Ellis",
"phone": "+14155550192",
"tags": ["Region:West", "Segment:Lapsed"],
"metadata": { "cart_value": 850 }
}
],
"mode": "upsert"
}
FieldNotes
contactsUp to 1,000 records per request
modeupsert (merge by phone) or append (always create new)

Response:

{
"created": 218,
"updated": 66,
"errors": [
{ "row": 4, "phone": "+10000000000", "reason": "invalid phone format" }
]
}

One bad row does not fail the batch. Paginate for lists larger than 1,000.


Import from file​

POST /api/voice/customers/import — Rate limit: 5/minute

Imports from a CSV or JSON file. Upserts by phone number.

Request: multipart/form-data

  • file — .csv or .json

CSV format:

name,phone,email,tags,preferred_language,metadata
Jordan Ellis,+14155550192,[email protected],"Region:West,Segment:Lapsed",en,"{""cart_value"":850}"

Response:

{ "imported": 284, "updated": 41, "skipped": 2, "errors": [] }

Metadata endpoints​

EndpointReturns
GET /api/voice/customers/meta/tagsAll unique tag values in the organisation
GET /api/voice/customers/meta/branchesTags prefixed with Branch:
GET /api/voice/customers/meta/count?tags=...Count of contacts matching given tags