Documentação Técnica

API NimoChat Pro+

Integre o NimoChat Pro+ ao seu CRM, ERP, n8n, Make ou qualquer plataforma. Automatize disparos, sincronize contatos, gerencie tickets e construa fluxos personalizados via API REST.

O que é a API NimoChat Pro+

A API NimoChat Pro+ é uma interface REST que permite integrar a plataforma de atendimento com qualquer sistema externo: CRM, ERP, sistema de RH, plataformas de automação como n8n, Make, Zapier, ou aplicações sob medida.

Com a API você pode automatizar tarefas repetitivas, sincronizar dados entre sistemas, criar fluxos personalizados e levar a inteligência do NimoChat Pro+ para fora dele. A API é organizada em torno de princípios REST: usa URLs previsíveis, recebe e devolve JSON, e usa códigos HTTP padrão.

Casos de uso comuns

Começando

Para usar a API você precisa de 3 informações: a URL base do seu painel, uma chave de API (token) e o ID da sua conexão WhatsApp. Tudo pode ser obtido em poucos cliques no painel administrativo.

Passo 1 — Gerar sua chave de API

  1. Faça login no seu painel NimoChat Pro+ como Administrador.
  2. No menu lateral, acesse API.
  3. Clique em Nova API, dê um nome e selecione a conexão WhatsApp.
  4. Copie e guarde o Token e o API ID. O token só é exibido uma vez.
Mantenha seu token seguro. Ele dá acesso total à sua conta. Nunca exponha o token em código frontend, repositórios públicos ou logs. Se suspeitar que o token vazou, gere um novo imediatamente.

Passo 2 — Identificar sua URL base

Cada cliente NimoChat Pro+ tem sua própria URL de back-end. Ela aparece no painel administrativo e tem o formato:

https://gestor.nimochat.com.br        // contas do Atende
https://atende1admin.nimochat.com.br  // contas do Atende1

Atenção: o endereço da API não é o mesmo que você usa para entrar no painel. O painel é a tela em que você faz login; a API atende num endereço próprio. Se você usar o endereço do painel nas chamadas, elas serão redirecionadas para a tela de login e nada funcionará, sem uma mensagem de erro que explique o motivo.

Para descobrir qual é o seu, veja por onde você acessa o painel:

Se você acessa o painel ema URL base da sua API é
atende.nimochat.com.br ou hub.nimochat.com.brhttps://gestor.nimochat.com.br
atende1.nimochat.com.br ou hub1.nimochat.com.brhttps://atende1admin.nimochat.com.br

Na dúvida, fale com o suporte antes de começar a integração: usar o endereço errado é o motivo mais comum de a primeira chamada não responder.

Autenticação

A API NimoChat Pro+ usa o padrão Bearer Token. Toda requisição precisa incluir o token no cabeçalho HTTP Authorization:

Authorization: Bearer SEU_TOKEN_AQUI

O sistema valida o token em todas as chamadas e isola os dados por empresa: você só consegue acessar e modificar dados da sua própria conta. Tentar usar um token inválido, ou esquecer o cabeçalho, retorna HTTP 403 Forbidden com {"error": "Invalid token."} ou {"error": "Token was not provided."}.

Boas práticas: guarde o token em variáveis de ambiente (.env), nunca direto no código. Em ambientes de produção, use um cofre de secrets como AWS Secrets Manager, Doppler ou Vault.

URL Base e formato dos endpoints

Todos os endpoints da API seguem o mesmo padrão de URL:

https://SUA_URL_BASE/v2/api/external/{apiId}/{endpoint}

Onde:

Convenções da API

Formato de datas

Onde houver filtro por período (startDate, endDate), use o formato ano-mês-dia: 2026-08-21. O formato brasileiro 21/08/2026 não funciona e provoca um erro 500, que parece falha do servidor mas é só o formato errado.

?startDate=2026-08-01&endDate=2026-08-21   // certo
?startDate=01/08/2026&endDate=21/08/2026   // erro 500

Onde vem a lista nas listagens

Toda resposta traz success e data, mas o lugar da lista dentro de data muda conforme o endpoint. Confira antes de programar o laço, para não escrever um código que funciona numa rota e quebra na outra:

Onde está a listaEndpoints
data (a própria lista)listContacts, listTickets, listTags, listUsers, listQueues, todo/list, gallery/list
data.campaignscampaign/list
data.datapipeline/list, listOpportunities

Paginação

Quase toda listagem traz um bloco pagination ao lado de data. O campo total está sempre presente; os demais variam por endpoint, então não assuma que o bloco é igual em todos:

Campos do paginationEndpoints
totallistTags, listQueues, listSessions
total, hasMorelistContacts, listTickets, listUsers, gallery/list
total, page, limit, hasMoreappointment/list, bulkDispatch/list, searchMessages
total, page, limit, totalPagescallLog/list, pipeline/list, listOpportunities
page, limit, total, totalPages, hasMorecontacts/search
não traz o blocolistChatFlows, todo/list, campaign/list
Duas pegadinhas. Em listContacts o total vem como texto ("3068") e não como número: converta antes de comparar ou somar. E em pipeline/list e listOpportunities o bloco pagination fica dentro de data, não ao lado dele.

Nos três endpoints que não trazem o bloco, peça a próxima página (pageNumber, quando disponível) até vir uma lista vazia.

Ritmo de chamadas

Não publicamos um limite fixo de requisições por minuto, e em uso normal a API não bloqueia: em teste, 30 chamadas seguidas foram todas respondidas. Ainda assim, evite rajadas grandes em paralelo, principalmente em importações: prefira sequencial, ou poucas chamadas simultâneas. Se algum dia receber 429, espere alguns segundos e repita a chamada.

Códigos de resposta

CódigoSignificado
200 OKRequisição bem-sucedida. O corpo contém os dados solicitados.
201 CreatedRegistro criado. Algumas rotas de criação respondem 201 em vez de 200 (ex: createNotes). Trate os dois como sucesso.
400 Bad RequestParâmetros inválidos ou faltando.
403 ForbiddenToken inválido, ausente, ou sem permissão para o recurso. O corpo traz {"error": "Invalid token."} ou {"error": "Token was not provided."}.
402 Payment RequiredO plano da conta não inclui o recurso (ERR_FEATURE_NOT_IN_PLAN) ou a conta está bloqueada por pagamento (ERR_TENANT_PAYMENT_BLOCKED).
404 Not FoundRecurso não encontrado (ex: ID inexistente ou de outra conta).
409 ConflictA ação conflita com o estado atual. Ex: enviar para um ticket fechado sem pedir para reabrir (ERR_TICKET_CLOSED).
413 Payload Too LargeEnvio grande demais (ex: respostas de formulário acima de 32 KB, cota da galeria estourada).
422 Unprocessable EntityO número informado corresponde a mais de um contato (ERR_NUMBER_AMBIGUOUS). A resposta traz os candidatos: nas rotas que aceitam contactId (ex: telegramInvite), repita a chamada com ele.
424 Failed DependencyUm serviço externo não respondeu. Ex: ERR_WAPP_CONTACT_UNVERIFIED (número sem WhatsApp, ou a consulta ao WhatsApp falhou na hora). Pode tentar de novo.
429 Too Many RequestsChamadas demais em pouco tempo. Não há um limite fixo publicado (veja Convenções). Se receber, aguarde alguns segundos e tente de novo.
500 Internal Server ErrorErro no servidor. Reporte ao suporte se persistir.
Leia o campo error. Quase todo erro traz um código como {"error": "ERR_MISSING_FIELDS", "fields": ["userId"]}. Quando vier field ou fields, eles dizem exatamente qual campo corrigir. Os códigos de cada rota estão no cartão dela.

Exemplo rápido — sua primeira chamada

Vamos listar todos os canais (conexões WhatsApp) da sua conta. Substitua SEU_TOKEN, SUA_URL_BASE e SEU_API_ID pelos valores reais:

cURL

curl --request GET \
  --url 'https://SUA_URL_BASE/v2/api/external/SEU_API_ID/listChannels' \
  --header 'Authorization: Bearer SEU_TOKEN'

Resposta esperada

{
  "success": true,
  "data": [
    {
      "id": 1,
      "name": "WhatsApp Principal",
      "type": "whatsapp",
      "status": "CONNECTED",
      "number": "5511999999999",
      "isActive": true
    }
  ]
}
O formato das respostas. Toda resposta de sucesso vem embrulhada em success e data: os dados que você quer estão sempre dentro de data, nunca na raiz. Em caso de erro, vem {"error": "MENSAGEM"}. Ao programar, leia resposta.data e não resposta.channels ou resposta.contacts.
Recebeu uma resposta? Você está pronto para usar a API. Agora explore os endpoints abaixo ou abra a coleção do Postman que tem todos os endpoints prontos para testar.

🤖 Usando a API com uma IA

Se você vai pedir para uma IA (ChatGPT, Claude, Gemini, Copilot ou outra) escrever a sua integração, entregue a ela o endereço desta página e o bloco abaixo. As IAs costumam completar o que não sabem com o que viram em outras plataformas parecidas, e erram justamente nos pontos listados, que conferimos no nosso servidor.

  1. Copie o bloco Regras para a IA e cole no começo da conversa.
  2. Se a IA consegue abrir links, passe também o endereço clientes.nimochat.com.br/llms-full.txt: é esta documentação inteira em texto, que a IA lê melhor do que a página.
  3. Peça uma rota de cada vez e teste cada uma antes de seguir (o Exemplo rápido serve de primeiro teste).
  4. Quando der erro, cole na conversa a resposta inteira da API: o código em error diz o que corrigir.
Não cole o seu token na conversa com a IA. Peça o código com SEU_TOKEN e coloque o valor real só no seu servidor, numa variável de ambiente. Quem tem o token envia mensagens em nome da sua empresa.

Regras para a IA (copie e cole no começo da conversa)

Você vai escrever uma integração com a API do NimoChat Pro+.
Use SÓ as rotas e os campos da documentação oficial:
https://clientes.nimochat.com.br/documentacaoapiproplus.php
(a mesma documentação em texto: https://clientes.nimochat.com.br/llms-full.txt)
Se precisar de algo que não está lá, pergunte. Não invente rota nem campo.

Regras conferidas no nosso servidor (última conferência em 29/10/2026):
1. URL: https://SUA_URL_BASE/v2/api/external/{apiId}/{rota}. A URL base da API
   não é o endereço do painel (tabela no Passo 2 da documentação).
2. Autenticação: header "Authorization: Bearer SEU_TOKEN". Token errado ou
   ausente responde 403 (não 401). Exceção: no envio por GET (/params/) o
   token vai no parâmetro bearertoken da URL, e o header é ignorado. Nunca
   escreva o token no código: leia de uma variável de ambiente.
3. Rotas GET leem só a URL (caminho e query string). Filtro no corpo é
   ignorado sem erro.
4. Filtros de período (startDate, endDate) em AAAA-MM-DD. DD/MM/AAAA nesses
   filtros dá erro 500. Exceções: birthdayDate do contato é DD/MM/AAAA; datas de
   agendamento aceitam os dois; em tarefas (todo) use AAAA-MM-DD, porque com
   barra a data é lida como mês/dia (02/03 vira 3 de fevereiro).
5. Hora sem fuso vale o horário de Brasília (-03:00). Prefira mandar o fuso.
6. updateContact encontra o contato pelo campo number. Não existe contactId
   nessa rota; sem number a resposta é 500.
7. createTicket exige userId. sendLocation e sendVcard exigem ticketId.
8. Várias tags: campo tagIds (lista). Lista dentro de tagId dá erro.
9. updatetag SUBSTITUI todas as tags do contato. Para acrescentar, use addTag
   ou addTagContact.
10. createContact, updateContact, addTag e removeTag exigem um token criado
    com uma conexão (senão 400 ERR_API_REQUIRES_SESSION).
11. O lugar da lista na resposta muda por rota (data, data.campaigns,
    data.data) e o bloco pagination também. Confira em "Convenções da API".
12. Em erro, leia o campo error (ex: ERR_MISSING_FIELDS). Se vier field ou
    fields, é o campo a corrigir. 201 também é sucesso.
13. bulkIndividual e bulkFastMessage exigem whatsappType (tipo da conexão,
    como em listChannels). Sem ele a resposta é 200 com success:true e nada
    é enviado.
14. Antes de enviar para clientes, teste com o seu próprio número.

📊 Listagens (GET)

Endpoints de consulta. Use estas rotas para buscar informações da plataforma. Todas retornam JSON.

GET /v2/api/external/{apiId}/listChannels

Lista todas as conexões (canais) da sua conta — WhatsApp, Instagram, Facebook, WebChat, Telegram, etc.

Exemplo

curl --request GET \
  --url 'https://SUA_URL_BASE/v2/api/external/SEU_API_ID/listChannels' \
  --header 'Authorization: Bearer SEU_TOKEN'
GET /v2/api/external/{apiId}/listSessions

Lista todas as sessões (instâncias) ativas no seu sistema.

GET /v2/api/external/{apiId}/listTickets

Lista os tickets (atendimentos) com filtros avançados. Suporta paginação.

Query Parameters

ParâmetroTipoDescrição
pageNumberintNúmero da página (começa em 1).
statusstring obrigatórioFiltra por status: open, pending ou closed. Se você não enviar este parâmetro, a chamada falha com 404 e {"error": "ERR_NO_STATUS_SELECTED"}.
searchParamstringTexto para buscar em nome, número ou conteúdo.
queuesIdsarrayIDs das filas para filtrar.
whatsappIdsarrayIDs das conexões WhatsApp.
selectedUserintID do atendente responsável.

Exemplo — tickets abertos da fila 3

curl --request GET \
  --url 'https://SUA_URL_BASE/v2/api/external/SEU_API_ID/listTickets?status=open&queuesIds=3&pageNumber=1' \
  --header 'Authorization: Bearer SEU_TOKEN'
GET /v2/api/external/{apiId}/listContacts

Lista contatos cadastrados. Suporta filtros por nome, carteira (wallet) e tag.

Os filtros vão na URL, não no corpo. Esta rota é GET e lê só a query string, por exemplo listContacts?pageNumber=1&searchParam=5511999999999. Filtro enviado no corpo (Body) é ignorado sem erro e a resposta traz a lista sem filtro. A busca encontra o texto em qualquer parte do nome ou do número, então um pedaço do número pode trazer mais de um contato. As tags vêm só com nome e cor; para ter o id da tag, use a busca avançada.

Query Parameters

ParâmetroTipoDescrição
pageNumberintNúmero da página.
searchParamstringBusca por nome ou número.
walletIdintFiltra contatos da carteira de um vendedor.
tagIdintFiltra contatos com determinada tag.
Cada contato da resposta traz também o endereço: cep, logradouro, numeroEndereco, complemento, bairro, cidade e estado (vazios vêm null). Para filtrar por endereço, use a busca avançada.
GET /v2/api/external/{apiId}/listUsers

Lista todos os usuários (atendentes) cadastrados na sua conta.

Query Parameters

ParâmetroTipoDescrição
pageNumberintPágina de resultados.
searchParamstringBusca por nome ou e-mail.
GET /v2/api/external/{apiId}/listTags

Lista as tags (etiquetas) cadastradas para classificar contatos e tickets.

Query Parameters

ParâmetroTipoDescrição
isActivebooleanFiltrar apenas tags ativas (true) ou inativas (false).
GET /v2/api/external/{apiId}/listQueues

Lista as filas (departamentos) configuradas, como "Vendas", "Suporte", "Financeiro".

GET /v2/api/external/{apiId}/listOpportunities

Lista as oportunidades (cartões) do funil de vendas (Kanban).

Query Parameters

ParâmetroTipoDescrição
pageintPágina de resultados.
limitintItens por página.
statusstringStatus da oportunidade.
pipelineIdintID do pipeline (funil).
stageIdintID do estágio dentro do pipeline.
GET /v2/api/external/{apiId}/getUserStatus

Verifica em tempo real se um atendente específico está Online ou Offline.

Query Parameters

ParâmetroTipoDescrição
userIdintobrigatórioID do atendente.
GET /v2/api/external/{apiId}/listNotes

Lista todas as notas internas registradas em um ticket específico.

Query Parameters

ParâmetroTipoDescrição
ticketIdintobrigatórioID do ticket.
GET /v2/api/external/{apiId}/getContactExtraInfo

Retorna os campos personalizados (extraInfo) de um contato.

Query Parameters

ParâmetroTipoDescrição
contactIdintobrigatórioID do contato.

👤 Contatos (POST)

Endpoints para criar, atualizar e gerenciar contatos. Todos os métodos POST recebem o corpo em JSON com header Content-Type: application/json.

POST /v2/api/external/{apiId}/createContact

Cria um novo contato no sistema. Os campos de endereço são opcionais; texto acima do limite é cortado, sem erro.

Body

CampoTipoDescrição
namestringobrigatórioNome do contato.
numberstringobrigatórioTelefone com DDI + DDD (ex: 5511999999999).
emailstringopcionalE-mail do contato.
extraInfoarrayopcionalCampos personalizados [{"name":"...","value":"..."}].
cpfstringopcionalCPF do contato (ex: 012.345.678-90).
firstNamestringopcionalPrimeiro nome.
lastNamestringopcionalSobrenome.
businessNamestringopcionalNome da empresa do contato.
birthdayDatestringopcionalData de nascimento no formato DD/MM/AAAA (ex: 25/12/1990). Atenção: é o contrário do formato AAAA-MM-DD usado nos filtros de data da API.
externalKeystringopcionalIdentificador do contato no seu sistema, para você cruzar os dados depois. Não pode se repetir.
validateNumberbooleanopcionalPadrão true. Veja validateNumber no envio de texto.
cepstringopcionalCEP (até 20 caracteres).
logradourostringopcionalRua (até 255 caracteres).
numeroEnderecostringopcionalNúmero do endereço (até 255).
complementostringopcionalComplemento (até 255).
bairrostringopcionalBairro (até 255).
cidadestringopcionalCidade (até 255).
estadostringopcionalEstado (até 100), gravado como veio: GO ou Goiás. A busca por UF acha os dois.

Exemplo

curl --request POST \
  --url 'https://SUA_URL_BASE/v2/api/external/SEU_API_ID/createContact' \
  --header 'Authorization: Bearer SEU_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "name": "João Silva",
    "number": "5511999999999",
    "email": "joao@empresa.com.br"
  }'
