Skip to main content

Envio de Mensagens

Esta página documenta como enviar mensagens para o agente e receber respostas via webhook.

Add Message

Envia uma mensagem para ser processada pelo agente em uma sessão ativa.

Endpoint

POST https://api.tech4.ai/agent/v2/conversation/{session_id}/message

Headers

x-client-key: YOUR_API_KEY
Content-Type: application/json

Parâmetros

NomeTipoObrigatórioDescrição
session_iduuidSim(Path) ID da sessão ativa
typestringNãoTipo de mensagem: text, audio, document, image, video, interactive. Padrão: text
textobjectCondicionalObrigatório quando type=text. Ver Objeto text
audioobjectCondicionalObrigatório quando type=audio. Ver Objeto de mídia
documentobjectCondicionalObrigatório quando type=document. Ver Objeto de mídia
imageobjectCondicionalObrigatório quando type=image. Ver Objeto de mídia
videoobjectCondicionalObrigatório quando type=video. Ver Objeto de mídia
interactiveobjectCondicionalObrigatório quando type=interactive. Ver Objeto interactive

Objeto text

NomeTipoObrigatórioDescrição
bodystringSimConteúdo textual da mensagem

Objeto de mídia

Aplica-se para audio, document, image e video.

NomeTipoObrigatórioDescrição
iduuidCondicionalID do arquivo previamente enviado via Upload File.
captionstringNãoLegenda ou mensagem associada ao arquivo

Objeto interactive

Usado para representar a resposta do usuário a um componente interativo (botão ou lista). O campo type discrimina entre os dois subtipos.

NomeTipoObrigatórioDescrição
typestringSimSubtipo da interação: button_reply ou list_reply
button_replyobjectCondicionalObrigatório quando type=button_reply. Ver tabela abaixo
list_replyobjectCondicionalObrigatório quando type=list_reply. Ver tabela abaixo

Campos de button_reply — resposta do usuário ao clicar em um botão de resposta rápida:

NomeTipoObrigatórioDescrição
idstringSimID do botão selecionado
titlestringSimTexto do botão selecionado

Campos de list_reply — resposta do usuário ao selecionar uma opção de lista:

NomeTipoObrigatórioDescrição
idstringSimID da opção selecionada
titlestringSimTítulo da opção selecionada
descriptionstringNãoDescrição da opção selecionada

Exemplos de Requisições

Mensagem de Texto

curl -X POST https://api.tech4.ai/agent/v2/conversation/550e8400-e29b-41d4-a716-446655440000/message \
-H "x-client-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "text",
"text": {
"body": "Olá, você pode me ajudar?"
}
}'

Mensagem de Áudio

curl -X POST https://api.tech4.ai/agent/v2/conversation/550e8400-e29b-41d4-a716-446655440000/message \
-H "x-client-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "audio",
"audio": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"caption": "Mensagem de voz do cliente"
}
}'

Mensagem com Documento

curl -X POST https://api.tech4.ai/agent/v2/conversation/550e8400-e29b-41d4-a716-446655440000/message \
-H "x-client-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "document",
"document": {
"id": "b2c3d4e5-f6a7-8901-bcde-f23456789012",
"caption": "Contrato assinado"
}
}'

Mensagem com Imagem

curl -X POST https://api.tech4.ai/agent/v2/conversation/550e8400-e29b-41d4-a716-446655440000/message \
-H "x-client-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "image",
"image": {
"id": "c3d4e5f6-a7b8-9012-cdef-345678901234",
"caption": "Foto do problema"
}
}'

Mensagem com Vídeo

curl -X POST https://api.tech4.ai/agent/v2/conversation/550e8400-e29b-41d4-a716-446655440000/message \
-H "x-client-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "video",
"video": {
"id": "d4e5f6a7-b8c9-0123-def4-567890123456",
"caption": "Demonstração do problema"
}
}'

Resposta Interativa — Botão (button_reply)

Enviada quando o usuário clica em um botão de resposta rápida exibido no Whatsapp.

