1. Tickets
Khal Docs
  • Raiz
    • CRM
      • CRM — Overview
      • Genérico (data)
        • CRM - Genérico
        • Listar/filtrar registros (qualquer tabela do CRM)
        • Criar/atualizar registros (qualquer tabela do CRM)
        • Hard-delete (não permitido para tabelas company)
      • Empresas Relacionadas
        • CRM - Empresas relacionadas
        • Listar relacionamentos de uma empresa (ambas as direções)
        • Tipos de relacionamento disponíveis
        • Criar relacionamento entre duas empresas
        • Alterar o tipo do relacionamento
        • Inativar relacionamento (reversível)
        • Excluir relacionamento (hard-delete)
      • Ticket ↔ Empresa
        • CRM - Ticket <-> Empresa
        • Empresas elegíveis para o ticket
        • Vincular / trocar a empresa do ticket
      • Templates
        • CRM - Templates
        • Listar templates do tenant (apenas IDs, sem fields)
        • Detalhe completo do template (com fields)
        • Pesquisa formatada por scope (CRMSearchResult)
      • Empresas do cliente
        • Listar tipos de relacionamento
        • Listar empresas de um cliente
        • Vincular empresa ao cliente
        • Trocar a empresa principal
        • Alterar tipo de relacionamento
        • Inativar vínculo
        • Reativar vínculo
        • Excluir vínculo
    • Workflow
      • Workflow - Overview
      • Tickets
        • Workflow - Tickets
        • Criar ticket com auto-vínculo / criação de customer via contato
          POST
        • Detalhe completo do ticket (mensagens, SLA, fases, customer, empresa, business)
          GET
        • Criar ticket (básico, com integração CRM opcional)
          POST
        • Movimentar ticket entre fases (ou finalizar)
          POST
        • Atualizar dados do ticket (nome, descrição, valor)
          PUT
        • Vincular cliente CRM a um ticket existente
          PUT
        • Adicionar nota ou mensagem ao ticket
          POST
        • Atribuir ou transferir o responsável do ticket
          PUT
      • Workspaces & Workflows
        • Workflow - Workspaces & Workflows
        • Listar workspaces e workflows acessíveis ao usuário
        • Listar fases de um workflow
        • Listar tickets de uma ou mais fases (paginado)
    • Webhooks
      • Eventos
        • 📨 [Contrato] Evento entregue na sua URL
        • Catálogo de eventos disponíveis
      • Assinaturas
        • Listar webhooks cadastrados
        • Criar webhook (o secret aparece UMA única vez)
        • Testar conectividade de uma URL
        • Detalhar webhook
        • Atualizar webhook
        • Ativar/desativar webhook (toggle)
        • Remover webhook (soft delete)
      • Entregas
        • Log de entregas de uma assinatura
        • Reprocessar uma entrega
        • Reprocessar TODAS as entregas com falha
      • Integrações
        • Voz IA (ElevenLabs)
          • Criar ou atualizar a integração de Voz IA
          • Consultar a integração de Voz IA
          • Alterar a integração de Voz IA (secret, toggle, allowlist)
          • Remover a integração de Voz IA
          • Webhook pós-chamada (chamado pelo provedor de voz)
    • Configurações
      • Canais
        • WhatsApp oficial
          • Listar conexões de WhatsApp (a tela Canais › WhatsApp oficial)
          • Listar números disponíveis de uma WABA
          • Criar conexão de WhatsApp oficial
          • Conectar o número oficial (ativar a conexão)
        • Telefonia
          • Anexar gravação de ligação (telefonia de terceiros)
          • Registrar CSAT de ligação
          • Consultar status da gravação enviada
      • Templates
        • WhatsApp oficial
          • Listar templates HSM (a tela Templates › WhatsApp oficial)
          • Criar template HSM (+ Novo modelo)
          • Detalhar template
          • Atualizar template
          • Excluir template
          • Sincronizar templates com a Meta
          • Upload de mídia para o header do template
        • RCS
          • Listar templates de RCS
      • Base de dados
        • Modelo de colunas do CSV
        • Importar base (CSV) para uma tabela do CRM
        • Acompanhar as importações (validar o resultado)
        • Erros de uma importação
        • Exportar os erros em planilha
    • Raiz
      • Workflow
        • Tarefas
          • Criar tarefa
          • Listar tarefas
          • Resumo de tarefas
          • Consultar tarefa
          • Editar tarefa
          • Concluir tarefa
          • Cancelar (excluir) tarefa
          • Silenciar alarme
          • Tarefas do ticket
  • Esquemas
    • LoginRequest
    • FilterOperator
    • WebhookSubscription
    • ClienteEmpresaTipo
    • LoginResponse
    • FilterCondition
    • WebhookSubscriptionResumo
    • ClienteEmpresa
    • Error
    • Pagination
    • WebhookDelivery
    • ErroRegra
    • FilterRequest
    • WebhookEventEnvelope
    • FilterResponse
    • SetMethod
    • SetRequest
    • DeleteRequest
    • SuccessEnvelope
    • ErrorEnvelope
    • Empresa
    • EmpresaResumo
    • EmpresaRelacionadaCreate
    • EmpresaRelacionada
    • TicketInfoResponse
  1. Tickets