Testado por nós em 29/10/2026. Número já cadastrado responde 409 ERR_CONTACT_EXISTS com existingContactId: use esse id para seguir com o contato que já existe, sem criar outro. Sem number: 400 ERR_MISSING_NUMBER_OR_BSUID. Token de API sem canal vinculado: 400 ERR_API_REQUIRES_SESSION (crie a API escolhendo uma conexão).
POST /v2/api/external/{apiId}/updateContact

Atualiza dados de um contato existente. Só mudam os campos enviados.

Body

CampoTipoDescrição
numberstringobrigatórioNúmero do contato que será atualizado. É por ele que o contato é encontrado: esta rota não tem contactId. Número sem cadastro: 404 ERR_CONTACT_NOT_FOUND.
namestringopcionalNovo nome.
emailstringopcionalNovo e-mail.
cpfstringopcionalCPF do contato (ex: 012.345.678-90).
firstNamestringopcionalPrimeiro nome.
lastNamestringopcionalSobrenome.
businessNamestringopcionalNome da empresa do contato.
birthdayDatestringopcionalData de nascimento no formato DD/MM/AAAA (ex: 25/12/1990). Atenção: é o contrário do formato AAAA-MM-DD usado nos filtros de data da API.
kanbanintopcionalID da coluna do kanban do contato.
cepstringopcionalCEP (até 20 caracteres). "" ou null apaga; campo ausente fica como está.
logradourostringopcionalRua (até 255 caracteres).
numeroEnderecostringopcionalNúmero do endereço (até 255).
complementostringopcionalComplemento (até 255).
bairrostringopcionalBairro (até 255).
cidadestringopcionalCidade (até 255).
estadostringopcionalEstado (até 100), gravado como veio: GO ou Goiás. A busca por UF acha os dois.
Corrigido em 29/10/2026. Esta página indicava contactId como campo obrigatório. O campo não existe nesta rota: a chamada sem number responde 500. Se a sua integração usa contactId aqui, troque por number.

Exemplo: preencher o endereço e depois apagar só o complemento

curl --request POST \
  --url 'https://SUA_URL_BASE/v2/api/external/SEU_API_ID/updateContact' \
  --header 'Authorization: Bearer SEU_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "number": "5511999999999",
    "cep": "01310-100",
    "logradouro": "Avenida Paulista",
    "numeroEndereco": "1000",
    "bairro": "Bela Vista",
    "cidade": "São Paulo",
    "estado": "SP"
  }'

# depois, apagar só o complemento (os outros campos ficam como estão)
curl --request POST \
  --url 'https://SUA_URL_BASE/v2/api/external/SEU_API_ID/updateContact' \
  --header 'Authorization: Bearer SEU_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "number": "5511999999999",
    "complemento": ""
  }'
Testado por nós em 29/10/2026. Gravar o endereço, mudar só o bairro e apagar tudo com ""/null: cada passo conferido na ficha do contato.
POST /v2/api/external/{apiId}/updateContactKanban

Move um contato para uma posição (kanban) específica do funil.

Body

CampoTipoDescrição
contactIdintobrigatórioID do contato.
kanbanint|stringobrigatórioID ou nome do kanban de destino.
POST /v2/api/external/{apiId}/updateContactWallet

Define a carteira (wallet) de um contato — vincula o contato a um vendedor responsável. Útil para distribuir leads automaticamente.

Body

CampoTipoDescrição
contactIdintobrigatórioID do contato.
walletIdintopcionalID de um vendedor. Não aceita lista: um array aqui volta com 400 ERR_INVALID_WALLET_ID.
walletIdsarrayopcionalLista de IDs de vendedores. Envie walletId ou walletIds.
O que você envia substitui as carteiras atuais. Não é um acréscimo: se o contato está com os vendedores 3 e 7 e você envia "walletIds": [7], ele fica só com o 7. Isso vale também para o walletId. Para acrescentar um vendedor, envie a lista completa; para tirar todos, envie "walletIds": [].
POST /v2/api/external/{apiId}/blockContact

Bloqueia ou desbloqueia o chatbot para um contato. Útil para evitar respostas automáticas para contatos VIP.

Body

CampoTipoDescrição
contactIdintobrigatórioID do contato.
blockedbooleanobrigatóriotrue para bloquear, false para desbloquear.
POST /v2/api/external/{apiId}/findDuplicateContacts

Procura contatos repetidos na sua conta e devolve os pares encontrados, sem alterar nada. Útil antes de importar uma base ou ao integrar com um CRM, onde é comum o mesmo cliente entrar duas vezes com números diferentes. O corpo pode ir vazio ({}) ou com os filtros abaixo.

Body

CampoTipoDescrição
limitintopcionalQuantos pares devolver, no máximo. Padrão 500.
matchKindsarrayopcionalCritérios de busca: "nine_digit_variant" (mesmo número com e sem o nono dígito) e "cross_collision". Sem o campo, usa os dois.

Resposta

{
  "success": true,
  "data": {
    "pairs": [
      {
        "matchKind": "cross_collision",
        "confidence": "high",
        "primaryId": 3,
        "duplicateId": 47626,
        "primaryNumber": "5511999999999",
        "duplicateNumber": "5511988888888",
        "primaryName": "Maria Souza",
        "duplicateName": "Maria S.",
        "ticketsToMove": 0,
        "messagesToMove": 5,
        "conflictReason": null
      }
    ]
  }
}

ticketsToMove e messagesToMove mostram quanto histórico seria transferido se você juntasse o par. confidence indica o quanto o sistema tem certeza de que são a mesma pessoa: confira antes de juntar.

POST /v2/api/external/{apiId}/mergeContacts

Junta pares de contatos repetidos: o histórico do duplicado passa para o principal. Use os pares devolvidos por findDuplicateContacts.

Confira antes de chamar. Juntar contatos move conversas e mensagens de um para o outro. Revise os pares primeiro; não junte tudo automaticamente só porque veio na lista.

Body

CampoTipoDescrição
pairsarrayobrigatórioPares a juntar, no formato [{"primaryId":3,"duplicateId":47626}]. Cada par também aceita os números no lugar dos IDs, nos campos primaryNumber e duplicateNumber. Se um número corresponder a mais de um contato, aquele par volta com ERR_NUMBER_AMBIGUOUS e a lista dos candidatos. Até 150 pares por chamada; para mais, divida em lotes. Falha com 400 e {"error":"ERR_PAIRS_REQUIRED"} se o campo estiver ausente, vazio ou não for uma lista.
Status 200 não quer dizer que juntou. A resposta é avaliada par a par: vem 200 com success: true mesmo quando todos os pares falharam. O que vale é data.summary (total, consolidated, skipped, errors) e data.results, com um item por par (index, status, code, field). Confira summary.errors antes de considerar a operação concluída.
POST /v2/api/external/{apiId}/unmergeContacts

Desfaz uma junção feita anteriormente, separando os contatos de novo.

Body

CampoTipoDescrição
duplicateIdsarrayopcionalIDs dos contatos duplicados que foram juntados (o duplicateId usado no mergeContacts). Use este campo ou o mergeLogIds.
mergeLogIdsarrayopcionalIDs dos registros de junção, como alternativa ao duplicateIds. Até 150 IDs por chamada. As junções são desfeitas da mais recente para a mais antiga.
O campo se chama duplicateIds (ou mergeLogIds), não ids. Um campo com outro nome é ignorado e a chamada falha com 400 e {"error":"ERR_IDS_REQUIRED"}, como se o corpo estivesse vazio. Com o campo certo, a resposta vem 200 e é avaliada item a item: confira data.summary (total, reverted, errors) e data.results. Um ID sem junção registrada aparece ali com "code": "ERR_MERGE_LOG_NOT_FOUND", e o success: true não quer dizer que algo foi desfeito.
POST /v2/api/external/{apiId}/updateContactExtraInfo

Atualiza os campos personalizados (extraInfo) de um contato.

Body

CampoTipoDescrição
contactIdintobrigatórioID do contato.
extraInfoarrayobrigatórioArray no formato [{"name":"campo","value":"valor"}].
POST /v2/api/external/{apiId}/contacts/search

Busca paginada com filtros avançados, por nome ou número (searchParam), tag, carteira e bloqueio. Devolve os dados de cadastro do contato e as tags com o id ("tags": [{"id": 7, "name": "Cliente", "color": "#22c55e"}]), o que a listContacts não traz. Os filtros vão no corpo (Body), em JSON. Cada contato da resposta traz o endereço (cep, logradouro, numeroEndereco, complemento, bairro, cidade, estado), as tags e as carteiras.

Body

CampoTipoDescrição
tagIdintopcionalID de uma tag.
tagIdsarrayopcionalLista de IDs de tags: traz os contatos com qualquer uma delas. Envie tagId ou tagIds.
pageintopcionalPágina (padrão: 1).
limitintopcionalItens por página (padrão: 40).
searchParamstringopcionalTexto para buscar.
walletIdintopcionalFiltra pela carteira de um vendedor.
blockedbooleanopcionalInclui (false) ou exclui (true) contatos bloqueados.
addressCidadestringopcionalCidade: busca por "contém", sem diferenciar acento nem maiúsculas (goiania acha Goiânia).
addressBairrostringopcionalBairro, com a mesma regra da cidade.
addressUfsstring ou arrayopcionalSiglas de UF: "SP,RJ" ou ["SP","RJ"]. Acha também o estado gravado por extenso (GO acha Goiás). Sigla que não existe devolve lista vazia, sem erro: confira a sigla.

Exemplo — buscar todos contatos com a tag "Lead Quente"

curl --request POST \
  --url 'https://SUA_URL_BASE/v2/api/external/SEU_API_ID/contacts/search' \
  --header 'Authorization: Bearer SEU_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "tagId": 42,
    "page": 1,
    "limit": 100
  }'

Exemplo: contatos de São Paulo e do Rio de Janeiro com a tag 42