curl -X POST https://api.tech4.ai/agent/v2/conversation/550e8400-e29b-41d4-a716-446655440000/message \
-H "x-client-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "interactive",
"interactive": {
"type": "button_reply",
"button_reply": {
"id": "manha",
"title": "Manhã"
}
}
}'

Resposta Interativa — Lista (list_reply)

Enviada quando o usuário seleciona uma opção de uma lista interativa exibida no Whatsapp.

curl -X POST https://api.tech4.ai/agent/v2/conversation/550e8400-e29b-41d4-a716-446655440000/message \
-H "x-client-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "interactive",
"interactive": {
"type": "list_reply",
"list_reply": {
"id": "pro_anual",
"title": "Pro Anual",
"description": "R$ 79,90/mês"
}
}
}'

Respostas da API

Sucesso (202 Accepted)

Mensagem aceita para processamento.

{
"status": "processing",
"message_id": "d4e5f6a7-b8c9-0123-def4-56789012345",
"detail": "message sent successfully"
}

Requisição Inválida (400 Bad Request)

A sessão está fechada.

{
"status": "session_closed",
"detail": "Session is closed"
}

Não Encontrado (404 Not Found)

A sessão não existe.

{
"detail": "Session not found"
}

Conflito (409 Conflict)

Mensagem duplicada detectada.

{
"detail": "Message duplicate detected"
}

Webhook Response

Após processar a mensagem, o agente envia a resposta para o webhook configurado na abertura da conversa.

Estrutura do Webhook

CampoTipoDescrição
responseobjectResposta do agente com role ("assistant") e content (array de conteúdos ver Tipos de item em response.content)
relation_message_idsarrayLista de UUIDs das mensagens consideradas no processamento
session_iduuidID da sessão
metadataobjectMetadados opcionais (ver Objeto metadata)
statusstringStatus do processo: done ou fail
detailstringMensagem detalhando o resultado

Headers HTTP do Webhook

HeaderDescrição
X-Tech4AI-Signature-256Assinatura HMAC-SHA256 do body no formato sha256=<hex>. Ver Verificação de Assinatura.

Objeto response.content

O array response.content pode conter itens de diferentes tipos numa mesma resposta. Cada item possui um campo type que indica o formato:

typeDescrição
textMensagem de texto simples. Contém campo text.body (string).
documentArquivo enviado pelo agente. Contém campo document com id, url, filename e expiration_time.
interactiveComponente interativo (botões ou lista). O subcampo interactive.type pode ser "button" (reply buttons) ou "list" (lista interativa).

Parâmetros do item text

CampoTipoObrigatórioDescrição
typestringSimSempre "text".
textobjectSimObjeto com o conteúdo textual.
text.bodystringSimTexto da mensagem.

Exemplo:

{
"type": "text",
"text": {
"body": "Olá! Como posso te ajudar hoje?"
}
}

Parâmetros do item interactive — Reply Buttons

CampoTipoObrigatórioDescrição
typestringSimSempre "interactive".
interactive.typestringSimSempre "button" para reply buttons.
interactive.bodyobjectSimObjeto com campo text (string, ≤ 1024 chars). Texto principal exibido acima dos botões.
interactive.body.textstringSimConteúdo textual do corpo.
interactive.actionobjectSimObjeto com campo buttons — lista de 1 a 3 botões.
interactive.action.buttons[]arraySimLista de reply buttons. Máximo 3 itens.
interactive.action.buttons[].typestringSimSempre "reply".
interactive.action.buttons[].replyobjectSimDados do botão.
interactive.action.buttons[].reply.idstringSimIdentificador único do botão. Máximo 256 chars.
interactive.action.buttons[].reply.titlestringSimTexto exibido no botão. Máximo 20 chars.
interactive.headerobjectNãoCabeçalho opcional. Ver tabela de tipos de header abaixo.
interactive.footerobjectNãoRodapé opcional.
interactive.footer.textstringNãoTexto do rodapé. Máximo 60 chars.

Tipos de interactive.header para reply buttons:

