Skip to main content

Contacts

Contact fields are a core part of how Turn stores data related to a given contact. We provide some standard fields for all contacts, as well as the ability for you to create your own custom fields. You can manage contact fields in the UI at Settings > People and you can import, and segment your users in the People tab.

Alternatively we document belowhow you can do the following programatically with our REST API:

  1. Manage your contact schema
  2. Managing individual contact data
  3. Bulk importing contacts

Turn Contact Profile API

By default the following fields are reserved, and immediately available, to all contacts in Turn:

  • name: STRING, allowed to be null
  • surname: STRING, allowed to be null
  • location: LOCATION, allowed to be null
  • language: ENUM, limited to ISO-639-3 country codes, allowed to be null
  • opted_in: BOOLEAN, defaults to false
  • opted_in_at: DATETIME, allowed to be null
  • birthday: DATETIME, allowed to be null
  • whatsapp_profile_name: STRING, allowed to be null
  • whatsapp_id: STRING, allowed to be null

Any fields created using the /v1/contacts/schema endpoint are created in addition to these fields.

Fetching the Contact Profile Schema

To get the current schema for a given number, you can do the following:

$ curl https://whatsapp.turn.io/v1/contacts/schemas \
-H 'Authorization: Bearer token' \
-H 'Content-Type: application/json'
> {
"fields": [
{
"custom": false,
"default": null,
"display": "WhatsApp Profile Name",
"is_private": true,
"name": "whatsapp_profile_name",
"null": true,
"type": "STRING"
},
{
"custom": false,
"default": null,
"display": "Last Seen At",
"is_private": false,
"name": "last_seen_at",
"null": true,
"type": "DATETIME"
},
{
"custom": false,
"default": null,
"display": "Name",
"is_private": true,
"name": "name",
"null": true,
"type": "STRING"
},
{
"custom": false,
"default": null,
"display": "Surname",
"is_private": true,
"name": "surname",
"null": true,
"type": "STRING"
},
{
"custom": false,
"default": null,
"display": "Location",
"is_private": true,
"name": "location",
"null": true,
"type": "LOCATION"
},
{
"custom": false,
"default": false,
"display": "Opted In",
"is_private": false,
"name": "opted_in",
"null": false,
"type": "BOOLEAN"
},
{
"custom": false,
"default": null,
"display": "Language",
"enum": [],
"is_private": true,
"name": "language",
"null": true,
"type": "ENUM"
},
{
"custom": false,
"default": null,
"display": "Birthday",
"is_private": true,
"name": "birthday",
"null": true,
"type": "DATETIME"
},
{
"custom": false,
"default": null,
"display": "First Message Received At",
"is_private": false,
"name": "first_message_received_at",
"null": true,
"type": "DATETIME"
},
{
"custom": false,
"default": null,
"display": "Last Message Sent At",
"is_private": false,
"name": "last_message_sent_at",
"null": true,
"type": "DATETIME"
},
{
"custom": false,
"default": null,
"display": "Last Message Received At",
"is_private": false,
"name": "last_message_received_at",
"null": true,
"type": "DATETIME"
},
{
"custom": false,
"default": null,
"display": "Opted In At",
"is_private": false,
"name": "opted_in_at",
"null": true,
"type": "DATETIME"
},
{
"custom": false,
"default": null,
"display": "WhatsApp Id",
"is_private": true,
"name": "whatsapp_id",
"null": true,
"type": "STRING"
},
{
"custom": false,
"default": false,
"display": "Is blocked",
"is_private": false,
"name": "is_blocked",
"null": false,
"type": "BOOLEAN"
}
],
"uuid": "d4deddbf-9cca-4e26-ab55-9b6653da1325",
"version": "0.0.1-alpha"
}

Creating New Contact Profile Fields

To create additional fields, issue an HTTP POST request to /v1/contacts/schemas. This will create a new schema definition with a new uuid.

From that point onwards, Turn will still return contacts according to previous schemas but will only allow writing of contacts according to the most recently created schema.

caution

It is the responsibility of API clients to migrate contact profile details between schema changes.

$ curl -X POST https://whatsapp.turn.io/v1/contacts/schemas \
-H 'Authorization: Bearer token' \
-H 'Accept: application/vnd.v1+json' \
-H 'Content-Type: application/json' \
-d '{
"version": "0.0.1-alpha",
"fields": [{
"name": "consent",
"display": "Consent Given",
"type": "BOOLEAN",
"default": false,
"is_private": true
}, {
"name": "gender",
"display": "Gender",
"type": "ENUM",
"null": false,
"is_private": false,
"default": "UNDISCLOSED",
"enum": [
{"value": "MALE", "display": "Male"},
{"value": "FEMALE", "display": "Female"},
{"value": "OTHER", "display": "Other"},
{"value": "UNDISCLOSED", "display": "Undisclosed"}
]
}]
}'
> {
"version": "0.0.1-alpha",
"uuid": "<the-new-schema-uuid>",
"fields": [{
"name": "consent",
"display": "Consent Given",
"type": "BOOLEAN",
"default": false,
"is_private": true
}, {
"name": "gender",
"display": "Gender",
"type": "ENUM",
"null": false,
"is_private": false,
"default": "UNDISCLOSED",
"enum": [
{"value": "MALE", "display": "Male"},
{"value": "FEMALE", "display": "Female"},
{"value": "OTHER", "display": "Other"},
{"value": "UNDISCLOSED", "display": "Undisclosed"}
]
}]
}