curl --request POST \
  --url 'https://SUA_URL_BASE/v2/api/external/SEU_API_ID/contacts/search' \
  --header 'Authorization: Bearer SEU_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "addressUfs": ["SP", "RJ"],
    "tagId": 42,
    "limit": 100
  }'
Testado por nós em 29/10/2026. Filtro de UF por sigla e por extenso, cidade sem acento, UF em lista e em texto, e o padrão de 40 por página. A resposta traz meta.addressFilter: true quer dizer que os filtros de endereço foram aplicados.
POST /v2/api/external/{apiId}/telegramInvite

Gera o link de convite do Telegram para um contato que já existe. Quando a pessoa abre o link e toca em Iniciar, o Telegram dela fica ligado a esse contato e você passa a atendê-la por lá. O link vale 7 dias, é de uso único e só o mais recente funciona (gerar de novo cancela o anterior).

CampoTipoDescrição
contactIdintopcionalID do contato. Envie contactId ou number.
numberstringopcionalTelefone do contato, no lugar do contactId. Se o número pertencer a mais de um contato: 422 ERR_NUMBER_AMBIGUOUS com a lista candidates.
whatsappIdintopcionalID do bot do Telegram. Dispensável quando o token já é de um canal do Telegram ou quando a conta tem um só bot conectado.

Exemplo

curl --request POST \
  --url 'https://SUA_URL_BASE/v2/api/external/SEU_API_ID/telegramInvite' \
  --header 'Authorization: Bearer SEU_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "contactId": 123
  }'

Resposta

{
  "success": true,
  "data": {
    "link": "https://t.me/seu_bot?start=CODIGO",
    "expiresAt": "2026-11-05T11:23:35.842Z",
    "contactId": 123,
    "whatsappId": 7
  }
}
Quem tiver o link consegue ligar o próprio Telegram ao contato. Envie o link só para a própria pessoa, por um canal em que você já fala com ela.
ErroQuando acontece
400 ERR_CONTACT_OR_NUMBER_REQUIREDFaltou contactId e number.
404 ERR_CONTACT_NOT_FOUNDContato não existe (ou foi juntado a outro).
409 ERR_TELEGRAM_ALREADY_LINKEDO contato já tem Telegram.
400 ERR_TELEGRAM_INVITE_GROUPO contato é um grupo.
400 ERR_TELEGRAM_BOT_REQUIREDA conta tem mais de um bot: informe whatsappId.
409 ERR_TELEGRAM_BOT_NOT_FOUND / ERR_TELEGRAM_BOT_OFFLINENenhum bot do Telegram conectado, whatsappId que não é um bot do Telegram da sua conta, ou o bot está desconectado.
403 ERR_SESSION_NOT_AUTH_TOKENO token é de um canal e o whatsappId enviado é de outro.
424 ERR_TELEGRAM_BOT_UNAVAILABLEO Telegram não respondeu. Tente de novo.
Testado por nós em 29/10/2026. Geração do link para um contato da nossa conta, o erro sem contato e o 403 com o bot de outro canal.

📨 Mensagens (POST)

Endpoints para envio de mensagens via WhatsApp através das suas conexões. Suporta texto, arquivos, áudio, localização, e os tipos interativos da API oficial WhatsApp Business (WABA).

POST /v2/api/external/{apiId}

Envia uma mensagem de texto para um número WhatsApp.

Body

CampoTipoDescrição
numberstringobrigatórioNúmero com DDI + DDD (ex: 5511999999999).
bodystringobrigatórioTexto da mensagem.
ticketIdintopcionalTicket ao qual vincular a mensagem.
externalKeystringopcionalIdentificador da mensagem no seu sistema. Não pode se repetir.
isClosedbooleanopcionaltrue fecha o ticket logo depois do envio.
validateNumberbooleanopcionalPadrão true: o sistema ajusta o nono dígito de números brasileiros. Com false, usa o número exatamente como você enviou.
API Oficial: prefira "validateNumber": false e envie o número exatamente como o cliente escreveu para você. O ajuste automático do nono dígito pode gerar um número diferente do que a Meta conhece e abrir um segundo ticket para a mesma pessoa. Em canais que não são WhatsApp, o number é o identificador do canal: IGSID no Instagram, PSID no Messenger e ID do Telegram. Para responder uma conversa que já existe, em qualquer canal, o mais simples é o envio pelo ticket.

Exemplo

curl --request POST \
  --url 'https://SUA_URL_BASE/v2/api/external/SEU_API_ID' \
  --header 'Authorization: Bearer SEU_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "number": "5511999999999",
    "body": "Olá! Esta é uma mensagem de teste."
  }'
Erro comum no envioO que fazer
424 ERR_WAPP_CONTACT_UNVERIFIEDO número não tem WhatsApp, ou a consulta ao WhatsApp falhou naquele momento. Confira o número; se estiver certo, tente de novo em alguns segundos.
400 ERR_CHANNEL_DISCONNECTEDA conexão por QR Code está esperando a leitura do QR. Reconecte o canal no painel.
400 ERR_INVALID_BODYbody veio como objeto ou lista. Mande texto (número é convertido para texto).
A tabela vale também para o envio de arquivo, de áudio, em base64, por GET e para grupo. No envio para grupo, mensagem sem body e sem arquivo responde 400 ERR_MISSING_BODY_OR_MEDIA.
POST /v2/api/external/{apiId}/url

Envia um arquivo (imagem, PDF, áudio, vídeo) a partir de uma URL pública.

Body

CampoTipoDescrição
numberstringobrigatórioNúmero de destino.
mediaUrlstringobrigatórioURL pública do arquivo.
bodystringopcionalLegenda / texto que acompanha a mídia.
POST /v2/api/external/{apiId}/sendMessageByTicket

Envia uma mensagem para uma conversa que já existe, usando o ID do ticket em vez do número. Funciona em qualquer canal da sua conta (WhatsApp, API Oficial, Instagram, Messenger, Telegram, Webchat e e-mail): o canal é o do próprio ticket. É o caminho mais simples para responder, a partir do seu sistema, uma conversa que chegou por webhook.

Body

CampoTipoDescrição
ticketIdintobrigatórioID do ticket (conversa).
bodystringopcionalTexto da mensagem. Obrigatório quando não há mídia; com mídia, vira a legenda.
mediaUrlstringopcionalEndereço público do arquivo (http ou https). Endereços internos são recusados com ERR_BLOCKED_HOST.
base64DatastringopcionalArquivo em base64. Exige mimeType (ex: image/png); aceita fileName.
mediafileopcionalArquivo enviado como multipart/form-data, com os outros campos no mesmo formulário.
externalKeystringopcionalChave sua para evitar envio em dobro: repetir a mesma chave em até 2 minutos não envia de novo (veja abaixo). Recomendado.
reopenbooleanopcionaltrue reabre o ticket se ele estiver fechado. Sem isso, ticket fechado volta com 409.
isClosedbooleanopcionaltrue fecha o ticket depois do envio.

Use uma fonte de mídia por chamada: media, mediaUrl ou base64Data.

Exemplo

curl --request POST \
  --url 'https://SUA_URL_BASE/v2/api/external/SEU_API_ID/sendMessageByTicket' \
  --header 'Authorization: Bearer SEU_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "ticketId": 1262,
    "body": "Olá! Recebemos seu pedido.",
    "externalKey": "pedido-8841-confirmacao"
  }'

Resposta

{
  "success": true,
  "data": {
    "message": "Message sent successfully",
    "ticketId": 1262,
    "messageId": null
  }
}

Se a mesma externalKey chegar de novo em até 2 minutos, nada é enviado e a resposta é {"success": true, "data": {"idempotent": true, "skipped": true, "ticketId": 1262}}. Isso protege contra mensagem duplicada quando o seu sistema repete a chamada depois de um erro de rede.

Erros

CódigoQuando
400 ERR_INVALID_TICKET_IDticketId ausente ou inválido.
400 ERR_MISSING_BODY_OR_MEDIASem texto e sem mídia.
400 ERR_BLOCKED_HOSTmediaUrl aponta para um endereço interno.
409 ERR_TICKET_CLOSEDTicket fechado. Envie "reopen": true para reabrir e enviar.
POST /v2/api/external/{apiId}/sendPresence

Envia indicadores de presença (estado) para o WhatsApp do contato — útil para humanizar bots mostrando "digitando..." antes de uma resposta automática.

Body

CampoTipoDescrição
ticketIdintobrigatórioID do ticket.
statestringobrigatóriotyping, paused ou recording.
API Oficial: só typing funciona, e a Meta marca como lida a última mensagem recebida do contato. paused e recording respondem 400 ERR_PRESENCE_NOT_SUPPORTED (paused testado em 29/10/2026).
POST /v2/api/external/{apiId}/sendButtonWABA

Envia mensagem com até 3 botões interativos via WhatsApp Business API (WABA).

Disponível apenas em conexões WABA (API oficial). Não funciona em conexão por QR Code.

Body

CampoTipoDescrição
numberstringobrigatórioNúmero de destino.
messagestringobrigatórioTexto principal da mensagem.
button1stringobrigatórioTexto do primeiro botão (máx 20 caracteres).
button2stringopcionalTexto do segundo botão.
button3stringopcionalTexto do terceiro botão.
ticketIdintopcionalVincular a um ticket existente.
Só em conexão da API Oficial: os outros tipos de canal respondem 400 ERR_CHANNEL_WABA_REQUIRED.
POST /v2/api/external/{apiId}/sendListWABA

Envia mensagem com lista interativa (menu suspenso) via WhatsApp Business API (WABA).

Body

CampoTipoDescrição
numberstringobrigatórioNúmero de destino.
headerstringobrigatórioCabeçalho da lista.
bodystringobrigatórioTexto principal.
footerstringopcionalRodapé.
button_textstringobrigatórioTexto do botão de abertura da lista.
sectionsarrayobrigatórioSeções com itens da lista.
ticketIdintopcionalVincular a um ticket existente.
Só em conexão da API Oficial: os outros tipos de canal respondem 400 ERR_CHANNEL_WABA_REQUIRED. Se a Meta recusar (número inválido, fora da janela de 24 h, lista fora do formato), a resposta é 400 ERR_WABA_SEND_FAILED com metaCode e a mensagem da Meta: é ela que diz o motivo. Cada item de sections tem title e rows, e cada linha tem id, title e description.

🎫 Tickets & Tags

POST /v2/api/external/{apiId}/updateTicketChannel

Altera o canal (conexão WhatsApp) de um ticket existente — útil quando você precisa migrar um atendimento de uma conexão para outra.

Body

CampoTipoDescrição
ticketIdintobrigatórioID do ticket.
whatsappIdintobrigatórioID da nova conexão WhatsApp.
channelstringobrigatórioContinua obrigatório só por compatibilidade: o tipo gravado no ticket vem do canal do whatsappId.
O whatsappId precisa ser um canal da sua conta (senão 404 ERR_NO_WAPP_FOUND). A troca fica registrada no histórico do atendimento como mensagem de sistema, que não é enviada ao contato.
POST /v2/api/external/{apiId}/createNotes

Adiciona uma nota interna (visível apenas para atendentes) em um ticket.

Body

CampoTipoDescrição
ticketIdintobrigatórioID do ticket.
notesstringobrigatórioTexto da nota.
userIdintopcionalID do atendente que aparece como autor. Precisa ser da sua conta (senão 404 ERR_USER_NOT_FOUND).
idFrontstringopcionalChave sua para não duplicar: uma segunda nota com o mesmo idFront é recusada com 409 ERR_NOTE_EXISTS. Útil quando a sua integração pode repetir a chamada.
Testado por nós em 29/10/2026. Nota criada responde 201 (não 200). Corpo sem ticketId e sem notes: 400 ERR_MISSING_FIELDS. idFront repetido: 409 ERR_NOTE_EXISTS.
POST /v2/api/external/{apiId}/updateNote

Atualiza o conteúdo de uma nota interna existente.

Body

CampoTipoDescrição
noteIdintobrigatórioID da nota.
notesstringobrigatórioNovo texto.
POST /v2/api/external/{apiId}/addTag

Adiciona uma ou mais tags ao contato do ticket sem remover as existentes. Ideal para classificar atendimentos durante automações sem perder histórico.

Body

CampoTipoDescrição
ticketIdintobrigatórioID do ticket.
tagIdintopcionalID de uma tag.
tagIdsarrayopcionalLista de IDs de tags, para adicionar várias de uma vez. Envie tagId ou tagIds.
Lista vai em tagIds. Um array dentro de tagId volta com 500 (ERR_ADD_TAG ou ERR_REMOVE_TAG), que parece falha do servidor mas é o formato do campo. A tag fica no contato do ticket, então aparece em todas as conversas dele. O token usado precisa estar vinculado a uma conexão; sem isso a resposta é 400 ERR_API_REQUIRES_SESSION.
POST /v2/api/external/{apiId}/removeTag

Remove uma ou mais tags específicas do contato do ticket.

Body

CampoTipoDescrição
ticketIdintobrigatórioID do ticket.
tagIdintopcionalID de uma tag.
tagIdsarrayopcionalLista de IDs de tags, para remover várias de uma vez. Envie tagId ou tagIds.
Lista vai em tagIds. Um array dentro de tagId volta com 500 (ERR_ADD_TAG ou ERR_REMOVE_TAG), que parece falha do servidor mas é o formato do campo. A tag fica no contato do ticket, então aparece em todas as conversas dele. O token usado precisa estar vinculado a uma conexão; sem isso a resposta é 400 ERR_API_REQUIRES_SESSION.

👥 Usuários (Atendentes)

POST /v2/api/external/{apiId}/createUser

Cria um novo usuário (atendente) na sua conta.

Body

CampoTipoDescrição
namestringobrigatórioNome do atendente.
emailstringobrigatórioE-mail (será o login).
passwordstringobrigatórioSenha inicial.
profilestringobrigatórioadmin, super ou user.
POST /v2/api/external/{apiId}/updateUser

Atualiza dados de um usuário existente.

Body

CampoTipoDescrição
userIdintobrigatórioID do usuário.
namestringopcionalNovo nome.
emailstringopcionalNovo e-mail.

📣 Campanhas