header.typeCampos adicionaisDescrição
"text"text (string, ≤ 60 chars)Cabeçalho de texto simples.
"image"image.link (string)URL pública da imagem.
"document"document.link (string)URL pública do documento.
"video"video.link (string)URL pública do vídeo.

Exemplo de item interactive com reply buttons:

{
"type": "interactive",
"interactive": {
"type": "button",
"header": {
"type": "text",
"text": "Agendamento"
},
"body": {
"text": "Qual período você prefere para o atendimento?"
},
"action": {
"buttons": [
{ "type": "reply", "reply": { "id": "manha", "title": "Manhã" } },
{ "type": "reply", "reply": { "id": "tarde", "title": "Tarde" } },
{ "type": "reply", "reply": { "id": "noite", "title": "Noite" } }
]
},
"footer": {
"text": "Seg a Sex, das 8h às 20h"
}
}
}

Parâmetros do item interactive — Lista Interativa

CampoTipoObrigatórioDescrição
typestringSimSempre "interactive".
interactive.typestringSimSempre "list" para listas interativas.
interactive.bodyobjectSimObjeto com campo text (string, ≤ 4096 chars). Texto principal exibido acima da lista.
interactive.body.textstringSimConteúdo textual do corpo.
interactive.actionobjectSimObjeto com button e sections.
interactive.action.buttonstringSimLabel do botão que abre a lista. Máximo 20 chars.
interactive.action.sections[]arraySimLista de 1 a 10 seções. Total de rows entre todas as seções: máximo 10.
interactive.action.sections[].titlestringSimTítulo da seção. Máximo 24 chars.
interactive.action.sections[].rows[]arraySimOpções da seção. Mínimo 1 item por seção.
interactive.action.sections[].rows[].idstringSimIdentificador único da opção. Máximo 256 chars.
interactive.action.sections[].rows[].titlestringSimTexto da opção exibido na lista. Máximo 24 chars.
interactive.action.sections[].rows[].descriptionstringNãoDescrição adicional da opção. Máximo 72 chars.
interactive.headerobjectNãoCabeçalho opcional. Apenas type: "text" é suportado pelo WhatsApp para listas.
interactive.header.typestringNãoSempre "text" quando presente.
interactive.header.textstringNãoTexto do cabeçalho. Máximo 60 chars.
interactive.footerobjectNãoRodapé opcional.
interactive.footer.textstringNãoTexto do rodapé. Máximo 60 chars.

Exemplo de item interactive com lista:

{
"type": "interactive",
"interactive": {
"type": "list",
"header": {
"type": "text",
"text": "Planos disponíveis"
},
"body": {
"text": "Selecione o plano de sua preferência"
},
"action": {
"button": "Ver planos",
"sections": [
{
"title": "Planos Mensais",
"rows": [
{ "id": "basico", "title": "Básico", "description": "R$ 49,90/mês" },
{ "id": "pro", "title": "Pro", "description": "R$ 99,90/mês" }
]
},
{
"title": "Planos Anuais",
"rows": [
{ "id": "basico_anual", "title": "Básico Anual", "description": "R$ 39,90/mês" },
{ "id": "pro_anual", "title": "Pro Anual", "description": "R$ 79,90/mês" }
]
}
]
},
"footer": {
"text": "Valores promocionais por tempo limitado"
}
}
}
Acesso a seleção do usuário via State Management

Quando o usuário responde a um componente interativo, o sistema popula automaticamente as chaves last_interactive e interactive_history no state da sessão, acessíveis em qualquer action subsequente.

Consulte a documentação completa em State Management — Componentes Interativos.

Objeto metadata

CampoTipoDescrição
functionsarrayLista de funções solicitadas (ver Funções especiais)
session_infoobjectDados coletados durante a sessão (ex: informações de contato)

Funções Especiais

O agente pode solicitar a execução de funções especiais através do campo metadata.functions. Para mais detalhes, consulte a documentação do Response Objects.

Transferência de Habilidade Externa

Quando o agente identifica a necessidade de transbordo para uma habilidade externa:

