VAI · API de integração
Referência para quem integra um sistema externo ao VAI (Vendedor Automático Inteligente). Escrita a partir das rotas em produção, não de um plano: o que está aqui existe hoje, e o que ainda não existe está marcado como tal.
Base: https://api.vendedorautomaticointeligente.com
1. O modelo: uma conta por operação
O VAI isola dados por conta. Conversas, leads, modelos de mensagem, campanhas e números de WhatsApp pertencem a uma conta e não atravessam para outra. Toda leitura é filtrada pela conta de quem chamou, e todo registro nasce carimbado com ela.
Numa rede com várias unidades, o desenho é uma conta do VAI por unidade:
- cada unidade tem o próprio número de WhatsApp, os próprios modelos de mensagem aprovados e o próprio histórico;
- uma unidade nunca enxerga a conversa da outra, nem por engano de parâmetro;
- a matriz opera por cima, provisionando e consultando as unidades.
Isso não é configuração: é como o sistema é construído. A regra vale inclusive para chamadas de API, então uma chave emitida para a unidade A não alcança a unidade B mesmo que o id da outra seja informado à mão.
2. Autenticação
Hoje existem dois caminhos, e eles servem a propósitos diferentes.
2.1 Chave por conta (recomendada para integração)
Usada hoje pelo endpoint de eventos. A chave pertence a uma conta e vai no cabeçalho:
Authorization: Bearer vai_xxxxxxxxxxxxxxxxxxxxxxxx
A conta é resolvida a partir da chave, nunca de um id no corpo da requisição. É isso que impede um sistema externo de disparar ação na conta errada.
2.2 Token de usuário
As rotas de conversa, modelo de mensagem e envio hoje autenticam com token de usuário, a mesma credencial que o aplicativo usa. Funciona para integrar já, com duas ressalvas: é a credencial de uma pessoa, e não tem escopo.
Em aberto: estender a chave por conta (2.1) aos endpoints das seções 4 a 6, com escopo por recurso. É o primeiro item a combinar numa integração nova.
3. Eventos: o sistema externo avisa o VAI
POST /api/v1/eventos · chave por conta
Um acontecimento no sistema do cliente (cadastro, pagamento, assinatura cancelada) entra no VAI e pode iniciar ou encerrar automações.
{
"evento": "usuario.cadastrado",
"id_externo": "cliente-8842",
"contato": {
"telefone": "5511960730881",
"nome": "Ana Souza",
"email": "[email protected]"
},
"dados": { "plano": "pro", "unidade": "tatuape" }
}
| Campo | Regra |
|---|---|
evento |
obrigatório, até 80 caracteres, [A-Za-z0-9._-]. O vocabulário é de quem envia |
id_externo |
opcional. Repetir o mesmo par evento + id não dispara de novo |
contato.telefone |
obrigatório, com DDI e DDD |
dados |
opcional, até 40 campos escalares. Viram variáveis na mensagem ({{plano}}) |
Resposta 202:
{
"sucesso": true,
"conta": "Unidade Tatuapé ([email protected])",
"evento_id": 1010,
"fluxos": 1,
"encerrados": 0,
"mensagem": "1 fluxo(s) iniciado(s)."
}
O nome da conta volta na resposta de propósito: chave trocada é o erro mais caro dessa integração, e assim ele aparece no primeiro envio, não semanas depois.
fluxos é quantas automações começaram; encerrados, quantas cadências foram
interrompidas para aquele contato. Um mesmo evento pode fazer as duas coisas:
usuario.cadastrado inicia a trilha de boas-vindas e, ao mesmo tempo, tira a
pessoa da prospecção.
Reenvio é regra em integração: com id_externo, a segunda chamada responde
duplicado: true e não repete nada.
4. Modelos de mensagem (templates da Meta)
Ciclo completo dentro do VAI: escrever, submeter à Meta, acompanhar aprovação e usar no disparo.
| Verbo | Rota | O que faz |
|---|---|---|
GET |
/api/message-templates |
lista os modelos da conta, com status e variáveis |
POST |
/api/message-templates |
cria o modelo no VAI |
POST |
/api/message-templates/meta/draft |
monta o rascunho no formato da Meta |
POST |
/api/message-templates/{id}/submit-meta |
submete para aprovação |
POST |
/api/message-templates/{id}/sync-meta |
traz o status atual de volta |
POST |
/api/message-templates/sync-meta-todos |
sincroniza a conta inteira |
POST |
/api/message-templates/{id}/duplicate |
copia um modelo existente |
DELETE |
/api/message-templates/{id} |
remove |
POST |
/api/ai/generate-template |
gera o texto do modelo com IA |
Fluxo normal: cria, submete, e a Meta devolve PENDING na maioria dos
casos. A aprovação vem depois, de forma assíncrona, e aparece no sync-meta.
Os status espelham os da Meta: pendente, aprovado, rejeitado.
Submeter um modelo já enviado devolve 422: a segunda submissão do mesmo
modelo é sempre engano.
O VAI monta as amostras que a Meta exige para cada variável do corpo, então o integrador não precisa inventar exemplos para passar na revisão.
5. Conversas e envio
| Verbo | Rota | Para quê |
|---|---|---|
POST |
/api/conversations/start |
abre conversa com um número |
GET |
/api/conversations/{id}/messages |
histórico |
POST |
/api/conversations/{id}/send |
texto livre |
POST |
/api/conversations/{id}/send-media |
documento, áudio, vídeo |
POST |
/api/conversations/{id}/send-image |
imagem |
POST |
/api/conversations/{id}/send-template |
modelo aprovado |
Abrir conversa exige o número e a conexão de WhatsApp que vai falar:
{
"phone_number": "5511960730881",
"whatsapp_instance_name": "unidade-tatuape",
"contact_name": "Ana Souza"
}
Texto livre aceita até 4096 caracteres em text. Vale a janela de 24 horas
da Meta: fora dela, só modelo aprovado.
Modelo aprovado:
{
"template_name": "confirmacao_agendamento",
"language_code": "pt_BR",
"parameters": ["Ana", "quinta-feira às 15h"]
}
Os dois canais convivem: API Oficial da Meta para modelo e escala, QR Code para mensagem livre. A conversa é a mesma dos dois lados; o histórico não se parte.
6. Recebimento: o VAI avisa o sistema externo
Ainda não existe. Hoje o VAI recebe os webhooks da Meta e da Evolution, normaliza e grava a conversa, mas não repassa para fora: a interface se atualiza por consulta interna.
Para um sistema externo receber mensagem nova e status de entrega, falta um webhook de saída por conta, com URL e segredo configuráveis, assinatura para o destino conferir a origem, e reentrega quando a chamada falha.
Duas decisões definem o desenho e precisam ser combinadas antes:
- entrega em tempo real ou busca por intervalo;
- status de entrega e leitura junto, ou só a mensagem recebida.
7. Entrar com a conta do Google
Serve para o cadastro e para o login. Quem nunca teve conta ganha uma; quem já tem, com a mesma caixa de e-mail, entra na dela.
| Verbo | Rota | Para quê |
|---|---|---|
GET |
/api/auth/google/redirect |
devolve para onde mandar a pessoa |
GET |
/api/auth/google/callback |
o Google chama de volta; o VAI identifica e redireciona ao app |
POST |
/api/auth/google/sessao |
troca o ticket pelo token de sessão |
O caminho inteiro, na ordem:
- O app pede a URL em
/api/auth/google/redirecte leva a pessoa para lá. - Ela escolhe a conta no Google.
- O Google devolve para o
callback, que identifica, cria a conta se precisar e redireciona para o app com?google=TICKET(e&novo=1em conta nova). - O app troca o ticket pela sessão:
POST /api/auth/google/sessao
{ "ticket": "o valor que veio em ?google=" }
Resposta:
{
"success": true,
"token": "…",
"user": { "…": "…" },
"precisa_whatsapp": true
}
Por que um ticket e não o token. O token de sessão não pode viajar na barra de endereço: fica no histórico do navegador, no cabeçalho de referência e no log de qualquer proxy no caminho. O ticket vale uma vez só e expira em três minutos.
Conta existente. É vinculada automaticamente quando o Google confirma o e-mail verificado, e a senha antiga continua valendo. Sem e-mail verificado não há vínculo: seria possível tomar a conta de alguém criando um Google com o e-mail dela.
O que a conta nova tem, e o que falta. Nasce com nome, e-mail e foto, mais
sete dias de teste. O e-mail já entra como verificado, então não há código para
confirmar. O Google não entrega telefone, e por isso a resposta traz
precisa_whatsapp: enquanto for true, o app deve pedir o WhatsApp, que é o
que a trilha de ativação usa. CPF e data de nascimento só fazem falta na hora
de assinar.
Erros voltam na URL do app, não como JSON, porque quem redireciona é o
Google: ?google_erro=cancelado, expirado, falhou ou email_nao_verificado.
8. Provisionamento pela matriz
Criar contas, definir plano e gerir usuários existe sob /api/admin, protegido
por sessão de administrador:
| Verbo | Rota |
|---|---|
GET |
/api/admin/users |
POST |
/api/admin/users |
POST |
/api/admin/users/{id}/plan |
DELETE |
/api/admin/users/{id} |
Em aberto: expor esse grupo para a matriz por chave com escopo administrativo, de forma que ela crie e configure unidades sem sessão de navegador.
9. Erros
| Código | Quando |
|---|---|
401 |
chave ausente, inválida ou revogada |
403 |
a conta não tem permissão para o recurso |
404 |
o recurso não existe ou pertence a outra conta |
409 |
operação já em andamento |
422 |
dado inválido, com a mensagem dizendo qual campo e por quê |
O 404 em recurso de outra conta é deliberado: responder 403 confirmaria que
o id existe em algum lugar.
10. Antes de escrever código
- Chave por conta para os endpoints das seções 4 a 6, em vez de token de usuário.
- Webhook de saída (seção 6), com as duas decisões da lista.
- Provisionamento por chave administrativa (seção 8).
- Ambiente de teste: uma conta com número conectado, para validar envio e modelo sem tocar em operação real.
Dúvida sobre algo que não está aqui: fale com a Agência Novo Foco. Este documento descreve o que existe em produção; o que ainda não existe está marcado como tal.