Adicionar nota ou mensagem ao ticket

POST
/api/v2/ticket/message
Adiciona uma entrada de mensagem no ticket. Use is_note: true pra nota interna
(visível só pra operadores). Sem is_note, é mensagem normal.

Body#

CampoObrigatórioO que faz
id_ticketsimUUID do ticket
is_notenãotrue = nota interna; default false = mensagem
messagesimConteúdo (texto livre)
triggersó mensagemNúmero de destino, E.164 sem +
id_channelsó mensagemCanal. 2 = WhatsApp
id_brokersó mensagemBroker. 9 = Pontal Tech (WhatsApp oficial)
channel_tokensó mensagemid da conexão que vai enviar
template_variablessó templateO template e seus parâmetros
filenãoAnexos. Mande [] quando não houver
Os campos marcados como "só mensagem" são obrigatórios em qualquer envio para
o cliente. Só a nota interna (is_note: true) dispensa todos eles.

Enviar um template (HSM) de WhatsApp#

Fora da janela de 24h, o WhatsApp só aceita template aprovado pela Meta. Nesse
caso message vai como string vazia ("") — o texto real vem do template.

1. Descubra o template#

Chame GET /api/v3/settings/channels-gateway/manager/templates?channel=whatsapp&contentType=hsm
(pasta Configurações › Templates › WhatsApp oficial) e escolha o modelo.

2. Descubra por qual conexão enviar#

O channel_token é o id da conexão (channelConfigId), obtido em
GET /api/v3/settings/channels-gateway/whatsapp?broker=pontal-tech.
⚠️ O template tem que pertencer à conexão que você usar. A busca é feita pelo
nome do template, dentro daquela conexão, e apenas entre os que estão active.
Antes de enviar, case o wabaId do template com o
pontalTechWhatsAppAccount.wabaId da conexão — se forem de WABAs diferentes, o
template não é encontrado e a mensagem não sai formatada.

3. Monte o template_variables#

"template_variables": {
  "type": "whatsapp",
  "content": {
    "messageType": "template",     // fixo, é o que liga o modo template
    "templateName": "<name>",      // campo `name` do template
    "languageCode": "pt_BR",       // `content.language`; se omitir, o back preenche
    "parameters": []               // um item por variável — veja abaixo
  }
}

4. Traduza variables → parameters#

O array variables que o GET de templates devolve é o molde; parameters é
esse molde preenchido. Um item para cada variável, na mesma ordem.
No template (variables[])No envio (parameters[])
{"type":"body","parameterName":"agente","default":"João"}{"type":"body","parameterName":"agente","value":"Maria"}
{"type":"body","default":"São Paulo"} (posicional){"type":"body","value":"Belo Horizonte"}
{"type":"header","subType":"image","default":"https://…"}{"type":"header","subType":"image","value":"https://…"}
{"type":"button","index":0,"subType":"url"}{"type":"button","index":0,"value":"abc123"}
Regras:
O valor sempre vai em value — texto, URL de mídia ou token de botão.
named (content.parameterFormat): o parameterName é o que casa com {{agente}} no texto.
positional: não há nome — o que vale é a posição no array, que vira {{1}}, {{2}}, …
Não pule nem reordene itens.
Sem variáveis ("variables": [] no template): mande "parameters": [].
Toda variável do template precisa de um parâmetro correspondente. Sem ele, o
placeholder ({{agente}}) chega literal para o cliente.