Crie e gerencie campanhas de envio programado via API: defina mensagens (com rotação de até 3 variações), data de início, conexão de envio e a lista de contatos. Ideal para integrar disparos a CRMs e automações.

Boas práticas: use o campo delay com valores adequados e evite enviar a mesma mensagem para muitos destinatários em janelas curtas — envios em massa sem intervalo podem causar restrições no número pelo WhatsApp/Meta. Prefira as 3 variações de mensagem.
POST /v2/api/external/{apiId}/campaign/create

Cria uma campanha. Após criar, adicione os contatos e inicie com campaign/start.

Body

CampoTipoDescrição
namestringobrigatórioNome da campanha.
startstringobrigatórioData/hora de início em ISO 8601 (ex: 2026-04-10T10:00:00.000Z).
message1stringobrigatórioMensagem principal. Aceita placeholder {nome}.
message2stringopcionalVariação 2 (rotação automática entre contatos).
message3stringopcionalVariação 3.
sessionIdintobrigatórioID da conexão (canal) que fará os envios — veja Listar Canais.
delaystringopcionalIntervalo (segundos) entre envios.

Exemplo

curl --request POST \
  --url 'https://SUA_URL_BASE/v2/api/external/SEU_API_ID/campaign/create' \
  --header 'Authorization: Bearer SEU_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "name": "Promoção Junho",
    "start": "2026-06-15T10:00:00.000Z",
    "message1": "Olá {nome}! Temos uma novidade para você.",
    "message2": "Oi {nome}, tudo bem? Confira nossa novidade.",
    "sessionId": 12,
    "delay": "5"
  }'
POST /v2/api/external/{apiId}/campaign/update/{campaignId}

Atualiza uma campanha existente (mesmos campos do create). Só é aceito com a campanha em pending, scheduled, paused ou canceled; em processing ou finished responde 404. O texto vai em message1/message2/message3 e a variável do nome é {{name}}. O status não muda por aqui: use iniciar, pausar, retomar ou cancelar.

GET /v2/api/external/{apiId}/campaign/list?page=1&limit=10

Lista as campanhas do tenant com paginação. Retorna status, totais e configuração de cada campanha.

GET /v2/api/external/{apiId}/campaign/report/{campaignId}

Relatório da campanha: contatos, status de entrega por contato e progresso geral.

POST /v2/api/external/{apiId}/campaign/<ação>/{campaignId}

Ações de ciclo de vida da campanha. Todas são POST com body vazio ({}).

Ações disponíveis

RotaEfeito
campaign/start/{campaignId}Inicia o disparo.
campaign/pause/{campaignId}Pausa o disparo em andamento.
campaign/resume/{campaignId}Retoma uma campanha pausada.
campaign/cancel/{campaignId}Cancela definitivamente.
campaign/skip/{campaignId}Pula o contato atual da fila.
campaign/duplicate/{campaignId}Duplica a campanha (nova cópia em rascunho).
campaign/delete/{campaignId}Exclui a campanha.
POST /v2/api/external/{apiId}/campaign/contacts/add/{campaignId}

Gerencia os contatos de uma campanha. O add recebe um array de contatos no body.

Rotas

MétodoRotaDescrição
POSTcampaign/contacts/add/{campaignId}Adiciona contatos (array de {name, number}).
GETcampaign/contacts/{campaignId}Lista os contatos da campanha.
POSTcampaign/contacts/remove/{campaignId}/{contactId}Remove um contato.
POSTcampaign/contacts/removeAll/{campaignId}Remove todos os contatos.

Exemplo (add)

curl --request POST \
  --url 'https://SUA_URL_BASE/v2/api/external/SEU_API_ID/campaign/contacts/add/123' \
  --header 'Authorization: Bearer SEU_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '[
    { "name": "João", "number": "5511999990001" },
    { "name": "Maria", "number": "5511999990002" }
  ]'

📤 Envio em Lote

Endpoints para disparo direto a múltiplos números, sem criar uma campanha. Os campos min e max definem o intervalo aleatório (em segundos) entre cada envio — essencial para envios saudáveis.

POST /v2/api/external/{apiId}/bulkSendMessage

Envia a mesma mensagem para uma lista de números.

Body

CampoTipoDescrição
whatsappIdintobrigatórioID da conexão de envio.
arrayNumbersarrayobrigatórioLista de números (DDI+DDD+número).
messagestringobrigatórioTexto a enviar.
min / maxintopcionalIntervalo aleatório entre envios, em segundos (ex: 3 a 8).

Exemplo

curl --request POST \
  --url 'https://SUA_URL_BASE/v2/api/external/SEU_API_ID/bulkSendMessage' \
  --header 'Authorization: Bearer SEU_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "whatsappId": 12,
    "arrayNumbers": ["5511999990001", "5511999990002"],
    "message": "Olá, tudo bem?",
    "min": 3,
    "max": 8
  }'
POST /v2/api/external/{apiId}/bulkSendMessageWithVariable

Envio em massa personalizado por contato: a mensagem usa placeholders e os valores vêm de uma lista CSV (uma linha por destinatário).

Body

CampoTipoDescrição
whatsappIdintobrigatórioID da conexão de envio.
messagestringobrigatórioMensagem com placeholders, ex: Olá {nome}, seu código é {codigo}.
dataInputstringobrigatórioLinhas numero,valor1,valor2 separadas por \n — os valores preenchem os placeholders na ordem.
min / maxintopcionalIntervalo aleatório entre envios (segundos).

Exemplo

curl --request POST \
  --url 'https://SUA_URL_BASE/v2/api/external/SEU_API_ID/bulkSendMessageWithVariable' \
  --header 'Authorization: Bearer SEU_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "whatsappId": 12,
    "message": "Olá {nome}, seu código é {codigo}",
    "dataInput": "5511999990001,João,ABC123\n5511999990002,Maria,DEF456",
    "min": 3,
    "max": 8
  }'
POST /v2/api/external/{apiId}/bulkFastMessage

Variante do envio em massa que informa o tipo da conexão (whatsappType) — útil quando o tenant tem conexões de tipos diferentes.

Body

CampoTipoDescrição
whatsappIdintobrigatórioID da conexão.
whatsappTypestringobrigatórioTipo da conexão (conforme retornado em Listar Canais).
arrayNumbersarrayobrigatórioLista de números.
messagestringobrigatórioTexto a enviar.
min / maxintopcionalIntervalo aleatório entre envios (segundos).
Segundo o painel, esta rota só funciona em conexões do tipo whatsapp e baileys, e o whatsappType é obrigatório. Ela não cria atendimento. Para os outros tipos de conexão, use o bulkIndividual número a número.
POST /v2/api/external/{apiId}/bulkIndividual

Envio individual pela mesma infraestrutura do lote — aceita externalKey para controle/idempotência do seu lado.

Body

CampoTipoDescrição
whatsappIdintobrigatórioID da conexão.
whatsappTypestringobrigatórioTipo da conexão, como vem em Listar Canais (ex: whatsapp, baileys, uazapi, waba). Sem ele a resposta é 200 com success: true e nada é enviado.
numberstringobrigatórioNúmero de destino.
messagestringobrigatórioTexto a enviar.
externalKeystringopcionalChave externa para rastrear o envio.

Exemplo

curl --request POST \
  --url 'https://SUA_URL_BASE/v2/api/external/SEU_API_ID/bulkIndividual' \
  --header 'Authorization: Bearer SEU_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "whatsappId": 246,
    "whatsappType": "uazapi",
    "number": "5511999999999",
    "message": "Olá! Sua encomenda saiu para entrega."
  }'
Testado por nós em 29/10/2026. Sem whatsappType: 200 com success: true, ticketId: null e nenhuma mensagem enviada. Com ele, em JSON ou em form-data: a mensagem chegou e a resposta trouxe o ticketId do atendimento. Até 29/10/2026 esta página não listava o whatsappType nesta rota. Confira sempre se veio ticketId.
GET /v2/api/external/{apiId}/bulkDispatch/list?page=1&limit=20

Acompanhe os despachos em lote criados pelos endpoints acima.

Rotas

MétodoRotaDescrição
GETbulkDispatch/listLista despachos (paginação page/limit).
GETbulkDispatch/show/{dispatchId}Detalhe de um despacho (progresso, status).
POSTbulkDispatch/update/{dispatchId}Atualiza um despacho — ex: {"status": "cancelled", "cancellationReason": "..."}.

👥 Grupos WhatsApp

Crie e administre grupos do WhatsApp via API: participantes, administradores, título, descrição, foto e link de convite. Em todas as rotas, whatsappId é o ID da conexão (veja Listar Canais) e groupId é o identificador do grupo (formato 123456789@g.us).

POST /v2/api/external/{apiId}/group/create

Cria um ou mais grupos e adiciona o número informado como primeiro participante.

Body (exemplo)

{
  "whatsappId": 12,
  "titles": ["Meu Grupo 1"],
  "number": "5511999990001"
}
POST /v2/api/external/{apiId}/group/<ação>

Ações de administração. Todas recebem whatsappId + groupIds (array) e os campos específicos abaixo.

Rotas

MétodoRotaDescrição
POSTgroup/addParticipantAdiciona participantes — body extra: participants (array de números).
POSTgroup/removeParticipantRemove participantes — participants.
POSTgroup/promotePromove participantes a admin — participants.
POSTgroup/demoteRebaixa admins a participante — participants.
POSTgroup/changeTitleAltera o título — title.
POSTgroup/changeDescriptionAltera a descrição — description.
POSTgroup/changePictureAltera a foto — picture: link público da imagem (em JSON) ou o próprio arquivo (em form-data). Testado em 29/10/2026 numa conexão por QR Code: a foto do grupo mudou. A resposta só diz success e não devolve a foto: confira no próprio grupo.
POSTgroup/setAdminsOnlyRestringe mensagens a admins — adminsOnly (true/false).
POSTgroup/revokeInviteLinkInvalida o link de convite atual. Recebe whatsappId e groupId (um grupo só, não lista).
POSTgroup/leaveA conexão sai do grupo. Recebe whatsappId e groupId. Só funciona em conexão da API Oficial; nas outras volta ERR_LEAVE_GROUP_WABA_ONLY.

Body (exemplo)

{
  "whatsappId": 12,
  "groupIds": ["123456789@g.us"],
  "participants": ["5511999990001"]
}
Leia a resposta número a número. Em adicionar, remover, promover e rebaixar, a resposta traz um item por grupo (groupId, status, reason) e, nas conexões por QR Code, participants com o resultado de cada número. Motivos comuns: invite_required (a pessoa só entra por convite), already_in_group, not_on_whatsapp, not_in_group; no grupo, not_admin (a conexão não é administradora) e session_not_connected. Se todos os números forem recusados, vem ERR_GROUP_PARTICIPANTS_REJECTED; lista vazia, ERR_GROUP_PARTICIPANTS_REQUIRED.
POST /v2/api/external/{apiId}/group/list

Consultas de grupos da conexão.

Rotas

MétodoRotaDescrição
POSTgroup/listLista os grupos — body: {whatsappId}.
POSTgroup/listParticipantsParticipantes de grupos — {whatsappId, groupIds}.
POSTgroup/showByIdDetalhe de um grupo — {whatsappId, groupId}.
POSTgroup/getInviteLinkLink de convite — {whatsappId, groupId}.
POSTlistGroupInfoListagem geral com participantes — {listGroups: true, listParticipants: true} (opcional groupId).
POST /v2/api/external/{apiId}/group

Envia mensagens para um grupo. No campo number, informe o ID numérico do grupo (sem o sufixo @g.us).

Rotas

MétodoRotaDescrição
POST/v2/api/external/{apiId}/groupTexto para grupo — body: {body, number, externalKey, isClosed}.
POST/v2/api/external/{apiId}/groupMediaUrlArquivo via URL — body: {mediaUrl, body, number, externalKey, isClosed}.

Body (exemplo)

{
  "body": "A mensagem desejada",
  "number": "123456789015189153",
  "externalKey": "SUA_CHAVE_EXTERNA",
  "isClosed": false
}

📨 Mensagens — Recursos Adicionais

Complementos aos endpoints de envio de texto e arquivo: áudio como mensagem de voz, arquivo em base64, localização, cartão de contato (vCard), busca e consulta de mensagens.

POST /v2/api/external/{apiId}/voice

Envia um áudio como mensagem de voz (PTT) a partir de uma URL pública.

Body (exemplo)

{
  "audio": "https://exemplo.com/audio.ogg",
  "number": "5511999999999",
  "externalKey": "SUA_CHAVE_EXTERNA",
  "isClosed": false
}
POST /v2/api/external/{apiId}/base64

Envia arquivo codificado em base64 — útil quando o arquivo não tem URL pública.

Body (exemplo)

{
  "body": "A mensagem desejada",
  "number": "5511999999999",
  "base64Data": "iVBORw0KGgoAAAANSUhEUgAA...",
  "mimeType": "image/png",
  "fileName": "exemplo_imagem",
  "isClosed": false
}
GET /v2/api/external/{apiId}/params/?body=...&number=...&externalKey=...&bearertoken=...

Envio de texto via GET (parâmetros na URL) — para integrações que só conseguem disparar GET. Informe body, number, externalKey, bearertoken e opcional isClosed.

POST /v2/api/external/{apiId}/sendLocation

Envia uma localização (pin no mapa) com nome e endereço. O destino vem de number e o ticketId é obrigatório: é o atendimento em que a mensagem fica registrada. Ticket de outra conta: 404 ERR_TICKET_NOT_FOUND.

Body (exemplo)

{
  "number": "5511999990001",
  "latitude": -23.5505,
  "longitude": -46.6333,
  "name": "São Paulo",
  "address": "Av. Paulista, 1000",
  "ticketId": 1262,
  "externalKey": "SUA_CHAVE_EXTERNA"
}
Testado por nós em 29/10/2026. Sem ticketId: 400 ERR_MISSING_TICKET e nada é enviado. Com o ticket, a localização chega normalmente. Até 29/10/2026 o exemplo desta página trazia "ticketId": null, que não funciona.
POST /v2/api/external/{apiId}/sendVcard

Envia um cartão de contato (vCard). O ticketId é obrigatório, como na localização. Dentro de contact, os campos fullName, wuid e phoneNumber são obrigatórios; organization e email são opcionais.