The response is the schema with the uuid value identifying the schema in the payload.

The support field types are:

  • DATETIME, restricted to ISO8601 formatted timestamps in the UTC timezone.
  • BOOLEAN, restricted to true and false JSON boolean types.
  • STRING
  • FLOAT
  • INTEGER
  • ENUM which requires an extra enum parameter that specifies the list of allowed values.
  • LOCATION which expects a {"latitude": ..., "longitude": ...} object.

All fields can be configured to accept null values by specifying "null": true or "null": false.

The only exception is the BOOLEAN field type.

The BOOLEAN field requires a default value set to either true or false, it cannot be configured to allow null values.

All fields must supply a default value, this can be null if "null": true is specified, with the exception of BOOLEAN fields.

The display parameter is the human friendly display name for the field.

The name parameter must be lower case, must start with a letter, and can only contain lower case alphanumeric characters and underscores.

The enum parameter only accepts a list of options. Options are objects have a value and a display field. The value must be upper case, must start with a letter, and can only contain upper case alphanumeric characters or underscores.

To retrieve an older schema do an HTTP GET request to the /v1/contacts/schemas/<uuid> endpoint.

Retrieve a Contact Profile

To retrieve a contact's profile details make a call to the /v1/contacts/<wa-id>/profile endpoint

$ curl https://whatsapp.turn.io/v1/contacts/27123456789/profile \
-H 'Authorization: Bearer token' \
-H 'Accept: application/vnd.v1+json'
> '{
"version": "0.0.1-alpha",
"schema": "<the-schema-uuid>",
"generation": 0,
"fields": {
"language": "ENG",
"date_of_birth": null,
"age": 2,
"consent": false,
"location": null,
"name": null,
"risk_profile": 2.0,
"surname": null,
"birthday": null,
"opted_in": false,
"opted_in_at": null,
"whatsapp_id": null,
"whatsapp_profile_name": null
}
}'
info

Please note that a newly created field will only appear in a contact's profile if its value has been explicitly set or updated. Default values for new fields are not automatically included in the contact's profile. For instance, if a contact field is referenced but has not been explicitly set, the default value for that field will be displayed wherever it is referenced. But when querying contact fields via the /v1/contacts/{wa_number}/profile endpoint, the response will only include fields that have been explicitly persisted in the contact's data. New fields created in the UI will not appear in the response unless their values have been updated and saved.

To retrieve a contact profile for an earlier schema, supply the schema uuid as a query parameter:

$ curl "https://whatsapp.turn.io/v1/contacts/27123456789/profile?schema=<the-schema-uuid>" \
-H 'Authorization: Bearer token' \
-H 'Accept: application/vnd.v1+json'
> '{
"version": "0.0.1-alpha",
"schema": "<the-schema-uuid>",
"generation": 0,
"fields": {
"language": "ENG",
"date_of_birth": null,
"age": 2,
"consent": false,
"location": null,
"name": null,
"risk_profile": 2.0,
"surname": null,
"birthday": null,
"opted_in": false,
"opted_in_at": null,
"whatsapp_id": null,
"whatsapp_profile_name": null
}
}'

The generation field automatically increments by 1 for every change made to the contact's profile.

Replace a Contact Profile

To replace a contact's profile full details make an HTTP PUT call to the /v1/contacts/<wa-id>/profile endpoint. If the full set is not supplied, the defaults from the schema are applied for the missing fields.

$ curl -X PUT https://whatsapp.turn.io/v1/contacts/27123456789/profile \
-H 'Authorization: Bearer token' \
-H 'Accept: application/vnd.v1+json' \
-H 'Content-Type: application/json' \
-d '{"name": "Fizbo"}'
> '{
"version": "0.0.1-alpha",
"generation": 1,
"schema": "<the-schema-uuid>",
"fields": {
"language": nil,
"date_of_birth": null,
"age": 0,
"consent": false,
"location": null,
"name": "Fizbo",
"risk_profile": 0.0,
"surname": null,
"birthday": null,
"opted_in": false,
"opted_in_at": null,
"whatsapp_id": null,
"whatsapp_profile_name": null
}
}'
info

Please note that contact profile updates will always be validated according to the latest schema.

Update a Contact profile

To partially update a contact's profile make an HTTP PATCH call to the /v1/contacts/<wa-id>/profile endpoint and only supply the fields needing to be updated.

The details from the previous contact profile generation are automatically inherited.