{
"name": "external_skill_transfer",
"parameters": {
"external_skill_name": "pagamentos",
"external_skill_id": "96db1376-1ce6-4dc7-a09d-e04e68f6bed2",
"user_id": "123e4567-e89b-12d3-a456-426614174000",
"reason": "o cliente deseja efetuar o pagamento de um boleto pendente"
}
}

Encerramento de Conversa

Quando o agente decide finalizar a conversa:

{
"name": "end_chat",
"parameters": {
"reason": "Cliente pagou o boleto e agradeceu.",
"service_evaluation": true
}
}

service_evaluation (bool, default true) controla se o servidor consumidor deve disparar a pesquisa de satisfação (CSAT) ao final do atendimento. Quando omitido ou true, o comportamento é o mesmo de sempre (CSAT disparado). Quando false — ex. em um retorno de HSM informativo — o servidor deve pular o disparo do CSAT.


Exemplos de Webhook

Webhook - Sucesso

{
"response": {
"role": "assistant",
"content": [
{
"type": "text",
"text": {
"body": "Sua fatura do mês é:"
}
},
{
"type": "document",
"document": {
"id": "4c80b53a-a667-4edd-b943-d1e9dc387009",
"url": "https://storage.example.com/files/fatura.pdf",
"expiration_time": "2025-09-26T20:53:48.368151+00:00",
"filename": "fatura_outubro.pdf"
}
}
]
},
"relation_message_ids": [
"b15c35a4-da85-4d58-842c-cff5a1f0b45a",
"4ab9a72d-5497-4a76-8bbf-a0025c6ad09f"
],
"session_id": "8d1a4259-c4d0-45b8-845b-b290c0264754",
"metadata": {
"functions": [
{
"name": "external_skill_transfer",
"parameters": {
"external_skill_name": "pagamentos",
"external_skill_id": "96db1376-1ce6-4dc7-a09d-e04e68f6bed2",
"user_id": "123e4567-e89b-12d3-a456-426614174000",
"reason": "o cliente deseja efetuar o pagamento de um boleto pendente"
}
}
],
"session_info": {
"contact": {
"name": "João Silva",
"cpf": "123.456.789-00"
}
}
},
"status": "done",
"detail": "response generated successfully"
}

Webhook - Falha

{
"relation_message_ids": [
"b15c35a4-da85-4d58-842c-cff5a1f0b45a",
"4ab9a72d-5497-4a76-8bbf-a0025c6ad09f"
],
"session_id": "8d1a4259-c4d0-45b8-845b-b290c0264754",
"metadata": null,
"status": "fail",
"detail": "fail to generate response"
}

Webhook - Resposta com Reply Buttons

Quando o agente envia botões de resposta rápida, response.content contém um item type: "interactive" com interactive.type: "button".

{
"response": {
"role": "assistant",
"content": [
{
"type": "interactive",
"interactive": {
"type": "button",
"body": {
"text": "Qual período você prefere para o atendimento?"
},
"action": {
"buttons": [
{ "type": "reply", "reply": { "id": "manha", "title": "Manhã" } },
{ "type": "reply", "reply": { "id": "tarde", "title": "Tarde" } },
{ "type": "reply", "reply": { "id": "noite", "title": "Noite" } }
]
},
"footer": {
"text": "Seg a Sex, das 8h às 20h"
}
}
}
]
},
"relation_message_ids": ["b15c35a4-da85-4d58-842c-cff5a1f0b45a"],
"session_id": "8d1a4259-c4d0-45b8-845b-b290c0264754",
"metadata": {},
"status": "done",
"detail": "response generated successfully"
}

Webhook - Resposta com Lista Interativa

Quando o agente envia uma lista de opções, response.content contém um item type: "interactive" com interactive.type: "list".