5. Anexo dinâmico (mídia que muda a cada envio)#

Templates com cabeçalho de mídia aceitam um arquivo diferente em cada disparo — a
imagem aprovada no template serve só de exemplo para a Meta. Basta mandar a URL do
arquivo no parâmetro de type: "header":
subTypeO que enviarVira, na Meta
imagevalue = URL da imagemimage.link
videovalue = URL do vídeovideo.link
documentvalue = URL do arquivo + fileNamedocument.link + filename
"parameters": [
  { "type": "header", "subType": "image", "value": "https://seu-dominio/promo-agosto.jpg" },
  { "type": "body",   "parameterName": "cliente", "value": "Maria" }
]
Documento, que também leva o nome exibido ao cliente:
{ "type": "header", "subType": "document", "value": "https://seu-dominio/nota-fiscal.pdf", "fileName": "nota-fiscal.pdf" }
Regras do anexo:
A URL precisa ser pública — quem baixa o arquivo é a Meta, não a plataforma.
O subType tem que ser o mesmo do cabeçalho aprovado no template: um template com
header de imagem não aceita vídeo no lugar.
O tipo e o tamanho seguem os limites da Meta para cada formato de mídia.
body também aceita subType: "image", para os poucos templates que trazem mídia
no corpo.
Opcionalmente, repita a URL em default no mesmo parâmetro: isso não muda o que o
cliente recebe, só faz o histórico no Cockpit exibir a mídia enviada em vez da
imagem de exemplo do template.

Exemplo prático, ponta a ponta#

Passo 1 — o template, como vem do GET (campos omitidos por brevidade):
{
  "id": "d41a8c76-3b52-4e09-9f18-6c7b204ea351",
  "name": "continuacao_conversa",
  "wabaId": "900000000000001",
  "status": "active",
  "content": {
    "type": "hsm",
    "language": "pt_BR",
    "parameterFormat": "named",
    "components": [
      {
        "type": "body",
        "text": "Olá! Meu nome é {{agente}} e vou dar continuidade ao seu atendimento.",
        "variables": [{ "parameterName": "agente", "example": "João" }]
      }
    ]
  },
  "variables": [{ "type": "body", "parameterName": "agente", "default": "João" }]
}
Passo 2 — a conexão, como vem do GET de canais:
{
  "id": "3f2a91c4-7b60-4d15-9a2e-1c8d5e0b7411",
  "name": "Atendimento Comercial",
  "status": "active",
  "whatsappAccount": {
    "pontalTechWhatsAppAccount": { "wabaId": "900000000000001" }
  }
}
O wabaId dos dois é o mesmo (900000000000001) — pode enviar por essa conexão.
Passo 3 — o body que sai disso:
{
  "id_ticket": "6a432bac-0e83-41eb-8931-5ab897f7ba66",
  "trigger": "5511999990000",
  "message": "",
  "file": [],
  "id_channel": 2,
  "id_broker": 9,
  "channel_token": "3f2a91c4-7b60-4d15-9a2e-1c8d5e0b7411",
  "template_variables": {
    "type": "whatsapp",
    "content": {
      "messageType": "template",
      "templateName": "continuacao_conversa",
      "languageCode": "pt_BR",
      "parameters": [
        { "type": "body", "parameterName": "agente", "value": "Maria" }
      ]
    }
  }
}
De onde veio cada campo:
Campo do bodyOrigem
channel_tokenid da conexão (passo 2)
templateNamename do template (passo 1)
languageCodecontent.language do template
parameters[0].type / .parameterNameitem de variables[] do template
parameters[0].valuevocê define — é o valor que o cliente vai ler
triggernúmero do destinatário
id_ticketticket onde a mensagem será registrada
O cliente recebe: "Olá! Meu nome é Maria e vou dar continuidade ao seu atendimento."

Enviar um template de RCS#