$ curl -X PATCH https://whatsapp.turn.io/v1/contacts/27123456789/profile \
-H 'Authorization: Bearer token' \
-H 'Accept: application/vnd.v1+json' \
-H 'Content-Type: application/json' \
-d '{"name": "Fizbo"}'
> '{
"version": "0.0.1-alpha",
"generation": 2,
"schema": "<the-schema-uuid>",
"fields": {
"language": "ENG",
"date_of_birth": null,
"age": 2,
"consent": false,
"location": null,
"name": "Fizbo",
"risk_profile": 2.0,
"surname": null,
"birthday": null,
"opted_in": false,
"opted_in_at": null,
"whatsapp_id": null,
"whatsapp_profile_name": null
}
}'
info

Please note that partial contact profile updates will always be validated according to the latest schema.

Reset a Contact profile

To reset a contact's profile full details to the schema defaults make an HTTP DELETE call to the /v1/contacts/<wa-id>/profile endpoint.

caution

Please note that this behaviour will delete the Contact's profile by resetting all of the values to their defaults, or null. If you want to delete the Contact itself, as well as the contact profile use the Chat deletion endpoint

$ curl -X DELETE https://whatsapp.turn.io/v1/contacts/27123456789/profile \
-H 'Authorization: Bearer token' \
-H 'Accept: application/vnd.v1+json' \
-H 'Content-Type: application/json'
> '{}'

Import Contact Profiles via CSV

To import contact information in bulk you can upload a CSV file with all the details using the /v1/contacts API endpoint.

The data imported from the CSV file is governed by the contact schema set using the Contact Profile API.

The CSV file you supply must have a column named urn which has the phone number of the contact you either you want to create or update. If a contact profile exists for the given phone number then it is updated with the fields supplied. If it doesn't exist then it is created with the fields supplied.

The values from the columns will overwrite an existing values for the contacts in the database. Any other contact field values will remain as is.

Here is an example format:

urn,name,surname,age
+271234567890,Peter,Parker,21
+271234567891,Peter,Barker,22
+271234567892,Peter,Porker,19
+271234567893,Spider,Ham,20

For each of these the import would look up a profile for the given URN and then update the fields. The fields are converted to the matched types in the contact profile and will return an error if the field cannot be converted.

note

Note that any fields supplied in the CSV that aren't known to the contact schema are ignored.

The contacts are imported is immediate into our primary database and they will be available for messaging. They are asynchronously indexed into our secondary search database and so it may take a minute or two for them to be available in Reminders and Triggers after the import completes.

This API will stream the result of the import in realtime as a response formatted as CSV:

$ curl \
-X POST \
-H 'Content-Type: text/csv' \
-H 'Authorization: Bearer token' \
--data-binary 'urn,name,surname
+27123456789,spider,ham
+27123456790,peter,parker
' https://whatsapp.turn.io/v1/contacts
> 'urn,name,surname
+27123456789,spider,ham
+27123456790,peter,parker'

Error reporting on mismatched types

If we attempt to send a CSV with invalid data the API will return inline with the error prefixed with ERROR at the position of the field in question.

$ curl \
-X POST \
-H 'Content-Type: text/csv' \
-H 'Authorization: Bearer token' \
--data-binary 'urn,name,surname,opted_in
+27123456789,peter,parker,"yes"
' https://whatsapp.turn.io/v1/contacts
> "+27123456789,,,ERROR: cannot cast value of 'yes' to boolean"

Unknown columns

If the URN itself is malformed the error will be in the second column.

Any column supplied in the CSV which are not able to be mapped against a known field are dropped and will not be streamed back as part of the reply.

$ curl \
-X POST \
-H 'Content-Type: text/csv' \
-H 'Authorization: Bearer token' \
--data-binary 'urn,name,surname,unknown_field
+27123456789,peter,parker,unknown_field_value
' https://whatsapp.turn.io/v1/contacts
> 'urn,name,surname
+27123456789,peter,parker'

Casting to Contact Field types

All known values are streamed back after parsing and type casting, representing Turn's internal representations of those values. The URN will be normalised into the E164 format and the string "true" is converted to boolean true for the opted_in field:

$ curl \
-X POST \
-H 'Content-Type: text/csv' \
-H 'Authorization: Bearer token' \
--data-binary 'urn,name,surname,opted_in
+27123456789,peter,parker,"true"
' https://whatsapp.turn.io/v1/contacts
> '+27123456789,peter,parker,true'

File Size Limitations

The API is currently limited to allow files of a maximum of 1 Megabyte in size. If you need to upload more contact data than can be expressed in 1 Megabyte then you will need to split the total contact database into separate CSV files, each with their own header row.

[Deprecated] Retrieving Contact Profiles

The endpoint at /v1/export/contacts/details has been deprecated. To export contact data, please use the Data Export API.

[Deprecated] WhatsApp status check

caution

The original on-premise Contacts API endpoint has been repurposed starting from WhatsApp Business API v2.43. It does no longer provide status information about a phone number.

Responses from this endpoint no longer return an 'invalid' status. A 'valid' status and wa_id will be provided for all requests, regardless of whether the phone number is connected to a WhatsApp account. There is no guarantee that the returned wa_id will be valid. These changes are applicable for both direct responses, as well as webhook responses for non-blocking calls. WhatsApp will continue to return the same 'processing' and 'failed' responses as they do today.

The original implementation of the API remains in place but is undocumented and will be removed entirely in the future.