Body (exemplo)

{
  "number": "5511999990001",
  "contact": [
    {
      "fullName": "João Silva",
      "wuid": "5511999990002@s.whatsapp.net",
      "phoneNumber": "5511999990002"
    }
  ],
  "ticketId": 1262,
  "externalKey": "SUA_CHAVE_EXTERNA"
}
Testado por nós em 29/10/2026. contact aceito como lista (exemplo acima) e como objeto único ("contact": {"fullName": "...", "wuid": "...", "phoneNumber": "..."}). Sem ticketId: 400 ERR_MISSING_TICKET.
GET /v2/api/external/{apiId}/searchMessages

Consulta de mensagens.

Rotas

MétodoRotaDescrição
GETsearchMessages?ticketId=...&searchParam=...&page=1&limit=20Busca textual nas mensagens de um ticket.
GETgetMessageByMessageId?messageId=...Recupera uma mensagem pelo ID do WhatsApp.

🧩 Mensagens Interativas

Botões, listas, enquetes e templates ricos. Os recursos disponíveis dependem do tipo da conexão do ticket. Todos os endpoints recebem ticketId e respondem dentro da conversa existente.

POST /v2/api/external/{apiId}/sendInteractive/baileys/<tipo>

Conexões padrão (não-oficiais).

Rotas

MétodoRotaDescrição
POSTsendInteractive/baileys/quickReplyBotões de resposta rápida — {ticketId, body:{text}, footer:{text}, buttons:[{display_text,id}]}.
POSTsendInteractive/baileys/singleSelectLista de seleção única — {ticketId, body, footer, list:{title, sections:[{title, rows:[{id,title,description}]}]}}.
POSTsendInteractive/baileys/pixButtonBotão PIX — {ticketId, pixType, pixKey, pixName, bodyText}.
POSTsendInteractive/baileys/ctaCopyBotão copiar código — {ticketId, body, footer, displayText, copyCode}.
POSTsendInteractive/baileys/ctaUrlBotão abrir URL — {ticketId, body, footer, displayText, url}.
POSTsendInteractive/baileys/ctaCallBotão ligar — {ticketId, body, footer, displayText, phoneNumber}.

Body (exemplo)

{
  "ticketId": 1262,
  "body": { "text": "Escolha uma opção:" },
  "footer": { "text": "Rodapé opcional" },
  "buttons": [
    { "display_text": "Opção 1", "id": "op1" },
    { "display_text": "Opção 2", "id": "op2" }
  ]
}
POST /v2/api/external/{apiId}/sendInteractive/uazapi/<tipo>

Conexões do tipo uazapi.

Rotas

MétodoRotaDescrição
POSTsendInteractive/uazapi/buttonBotões — {ticketId, text, choices:[...], footerText, imageButton}.
POSTsendInteractive/uazapi/listLista — {ticketId, text, choices, listButton, footerText}.
POSTsendInteractive/uazapi/pollEnquete — {ticketId, text, choices, selectableCount}.
POSTsendInteractive/uazapi/carouselCarrossel de cards — {ticketId, text, carousel:[{text,image,buttons:[{text,type}]}]}.
POSTsendInteractive/uazapi/pixButtonBotão PIX — {ticketId, pixType, pixKey, pixName}.
POSTsendInteractive/uazapi/locationButtonSolicitar localização — {ticketId, text}.
POSTsendInteractive/uazapi/requestPaymentSolicitação de pagamento — {ticketId, amount, title, text, footer, itemName, invoiceNumber, pixType, pixKey, pixName}. Obrigatórios: ticketId e amount. Aceita também boleto (boletoCode), link de pagamento (paymentLink) e anexo (fileUrl, fileName).

Body (exemplo)

{
  "ticketId": 1262,
  "text": "Qual sua preferência?",
  "choices": ["Opção A", "Opção B", "Opção C"],
  "selectableCount": 1
}
POST /v2/api/external/{apiId}/sendInteractive/instagram/<tipo>

Conexões Instagram Direct.

Rotas

MétodoRotaDescrição
POSTsendInteractive/instagram/quickReplyRespostas rápidas — {ticketId, message, quickReplies:[{content_type,title,payload}]}.
POSTsendInteractive/instagram/buttonTemplateBotões — {ticketId, message, buttons:[{type,title,payload}]}.
POSTsendInteractive/instagram/genericTemplateCards — {ticketId, elements:[{title,subtitle,image_url,buttons}]}.
POSTinstagram/iceBreakersPerguntas iniciais (get/set/delete) — {action, iceBreakers:[{question,payload}]}.
POSTinstagram/persistentMenuMenu persistente (get/set/delete) — {action, composerInputDisabled, menuItems}.
POST /v2/api/external/{apiId}/sendInteractive/messenger/<tipo>

Conexões Facebook Messenger.

Rotas

MétodoRotaDescrição
POSTsendInteractive/messenger/quickReplyRespostas rápidas — {ticketId, message, quickReplies}.
POSTsendInteractive/messenger/buttonTemplateBotões — {ticketId, message, buttons} (suporta web_url).
POSTsendInteractive/messenger/genericTemplateCards — {ticketId, elements}.
POSTsendInteractive/messenger/mediaTemplateMídia + botões — {ticketId, mediaType, mediaUrl, buttons}.
POSTsendInteractive/messenger/receiptTemplateRecibo de compra — {ticketId, receipt:{recipient_name, order_number, currency, summary, elements}}.
POSTsendInteractive/messenger/messageTagMensagem fora da janela 24h com tag — {ticketId, message, tag} (ex: POST_PURCHASE_UPDATE).
POSTsendInteractive/messenger/customerFeedbackPesquisa NPS/CSAT — {ticketId, title, subtitle, business_privacy_url, expires_in_days, feedback_screens}.
POSTmessenger/greetingSaudação da página (get/set/delete) — {action, greetings:[{locale,text}]}.
POSTmessenger/personasPersonas de atendente (list/create/delete) — {action, name, profilePictureUrl}.

📋 Templates WABA (API Oficial)

Na API Oficial do WhatsApp, a primeira mensagem para um contato, ou qualquer mensagem depois de 24 horas sem resposta dele, só pode ser um modelo (template) aprovado pela Meta.

O token precisa estar ligado à conexão da API Oficial. O modelo sai pela conexão escolhida quando a chave foi criada (menu API > Nova API). Com a chave ligada a uma conexão por QR Code, o envio de modelo não funciona. Se você usa as duas conexões, crie uma chave para cada uma.
POST /v2/api/external/{apiId}/template

Envia um modelo aprovado, com ou sem variáveis. Cria (ou usa) o atendimento do contato no painel.

Qual rota usar

RotaQuando
templateUse esta. Serve para modelo sem variáveis, com variáveis no corpo, com botão de link e com imagem, vídeo ou documento no cabeçalho.
templateBodyAceita o mesmo formato de corpo da rota template.
templateMarketingBodyMesmo corpo da rota template, com o caminho escolhido pela categoria do modelo: modelo de Marketing sai pela Marketing Messages API da Meta quando a conta já aceitou os termos dela (senão, pelo envio comum); Utilidade e Autenticação saem pelo envio comum, então a rota também aceita modelo de Utilidade. Testado em 30/10/2026: o modelo de Marketing foi cobrado pela Meta na categoria da Marketing Messages API (marketing_lite) e o de Utilidade foi aceito pela mesma rota.

Body

CampoTipoDescrição
numberstringobrigatórioNúmero do contato só com dígitos: DDI + DDD + número (ex.: 5511999999999).
templateDataobjectobrigatórioO objeto completo no formato da Meta: messaging_product, type e template. O campo to é opcional: o sistema usa o number.
isClosedbooleanopcionaltrue fecha o atendimento logo depois do envio (aviso automático, sem conversa). false deixa aberto.
validateNumberbooleanopcionalPadrão true: o sistema ajusta o número (9º dígito). Com false, usa o número exatamente como enviado. Recomendado false na API Oficial: evita contato e atendimento em dobro para a mesma pessoa.

Dentro de template

CampoDescrição
nameNome do modelo exatamente como aprovado na Meta (minúsculas e _).
language.codeO idioma em que o modelo foi aprovado, ex.: pt_BR. Com outro idioma, a Meta recusa como se o modelo não existisse.
componentsSó quando o modelo tem variáveis, botão de link com final variável ou mídia no cabeçalho. Os valores vão na ordem das variáveis ({{1}}, {{2}}...) e na mesma quantidade. Modelo sem variáveis: não envie components, nem vazio.

Exemplo 1: modelo sem variáveis

{
  "number": "5511999999999",
  "isClosed": false,
  "validateNumber": false,
  "templateData": {
    "messaging_product": "whatsapp",
    "type": "template",
    "template": {
      "name": "boas_vindas",
      "language": { "code": "pt_BR" }
    }
  }
}

Exemplo 2: variáveis no corpo ({{1}} = nome, {{2}} = valor) e botão de link com final variável

{
  "number": "5511999999999",
  "isClosed": true,
  "validateNumber": false,
  "templateData": {
    "messaging_product": "whatsapp",
    "type": "template",
    "template": {
      "name": "fatura_disponivel",
      "language": { "code": "pt_BR" },
      "components": [
        {
          "type": "body",
          "parameters": [
            { "type": "text", "text": "Maria" },
            { "type": "text", "text": "R$ 150,00" }
          ]
        },
        {
          "type": "button",
          "sub_type": "url",
          "index": "0",
          "parameters": [ { "type": "text", "text": "abc123" } ]
        }
      ]
    }
  }
}

Exemplo 3: vídeo no cabeçalho, variáveis no corpo e botão de link com final variável

{
  "number": "5511999999999",
  "isClosed": true,
  "validateNumber": false,
  "templateData": {
    "messaging_product": "whatsapp",
    "type": "template",
    "template": {
      "name": "reuniao_confirmada",
      "language": { "code": "pt_BR" },
      "components": [
        {
          "type": "header",
          "parameters": [
            { "type": "video", "video": { "link": "https://seusite.com.br/video.mp4" } }
          ]
        },
        {
          "type": "body",
          "parameters": [
            { "type": "text", "text": "Maria" },
            { "type": "text", "text": "29/10" },
            { "type": "text", "text": "19:30" }
          ]
        },
        {
          "type": "button",
          "sub_type": "url",
          "index": "0",
          "parameters": [ { "type": "text", "text": "abc123" } ]
        }
      ]
    }
  }
}

