Skip to navigation

Contacts

A contact is the person on the other side of a conversation. The send endpoint creates a contact when you send to a phone number that has no contact. Most integrations therefore only read contacts. Create and update contacts directly when your own system is the main record of your customers.

All the endpoints on this page are under /api/v1/accounts/{account_id}/developer/contacts. They take the same api_access_token header as the send endpoint. The contact id in the path is the id that the API returns in id and contact_id.

Find a contact

POST /api/v1/accounts/{account_id}/developer/contacts/filter

Each item in payload is 1 condition on a contact attribute. query_operator joins the item to the next item. Leave it out on the last item.

curl -X POST https://omni-channel.hams.ai/api/v1/accounts/1/developer/contacts/filter \
-H "api_access_token: $HAMS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"payload": [
{ "attribute_key": "phone_number", "filter_operator": "equal_to", "values": ["+971500000000"] }
]
}'
{
"meta": { "count": 1, "current_page": 1 },
"payload": [
{
"id": 42,
"name": "Katherine Johnson",
"email": "katherine@example.com",
"phone_number": "+971500000000",
"identifier": "cust_9931",
"custom_attributes": { "plan": "pro" }
}
]
}
attribute_keyOperators
name, emailequal_to, not_equal_to, contains, does_not_contain
phone_numberequal_to, not_equal_to, contains, does_not_contain, starts_with
identifier, country_codeequal_to, not_equal_to
city, company_nameequal_to, not_equal_to, contains, does_not_contain
labelsequal_to, not_equal_to, is_present, is_not_present
created_at, last_activity_atis_greater_than, is_less_than, days_before
blockedequal_to, not_equal_to

A contact custom attribute can also be an attribute_key. A key or an operator that the API does not know returns 422. The result has 20 contacts on each page. Add ?page=2 for the next page.

Read a contact by phone number

GET /api/v1/accounts/{account_id}/developer/contacts/by_phone?phone_number=%2B971500000000

The phone_number query parameter is the phone number of the contact in E.164 format. The API adds the + if it is not there. A + in a query string reads as a space, so encode it as %2B.

{
"payload": {
"id": 42,
"name": "Katherine Johnson",
"email": "katherine@example.com",
"phone_number": "+971500000000",
"identifier": "cust_9931",
"blocked": false,
"custom_attributes": { "plan": "pro" },
"additional_attributes": {},
"notes": [
{ "id": 7, "content": "Prefers calls after 2 PM.", "created_at": 1790000000 }
]
}
}

The payload has the same fields as Read a contact, without contact_inboxes. It also has notes: the 10 newest notes of the contact, newest first. Each note has id, content, and created_at as a Unix timestamp in seconds.

If no contact in the account has the number, the API returns 404. If phone_number is missing, the API returns 422 with the code phone_number_required. If the number is not valid, the API returns 422 with the code invalid_phone_number.

Create or update a contact by phone number

POST /api/v1/accounts/{account_id}/developer/contacts/create_or_update

Use this endpoint to keep a contact in sync with your own system. The phone number identifies the contact. If the account has a contact with the number, the API updates it. If not, the API creates a contact. You do not need to find the contact first.

{
"phone_number": "+971500000000",
"name": "Katherine Johnson",
"email": "katherine@example.com",
"identifier": "cust_9931",
"custom_attributes": { "plan": "enterprise" },
"notes": ["Upgraded to Enterprise."]
}

The request must have phone_number in E.164 format. The optional fields are name, email, identifier, blocked, custom_attributes, additional_attributes, notes, and avatar_url. The API changes only the fields that you send.

  • custom_attributes and additional_attributes merge into the stored values, as in Update a contact.
  • notes is a list of text values. The API adds each value as a new note on the contact. It never changes or deletes a note that exists.
  • avatar_url is the URL of an image. The API downloads the image in the background and sets it as the avatar of the contact.

The API does the whole request as 1 unit. If a field is not valid, the API changes nothing. A blank note is not valid. Two requests for the same new number at the same time create 1 contact.

{
"payload": {
"id": 42,
"name": "Katherine Johnson",
"phone_number": "+971500000000",
"custom_attributes": { "plan": "enterprise" },
"notes": [
{ "id": 8, "content": "Upgraded to Enterprise.", "created_at": 1790000500 },
{ "id": 7, "content": "Prefers calls after 2 PM.", "created_at": 1790000000 }
]
},
"created": false
}

The payload has the same shape as in Read a contact by phone number. created is true and the status is 201 when the API created the contact. created is false and the status is 200 when the API updated a contact.

StatusCodeCause
422phone_number_requiredThe request has no phone_number.
422invalid_phone_numberThe number is not in E.164 format.
422validation_failedA field is not valid. For example, another contact has the email address or the identifier. A blank note also causes this error. message gives the reason.

Read a contact

GET /api/v1/accounts/{account_id}/developer/contacts/{id}

The response has the contact in payload. It includes contact_inboxes, the inboxes where the contact has an address.

Create a contact

POST /api/v1/accounts/{account_id}/developer/contacts
{
"name": "Katherine Johnson",
"email": "katherine@example.com",
"phone_number": "+971500000000",
"identifier": "cust_9931",
"custom_attributes": { "plan": "pro" }
}

The fields are name, email, phone_number, identifier, custom_attributes, additional_attributes, blocked, and avatar_url. phone_number must be in E.164 format. The response has the contact in payload.contact.

email, phone_number, and identifier are each unique in the account. A value that another contact has already fails with 422. Set identifier to the key of the customer in your own system. Then you can find the same contact later, also after the customer changes a phone number.

A new contact has no conversation. To start a conversation, send a message with contact_id. See Messages.

Update a contact

PATCH /api/v1/accounts/{account_id}/developer/contacts/{id}

The API changes only the fields that you send. custom_attributes and additional_attributes merge into the stored values. A key that you send replaces the same key. A key that you do not send stays.

{ "custom_attributes": { "plan": "enterprise", "renewal": "2027-01-31" } }

List contacts

GET /api/v1/accounts/{account_id}/developer/contacts?page=1

Each page has 20 contacts. The list includes only contacts with an email address, a phone number, or an identifier. The count can therefore be smaller than the total in the dashboard. To show only contacts with a label, add labels[]=vip.

The developer API cannot delete a contact. An administrator deletes contacts in the dashboard.