Turn.io Channel API
A API de Canal permite que você crie um Conector de Canal que possibilita enviar e receber mensagens de plataformas além do WhatsApp. É uma API REST que se baseia na Especificação de Interoperabilidade de Fluxo.
Esta API está em desenvolvimento muito ativo e ainda sofrerá alterações garantidas nos próximos 6 meses (a partir de setembro de 2024). Recomendamos que você acompanhe o versionamento, pois estamos garantindo mudanças significativas pelo menos nos próximos 6 meses.
Não há um sandbox como o do WhatsApp, mas você pode criar múltiplos Canais para testes, staging ou outros usos.
Implementação de Referência
Temos um exemplo de implementação de um Conector de Canal integrado com a Turn.io Channel API. Você pode baixá-lo aqui.
Criando um Canal
Os canais são criados usando o token de autenticação de um número do WhatsApp existente ou de um número sandbox na sua conta Turn.io. Este é um problema temporário de inicialização e será modificado no futuro. Uma vez que seu Canal tenha sido criado, você pode solicitar um token de autenticação para o Canal usando a interface do Turn.io, como faria normalmente.
$ curl https://whatsapp.turn.io/v1/numbers \
> -X POST \
> -H 'Content-Type: application/json' \
> -H 'Authorization: Bearer <your-token>' \
> -d '{
> "backend_type": "channel",
> "number_type": "channel_http_api",
> "from_addr": "the-address-for-your-channel",
> "name": "the display name of your channel",
> "endpoint": "https://example.org/your/webhook/endpoint"
> }'
{
"number": {
"uuid": "the-uuid-for-your-channel"
},
"webhook": {
"uuid": "the-uuid-for-your-webhook",
"hmac_secret": "the-secret-for-your-webhook"
}
}
Enviando mensagens recebidas para o seu Canal
Quando você recebe uma mensagem de entrada, precisa enviá-la para o Canal do Turn.io via nossa API. É necessário fornecer um objeto JSON com os seguintes campos:
contact
: OBJETO, obrigatório - Objeto de contato com informações do usuário que enviou uma mensagem para o Canal. Os detalhes de contato são atualizados com essas informações.message
: OBJETO, obrigatório - Informações sobre uma mensagem recebida pelo Canal.
Parâmetros para o objeto contact
Nome | Obrigatório | Descrição |
---|---|---|
id | sim | String. O ID do usuário. |
profile | sim | Objeto. Um objeto de perfil de usuário. Objetos de perfil possuem as seguintes propriedades: name - String. O nome do usuário. |
Parâmetros para o objeto message
O objeto de mensagem está aninhado dentro do objeto value
e deve ser enviado quando um usuário envia uma mensagem usando o Canal.
Nome | Obrigatório | Descrição |
---|---|---|
type | sim | String. O tipo de mensagem que foi recebida pelo Canal. Os valores possíveis podem ser: text , video , audio , button , image , document , sticker , interactive . |
audio | não | Objeto. Quando o type da mensagem é definido como audio , este objeto deve ser incluído. Objetos de áudio possuem as seguintes propriedades: - id - String. ID do arquivo de áudio. - mime_type - String. Tipo MIME do arquivo de áudio. |
document | não | Objeto. Quando o type da mensagem é definido como document , este objeto deve ser incluído. Objetos de documento possuem as seguintes propriedades: - id - String. ID do arquivo do documento. - mime_type - String. Tipo MIME do arquivo do documento. - link - String. Link para o arquivo do documento. - sha256 - String. Hash SHA256 do arquivo do documento. - filename - String. Nome do arquivo do documento. - caption - String (opcional). Legenda do arquivo do documento. |
video | não | Objeto. Quando o type da mensagem é definido como video , este objeto deve ser incluído. Objetos de vídeo possuem as seguintes propriedades: - id - String. ID do arquivo de vídeo. - mime_type - String. Tipo MIME do arquivo de vídeo. - link - String. Link para o arquivo de vídeo. - sha256 - String. Hash SHA256 do arquivo de vídeo. - filename - String. Nome do arquivo de vídeo. - caption - String (opcional). Legenda do arquivo de vídeo. |
image | não | Objeto. Quando o type da mensagem é definido como image , este objeto deve ser incluído. Objetos de imagem possuem as seguintes propriedades: - id - String. ID do arquivo de imagem. - mime_type - String. Tipo MIME do arquivo de imagem. - link - String. Link para o arquivo de imagem. - sha256 - String. Hash SHA256 do arquivo de imagem. - caption - String (opcional). Legenda do arquivo de imagem. |
text | não | Objeto. Quando o type da mensagem é definido como text , este objeto deve ser incluído. Objetos de texto possuem as seguintes propriedades: - body - String. O texto da mensagem. |
sticker | não | Objeto. Quando o type da mensagem é definido como sticker , este objeto deve ser incluído. Objetos de sticker possuem as seguintes propriedades: - id - String. ID do arquivo do sticker. - mime_type - String. Tipo MIME do arquivo do sticker. - link - String. Link para o arquivo do sticker. - sha256 - String. Hash SHA256 do arquivo do sticker. - filename - String. Nome do arquivo do sticker. - caption - String (opcional). Legenda do arquivo do sticker. |
interactive | não | Objeto. Quando o type da mensagem é definido como interactive , este objeto deve ser incluído. Deve ser usado quando um usuário interagir com sua mensagem. Objetos interativos possuem as seguintes propriedades: - type - Objeto com as seguintes propriedades:
|
from | sim | String. O ID do usuário. Um Canal pode responder a um usuário usando este ID. |
id | sim | String. O ID da mensagem que foi recebida pelo Canal. |
timestamp | sim | String. Timestamp Unix indicando quando a mensagem foi recebida do usuário. |
Texto
$ curl -X POST https://whatsapp.turn.io/v1/numbers/eb2828fa-7605-4c97-bba1-ea74e25c7452/messages \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"contact": {
"id": "the-user-id",
"profile": {
"name": "the-user-name"
}
},
"message": {
"type": "text",
"text": {
"body": "the-text-of-the-message"
},
"from": "the-user-id",
"id": "the-message-id",
"timestamp": "1727291610"
}
}'
> {"success": true}
Documento
{
"message": {
"from": "1234567890",
"id": "message_id_789",
"type": "document",
"document": {
"id": "document_id_1",
"mime_type": "application/pdf",
"link": "https://example.org/document.pdf",
"sha256": "hash_value",
"filename": "the-filename.pdf"
},
"timestamp": "1617183833"
},
"contact": {
"profile": {
"name": "Charlie Brown"
}
}
}
Video
{
"contact": {
"id": "the-user-id",
"profile": {
"name": "the-user-name"
}
},
"message": {
"type": "video",
"video": {
"link": "https://example.org/video.mp4",
"mime_type": "video/mp4",
"id": "video",
"sha256": "hash_value",
"filename": "the-filename.mp4"
},
"from": "the-user-id",
"id": "the-message-id",
"timestamp": "1727291610"
}
}
Audio
{
"contact": {
"id": "the-user-id",
"profile": {
"name": "the-user-name"
}
},
"message": {
"type": "audio",
"audio": {
"link": "https://example.org/audio.mp3",
"mime_type": "audio/mp3",
"id": "audio"
},
"from": "the-user-id",
"id": "the-message-id",
"timestamp": "1727291610"
}
}
Imagem
{
"message": {
"from": "1234567890",
"id": "message_id_456",
"type": "image",
"image": {
"id": "image_id_789",
"mime_type": "image/jpeg",
"link": "https://example.org/image.jpg",
"sha256": "hash_value",
"caption": "Beautiful sunset"
},
"timestamp": "1617183830"
},
"contact": {
"profile": {
"name": "Jane Smith"
}
}
}
Sticker
{
"message": {
"from": "1234567890",
"id": "message_id_789",
"type": "sticker",
"sticker": {
"id": "sticker_id_1",
"mime_type": "image/png",
"link": "https://example.org/sticker.png",
"sha256": "hash_value",
"filename": "the-filename.png",
"caption": "Beautiful sunset"
},
"timestamp": "1617183831"
},
"contact": {
"profile": {
"name": "Bob Johnson"
}
}
}
Resposta de botão interativa
{
"message": {
"from": "1234567890",
"id": "message_id_789",
"type": "interactive",
"interactive": {
"type": "button_reply",
"button_reply": {
"id": "button_id_1",
"title": "Yes"
}
},
"timestamp": "1617183831"
},
"contact": {
"profile": {
"name": "Bob Johnson"
}
}
}
Resposta de lista interativa
{
"message": {
"from": "1234567890",
"id": "message_id_789",
"type": "interactive",
"interactive": {
"type": "list_reply",
"list_reply": {
"id": "list_id_1",
"title": "Option 1",
"description": "Description for option 1"
}
},
"timestamp": "1617183832"
},
"contact": {
"profile": {
"name": "Alice Johnson"
}
}
}
Respostas de erro
{
"errors": {
"contact": {
"id": [
"can't be blank"
],
"profile": [
"can't be blank"
]
},
"message": {
"from": [
"can't be blank"
],
"id": [
"can't be blank"
],
"timestamp": [
"can't be blank"
],
"type": [
"can't be blank"
]
}
},
"message": "Bad Request"
}
Recebendo mensagens de saída do seu Canal
Quando o Turn.io precisar enviar uma mensagem para um assinante no seu Canal, ele notificará você via o endpoint do webhook fornecido ao criar o canal.
A payload JSON é padronizada da seguinte forma:
{
"to": "o-endereço-do-destinatário",
"version": "0.0.1-alpha",
// o bloco FLOIP para o passo atual na jornada
"block": {...},
// o contexto da Jornada no momento deste passo
"context": {...},
// a lista de recursos FLOIP necessários para renderizar a configuração do bloco
// com base no contexto fornecido
"resources": [...],
// os recursos FLOIP acima, mas avaliados no contexto fornecido usando
// a biblioteca de Expressão do Turn.io
"evaluated_resources": [...],
// resposta avaliada do Turn.io para o passo atual do bloco para sua
// referência. Em alguns casos, pode ser mais fácil usar isso
// para a entrega de mensagens via o canal, já que a avaliação do bloco FLOIP e dos recursos
// pode exigir muito mais esforço de desenvolvimento
"turn": {...}
}
As especificações para as payloads úteis da Turn.io estão descritas abaixo.
Todos os ativos de mídia são fornecidos como URLs assinados válidos por 2 minutos.
O conector de Canal receberá a payload JSON acima via HTTP e ela será assinada com o HMAC Secret do Canal. É responsabilidade do implementador validar a assinatura em relação à payload para prevenir acessos indevidos ao endpoint da API.
O endpoint do Conector de Canal deve responder com uma resposta HTTP 200, o cabeçalho HTTP Content-Type configurado como application/json e o seguinte corpo JSON:
{
"messages": [{"id": "the-channel-message-id"}]
}
O Turn.io armazenará este ID de mensagem como referência para a mensagem entregue, e quaisquer atualizações de status sobre o roteamento ou entrega deverão se referir a este ID de mensagem para o processamento e armazenamento corretos.
Mensagem de Texto
Esta é uma mensagem de texto simples, que pode incluir formatação Markdown e Emojis.
{
"type": "text",
"text": {
"body": "the evaluated message"
}
}
Mensagem de Conteúdo
Esta é uma Carta de Conteúdo da interface do Turn.io enviada como mensagem.
Se a carta de conteúdo tiver anexos de mídia, eles não estão incluídos no momento. Esses são enviados como mensagens de texto regulares.
O bloco de configuração FLOIP contém todos os detalhes da carta, no entanto.
{
"type": "text",
"text": {
"body": "the evaluated message"
}
}
Mensagem de Modelo
Esta é uma mensagem de modelo do WhatsApp que precisa ser enviada. Os implementadores do Conector de Canal precisarão decidir como melhor representar esta mensagem no canal de destino. Todas as informações disponíveis para o próprio modelo estão incluídas na configuração do bloco FLOIP.
{
"template": {
"components": [
{
"parameters": [
{
"text": "Jane",
"type": "text"
}
],
"type": "body"
}
],
"language": {
"code": "en"
},
"name": "hello_template",
"namespace": "message_template_namespace_10"
}
}
Mensagem Interativa
Estas são mensagens que incluem botões de resposta rápida, botões de lista ou mensagens de fluxo do WhatsApp. Abaixo estão os detalhes para cada uma.
Botões de resposta rápida
{
"type": "interactive",
"interactive": {
"action": {
"buttons": [
{
"reply": {
"id": "QmxhY2sgQ29mZmVl",
"title": "Black Coffee"
},
"type": "reply"
},
{
"reply": {
"id": "QmxhY2sgVGVh",
"title": "Black Tea"
},
"type": "reply"
}
]
},
"body": {
"text": "Please select an order"
},
"type": "button"
}
}
Botões de lista
{
"type": "interactive",
"interactive": {
"action": {
"button": "Option",
"sections": [
{
"rows": [
{
"id": "8J+YgCBPbmU=",
"title": "😀 One"
}
]
}
]
},
"body": {
"text": "Hello this is my first text."
},
"type": "list"
}
}
WhatsApp Flow
{
"interactive": {
"action": {
"name": "flow",
"parameters": {
"flow_action": "navigate",
"flow_action_payload": {
"screen": "BOOKING"
},
"flow_cta": "Make a booking!",
"flow_id": "1271481943527749",
"flow_message_version": "3",
"flow_token": "3b523386-adbd-4715-b977-7e292aaf57c3",
"mode": "published"
}
},
"body": {
"text": "Please pick a convenient date and time and we'll complete the booking."
},
"footer": {
"text": "This is a demo. No real booking will be made."
},
"header": {
"text": "Make a booking",
"type": "text"
},
"type": "flow"
},
"type": "interactive"
}
Mensagem de vídeo
{
"type": "video",
"video": {
"caption": "hello world",
"link": "https://whatsapp.turn.io/uploads/2024/09/24/26954/9fa7b372-faf5-483f-a82a-61c0129d50fe",
"mime_type": "video/mp4"
}
}
Mensagem de áudio
{
"audio": {
"link": "https://whatsapp.turn.io/uploads/2024/09/24/27038/2f9f3c3f-f965-46d4-9298-1fc66193babd",
"mime_type": "audio/mp3"
},
"type": "audio"
}
Mensagem de documento
{
"document": {
"caption": "hello world",
"filename": "the-filename.pdf",
"link": "https://whatsapp.turn.io/uploads/2024/09/24/27086/056dca1b-b8a4-49a5-8491-9561b0efe19b",
"mime_type": "application/pdf"
},
"type": "document"
}
Mensagem de imagem
{
"image": {
"caption": "hello world",
"link": "https://whatsapp.turn.io/uploads/2024/09/24/27164/2bf705ba-607c-49e4-8b06-7aa1340170b8",
"mime_type": "image/jpeg"
},
"type": "image"
}
Enviando o status de mensagem de saída para o seu Canal
Quando você envia uma mensagem para um usuário, pode enviar o status quando a mensagem é enviada ou entregue a um usuário, ou quando o usuário lê a mensagem entregue pelo seu Canal.
Para fazer isso, você precisa enviar um objeto JSON:
status
: OBJECT, obrigatório - Objeto de status para uma mensagem enviada pelo Canal.
Parâmetros do objeto status
Nome | Obrigatório | Descrição |
---|---|---|
id | sim | String. O ID da mensagem que foi enviada pelo Canal. |
status | sim | String. O status da mensagem que foi enviada pelo Canal. Os valores possíveis podem ser um dos seguintes: sent , delivered , read . |
timestamp | sim | String. Timestamp Unix indicando quando a mensagem foi enviada, entregue ou lida pelo usuário. |
$ curl https://whatsapp.turn.io/v1/numbers/eb2828fa-7605-4c97-bba1-ea74e25c7452/statuses \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"status": {
"id": "the-message-id",
"status": "sent",
"timestamp": "1727291610"
}
}'
> {"success": true}
Resposta de Erro
{
"message": "Bad Request",
"errors": {
"status": {
"id": [
"is invalid"
],
"status": [
"is invalid"
],
"recipient_id": [
"is invalid"
]
}
}
}