O cabeçalho vai em components, antes do body. O modelo aprovado com mídia no cabeçalho pede a mídia em todo envio: sem ela, a Meta recusa com (#132012). Para imagem ou documento, troque o item de parameters do cabeçalho por { "type": "image", "image": { "link": "https://..." } } ou { "type": "document", "document": { "link": "https://...", "filename": "proposta.pdf" } }.

Quem baixa o arquivo é a Meta, pelo link, depois que a API já respondeu. O endereço precisa ser público, abrir o arquivo direto (página do YouTube ou do Google Drive não serve) e aceitar o acesso dos servidores da Meta: CDN ou proteção contra robôs no seu site podem barrar o download. Limites da Meta: vídeo MP4 com H.264 e áudio AAC, até 16 MB; imagem JPG ou PNG, até 5 MB; documento até 100 MB.

Resposta de sucesso

{
  "success": true,
  "data": {
    "message": "Message sent successfully",
    "ticketId": 4521
  }
}

A resposta traz o ticketId do atendimento, não o código da mensagem na Meta. Confira sempre o success: há falhas que voltam com HTTP 200 e "success": false.

Erros mais comuns

RespostaO que conferir
400 ERR_SEND_UPSTREAMA Meta recusou o envio. O motivo vem no error, com o código da Meta entre parênteses: (#132012) é parâmetro em formato diferente do modelo (por exemplo, cabeçalho de vídeo sem o vídeo); (#131008) é variável vazia ou faltando. Versões anteriores respondiam 500 ERR_SEND_TEMPLATE, sem o motivo: trate os dois. Confira, nesta ordem: (1) a chave está ligada à conexão da API Oficial; (2) templateData tem messaging_product, type e template (só name e language soltos não funcionam); (3) name e language.code iguais aos do modelo aprovado; (4) a quantidade de variáveis bate com o modelo; (5) o modelo está aprovado, e não pausado ou em análise. Se tudo confere, fale com o suporte informando o número e a hora do envio.
200 e a mensagem não chegaCom mídia no cabeçalho, a Meta baixa o arquivo depois de aceitar o envio. Se o download falha (erro 131053 da Meta), ela descarta a mensagem e a resposta da API não muda. Abra o link numa janela anônima, confira o formato e o tamanho, e desligue a CDN ou a proteção contra robôs para o endereço do arquivo. Se tudo confere, fale com o suporte informando o número e a hora do envio.
Erros de formato (as três rotas de modelo): sem number, 400 ERR_MISSING_NUMBER_OR_BSUID; templateData que não é objeto, 400 ERR_INVALID_TEMPLATE_DATA (texto com JSON também é aceito); components que não é lista, 400 ERR_INVALID_TEMPLATE_COMPONENTS; objeto ou lista no text de um parâmetro, 400 ERR_INVALID_TEMPLATE_PARAMETER (número vira texto).

🎫 Tickets — Gestão Completa

Criação de tickets, consulta por número ou ID, atualização de fila/status/integrações, tags por contato, pausa e compartilhamento.

POST /v2/api/external/{apiId}/createTicket

Cria um ticket com mensagem inicial. userId (o atendente do ticket) é obrigatório: sem ele, 400 ERR_MISSING_FIELDS com "fields": ["userId"]; usuário de outra conta, 404 ERR_USER_NOT_FOUND. Para canais de e-mail (webmail), use email em vez de number e informe o channelId do canal webmail. Campos opcionais: name/firstName/lastName (criação do contato), kanbanId, chatFlowId, queueId, reasonId, value.

Body (exemplo)

{
  "body": "A mensagem desejada",
  "number": "5511999999999",
  "externalKey": "SUA_CHAVE_EXTERNA",
  "userId": 3,
  "status": "pending"
}
Testado por nós em 29/10/2026. A chamada sem userId falha antes de enviar qualquer mensagem.
POST /v2/api/external/{apiId}/showticket

Consultas de tickets e mensagens.

Rotas

MétodoRotaDescrição
POSTshowticketTicket ativo do contato — {number}.
POSTshowallticketTodos os tickets do contato — {number}.
POSTshowticketchatbotTicket em atendimento por chatbot — {number}.
POSTshowTicketByIdTicket por ID — {ticketId}.
POSTshowAllMessagesTodas as mensagens de um ticket — {ticket} (também aceita ticketId). Sem ele: 400 ERR_MISSING_TICKET.
O showAllMessages traz também mensagens de sistema, que nunca foram enviadas ao contato: notas internas (mediaType notes ou callNotes) e marcas de transferência (mediaType transfer; nas novas, dataJson traz origem, destino e autor). Se você exporta a conversa ou manda para uma IA resumir, filtre por mediaType para não misturar nota interna com o que o cliente leu.
POST /v2/api/external/{apiId}/updateticketinfo

Atualizações do ticket.

Rotas

MétodoRotaDescrição
POSTupdateticketinfoStatus, fila e integrações — {ticketId, userId, status, queueId, typebotStatus, chatgptStatus, dialogflowStatus, difyStatus, n8nStatus, chatFlowId}.
POSTupdatequeueAltera a fila — {ticketId, queueId}. Só esses dois campos são lidos; null tira a fila. Fila de outra conta: 404 ERR_QUEUE_NOT_FOUND.
POSTupdatetagSubstitui todas as tags do contato pela tag informada: {ticketId, tag}. Para acrescentar sem apagar as outras, use addTag.
updatetag apaga as outras tags. Num teste, um contato com 5 tags ficou só com a tag enviada, e o efeito vale para todos os tickets desse contato. Esta página dizia antes "define a tag principal", o que não descrevia isso.
Validação (updateticketinfo)Resposta
ticketId que não é número400 ERR_INVALID_TICKET_ID
status fora de open, pending, closed400 ERR_INVALID_STATUS
Campos *Status (typebotStatus, chatgptStatus...) com valor que não seja true/false, 1/0 ou enabled/disabled400 ERR_INVALID_FIELD com field
queueId, userId ou chatFlowId de outra conta404 ERR_QUEUE_NOT_FOUND, ERR_USER_NOT_FOUND ou ERR_CHATFLOW_NOT_FOUND
Trocar fila, atendente, fluxo ou canal registra a transferência no histórico do atendimento como mensagem de sistema (mediaType transfer), que não é enviada ao contato.
Testado por nós em 29/10/2026. status e typebotStatus inválidos no updateticketinfo, ticketId que não é número e a troca de todas as tags no updatetag, fila inexistente no updatequeue (as tags do contato de teste foram restauradas depois).
POST /v2/api/external/{apiId}/addTagContact

Tags por contato (sem precisar de ticket). Localize por contactId ou number; aceita tagId ou tagIds. O addTagContact mantém as tags existentes.

Rotas

MétodoRotaDescrição
POSTaddTagContactAdiciona tag(s) ao contato.
POSTremoveTagContactRemove tag(s) do contato.

Body (exemplo)

{
  "contactId": 123,
  "tagId": 1
}
POST /v2/api/external/{apiId}/ticket/pause/start/{ticketId}

Pausa de atendimento com motivo e histórico.

Rotas

MétodoRotaDescrição
POSTticket/pause/start/{ticketId}Inicia pausa — body: {pauseReason}.
POSTticket/pause/end/{ticketId}Encerra a pausa.
GETticket/pause/logs/{ticketId}Histórico de pausas do ticket.
Iniciar pausa num ticket já pausado: 409 ERR_TICKET_ALREADY_PAUSED. Encerrar sem pausa ativa: 409 ERR_TICKET_NOT_PAUSED. Ticket inexistente: 404 ERR_NO_TICKET_FOUND.
POST /v2/api/external/{apiId}/ticket/share

Compartilhamento e avaliações.

Rotas

MétodoRotaDescrição
POSTticket/shareCompartilha o ticket com outros usuários: {ticketId, userIdArray: [2, 5]}. Cada ticket aceita um compartilhamento só: repetir responde 400.
GETticket/share/{ticketId}Consulta o compartilhamento.
GETlistTicketEvaluations?page=1&limit=20Lista avaliações de atendimento. Filtros: startDate, endDate (AAAA-MM-DD), evaluation e search.
POSTsendEvaluationEnvia a pesquisa de avaliação no ticket. Recebe ticketId; aceita também body, externalKey e force.

🏗️ CRM — Pipelines, Oportunidades e Cadastros

Estruture o funil de vendas via API: pipelines e estágios, oportunidades vinculadas a contatos, e os cadastros de apoio (kanban, tags, motivos de encerramento e filas).

POST /v2/api/external/{apiId}/pipeline/create

CRUD de pipelines (funis) e estágios.

Rotas

MétodoRotaDescrição
POSTpipeline/createCria — {name}. O pipeline só tem nome: description é ignorado.
GETpipeline/list?page=1&limit=20Lista.
GETpipeline/show/{id}Detalhe.
POSTpipeline/update/{id}Atualiza — {name}.
POSTpipeline/delete/{id}Exclui. Só funciona com o pipeline vazio (sem etapas nem oportunidades); senão responde 500.
POSTstage/createCria estágio — {name, pipelineId, order, color}. Opcionais: notifyUserId e notifyQueueId (quem é avisado quando uma oportunidade entra na etapa).
GETstage/list?pipelineId=...Lista estágios do pipeline.
GETstage/show/{id}Detalhe do estágio.
POSTstage/update/{id}Atualiza estágio.
POSTstage/delete/{id}Exclui estágio. Só funciona sem oportunidades nele (mova-as antes); senão responde 500.
Testado por nós em 29/10/2026. Criar pipeline com description: o campo foi ignorado e o pipeline nasceu só com o nome.
POST /v2/api/external/{apiId}/createOpportunity

Oportunidades de venda. status: open, win ou lose. Se o contato não existir, é criado a partir de number/contactName/email. Veja também Listar Oportunidades.

Rotas

MétodoRotaDescrição
POSTcreateOpportunityCria oportunidade.
POSTupdateOpportunityAtualiza — {opportunityId, ...}.
POSTdeleteOpportunityExclui — {opportunityId}.

Body (exemplo)

{
  "number": "5511999990001",
  "contactName": "Nome do Contato",
  "email": "contato@email.com",
  "name": "Proposta Empresa X",
  "value": 10000.00,
  "status": "open",
  "pipelineId": 16,
  "stageId": 7,
  "responsibleId": 1,
  "closingForecast": "2026-12-31",
  "description": "Descrição da oportunidade"
}
Obrigatórios: number, name (até 255 caracteres), pipelineId, stageId (uma etapa desse pipeline) e responsibleId. closingForecast aceita AAAA-MM-DD, DD/MM/AAAA ou data e hora com fuso. Erros: 400 ERR_MISSING_FIELDS (com fields), ERR_INVALID_OPPORTUNITY_REFERENCE (pipeline, etapa ou responsável de outra conta, com field) e ERR_STAGE_NOT_IN_PIPELINE. No updateOpportunity só opportunityId é obrigatório.
POST /v2/api/external/{apiId}/createTag

Cadastros de apoio: tags, colunas de kanban, motivos de encerramento e filas.

Rotas

MétodoRotaDescrição
POSTcreateTagCria tag — {tag, color, isActive} (tag e color obrigatórios). Nome repetido: 409 ERR_TAG_DUPLICATED.
POSTupdateTagData/{id}Atualiza tag — {tag, color}.
POSTdeleteTag/{id}Exclui tag.
POSTcreateKanbanCria coluna kanban — {name, position}.
GETlistKanbanLista colunas.
POSTupdateKanban/{id}Atualiza coluna.
POSTdeleteKanban/{id}Exclui coluna.
POSTcreateReasonCria motivo — {name}.
GETlistReasonsLista motivos.
POSTupdateReason/{id}Atualiza motivo.
POSTdeleteReason/{id}Exclui motivo.
POSTcreateQueueDataCria fila — {queue, isActive}. Opcionais: businessHours e messageBusinessHours (mensagem fora do horário). A fila nasce sem cor.
POSTupdateQueueData/{id}Atualiza fila. Atenção: zera a cor da fila e refaz o vínculo da fila com o usuário dono do token. Para manter a cor, edite pelo painel.
POSTdeleteQueueData/{id}Exclui fila.
As rotas de kanban e de fila não definem cor: a cor só é ajustada pelo painel. O nome da tag vai no campo tag (não name), e o da fila no campo queue.

📅 Agendamentos e Lembretes

Agende compromissos vinculados a contatos e configure lembretes automáticos por WhatsApp antes de cada evento.

POST /v2/api/external/{apiId}/appointment/create

Agendamentos ligados a contatos. Obrigatórios: title (até 255 caracteres) e startAt. status: pending (padrão), confirmed, cancelled, completed ou no_show.

Rotas

MétodoRotaDescrição
POSTappointment/createCria.
GETappointment/list?page=1&limit=20&status=pendingLista em ordem de início. Filtros: status, search, startFrom e startTo (mesmos formatos de data do create).
GETappointment/show/{id}Detalhe.
POSTappointment/update/{id}Atualiza só os campos enviados. Aceita também checkInStatus, location e meetingLink.
POSTappointment/delete/{id}Exclui.

Body (exemplo)

{
  "title": "Reunião de Vendas",
  "description": "Reunião sobre proposta comercial",
  "contactId": 123,
  "contactName": "João Silva",
  "contactPhone": "5511999999999",
  "whatsappId": 12,
  "userId": 3,
  "startAt": "15/06/2026 14:00",
  "endAt": "2026-06-15T15:00:00-03:00",
  "status": "pending",
  "notes": "Levar proposta impressa"
}
CampoTipoDescrição
startAtstringobrigatórioInício. Aceita 2026-06-15, 2026-06-15 14:00, 2026-06-15T14:00:00-03:00 ou 15/06/2026 14:00. Com barra é sempre dia/mês/ano. Sem fuso, vale o horário de Brasília. Só a data = 00:00. Número (timestamp) ou outro formato: 400 ERR_INVALID_START_AT.
endAtstringopcionalFim, nos mesmos formatos. Antes do início: 400 ERR_END_AT_BEFORE_START_AT. No update, null apaga o fim.
userIdintopcionalAtendente responsável. Sem ele, o agendamento nasce sem atendente.
bookingProfessionalIdintopcionalProfissional da página de agendamento.
contactIdintopcionalContato. contactId, whatsappId, userId e bookingProfessionalId precisam ser da sua conta (senão 404).
checkInStatusstringopcionalSó no update. none, confirmed, on_the_way ou no_show. Outro valor: 400 ERR_INVALID_FIELD com field.
Testado por nós em 29/10/2026. Criar com 15/11/2026 14:00 gravou 17:00 UTC (horário de Brasília). checkInStatus enviado no create foi ignorado (nasceu none); no update funcionou. Fim antes do início, timestamp numérico, check-in inválido e filtro de data inválido responderam com os códigos da tabela.
POST /v2/api/external/{apiId}/scheduleReminder/create

Regras de lembrete automático: a mensagem é enviada N horas antes do agendamento, pela conexão indicada.

Rotas

MétodoRotaDescrição
POSTscheduleReminder/createCria regra.
GETscheduleReminder/listLista regras.
POSTscheduleReminder/update/{id}Atualiza.
POSTscheduleReminder/toggle/{id}Ativa/desativa.
POSTscheduleReminder/delete/{id}Exclui.

Body (exemplo)

{
  "name": "Lembrete 24h antes",
  "description": "Envia mensagem ao contato 24h antes do agendamento",
  "hoursBeforeEvent": 24,
  "messageType": "message",
  "messageContent": "Olá {nome}, lembrete da sua reunião amanhã!",
  "whatsappId": 12,
  "active": true
}
O lembrete não tem data própria: dispara hoursBeforeEvent horas antes de cada agendamento. Obrigatórios: hoursBeforeEvent (número inteiro) e messageType (message ou waba_template); faltando, 400 ERR_MISSING_FIELDS. whatsappId de outra conta: 404 ERR_NO_WAPP_FOUND. No update, campo com outro nome é descartado sem erro.

📊 Dashboard e Relatórios

Indicadores de atendimento para alimentar BI e relatórios externos. Todos os endpoints são GET e aceitam startDate e endDate (formato AAAA-MM-DD).

GET /v2/api/external/{apiId}/dash/<indicador>?startDate=...&endDate=...

Indicadores disponíveis:

Rotas

MétodoRotaDescrição
GETdash/ticketsAndTimesTotais de tickets e tempos médios (espera/atendimento).
GETdash/ticketsStatusDistribuição por status.
GETdash/ticketsChannelsDistribuição por canal.
GETdash/ticketsQueueDistribuição por fila.
GETdash/ticketsPerUserVolume por atendente.
GETdash/ticketsUserDetalhe por atendente.
GETdash/ticketsReasonsDistribuição por motivo de encerramento.
GETdash/ticketsEvolutionEvolução diária de tickets.
GETdash/ticketsEvolutionByValueEvolução por valor.
GETdash/ticketsEvolutionChannelsNameEvolução diária por canal.
Os indicadores contam só conversas individuais. Para ver só os grupos, acrescente &isGroup=true (testado em 29/10/2026: os números mudam por completo).

🔌 Canais e Sessões

Gerencie as conexões WhatsApp do tenant via API: criar, iniciar, obter QR Code e consultar canais. Complementa Listar Canais e Listar Sessões.

POST /v2/api/external/{apiId}/<ação>

Ciclo de vida da sessão (conexão).

Rotas

MétodoRotaDescrição
POSTcreatetSessionCria sessão — {name, status, type}. Atenção: a rota é createtSession mesmo (grafia da plataforma).
POSTstartSessionInicia — {whatsappId}.
POSTqrCodeSessionObtém o QR Code atual — {whatsappId}.
POSTrequestNewQrCodeSessionForça novo QR Code — {whatsappId}.
POSTdeleteSessionExclui a sessão — {whatsappId}.
POSTshowChannelConsulta canal pelo número — {number}.
POSTshowChannelByIdConsulta canal por ID — {id}.
GETgetAllSessionApisLista as configurações de API do tenant.

🗂️ Utilitários

Galeria de arquivos, tarefas (to-do), logs de chamadas, consulta de contato por número e listagens auxiliares.

POST /v2/api/external/{apiId}/showcontact

Consulta um contato pelo número de WhatsApp (retorna dados completos, incluindo tags e carteiras).

Body (exemplo)

{
  "number": "5511999999999"
}
POST /v2/api/external/{apiId}/gallery/upload

Galeria de arquivos do tenant. O upload é multipart/form-data (campo de arquivo + description).

Rotas

MétodoRotaDescrição
POSTgallery/uploadEnvia até 10 arquivos (multipart, campo files, limite de 500 MB). Opcionais: description (vale para todos) e folderId (pasta).
GETgallery/list?pageNumber=1&fileType=imageLista arquivos, 20 por página. Filtros: fileType, searchParam e folderId (none = só os sem pasta; o id = só os da pasta).
GETgallery/foldersLista as pastas em ordem alfabética, com a quantidade de arquivos de cada uma.
POSTgallery/delete/{id}Exclui arquivo.

Resposta do gallery/folders

{
  "success": true,
  "data": {
    "available": true,
    "folders": [{ "id": 1, "name": "Produtos", "count": 12 }],
    "unfiledCount": 30,
    "totalCount": 42
  }
}
unfiledCount = arquivos sem pasta; totalCount = todos. Pasta inexistente ou de outra conta: 400 ERR_GALLERY_FOLDER_NOT_FOUND. Cota da galeria estourada no upload: 413 ERR_GALLERY_QUOTA_EXCEEDED, e nenhum arquivo é salvo. As pastas são criadas pelo painel.
Testado por nós em 29/10/2026. gallery/folders, a lista com folderId=none (a resposta traz folderFilter) e a pasta inexistente.
POST /v2/api/external/{apiId}/todo/create

Tarefas (to-do) por usuário. priority: low/medium/high; status: pending/finished.

Rotas

MétodoRotaDescrição
POSTtodo/createCria — {name, description, owner, ownerId, status, priority, limitDate, reminderMinutes, contactId, ticketId}.
GETtodo/listLista.
POSTtodo/update/{id}Atualiza só os campos enviados, ex: {status: finished}. Prazo novo ainda não vencido volta delayed para pending.
POSTtodo/delete/{id}Exclui.
GETtodo/logs/{userId}Histórico de alterações por usuário.
CampoTipoDescrição
limitDatestringopcionalPrazo. 2026-06-15 = tarefa do dia (atrasa quando o dia acaba). 2026-06-15 14:30 = tarefa com horário (sem fuso, horário de Brasília). Não use barra: 02/03/2027 é lido como mês/dia e vira 3 de fevereiro. Data ilegível: 400 ERR_INVALID_FIELD.
reminderMinutesintopcionalLembrete ao responsável: 0, 10, 30 ou 60 minutos antes. Só vale para tarefa com horário e com ownerId. Outro valor: 400 ERR_INVALID_FIELD.
ownerIdintopcionalResponsável (id de usuário, veja listUsers): recebe o lembrete. Id que não é da sua conta cria a tarefa sem responsável e a resposta traz "ownerIgnored": true. Confira esse campo.
contactIdintopcionalLiga a tarefa a um contato. Com ticketId, o ticket precisa ser do mesmo contato.
ticketIdintopcionalLiga a tarefa a um atendimento (liga também o contato dele).
Testado por nós em 29/10/2026. Prazo com e sem hora (a resposta traz limitHasTime e reminderAt), lembrete inválido, ownerIgnored com usuário inexistente e a data com barra lida como mês/dia.
POST /v2/api/external/{apiId}/formSubmit

Envia as respostas de um formulário da sua conta, como se a pessoa tivesse preenchido a página do formulário: acha ou cria o contato, aplica as etiquetas, abre o atendimento (ou entra no que já está aberto) e manda a confirmação, se o formulário tiver. Útil para levar ao Pro+ um formulário do seu site ou de outro sistema. Não passa pelas travas contra robôs da página pública.

CampoTipoDescrição
formIdintopcionalId do formulário (aba Compartilhar do formulário). Envie formId ou slug; com os dois, vale o formId.
slugstringopcionalO endereço do formulário: o que vem depois de /f/ no link.
answersobjectobrigatórioRespostas pela chave de cada campo (mostrada na aba Compartilhar). Telefone com + e o código do país; lista = id da opção; múltipla escolha = lista de ids; caixa de confirmação = true; data em AAAA-MM-DD.
refstringopcionalPágina de onde veio o envio (guardada sem a query).

Exemplo

curl --request POST \
  --url 'https://SUA_URL_BASE/v2/api/external/SEU_API_ID/formSubmit' \
  --header 'Authorization: Bearer SEU_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "formId": 12,
    "answers": {
      "f_nome": "Maria Souza",
      "f_telefone": "+5511999998888",
      "f_email": "maria@exemplo.com"
    },
    "ref": "https://www.seusite.com.br/contato"
  }'

Resposta

{ "ok": true, "submissionId": 87, "status": "processed", "ticketId": 1262, "contactId": 123, "errorCode": null }
status: processed = atendimento aberto ou atualizado; failed = o envio ficou em Respostas sem atendimento (errorCode diz o motivo) e pode ser refeito pelo painel; skipped = contato bloqueado.
ErroQuando acontece
400 ERR_FORM_ID_OR_SLUG_REQUIREDFaltou formId e slug.
404 ERR_FORM_NOT_FOUNDFormulário não existe, está desativado, é de outra conta ou não tem canal ativo.
400 ERR_FORM_INVALIDanswers fora do formato, ou campo inválido: details.fields traz um código por campo (required, invalidPhone, invalidEmail, invalidOption...).
400 ERR_FORM_CONTACT_REQUIREDFaltou o telefone ou o e-mail que o formulário usa para achar o contato.
402 ERR_FEATURE_NOT_IN_PLANO plano da conta não tem Formulários.
413 ERR_FORM_TOO_LARGERespostas acima de 32 KB.
Testado por nós em 29/10/2026. Os erros ERR_FORM_ID_OR_SLUG_REQUIRED e ERR_FORM_NOT_FOUND. O envio completo depende de um formulário criado na sua conta: faça o primeiro teste com o seu próprio telefone.
GET /v2/api/external/{apiId}/callLog/list

Logs de chamadas recebidas/realizadas.

Rotas

MétodoRotaDescrição
GETcallLog/list?page=1&limit=20Lista chamadas. Ordena com orderBy e orderDirection; não filtra por período.
GETcallLog/show/{id}Detalhe da chamada.
GETwavoip/calls?page=1&limit=20Lista chamadas de voz IP. Filtros: startDate, endDate, direction, callStatus, phone, contactId, ticketId, userId e queueId. Use esta rota para recortar por data.
GETwavoip/calls/{id}Detalhe da chamada de voz IP.
GET /v2/api/external/{apiId}/listChatFlows

Listagens auxiliares para montar integrações.

Rotas

MétodoRotaDescrição
GETlistChatFlowsFluxos de chatbot do tenant.
GETlistAutoRepliesRespostas automáticas.
GETlistFastRepliesRespostas rápidas.

🪝 Webhooks

Webhooks são notificações que o NimoChat Pro+ envia automaticamente para uma URL do seu sistema sempre que algo acontece na plataforma — uma nova mensagem chega, um ticket é criado, um atendimento é finalizado.

Em vez de seu sistema ficar perguntando à API "tem mensagem nova?" a cada minuto (polling), o NimoChat avisa em tempo real quando algo acontece. Isso é mais eficiente, mais rápido e consome menos recursos.

Quando usar webhooks: notificações em tempo real, automações que dependem de eventos (chegou mensagem → cria ticket no Zendesk), sincronização contínua entre sistemas, alertas para o time de plantão.

Configurando o Webhook

A configuração é feita no painel administrativo do NimoChat Pro+ em poucos cliques:

  1. Faça login como Administrador.
  2. Acesse Integrações > Webhook no menu.
  3. Ative a opção "Ativar webhook".
  4. Informe a URL do seu sistema que vai receber as notificações (precisa ser HTTPS pública).
  5. Opcionalmente, ative "Receber eventos de mensagem" para receber notificações também das mensagens individuais (não apenas dos tickets).
  6. Salve as alterações.
Sua URL precisa estar acessível pela internet e responder com HTTP 200 em até 15 segundos. URLs locais (localhost, 192.168.x.x) não funcionam. Use um servidor público, ngrok para testes ou um serviço como n8n/Make/Pipedream.

Estrutura do payload

Toda notificação é enviada como POST para sua URL com Content-Type: application/json. O corpo da requisição segue esta estrutura:

{
  "method": "...",           // tipo do evento (opcional)
  "tenantId": 123,            // ID da sua conta
  "ticketId": 98765,          // ID do ticket relacionado

  "msg": {                       // dados da mensagem
    "body": "texto da mensagem",
    "fromMe": false,           // true = atendente enviou, false = contato enviou
    "from": "5511999999999@s.whatsapp.net"
  },

  "ticket": {                    // dados do ticket
    "id": 98765,
    "tenantId": 123,
    "userId": null,           // null = não atribuído
    "queueId": 5,             // ID da fila
    "createdAt": "2026-04-06T13:00:00.000Z",
    "contact": {
      "id": 456,
      "name": "João Silva",
      "number": "5511999999999",
      "wallets": [{ "id": 7 }]
    }
  }
}

Campos principais

CampoTipoDescrição
methodstringTipo do evento. Quando ausente ou vazio, indica evento de entrada (mensagem recebida ou ticket criado).
tenantIdintID da sua conta. Sempre valide para garantir que o webhook é da sua conta.
ticketIdintID do ticket relacionado ao evento.
msg.bodystringTexto da mensagem (em mensagens de texto).
msg.fromMebooleantrue se a mensagem foi enviada pelo atendente, false se foi recebida do contato.
ticket.userIdint|nullnull indica ticket não atribuído (oportunidade de distribuição automática).
ticket.contact.numberstringTelefone do contato (sem prefixos do WhatsApp).

Tipos de eventos

O campo method identifica o tipo do evento. Os principais valores são:

EventoQuando dispara
(vazio) — Mensagem recebidaO contato enviou uma mensagem. msg.fromMe = false e method ausente. Frequentemente acompanhado de criação de ticket novo.
(vazio) — Novo ticketUm novo ticket foi criado a partir de uma conversa de entrada. ticket.userId = null.
message_sendO atendente enviou uma mensagem pelo painel. msg.fromMe = true.
message_send_uazapiMensagem enviada por uma sessão WhatsApp via QR Code (não-oficial).
message_send_waba / message_sent_wabaMensagem enviada pela API oficial do WhatsApp Business (Cloud API).
ackConfirmação de leitura/entrega do WhatsApp (entregue, lido, ouvido).
presenceAtualização de presença do contato (online, offline, digitando).
Foco no que importa: a maioria das integrações filtra apenas eventos de entrada (sem method e com fromMe = false) e ignora os eventos ack, presence e os de envio. Isso evita ruído e processa só o que é acionável.

Exemplos de payloads reais

Mensagem de texto recebida

{
  "msg": {
    "fromMe": false,
    "body": "Olá, gostaria de saber sobre o produto X",
    "from": "5511999999999@s.whatsapp.net"
  },
  "ticket": {
    "id": 12345,
    "tenantId": 2,
    "userId": null,
    "queueId": 3,
    "createdAt": "2026-04-06T13:00:00.000Z",
    "contact": {
      "id": 456,
      "name": "João Silva",
      "number": "5511999999999",
      "wallets": []
    }
  }
}

Mídia recebida (áudio, imagem, documento)

{
  "msg": {
    "fromMe": false,
    "content": [
      { "type": "audio", "url": "https://media.../audio.ogg" }
    ]
  },
  "ticket": {
    "id": 12346,
    "tenantId": 2,
    "contact": { "id": 456, "number": "5511999999999" }
  }
}
Atenção com mídias: em mensagens de mídia o campo msg.body pode estar vazio e o conteúdo vir em msg.content como array. Sempre faça type checking no seu código antes de processar.

Mensagem enviada pelo atendente (filtrar)

{
  "method": "message_send_uazapi",
  "msg": { "fromMe": true, "body": "Olá! Em que posso ajudar?" },
  "ticket": { "id": 12345, "tenantId": 2 }
}

Confirmação de leitura (filtrar)

{
  "method": "ack",
  "ticketId": 12345
}

Exemplo de receiver (Node.js)

Receiver simples em Node.js + Express que processa apenas mensagens recebidas:

const express = require('express');
const app = express();
app.use(express.json());

// Métodos a ignorar (saída + ruído)
const IGNORE = ['message_send', 'message_send_uazapi',
                'message_send_waba', 'message_sent_waba',
                'ack', 'presence'];

app.post('/webhook/nimochat', (req, res) => {
  const payload = req.body;

  // 1. Filtrar eventos que não interessam
  if (IGNORE.includes(payload.method)) return res.sendStatus(200);

  // 2. Validar tenant (segurança)
  if (payload.ticket?.tenantId !== SEU_TENANT_ID) return res.sendStatus(403);

  // 3. Processar apenas mensagens recebidas
  if (payload.msg?.fromMe === false) {
    const contact = payload.ticket?.contact;
    const body = typeof payload.msg.body === 'string' ? payload.msg.body : '';

    console.log(`Nova mensagem de ${contact?.name}: ${body}`);

    // Aqui você integra com seu CRM, dispara alerta, etc.
  }

  res.sendStatus(200);
});

app.listen(3000);

Segurança e boas práticas

Dica de produção: use plataformas como n8n, Make ou Pipedream para receber webhooks sem precisar manter um servidor. Elas oferecem URLs prontas, retry automático e logs visuais — ideal para começar.

💡 Casos de uso reais

1. Disparo em massa segmentado por tag

Você quer enviar uma promoção para todos os contatos com a tag "Lead Quente". Em vez de exportar a lista manualmente, busque os contatos via API e dispare via n8n / Make.

Passo 1 — Buscar contatos com a tag

curl --request POST \
  --url 'https://SUA_URL_BASE/v2/api/external/SEU_API_ID/contacts/search' \
  --header 'Authorization: Bearer SEU_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{ "tagId": 42, "limit": 500 }'

Passo 2 — Para cada contato, enviar a mensagem

curl --request POST \
  --url 'https://SUA_URL_BASE/v2/api/external/SEU_API_ID' \
  --header 'Authorization: Bearer SEU_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "number": "5511999999999",
    "body": "Oferta especial só para você! 30% OFF até hoje."
  }'