{
"response": {
"role": "assistant",
"content": [
{
"type": "interactive",
"interactive": {
"type": "list",
"header": {
"type": "text",
"text": "Planos disponíveis"
},
"body": {
"text": "Selecione o plano de sua preferência"
},
"action": {
"button": "Ver planos",
"sections": [
{
"title": "Planos Mensais",
"rows": [
{ "id": "basico", "title": "Básico", "description": "R$ 49,90/mês" },
{ "id": "pro", "title": "Pro", "description": "R$ 99,90/mês" }
]
},
{
"title": "Planos Anuais",
"rows": [
{ "id": "basico_anual", "title": "Básico Anual", "description": "R$ 39,90/mês" },
{ "id": "pro_anual", "title": "Pro Anual", "description": "R$ 79,90/mês" }
]
}
]
},
"footer": {
"text": "Valores promocionais por tempo limitado"
}
}
}
]
},
"relation_message_ids": ["c26d46b5-eb96-5e69-953d-dg6012g1456b"],
"session_id": "8d1a4259-c4d0-45b8-845b-b290c0264754",
"metadata": {},
"status": "done",
"detail": "response generated successfully"
}

Webhook - Resposta Mista (Texto + Componente Interativo)

response.content pode conter múltiplos itens de tipos diferentes na mesma resposta. Cada item deve ser renderizado sequencialmente.

{
"response": {
"role": "assistant",
"content": [
{
"type": "text",
"text": { "body": "Antes de continuar, escolha uma opção:" }
},
{
"type": "interactive",
"interactive": {
"type": "button",
"body": { "text": "Como deseja prosseguir?" },
"action": {
"buttons": [
{ "type": "reply", "reply": { "id": "agendar", "title": "Agendar" } },
{ "type": "reply", "reply": { "id": "planos", "title": "Ver planos" } }
]
}
}
}
]
},
"relation_message_ids": ["d37e57c6-fc07-6f7a-064e-eh7123h2567c"],
"session_id": "8d1a4259-c4d0-45b8-845b-b290c0264754",
"metadata": {},
"status": "done",
"detail": "response generated successfully"
}

Verificação de Assinatura

Toda requisição de webhook inclui o header X-Tech4AI-Signature-256 com a assinatura HMAC-SHA256 do body. Use essa assinatura para confirmar que a requisição foi enviada pela Tech4AI e que o conteúdo não foi alterado em trânsito.

Como funciona

  1. O body do webhook é serializado de forma determinística (chaves ordenadas alfabeticamente, sem espaços)
  2. O HMAC-SHA256 é calculado sobre esse body usando o x-client-key como secret
  3. O resultado é enviado no header no formato sha256=<hex_digest>

Como validar

Recalcule a assinatura no seu servidor e compare com o header recebido usando comparação segura contra timing attacks.

import hashlib
import hmac
import json

def verify_webhook_signature(
body: bytes,
secret: str,
received_signature: str,
) -> bool:
"""
Valida o header X-Tech4AI-Signature-256 do webhook.

Args:
body: Body HTTP cru recebido (bytes)
secret: x-client-key usado no add_message
received_signature: valor do header X-Tech4AI-Signature-256

Returns:
True se a assinatura for válida
"""
payload = json.loads(body)
canonical = json.dumps(payload, sort_keys=True, separators=(",", ":"))
expected = "sha256=" + hmac.new(
secret.encode("utf-8"),
canonical.encode("utf-8"),
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected, received_signature)
Atenção
  • Use sempre comparação segura (hmac.compare_digest em Python, crypto.timingSafeEqual em Node.js) para evitar timing attacks
  • O header X-Tech4AI-Signature-256 está sempre presente nas requisições de webhook
  • Rejeite requisições com assinatura inválida retornando HTTP 401

Boas Práticas

Recomendações
  • Valide o webhook: Certifique-se de que seu webhook retorna status 200
  • Valide a assinatura: Verifique o header X-Tech4AI-Signature-256 para garantir que o webhook veio da Tech4AI (ver Verificação de Assinatura)
  • Armazene message_id: Use para rastreamento e debugging
  • Use caption: Adicione contexto aos arquivos de mídia
  • Monitore relation_message_ids: Entenda quais mensagens foram processadas juntas
Atenção
  • Arquivos enviados por URL devem estar acessíveis publicamente
  • URLs de download de arquivos gerados pelo agente expiram após o tempo indicado em expiration_time
  • Seu webhook deve responder em até 30 segundos

Próximos Passos

Explore mais sobre: