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:
- Manage your contact schema
- Managing individual contact data
- 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 benull
surname
: STRING, allowed to benull
location
: LOCATION, allowed to benull
language
: ENUM, limited to ISO-639-3 country codes, allowed to benull
opted_in
: BOOLEAN, defaults tofalse
opted_in_at
: DATETIME, allowed to benull
birthday
: DATETIME, allowed to benull
whatsapp_profile_name
: STRING, allowed to benull
whatsapp_id
: STRING, allowed to benull
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.
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 toISO8601
formatted timestamps in theUTC
timezone.BOOLEAN
, restricted totrue
andfalse
JSON boolean types.STRING
FLOAT
INTEGER
ENUM
which requires an extraenum
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
}
}'
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
}
}'
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
}
}'
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.
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 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
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.