Cuidado com o WhatsApp: respeite os limites do WhatsApp (não dispare 1000+ mensagens em segundos) e adicione delays entre as mensagens (ex: 5-10 segundos). Use também a tag de opt-in para evitar bloqueios.

2. Distribuição automática de leads para vendedores

Quando um novo lead chega, atribua automaticamente para um vendedor disponível usando a função de carteirização (wallet).

Atribuir contato à carteira do vendedor

curl --request POST \
  --url 'https://SUA_URL_BASE/v2/api/external/SEU_API_ID/updateContactWallet' \
  --header 'Authorization: Bearer SEU_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "contactId": 4567,
    "walletId": 12
  }'

Combine com o webhook de novo ticket: quando o NimoChat avisar que um novo ticket foi criado, sua aplicação consulta qual vendedor está disponível (lógica round-robin, menor carga, especialidade) e atribui via API.

3. Sincronizar contatos do CRM com o NimoChat

Toda vez que um novo contato é cadastrado no seu CRM, replique-o automaticamente para o NimoChat:

curl --request POST \
  --url 'https://SUA_URL_BASE/v2/api/external/SEU_API_ID/createContact' \
  --header 'Authorization: Bearer SEU_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "name": "Maria Oliveira",
    "number": "5511988887777",
    "email": "maria@empresa.com",
    "extraInfo": [
      { "name": "empresa", "value": "ACME Ltda" },
      { "name": "cargo", "value": "Diretora Comercial" }
    ]
  }'