RCS usa o mesmo endpoint, mas o payload é diferente do WhatsApp em dois pontos
essenciais: o template é escolhido por template_id (não pelo nome), e
template_variables é um mapa simples de variáveis, não a estrutura
content.parameters.
WhatsApp oficialRCS
id_channel212
id_broker9 (Pontal Tech)9 (Pontal Tech)
Escolhe o template portemplate_variables.content.templateNametemplate_id (UUID)
template_variablesobjeto com content.parameters[]{ "variavel": "valor" }
Aprovação externaMetanão se aplica

Campos#

CampoObrigatórioO que faz
id_channelsim12 = RCS
id_brokersim9 = Pontal Tech
channel_tokensimid da conexão de RCS
triggersimNúmero de destino, E.164 sem +
template_idsó templateid do template (UUID) — é ele que liga o modo template
template_variablesnãoMapa { "nome": "valor" } das variáveis usadas no template
messagesimTexto da mensagem, ou "" quando o conteúdo vem do template
O channel_token é o id da conexão de RCS, obtido em
GET /api/v3/settings/channels-gateway?channel=rcs.
O template_id vem de GET /api/v3/settings/channels-gateway/rcs?channel=rcs
(pasta Configurações › Templates › RCS).

Variáveis#

Onde o template tem {{nome}} no título, na descrição ou no texto de um botão,
mande a chave correspondente:
"template_variables": { "nome": "Maria", "cidade": "Belo Horizonte" }
Sem estrutura aninhada, sem type, sem parameterName — é substituição direta de
{{chave}} pelo valor.

Exemplo — text#

Templates de texto não passam pelo formatador: o conteúdo enviado é o campo
message do body. Na prática, para texto simples você nem precisa de template.
{
  "id_ticket": "6a432bac-0e83-41eb-8931-5ab897f7ba66",
  "trigger": "5511999990000",
  "id_channel": 12,
  "id_broker": 9,
  "channel_token": "b1d47e30-8a52-4c96-a7f1-30e5c2849b64",
  "message": "Nosso atendimento estará indisponível neste domingo, das 2h às 6h."
}

Exemplo — rich-card#

{
  "id_ticket": "6a432bac-0e83-41eb-8931-5ab897f7ba66",
  "trigger": "5511999990000",
  "id_channel": 12,
  "id_broker": 9,
  "channel_token": "b1d47e30-8a52-4c96-a7f1-30e5c2849b64",
  "message": "",
  "template_id": "c8f37a02-4b19-4e65-9d10-72ab5f3e6841",
  "template_variables": { "nome": "Maria" }
}
Com o template abaixo, o cliente recebe o card com "Olá, Maria!" no título, a
imagem do template e os dois botões:
{
  "id": "c8f37a02-4b19-4e65-9d10-72ab5f3e6841",
  "contentType": "rich-card",
  "content": {
    "type": "rich-card",
    "title": "Olá, {{nome}}!",
    "description": "Condições especiais até o fim do mês.",
    "imageUrl": "https://seu-dominio/promo-agosto.jpg",
    "actions": [
      { "type": "openUrl", "text": "Ver ofertas", "value": "https://exemplo.com.br/ofertas" },
      { "type": "reply", "text": "Falar com consultor", "value": "FALAR_CONSULTOR" }
    ]
  }
}

Exemplo — carousel#

Mesmo payload; muda só o template_id. As variáveis valem para todos os cards
do carrossel de uma vez.
{
  "id_ticket": "6a432bac-0e83-41eb-8931-5ab897f7ba66",
  "trigger": "5511999990000",
  "id_channel": 12,
  "id_broker": 9,
  "channel_token": "b1d47e30-8a52-4c96-a7f1-30e5c2849b64",
  "message": "",
  "template_id": "e40b7c19-2d68-4a53-91cf-6b83d5027ae2",
  "template_variables": { "cidade": "Belo Horizonte" }
}
No carrossel, as variáveis são substituídas no título e na descrição de
cada card. O texto dos botões do carrossel sai como está cadastrado no template —
no rich-card, esse texto também aceita variável.

Mídia e anexos no RCS#

A imagem exibida vem do imageUrl cadastrado no template — no card, no
rich-card, e em cada item, no carousel. Ela é fixa por template: não há
parâmetro de envio que troque a imagem em tempo de disparo, como o header dinâmico
do WhatsApp.
Para variar a imagem, cadastre um template por arte. Para hospedar o arquivo dentro
do produto, use POST /api/v3/settings/channels-gateway/manager/templates/media e
aproveite a URL retornada no imageUrl do template.
O campo file do body não se aplica ao RCS — anexo avulso, fora de template,
não é suportado neste canal.

