API Extensions
The Turn API is completely aligned with the official WhatsApp Business API. However, we do provide a few extensions that are exposed via HTTP headers.
Bidirection webhooks
We expose webhooks for outbound messaging in addition the the inbound webhooks that the WhatsApp Business API supports. The section on webhooks explains this in further detail.
Message retrieval API
$ curl -X GET \
-H 'Accept: application/vnd.v1+json' \
-H 'Authorization: Bearer token' \
https://whatsapp.turn.io/v1/contacts/<wa-id>/messages
> '
{
"chat": {
"owner": "<wa-id>",
"assigned_to": {
"id": "account assignment id",
"name": "name of account holder",
"type": "OPERATOR"
} | null,
"state": "OPEN",
"state_reason": "Re-opened by inbound message.",
"unread_count": 1
},
"messages": [{
"to": "wa-id",
"from": "another-wa-id",
"type": "text",
...
"_vnd": {
"v1": {
"direction": "outbound",
"in_reply_to": "an-earlier-inbound-external-id",
"author": {
"name": "the name of the author",
"type": "SYSTEM | OPERATOR"
}
}
}
}]
}
Performing an HTTP GET request with our vendor specific Accept: application/vnd.v1+json
header unlocks this functionality. We only return the 50 most recent messages sent and received for the given WhatsApp ID, ordered by message timestamp
descending.
We return the original WhatsApp message but include our vendor specific _vnd
key in the payload with supplementary information Turn has on record.
We've decided to expose this functionality using HTTP headers to ensure we can remain continued compatibility with the WhatsApp Business API.
Label retrieval API
$ curl -X GET \
-H 'Accept: application/vnd.v1+json' \
-H 'Authorization: Bearer token' \
https://whatsapp.turn.io/v1/labels
> '
{
"labels": [{
"uuid": "the-uuid",
"value": "the-label-name",
"color": "the-label-color",
}]
}
Performing an HTTP GET request with our vendor specific Accept: application/vnd.v1+json
header unlocks this functionality. Return all labels currently
linked to a number in a single result. We may be adding pagination in the future.
Retrieving Labelled Messages
$ curl -X GET \
-H 'Accept: application/vnd.v1+json' \
-H 'Authorization: Bearer token' \
https://whatsapp.turn.io/v1/labels/<label-uuid>/messages
> '
{
"has_more": true,
"next": "/v1/labels/<label-uuid>/messages?p=1",
"message_labels": [{
"confidence": 0.8,
"metadata": {},
"deleted": false,
"message": {
// .. the message payload
}
}]
}
Performing an HTTP GET request with our vendor specific Accept: application/vnd.v1+json
header unlocks this functionality. We return messages in batches of 50. The response will inform whether or not there are more results available to retrieve.
Linking an outbound message to an inbound
$ curl -X POST "https://whatsapp.turn.io/v1/messages" \
-H "Authorization: Bearer token" \
-H "X-Turn-In-Reply-To: some-earlier-external-id" \
-H "Content-Type: application/json" \
-d '
{
...
}'
It is often desirable to be able to link outbound messages to inbound messages for auditing, analysis, or the training of machine learning models purposes.
To do so add our vendor specific X-Turn-In-Reply-To
HTTP header with the external id you want linked. We do not validate this value, it stored on file as is.
When you receive an outbound message via the webhooks, you can now retrieve the message that the outbound was a reply to.
Archiving a Chat
$ curl -X POST "https://whatsapp.turn.io/v1/chats/<wa-id>/archive" \
-H "Authorization: Bearer token" \
-H "Accept: application/vnd.v1+json" \
-H "Content-Type: application/json" \
-d '
{
"before": "the-message-id",
"reason": "the reason for archiving"
}
'
It can be useful to archive a chat if it has been processed in an automated fashion. To do so the chat archive endpoint expects a before
parameter that references a message id and a reason
parameter that has a text reason for the archive operation.
The chat will only be archived if the last inbound
message received in the chat is the same message as the before
parameter references. If successful the archive endpoint will return an HTTP 201 Created
response with the updated chat object.
If the last message received does not match the before
then the endpoint will return an HTTP 200 OK
response with the chat object unchanged.
Delete a chat
WhatsApp doesn't support deleting of chats or messages on a recipient's device. However, Turn does allow one to delete chats from the Turn user interface and databases.
When a chat is deleted, all that remains is a single anonymised record as a historical artefact. All messages, attachments, and contact details are permanently removed from the Turn database.
Any data that was relayed either through webhooks, API calls, or integrations such as BigQuery, is not removed. If that data needs to be removed, it is the responsibility of you as an account owner to delete those.
$ curl -X DELETE "https://whatsapp.turn.io/v1/chats/<wa-id>" \
-H "Authorization: Bearer token" \
-H "Accept: application/vnd.v1+json" \
-H "Content-Type: application/json"
> {
"chat": {
"state": "CLOSED",
"state_reason": "Chat culled",
"owner": "8bcc8dcf-2c09-473f-92f2-ea12da0ecdb3",
"unread_count": 0
}
}
If the chat has many messages and attachments, a single API call may not be enough to remove all messages. It will attempt to delete as many as it possibly can within 1 single minute.
Receiving an HTTP 200 OK
with chat object returned indicates the successful
completion.
If the response is an HTTP 202 Accepted
status with an empty JSON '' object
returned then one will need to make the API call again. Call the API repeatedly
until receiving an HTTP 200 OK
status response to ensure full deletion of the
chat.
Labelling a Message
$ curl -X POST "https://whatsapp.turn.io/v1/messages/<message-id>/labels" \
-H "Authorization: Bearer token" \
-H "Accept: application/vnd.v1+json" \
-H "Content-Type: application/json" \
-d '{
"labels": ["compliment", "other", {"label": "question", "confidence": 0.9}]
}'
The API allows one to add labels to messages, these are then automatically also promoted to the chat that the message is part of, and displayed in the UI in realtime. The labels
attribute can contain a combination of strings of label names and/or JSON objects with a label
and a confidence
attribute. The confidence indicator is typically derived based on text analysis and can be stored together with the label. Note that the confidence values are not shown the user interface, and are only available via the API.
Any labels provided that have not been defined in the UI will be ignored.
Marking a Message as handled/unhandled
$ curl -X PATCH "https://whatsapp.turn.io/v1/messages/<message-id>" \
-H "Authorization: Bearer token" \
-H "Accept: application/vnd.v1+json" \
-H "Content-Type: application/json" \
-d '{
"is_handled": true
}'
The API allows either human operators or chatbots to mark incoming messages as handled or unhandled. Messages marked as handled/unhandled are displayed on the UI with an informative icon which helps a human operator to identify pending questions/requests.
The is_handled
attribute is a boolean value. Defaults to null
in case that is not ever marked as handled/unhandled.
Creating Message Templates
$ curl -X POST "https://whatsapp.turn.io/graph/v14.0/<your phone number without leading +>/message_templates" \
-H "Content-Type: application/json" \
-d '
{
"access_token": <whatsapp-api-token>,
"category": "ISSUE_RESOLUTION",
"name": "test_template",
"language": "en_US",
"components": [
{
"type": "HEADER",
"format": "TEXT",
"text": "Welcome {{1}}",
"example": {
"header_text": ["Jane", "John"]
}
},
{
"type": "BODY",
"text": "Hi {{1}}, you are now registered as a {{2}}.",
"example":{
"body_text": [
["Jane", "beta tester"],
["John", "support hero"]
]
}
},
{
"type": "FOOTER",
"text": "Thanks again for signing up!"
}
]
}'
The message templates API allows one to create message templates for approval by Facebook. It is a REST API that works the same as Facebook's own Graph API as documented at developers.facebook.com.
Please note that this API is hosted on the graph/
path, as it mirrors Facebook's Graph APIs. We currently support version v14.0
of the graph API (under the graph/v14.0
path).
The payload can be submitted as JSON and using normal Authorization: Bearer ...
type authentication. We're allowing the form based post and access_token
based authentication to ensure compatibility.
The name
field is limited to 512 characters. The content
field is limited to 1024 characters.
Possible values for status
are:
APPROVED
PENDING
REJECTED
PENDING_DELETION
DELETED
Valid category
codes are:
AUTHENTICATION
MARKETING
UTILITY
Valid language
codes are:
af
: Afrikaanssq
: Albanianar
: Arabicaz
: Azerbaijanibn
: Bengalibg
: Bulgarianca
: Catalanzh_CN
: Chinese (CHN)zh_HK
: Chinese (HKG)zh_TW
: Chinese (TAI)hr
: Croatiancs
: Czechda
: Danishnl
: Dutchen
: Englishen_GB
: English (UK)en_US
: English (US)et
: Estonianfil
: Filipinofi
: Finnishfr
: Frenchde
: Germanel
: Greekgu
: Gujaratiha
: Hausahe
: Hebrewhi
: Hindihu
: Hungarianid
: Indonesianga
: Irishit
: Italianja
: Japanesekn
: Kannadakk
: Kazakhko
: Koreanlo
: Laolv
: Latvianlt
: Lithuanianmk
: Macedonianms
: Malayml
: Malayalammr
: Marathinb
: Norwegianfa
: Persianpl
: Polishpt_BR
: Portuguese (BR)pt_PT
: Portuguese (POR)pa
: Punjabiro
: Romanianru
: Russiansr
: Serbiansk
: Slovaksl
: Slovenianes
: Spanishes_AR
: Spanish (ARG)es_ES
: Spanish (SPA)es_MX
: Spanish (MEX)sw
: Swahilisv
: Swedishta
: Tamilte
: Teluguth
: Thaitr
: Turkishuk
: Ukrainianur
: Urduuz
: Uzbekvi
: Vietnamesezu
: Zulu
Listing Message Templates
$ curl -X GET "https://whatsapp.turn.io/graph/v14.0/<your phone number without leading +>/message_templates?access_token=<whatsapp-api-token>"
You can use the WhatsApp Business Management API to get a list of your existing message templates.
This API mimics the behavior of the Facebook Graph Message Templates API. For more details please check the official documentation at developers.facebook.com.
Media Message Templates
$ curl -X POST "https://whatsapp.turn.io/graph/v14.0/<your phone number without leading +>/message_templates" \
-d 'access_token=<whatsapp-api-token>' \
-d 'category=ISSUE_RESOLUTION' \
-d 'name=test_media_template' \
-d 'language=en_US' \
-d 'components=[
{
"type": "HEADER",
"format": "IMAGE",
"example": {
"header_handle": ["example-file-handle", "another-example-file-handle"]
}
},
{
"type": "BODY",
"text": "Hi {{1}}, you are now registered as a {{2}}.",
"example":{
"body_text": [
["Jane", "beta tester"],
["John", "support hero"]
]
}
}
]'
The message templates API also allows creating media templates, which are templates that include a media element in the header.
WhatsApp supports three types of media headers for templates:
TEXT
: A standard text header.DOCUMENT
: Header that includes a PDF document.VIDEO
: Header that includes a video.IMAGE
: Header that includes an image.
This API mimics the behavior of the Facebook Graph Message Templates API. For more details please check the official documentation at developers.facebook.com.
Example Media
To facilitate the approval of your template by Facebook, you should provide one or more example media files that exemplify the type of images/video/documents that will be used when sending messages based on the template.
To do that, you first need to upload the example media(s) using the Resumable Upload API and then provide the file handle(s) returned by that API inside the example -> header_handle
field of the HEADER
component (see example on the right).
Interactive Message Templates
$ curl -X POST "https://whatsapp.turn.io/graph/v14.0/<your phone number without leading +>/message_templates" \
-d 'access_token=<whatsapp-api-token>' \
-d 'category=ISSUE_RESOLUTION' \
-d 'name=test_interactive_template' \
-d 'language=en_US' \
-d 'components=[
{
"type": "HEADER",
"format": "TEXT",
"text": "Welcome {{1}}",
"example": {
"header_text": ["Jane", "John"]
}
},
{
"type": "BODY",
"text": "Hi {{1}}, you are now registered as a {{2}}.",
"example":{
"body_text": [
["Jane", "beta tester"],
["John", "support hero"]
]
}
},
{
"type": "FOOTER",
"text": "Thanks again for signing up!"
},
{
"type": "BUTTONS",
"buttons":[
{
"type": "PHONE_NUMBER",
"text": "Call us",
"phone_number": "+1(650) 555-1111"
},
{
"type": "URL",
"text": "Visit out website",
"url": "https://www.website.com/{{1}}",
"example": ["https://www.website.com/welcome", "https://www.website.com/register"]
}
]
}
]'
The message templates API also allows creating interactive templates. Interactive templates are message templates that include a component of type BUTTONS
, which includes a list of buttons.
WhatsApp supports three types of buttons:
PHONE_NUMBER
: A button that dials a phone number when clicked.URL
: A button that opens a URL when clicked.QUICK_REPLY
: A button that submits an automatic reply when clicked.
This API mimics the behavior of the Facebook Graph Message Templates API. For more details please check the official documentation at developers.facebook.com.
Resumable Upload API
$ curl -X POST "https://whatsapp.turn.io/graph/v14.0/app/uploads" \
-d 'number=<your phone number without leading +>' \
-d 'access_token=<whatsapp-api-token>' \
-d 'file_length=<file-length-in-bytes>' \
-d 'file_type=<file-mime-type>'
> {"id":"<upload-session-id>"}
# The previous API call returns an <upload-session-id>,
# which is needed as part of the URL for the next call
$ curl "https://whatsapp.turn.io/graph/<upload-session-id>" \
-F 'number=<your phone number without leading +>' \
-F 'access_token=<whatsapp-api-token>' \
-F 'file=@"/path/to/media/file.extension"' \
-H 'file_offset: 0'
> {"h":"<file-handle>"}
# The previous API call returns a <file-handle>,
# which can be used when creating a media template
The Resumable Upload API allows uploading media files to Facebook and returns file handles that can be used to specify examples when creating media templates.
The upload happens in two separate phases:
- A first API call is needed to initiate the upload session. An
upload-session-id
(a string similar toupload:###?sig=###
) is returned to be used in the second step. - A second API call is needed to upload the media file via
multipart/form-data
using the upload session that was just initiated.
The second API call uploads the media file to Facebook and returns an ID referred to as file handle (a string similar to 4::###
).
When specifying the file type, please use a standard MIME type such as image/jpeg
, video/mp4
, etc.
Conversation Claiming
Turn allows one to delegate the control of a conversation with an individual to an external system for a limited amount of time.
Extending a lease to an external system means all webhooks and all automation configured in Turn are sidestepped for the duration of the lease for that conversation.
This can be done in the Automation section in the Turn UI.
To ensure a webhook is suitable for use with conversation claiming, first add it as a normal webhook under the Webhooks settings, have it marked as enabled but not set to receive any inbound or outbound messages.
This ensures that the webhook is registered in Turn but is not automatically receiving any messaging traffic.
Next, set up the automation by selecting an appropriate trigger. For Step 2, Add an Action select the Call a webhook action.
In Step 3 select the webhook that is meant to receive the inbound traffic as a result of a specific action trigger happening.
$ curl -X POST "https://whatsapp.turn.io/v1/messages" \
-H "Authorization: Bearer token" \
-H "X-Turn-Claim-Extend: <claim-uuid>" \
-H "Content-Type: application/json" \
-d '
{
"preview_url": false | true,
"to": "whatsapp-id",
"type": "text",
"text": {
"body": "your-text-message-content"
}
}'
> {
"messages": [{
"id": "gBEGkYiEB1VXAglK1ZEqA1YKPrU"
}]
}
$ curl -X POST "https://whatsapp.turn.io/v1/messages" \
-H "Authorization: Bearer token" \
-H "X-Turn-Claim-Release: <claim-uuid>" \
-H "Content-Type: application/json" \
-d '
{
"preview_url": false | true,
"to": "whatsapp-id",
"type": "text",
"text": {
"body": "your-text-message-content"
}
}'
> {
"messages": [{
"id": "gBEGkYiEB1VXAglK1ZEqA1YKPrU"
}]
}
$ curl -X POST "https://whatsapp.turn.io/v1/messages/<message-id>/automation" \
-H "Authorization: Bearer token" \
-H "X-Turn-Claim-Release: <claim-uuid>"
> {}
Make sure to allow the webhook to claim the conversation using the toggle button
When the trigger fires, a lease will be created for the conversation in question.
Your webhook will receive an HTTP request with an X-Turn-Claim: <claim-uuid>
header set. This lease lasts for a maximum of 5 minutes.
The webhook has 5 minutes to reply, otherwise Turn will expire the lease and reclaim control over the conversation.
The webhook can do 2 things with this lease for outbound messages posted to the /v1/messages
endpoint:
It can set the X-Turn-Claim-Extend: <claim-uuid>
HTTP request header to extend the claim for another 5 minutes.
It can set the X-Turn-Claim-Release: <claim-uuid>
HTTP request header to release the claim and delegate control back to Turn.
Finally, there is a use case where the final response would want to submit the last message submitted back to Turn for re-evaluation by the Automation rules.
Submitting an HTTP POST to /v1/messages/<message-id>/automation
with the X-Turn-Claim-Release: <uuid>
HTTP header set results in the release of the claim and resubmitting the message to Turn for evaluation for Automation.
The use case here is where the webhook claiming the conversation can provide a fallback to Turn for functionality like "Reply 📌 Menu to return to the main menu".
Managing Conversation Claims
Sometimes, you may wish to get the details of, or delete a conversation claim for a single number. This is sometimes useful when you want to release a claim on a conversation being managed by a particular Stack, which has had some sort of unforeseen circumstance, such as a loop or a timeout that renders the conversation unmanageable. This endpoint allows you to get the details of a single claim, and/or delete it. This is primarily for debugging purposes and is not intended for large scale use.
$ curl -X GET "https://whatsapp.turn.io/v1/contacts/<wa_id>/claim" \
-H "Authorization: Bearer token" \
-H "Accept: application/vnd.v1+json"
> {
"claimed_at" : "2023-05-06T10:19:00.791854Z",
"claimed_until" : "2023-05-09T10:19:00.801389Z",
"inserted_at" : "2023-05-08T10:19:00.801498Z",
"message_uuid" : "6f988bca-462e-407e-afa4-1d7c3a3fc4f6",
"metadata" : {},
"scope" : "stack",
"scope_id" => "503db06f-0f29-41f4-8287-57326e9f4217",
"updated_at" : "2023-05-08T10:19:00.801498Z",
"uuid" : "f9dc8f9a-3b77-4a74-aeed-551cd51cb432"
}
and in order to delete the claim:
$ curl -X DELETE "https://whatsapp.turn.io/v1/contacts/<wa_id>/claim" \
-H "Authorization: Bearer token" \
-H "Accept: application/vnd.v1+json"
> {"claim_uuid": "f9dc8f9a-3b77-4a74-aeed-551cd51cb432"}
Managing Content
Turn allows content, FAQs and Automators, to be imported and exported via the API. This can be useful for translation services or adding turn to your own information pipeline.
Exporting Content
$ curl -X GET "https://whatsapp.turn.io/v1/export" \
-H "Authorization: Bearer token" \
-H "Accept: application/vnd.v1+json" > content.json
By default, the response will only include FAQ and Automator items which have not been flagged as deleted in the database through the is_deleted
property. If the consumer wishes to include deleted items as well, it is possible to specify the include_deleted
query string parameter:
$ curl -X GET "https://whatsapp.turn.io/v1/export?include_deleted=true" \
-H "Authorization: Bearer token" \
-H "Accept: application/vnd.v1+json" > content.json
The response from this API can be used as a valid input for the import APIs described below.
Importing Content
When using HTTP POST
the existing content is deleted and only the content present in the import will be available once it has completed.
If the import fails, no content will be deleted.
$ curl -X POST "https://whatsapp.turn.io/v1/import" \
-H "Authorization: Bearer token" \
-H "Content-Type: application/json" \
-H "Accept: application/vnd.v1+json" \
-d @content.json
Updating Content
When using HTTP PUT
the existing content is NOT deleted. Content will be updated if a matching entry with the same UUID is found.
Entries present in the import without a UUID will be created.
If the import fails, no content will be modified.
$ curl -X PUT "https://whatsapp.turn.io/v1/import" \
-H "Authorization: Bearer token" \
-H "Content-Type: application/json" \
-H "Accept: application/vnd.v1+json" \
-d @content.json
This endpoint can also be used to delete and undelete FAQ and Automator data, by modifying the is_deleted
property of the items with a corresponding UUID.