4. Cadastrar atendentes via integração com RH

Quando um novo colaborador é admitido no seu sistema de RH, crie automaticamente o usuário (atendente) no NimoChat:

curl --request POST \
  --url 'https://SUA_URL_BASE/v2/api/external/SEU_API_ID/createUser' \
  --header 'Authorization: Bearer SEU_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "name": "Carlos Santos",
    "email": "carlos.santos@empresa.com",
    "password": "SenhaTemp123!",
    "profile": "user"
  }'

5. Humanizar bots — mostrar "digitando..." antes da resposta

Antes do seu bot enviar uma resposta automática, envie um sinal de presence "typing" para humanizar a interação:

# 1. Envia "digitando..." (typing)
curl --request POST \
  --url 'https://SUA_URL_BASE/v2/api/external/SEU_API_ID/sendPresence' \
  --header 'Authorization: Bearer SEU_TOKEN' \
  --data '{ "ticketId": 12345, "state": "typing" }'

# 2. Aguarda 2-4 segundos
sleep 3

# 3. Envia a mensagem real
curl --request POST \
  --url 'https://SUA_URL_BASE/v2/api/external/SEU_API_ID' \
  --header 'Authorization: Bearer SEU_TOKEN' \
  --data '{ "number": "5511999999999", "body": "Resposta do bot" }'

6. Classificar tickets durante o atendimento

Use addTag para classificar o ticket sem perder as tags anteriores. Por exemplo, ao detectar uma palavra-chave no texto da mensagem:

curl --request POST \
  --url 'https://SUA_URL_BASE/v2/api/external/SEU_API_ID/addTag' \
  --header 'Authorization: Bearer SEU_TOKEN' \
  --data '{ "ticketId": 12345, "tagId": [42, 87] }'

📚 Referência completa de endpoints

A coleção do Postman é gerada a partir desta página: tem todas as rotas daqui, cada uma com corpo de exemplo, os campos, os avisos e o link para o cartão correspondente. O Postman monta o código em várias linguagens (cURL, Node.js, Python, PHP, Go, Java).

Abra a coleção, clique em qualquer endpoint, e copie o snippet na linguagem desejada usando o painel "Code" (lateral direita).

Acessar coleção completa no Postman

Endpoints por categoria

Resumo de tudo que está disponível na coleção:

📅 Agendamentos e Lembretes

Crie e gerencie agendamentos e lembretes automáticos.

POSTAppointmentCreateCriar agendamento
POSTAppointmentUpdateAtualizar agendamento
POSTAppointmentDeleteDeletar agendamento
GETAppointmentListListar agendamentos
GETAppointmentShowDetalhes de um agendamento
POSTScheduleReminderCreateCriar lembrete
POSTScheduleReminderUpdateAtualizar lembrete
POSTScheduleReminderDeleteDeletar lembrete
POSTScheduleReminderToggleAtivar/desativar lembrete
GETScheduleReminderListListar lembretes

📣 Campanhas

Crie, gerencie e dispare campanhas de marketing em massa.

POSTCampaignCreateCriar campanha
POSTCampaignUpdateEditar campanha
POSTCampaignDeleteDeletar campanha
POSTCampaignDuplicateDuplicar campanha
POSTCampaignStartIniciar disparo
POSTCampaignPausePausar campanha
POSTCampaignResumeRetomar campanha pausada
POSTCampaignCancelCancelar campanha
POSTCampaignSkipPular envio atual
GETCampaignListListar campanhas
GETCampaignReportRelatório de envios
POSTCampaignContactsAddAdicionar contatos
POSTCampaignContactsRemoveRemover contato
POSTCampaignContactsRemoveAllLimpar todos os contatos
GETCampaignContactsListListar contatos da campanha

🔌 Canais e Sessões

Gerencie conexões WhatsApp, escaneie QR Codes, inicie e finalize sessões.

POSTCreateSessionCriar nova sessão
POSTStartSessionIniciar sessão
POSTDeleteSessionDeletar sessão
POSTShowQrCodeExibir QR Code atual
POSTRequestNewQrCodeSolicitar novo QR Code
POSTShowChannelInformationInformações do canal
POSTShowChannelInformationByIdInformações por ID
POSTListGroupsInfoListar grupos do canal

👤 Contatos (avançado)

POSTShowContactDetalhes do contato
POSTTelegramInviteConvite do Telegram
POSTFormSubmitEnviar respostas de formulário

🏗️ CRM - Pipeline

Gerencie pipelines (funis) e estágios do CRM.

POSTPipelineCreateCriar funil
POSTPipelineUpdateAtualizar funil
POSTPipelineDeleteDeletar funil
GETPipelineListListar funis
GETPipelineShowDetalhes do funil
POSTStageCreateCriar estágio
POSTStageUpdateAtualizar estágio
POSTStageDeleteDeletar estágio
GETStageListListar estágios
GETStageShowDetalhes do estágio

📊 Dashboard

Métricas e gráficos para BI / dashboards externos.

GETDashTicketsAndTimesTickets e tempos médios
GETDashTicketsChannelsTickets por canal
GETDashTicketsEvolutionEvolução temporal
GETDashTicketsEvolutionByValueEvolução por valor
GETDashTicketsEvolutionChannelsNameEvolução por canal
GETDashTicketsPerUserTickets por atendente
GETDashTicketsQueueTickets por fila
GETDashTicketsReasonsTickets por motivo
GETDashTicketsStatusTickets por status
GETDashTicketsUserTickets de um usuário

📤 Envio em Lote (Bulk)

Disparos em massa de mensagens com controle de progresso.

POSTBulkSendMessageDisparo simples em lote
POSTBulkSendMessageWithVariableDisparo com variáveis personalizadas
POSTBulkFastMessageDisparo rápido
POSTBulkIndividualEnvio individual em lote
POSTBulkDispatchUpdateAtualizar disparo
POSTBulkDispatchIncrementProgressIncrementar progresso
GETBulkDispatchListListar disparos
GETBulkDispatchShowDetalhes do disparo

📂 Galeria de mídias

POSTGalleryUploadUpload de arquivo
POSTGalleryDeleteDeletar arquivo
GETGalleryListListar arquivos
GETGalleryFoldersListar pastas

👥 Grupos WhatsApp

Crie e gerencie grupos do WhatsApp.

POSTGroupCreateCriar novo grupo
POSTGroupChangeTitleAlterar título
POSTGroupChangeDescriptionAlterar descrição
POSTGroupChangePictureAlterar foto
POSTGroupAddParticipantAdicionar participante
POSTGroupRemoveParticipantRemover participante
POSTGroupPromotePromover a admin
POSTGroupDemoteRebaixar admin
POSTGroupSetAdminsOnlyRestringir só admins
POSTGroupGetInviteLinkObter link de convite
POSTGroupListParticipantsListar participantes
POSTGroupShowByIdDetalhes do grupo
POSTGroupListListar grupos

🏷️ Kanban / Tags / Motivos / Filas

CRUD completo de objetos auxiliares.

POSTCreateKanbanCriar coluna kanban
POSTUpdateKanbanAtualizar kanban
POSTDeleteKanbanDeletar kanban
GETListKanbanListar kanbans
POSTCreateTagCriar tag
POSTUpdateTagDataAtualizar tag
POSTDeleteTagDeletar tag
POSTCreateReasonCriar motivo
POSTUpdateReasonAtualizar motivo
POSTDeleteReasonDeletar motivo
GETListReasonsListar motivos
POSTCreateQueueDataCriar fila
POSTUpdateQueueDataAtualizar fila
POSTDeleteQueueDataDeletar fila

🔍 Listagens utilitárias

GETListAutoRepliesAuto-respostas
GETListChatFlowsFluxos de chatbot
GETListFastRepliesRespostas rápidas
GETGetAllSessionApisTodas as APIs de sessão

📞 Logs de Chamadas

GETCallLogListListar chamadas
GETCallLogShowDetalhes da chamada
GETWavoipCallListListar chamadas Wavoip
GETWavoipCallShowDetalhes Wavoip

📨 Mensagens (extras)

POSTSendMessageAPIFileEnviar arquivo (upload)
POSTSendMessageAPITextBase64Texto com mídia base64
POSTSendMessageAPIVoiceEnviar áudio (voice)
POSTSendLocationEnviar localização
POSTSendVcardEnviar contato (vCard)
GETGetMessageByMessageIdBuscar mensagem por ID
GETSearchMessagesBuscar mensagens
GETSendMessageParamsParâmetros disponíveis

👥 Mensagens em Grupo

POSTSendGroupMessageAPITextTexto para grupo
POSTSendGroupMessageAPIFileArquivo para grupo
POSTSendMessageAPIFileURLGroupArquivo via URL para grupo

💼 Oportunidades (CRM)

POSTCreateOpportunityCriar oportunidade
POSTUpdateOpportunityAtualizar oportunidade
POSTDeleteOpportunityDeletar oportunidade

📋 Templates WABA (API Oficial)

POSTSendTemplateWabaEnviar template aprovado
POSTSendTemplateWabaBodyMesmo corpo do SendTemplateWaba
POSTSendTemplateWabaMarketingMesmo corpo do SendTemplateWaba; marketing sai pela Marketing Messages API quando a conta aceitou os termos

🎫 Tickets (extras)

POSTCreateTicketCriar ticket manualmente
POSTCreateTicketFileCriar ticket com arquivo
POSTCreateTicketWebmailCriar ticket de webmail
POSTSetTicketInfoAtualizar info do ticket
POSTSetQueueMover ticket para fila
POSTSetTagDefinir tags (substitui)
POSTShowTicketByIdDetalhes por ID
POSTShowTicketInformationInformações do ticket
POSTShowAllTicketInformationTodas as informações
POSTShowAllMessagesTodas as mensagens
POSTStartTicketPausePausar ticket
POSTEndTicketPauseDespausar ticket
GETListTicketPauseLogsLogs de pausas
GETListTicketEvaluationsAvaliações
POSTTicketShareCreateCompartilhar ticket
GETTicketShareShowDetalhes do compartilhamento

✅ To-Do List

POSTTodoCreateCriar tarefa
POSTTodoUpdateAtualizar tarefa
POSTTodoDeleteDeletar tarefa
GETTodoListListar tarefas
GETTodoLogsLogs de tarefas

❓ FAQ — Perguntas frequentes

Posso testar sem código?

Sim. Acesse a coleção do Postman, clique em qualquer endpoint, preencha as variáveis (token, URL base, API ID) e clique em "Send". É a forma mais rápida de testar.

Como gerar exemplos de código em outras linguagens?

No Postman, abra o endpoint, clique no ícone { } (Code Snippets) na lateral direita e escolha a linguagem: cURL, Node.js, Python, PHP, Java, Go, C#, Ruby, etc.

Existe limite de requisições (rate limit)?

Sim. Use a API com bom senso — picos muito altos podem ser bloqueados. Para disparos em massa, use os endpoints específicos de Bulk com delay entre mensagens (recomendado: 5-10 segundos).

Como funciona a autenticação Bearer?

Você gera um token no painel admin (menu API) e envia ele em todas as requisições no header Authorization: Bearer SEU_TOKEN. O token é específico da sua conta e isola seus dados.

O que é o apiId no URL?

É o identificador único da sua chave de API. É gerado junto com o token quando você cria uma nova API no painel. Cada chave pode estar associada a uma conexão WhatsApp específica.

Por que minhas mensagens não chegam ao destino?

Possíveis causas: (1) número formatado errado — sempre use DDI+DDD+número (ex: 5511999999999), (2) conexão WhatsApp desconectada — verifique no painel, (3) número do destinatário não tem WhatsApp, (4) o WhatsApp pode ter bloqueado por excesso de mensagens.

O webhook não está chegando, o que fazer?

Confira: (1) URL configurada está correta e é HTTPS pública, (2) seu servidor responde com HTTP 200 em até 15 segundos, (3) a opção "Receber eventos de mensagem" está ativada se você quer receber mensagens individuais, (4) seu firewall não está bloqueando os IPs do NimoChat.

Onde reportar bugs ou sugerir novos endpoints?

Entre em contato pelo nosso canal de suporte — vamos avaliar e priorizar. Atualizações da API são notificadas via release notes no Postman.

Pronto para integrar? Comece pela coleção do Postman. Tem dúvidas técnicas? Fale com nosso time.