Skip to content

Contacts API

Two requests cover most integrations: find the contact, then read their history. Both need a bearer token. See Authentication.

On Boost, your integration can also create and edit contacts, so a new customer in your booking system or CRM is a named contact in SimpleVox before they ever call.

Search the workspace’s shared contacts with q. It matches part of a name, company, email address or phone number, ignoring case, and results are sorted by name. Reference: Search contacts.

Terminal window
curl --get 'https://api.simplevox.app/v1/contacts' \
-H 'Accept: application/json' \
-H "Authorization: Bearer $ACCESS_TOKEN" \
--data-urlencode 'q=Mary'

Two people match here, and the response says so. Trimmed, it looks like this:

{
"data": [
{
"id": "9b1f4c2e-3a5d-4e8f-9c71-2d6a8e0b4f13",
"revision": 3,
"name": "Mary Jensen",
"portrait_url": null,
"company": "Example Property Management",
"phones": [{ "label": "Mobile", "value": "+12025550142" }]
},
{
"id": "4e7a0d91-6c2b-4f3e-8a15-b9c3d2e1f706",
"revision": 1,
"name": "Mary Ortiz",
"portrait_url": null,
"company": null,
"phones": [{ "label": null, "value": "+12025550187" }]
}
],
"next_cursor": null,
"meta": { "resolution": "disambiguation_required" }
}

meta.resolution tells you whether the search settled on one person: resolved when one contact matched, not_found when none did, and disambiguation_required when several did.

Contact search reads only the shared contacts list. It never looks inside calls or texts to guess who someone is, and it doesn’t return the hidden placeholder contacts SimpleVox keeps for numbers nobody has saved. To find calls and texts from unsaved numbers, use activity.

Contacts are shared with the whole workspace, so search returns them even when you can’t see any calls or texts with that person.

revision counts saved changes to a contact, so you can tell when details changed. On Boost, you also send it back when you update a contact.

portrait_url is the contact’s photo, or null when nobody has added one. Show initials instead when it’s null or the image doesn’t load. The link loads without a token, so anyone you pass it to can see the photo. When someone replaces or removes the photo in SimpleVox, the old link stops working, so fetch the contact again rather than storing the link. Photos are added in the app; see Contacts.

Fetch one contact by ID to get its current details and revision, which also comes back as a quoted ETag header, such as "3". Any token can read contacts. Fetch the contact this way right before you update it. Reference: Retrieve a contact.

A contact’s history is their calls and texts across every number the token can read, newest first. Narrow it to one number, to calls or texts, to a call filter or to a time window; without dates, it isn’t limited to a window. Reference: List a contact’s history.

Terminal window
curl --get 'https://api.simplevox.app/v1/contacts/9b1f4c2e-3a5d-4e8f-9c71-2d6a8e0b4f13/history' \
-H 'Accept: application/json' \
-H "Authorization: Bearer $ACCESS_TOKEN" \
--data-urlencode 'channel=message' \
--data-urlencode 'occurred_from=2026-10-01T00:00:00Z' \
--data-urlencode 'limit=20'

Each entry is one call or text. A few things to know when you read them:

  • contact can be null. History and activity entries have the same shape, and in activity, calls and texts from numbers nobody has saved have no contact.
  • Status is current. conversation_status is the conversation’s shared status now (open, unread or done), with who last changed it, not its status when the call or text happened. Reading never changes it.
  • Sent isn’t delivered. state is where a call or text stands as reported. A sent text isn’t necessarily delivered or read.
  • Missing isn’t empty. content_state says whether content is available, pending, unavailable or deleted. A recording or transcript that isn’t available yet doesn’t mean nothing was said.
  • Cite the original. source identifies the call or text with its kind, id and source_version. To fetch it from code, see Quote calls and texts. Its url opens the call or text in the SimpleVox inbox for a person, who signs in with their own account and sees it only if their access allows.
  • Test records are marked. synthetic is true only for test records that never went through a carrier.

Treat state strings as open-ended, and ignore fields you don’t use. meta.coverage says how many entries the token can see and whether there are more; see Pagination.

call_filter uses the same rules as the inbox:

Value Includes
incoming, outgoing Calls in that direction.
missed Incoming calls that were missed, busy or canceled.
voicemail Calls that went to voicemail, even if the recording isn’t available yet.
unresponded Missed calls and voicemails with no later connected call back or accepted outbound text in the same conversation, including replies after your window. Failed or unknown replies don’t count.
any All calls.

A call filter returns only calls, so it can’t be combined with channel=message. These filters don’t create follow-up tasks or change conversation status.

History covers saved contacts. To see calls and texts from numbers nobody has saved, use activity. It takes the same filters as history, but needs a window of up to 31 days, and an unsaved caller’s entries have contact set to null. Reference: List recent activity.

Terminal window
curl --get 'https://api.simplevox.app/v1/activity' \
-H 'Accept: application/json' \
-H "Authorization: Bearer $ACCESS_TOKEN" \
--data-urlencode 'occurred_from=2026-10-19T00:00:00-05:00' \
--data-urlencode 'occurred_until=2026-10-26T00:00:00-05:00' \
--data-urlencode 'call_filter=unresponded'

AI apps use list_history for the same job. See MCP tools.

On Boost, a personal API token with Edit shared contacts can create and update contacts. Contacts are shared with the whole workspace, so changes show up for everyone right away. Only admins can give a token this permission. See Write permissions on Boost.

Every write needs an Idempotency-Key header with a new UUID, so a retry after a timeout never makes a second change. See Writes on Boost. Phone numbers use E.164 format, such as +12025550142; the reference lists every field and its limits.

Reference: Create a contact.

Terminal window
curl https://api.simplevox.app/v1/contacts \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"name": "Glen Parker",
"company": "Northside Bakery",
"phones": [{ "label": "Office", "value": "+12025550173" }],
"notes": "Booked a furnace check for the bakery online."
}'

A new contact returns 201 Created with the saved contact, its revision of 1 and an ETag of "1". Calls and texts SimpleVox already has with that phone number move onto the new contact, the same as when someone saves a contact in the app.

Send only the fields you want to change. Fields you leave out stay as they are. A list you send, such as phones, replaces the whole list, so include every phone number the contact should keep; send [] to clear it. Reference: Update a contact.

Besides an Idempotency-Key, an update needs If-Match with the contact’s current revision as a quoted string, such as "3", from the ETag of your last read. That stops you from overwriting a change someone else made in the meantime.

Terminal window
curl -X PATCH https://api.simplevox.app/v1/contacts/9b1f4c2e-3a5d-4e8f-9c71-2d6a8e0b4f13 \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H 'If-Match: "3"' \
-d '{ "company": "Example Property Group" }'

The response has the updated contact and its new ETag. If someone changed the contact since you read it, you get 412. Fetch it again, apply your change to the current details, and send it with the new revision.

For the error codes each contact request can return, see the reference; Handle errors covers what to do about them.