Skip to main content

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.

info

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
}
}
note

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.

note

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: Afrikaans
  • sq: Albanian
  • ar: Arabic
  • az: Azerbaijani
  • bn: Bengali
  • bg: Bulgarian
  • ca: Catalan
  • zh_CN: Chinese (CHN)
  • zh_HK: Chinese (HKG)
  • zh_TW: Chinese (TAI)
  • hr: Croatian
  • cs: Czech
  • da: Danish
  • nl: Dutch
  • en: English
  • en_GB: English (UK)
  • en_US: English (US)
  • et: Estonian
  • fil: Filipino
  • fi: Finnish
  • fr: French
  • de: German
  • el: Greek
  • gu: Gujarati
  • ha: Hausa
  • he: Hebrew
  • hi: Hindi
  • hu: Hungarian
  • id: Indonesian
  • ga: Irish
  • it: Italian
  • ja: Japanese
  • kn: Kannada
  • kk: Kazakh
  • ko: Korean
  • lo: Lao
  • lv: Latvian
  • lt: Lithuanian
  • mk: Macedonian
  • ms: Malay
  • ml: Malayalam
  • mr: Marathi
  • nb: Norwegian
  • fa: Persian
  • pl: Polish
  • pt_BR: Portuguese (BR)
  • pt_PT: Portuguese (POR)
  • pa: Punjabi
  • ro: Romanian
  • ru: Russian
  • sr: Serbian
  • sk: Slovak
  • sl: Slovenian
  • es: Spanish
  • es_AR: Spanish (ARG)
  • es_ES: Spanish (SPA)
  • es_MX: Spanish (MEX)
  • sw: Swahili
  • sv: Swedish
  • ta: Tamil
  • te: Telugu
  • th: Thai
  • tr: Turkish
  • uk: Ukrainian
  • ur: Urdu
  • uz: Uzbek
  • vi: Vietnamese
  • zu: 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:

  1. A first API call is needed to initiate the upload session. An upload-session-id (a string similar to upload:###?sig=###) is returned to be used in the second step.
  2. 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.

info

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>"

> {}

claiming screenshot

info

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.