Enviar um e-mail#

O e-mail usa o mesmo endpoint. O que muda é o canal (id_channel: 4), a caixa
que envia (channel_token) e os campos de destinatário e assunto.

Campos#

CampoObrigatórioO que faz
id_channelsim4 = E-mail
channel_tokensimToken da caixa de e-mail que vai enviar
triggersimE-mail do cliente (contato principal da conversa)
tosim*Destinatários, string separada por vírgula
ccnãoCópia, string separada por vírgula
bccnãoCópia oculta, string separada por vírgula
subjectnãoAssunto (ver regras abaixo)
subject_from_operatornãotrue = o assunto sai exatamente como enviado
messagesimCorpo do e-mail. Aceita HTML; quebras de linha viram <br/>
send_historyrecomendadoIncluir o histórico do atendimento. Ausente conta como true
continue_threadnãotrue = continuar uma conversa de e-mail já existente no ticket, mesmo enviando por outra caixa
thread_channel_tokencom continue_threadToken da caixa da conversa a continuar
filenãoAnexos. Mande [] quando não houver
* to ou cc: pelo menos um dos dois precisa ter destinatário.
id_broker não se aplica ao e-mail e pode ser omitido.
O channel_token é o token da caixa de e-mail cadastrada em Configurações › Canais ›
E-mail
. Para responder uma conversa que já existe, use o token da caixa daquela
conversa — ele aparece nos protocolos de e-mail do ticket.

⚠️ send_history: mande sempre explícito#

Quando send_history não é enviado, o e-mail sai com todo o histórico do
atendimento
citado abaixo da mensagem. Se o destinatário não é o cliente (um
fornecedor, alguém em cópia), ele recebe a conversa inteira.
Mande "send_history": false, a menos que você queira o histórico de propósito.

Responder ou abrir conversa nova#

O ticket já tem conversa de e-mail aberta nessa caixa: a mensagem é uma
resposta nessa conversa e mantém o assunto dela.
Não tem: o envio abre uma conversa nova com o cliente.
Caixa configurada como "Somente Receptivo" não abre conversa nova: responde
400. Responder conversa existente é sempre permitido.

Assunto da conversa nova#

O que você mandaAssunto que sai
subject + subject_from_operator: trueexatamente o subject
continue_thread: trueo assunto da conversa continuada
só subject#<número do ticket> <subject>
nada#<número do ticket> <nome do ticket>

Exemplo — e-mail novo, sem histórico#

{
  "id_ticket": "6a432bac-0e83-41eb-8931-5ab897f7ba66",
  "id_channel": 4,
  "channel_token": "<token da caixa>",
  "trigger": "maria@cliente.com.br",
  "to": "maria@cliente.com.br",
  "cc": "financeiro@cliente.com.br",
  "subject": "Segunda via do boleto",
  "subject_from_operator": true,
  "message": "Olá, Maria!\nSegue a segunda via do boleto em anexo.",
  "send_history": false,
  "file": []
}
Contato em blacklist responde 403 (CONTACT_BLACKLISTED) e nada é enviado.

Requisição

Parâmetros Header

Parâmetros Bodyapplication/jsonObrigatório

Examples

Respostas

🟢200OK
application/json
Mensagem/nota adicionada.
Bodyapplication/json

🟠400Pedido Inválido
🟠401Não Autorizado
🟠403Proibido
🟠404Registo Não Encontrado
Request Request Example
Shell
JavaScript
Java
Swift
cURL
curl --location '/api/v2/ticket/message' \
--header 'api-key: {{api-key}}' \
--header 'Content-Type: application/json' \
--data '{
    "id_ticket": "6a432bac-0e83-41eb-8931-5ab897f7ba66",
    "is_note": true,
    "message": "Cliente solicitou retorno na próxima semana"
}'
Response Response Example
200 - Exemplo de Sucesso
{
    "ok": true,
    "message": "Mensagem adicionada com sucesso."
}
Modificado em 2026-11-04 02:31:07
Página anterior
Vincular cliente CRM a um ticket existente
Próxima página
Atribuir ou transferir o responsável do ticket
Built with