Tema
API interna do painel
INFO
Estas são as rotas que as telas do painel usam, com o login de uma pessoa. Para integrar outro sistema, prefira a API v1 com chave.
Todas as rotas abaixo ficam em /api/ e pedem Authorization: Bearer <token> (veja Como usar a API). Onde aparece (admin), só admin e dono podem chamar.
Eu e a equipe
| Método | Rota | Corpo / Query | Resposta |
|---|---|---|---|
| GET | at/equipe/eu | { id, nome, email, papel, presenca, presenca_motivo, max_simultaneas, setores:[ids], supervisiona:[ids], empresa:{id,nome,marca} } | |
| POST | at/equipe/presenca | { presenca:'online'|'pausa'|'offline', motivo? } | { ok, presenca } |
| GET | at/equipe | [{ id, nome, email, papel, presenca, presenca_motivo, max_simultaneas, visto_em, setores:[{id,nome,cor,supervisor}], ativas }] | |
| POST | at/equipe (admin) | { nome, email, senha(≥10), papel:'admin'|'supervisor'|'atendente', max_simultaneas?, setores?:[ids] } | pessoa |
| PATCH | at/equipe/:id (admin) | { nome?, papel?, max_simultaneas?, setores? } | |
| DELETE | at/equipe/:id (admin) | { ok } (as conversas da pessoa voltam para a fila) |
Conversas
Objeto conversa (lista e detalhe):
| Grupo | Campos |
|---|---|
| Identificação | id, protocolo, status: bot | fila | aberta | pendente | adiada | avaliacao | encerrada |
| Classificação | prioridade: baixa | normal | alta | urgente; sentimento; etiquetas[] |
| Prévia | ultima_mensagem, ultima_mensagem_em, nao_lidas |
| Tempos | entrou_fila_em, atribuida_em, primeira_resposta_em, encerrada_em, sla_avisado (0 ok, 1 ≥ 50%, 2 ≥ 80%, 3 estourado), sla_primeira_resp_s |
| Contato | contato_id, contato_nome, contato_telefone, contato_email |
| Setor e atendente | setor_id, setor_nome, setor_cor (1–8), atendente_id, atendente_nome |
| Canal | canal_id, canal_tipo: webchat | evolution | cloud; canal_nome |
| IA | resumo_ia |
| Método | Rota | Corpo / Query | Resposta |
|---|---|---|---|
| GET | at/conversas | aba: minhas|fila|bot|encerradas|todas, setor_id, busca, limite, antes (ISO, paginação por ultima_mensagem_em) | [conversa] |
| GET | at/conversas/contagens | { minhas, fila, bot, todas } | |
| GET | at/conversas/:id | conversa + contato{...}, qualidade (auditoria IA ou null), csat ({nota} ou null), historico (conversas anteriores) | |
| GET | at/conversas/:id/mensagens | antes (id) | [mensagem] em ordem crescente (100 por página); zera nao_lidas |
| GET | at/conversas/:id/eventos | [{ tipo, usuario_nome, dados, criado_em }] (criada, fila, atribuida, transferida, primeira_resposta, csat, encerrada, alerta, triagem_ia, ia_fontes…) | |
| POST | at/conversas/:id/mensagens | { texto, privada?, resposta_a?, midia?:{ base64, mime, nome?, tipo:'imagem'|'audio'|'documento' } } | mensagem. Responder conversa da fila assume ela. privada:true = nota interna (@Nome avisa a pessoa) |
| POST | at/conversas/:id/mensagens/:mid/reenviar | só para status falhou | |
| POST | at/conversas/:id/lida | zera não lidas | |
| POST | at/conversas/:id/assumir | conversa | |
| POST | at/conversas/:id/transferir | { setor_id? | usuario_id?, nota? } | conversa |
| PATCH | at/conversas/:id | { prioridade?, etiquetas?, status?:'aberta'|'pendente', adiada_ate?: ISO } | conversa |
| POST | at/conversas/:id/encerrar | { motivo, mensagem_final?, csat?:bool } | conversa (avaliacao se enviou a pesquisa) |
| POST | at/conversas/:id/reabrir | conversa | |
| POST | at/conversas | { canal_id, contato_id, texto } | inicia conversa (WhatsApp) |
| POST | at/conversas/:id/ia/sugerir | { sugestoes:[3 textos], fontes:[títulos] } | |
| POST | at/conversas/:id/ia/resumo | { resumo } | |
| POST | at/conversas/:id/ia/auditar | auditoria de qualidade | |
| POST | at/conversas/ia/reescrever | { texto, tom:'cordial'|'formal'|'curto'|'corrigir'|'empatico' } | { texto } |
Objeto mensagem:
| Campo | Valores |
|---|---|
direcao | entrada | saida | sistema |
autor_tipo | contato | usuario | bot | ia | sistema |
autor_nome | texto |
tipo | texto | menu | audio | imagem | documento | video | localizacao |
conteudo | texto |
midia | { url, mime, nome, opcoes?[{id,titulo}] } |
privada | boolean (nota interna) |
status | pendente | enviada | entregue | lida | falhou | nota |
erro, transcricao | texto do áudio |
criado_em | data |
As URLs de mídia são relativas (/api/at/midias/...), assinadas e com validade. Elas funcionam direto em <img> e <audio>.
Setores, horários, respostas, etiquetas
GET/POST at/setores,PATCH/DELETE at/setores/:id,PUT at/setores/:id/membros { membros:[{usuario_id, supervisor}] }.- Campos do setor:
nome,cor(1–8),distribuicao:menor_carga|rodizio|carteira|manual;horario_id,sla_primeira_resp_s,sla_resolucao_s,escalar_apos_s,transbordo_setor_id;msg_fila,msg_fora_horario,msg_encerramento,enviar_csat,ativo;- na lista também vem
membros[{id,nome,supervisor,presenca,max_simultaneas,papel}].
GET/POST at/horarios,PATCH at/horarios/:id. Campos:nome;grade:{ "0".."6": [["09:00","12:00"],["13:00","18:00"]] }(0 = domingo);feriados:["2026-12-24"];feriados_nacionais: bool.
GET/POST at/respostas,PATCH/DELETE at/respostas/:id. Campos:atalho(minúsculas, sem espaço),texto(aceita),setor_id?.GET/POST at/etiquetas,DELETE at/etiquetas/:id. Campos:nome,cor.
Canais
GET/POST at/canais,PATCH/DELETE at/canais/:id(admin).- Criar:
{ tipo:'webchat'|'evolution'|'cloud', nome, config, segredos, fluxo_id?, setor_padrao_id? }.
- Config por tipo:
- webchat:
{ titulo, subtitulo, saudacao, cor:'#rrggbb', posicao:'direita'|'esquerda', dominios:['meusite.com'], pedir_telefone, pedir_email, texto_privacidade, link_privacidade, whatsapp }; - evolution:
{ url, instancia }com segredos{ apikey }; - cloud:
{ phone_number_id, versao }com segredos{ token, app_secret }.
- webchat:
- Resposta:
segredosmostra só quais estão preenchidos ({apikey:true}), maiswebhook_url,snippet(widget),capacidadesestatus_conexao. - Segredos: mandar
nullapaga; omitir mantém.
Fluxos
| Método | Rota | Corpo | Resposta |
|---|---|---|---|
| GET | at/fluxos/tipos | [{ tipo, grupo, titulo, espera, final }] | |
| GET | at/fluxos | [{ id, nome, descricao, versao_publicada, tem_rascunho, canais }] | |
| GET | at/fluxos/:id | { ..., rascunho:{nodes,edges,viewport}, versoes:[{numero,publicado_em,publicado_por}], validacao:{erros,avisos} } | |
| POST | at/fluxos | { nome, descricao?, modelo?:'vazio'|'boas_vindas', definicao? } | fluxo |
| PUT | at/fluxos/:id | { nome?, descricao?, rascunho? } | fluxo + validacao |
| POST | at/fluxos/:id/publicar | { numero, avisos } (400 com os erros) | |
| POST | at/fluxos/:id/restaurar/:numero | volta o rascunho para a versão | |
| POST | at/fluxos/validar | definição | { erros, avisos } |
| POST | at/fluxos/simular | { definicao, respostas:[textos] } | { mensagens:[{de:'bot'|'cliente'|'sistema', texto, no, opcoes?}], variaveis } |
| POST | at/fluxos/gerar | { pedido } | { nome, definicao, validacao } (IA) |
Definição: é o formato do Vue Flow.
- Nó:
{ id, type, position:{x,y}, data }. - Aresta:
{ id, source, target, sourceHandle }. sourceHandleé o nome da saída.
Tipos de nó, data e saídas:
| Tipo | data | Saídas |
|---|---|---|
inicio | — | saida |
mensagem | { texto } | saida |
menu | { texto, opcoes:[{id,titulo}], espera_min, tentativas, variavel?, msg_invalido? } | o id de cada opção, invalido, timeout |
pergunta | { texto, variavel, validacao: texto|numero|email|cpf|cnpj|cpf_cnpj|telefone|data|regex, regex?, salvar_no_contato?: nome|email|<campo>, espera_min, tentativas, msg_invalido? } | resposta, invalido, timeout |
condicao | { regras:[{campo, op: igual|diferente|contem|vazio|nao_vazio|maior|menor, valor}], modo:'e'|'ou' } | sim, nao |
horario | { setor_id? } | dentro, fora |
esperar | { minutos } | saida |
ir_para | { no_id } | — |
definir_variavel | { variavel, valor } | saida |
atualizar_contato | { nome?, email?, etiqueta?, campo?, valor?, opt_out? } | saida |
http | { metodo, url (https), headers?, corpo?, salvar_em? } | ok, erro |
ia_agente | { agente_id? | instrucoes, usar_conhecimento, max_turnos, espera_min } | concluiu, humano, erro |
ia_classificar | { categorias:[{id,titulo,descricao}], variavel? } | o id de cada categoria, incerto |
ia_extrair | { campos:[{nome,descricao}] } | saida |
transferir | { setor_id | usuario_id, prioridade?, mensagem?, resumo_ia?, avisar_fila? } | fim |
notificar | { setor_id? | usuario_id?, texto } | saida |
encerrar | { mensagem?, motivo?, csat? } | fim |
Todos os nós aceitam data.titulo, um nome opcional exibido no canvas. Nos textos, , e são substituídos.
IA: agentes e base de conhecimento
GET/POST at/agentes,PATCH/DELETE at/agentes/:id. Campos:nome,instrucoes,usar_conhecimento,modelo?,ativo.GET at/conhecimento→[{ id, titulo, fonte, tamanho, trechos, criado_em }].GET at/conhecimento/:id.GET at/conhecimento/buscar?q=→[{ titulo, texto, rank }](para testar).POST at/conhecimentoaceita três formatos:{tipo:'texto', titulo, texto};{tipo:'url', url};{tipo:'pdf', nome, base64}.
Todos aceitam
setor_id?.DELETE at/conhecimento/:id.
Contatos (LGPD)
GET at/contatos?busca=&pagina=.PATCH at/contatos/:id { nome?, email?, etiquetas?, campos?, carteira_usuario_id?, opt_out? }.GET at/contatos/:id/exportar(admin) devolve tudo o que há sobre o titular.DELETE at/contatos/:id(admin) apaga o contato, as conversas e as mídias.
Métricas
GET at/metricas/ao-vivo:filas:[{ id, nome, cor, aguardando, em_atendimento, no_bot, maior_espera_s, online, membros }];hoje:{ novas_hoje, na_fila, em_atendimento, no_bot, tme_s };csat:{ media, respostas, satisfeitos_pct };sla:{ pct, em_risco };equipe:[{ id, nome, presenca, presenca_motivo, ativas, max_simultaneas, tma_s, setores }];risco:[conversas].
GET at/metricas/periodo?inicio=&fim=(ISO; padrão 7 dias):resumo:{ conversas, encerradas, tme_mediana_s, tme_media_s, tme_p90_s, frt_mediana_s, tma_media_s, resolucao_media_s, resolvidas_bot, abandonadas, entraram_fila };csat,qualidade;porDia,porHora,porSetor,porAtendente,motivos,iaUso.
GET at/metricas/qualidade:piores:[{ conversa_id, nota, resumo, problemas, criterios, atendente, contato, protocolo }];criterios:{ cordialidade, agilidade, resolucao, precisao, vendas, processo }.
Notificações
GET at/notificacoes→[{ id, tipo, titulo, texto, conversa_id, lida_em, criado_em }].POST at/notificacoes/lidas { ids? }.
Tempo real (Socket.IO)
Equipe:
- Conexão:
io(location.origin, { path: '/api/socket', auth: { token: <JWT> } }). - Para receber "digitando" de uma conversa:
emit('conversa:abrir', id). emit('digitando', { conversaId, ativo })repassa para o visitante do chat do site.
| Evento | Dados |
|---|---|
conversa:atualizada | conversa |
mensagem:nova | { conversaId, mensagem } |
mensagem:status | { conversaId, id, status, erro? } |
mensagem:transcricao | { conversaId, id, transcricao } |
digitando | { conversaId } (visitante do site digitando) |
notificacao | notificação |
presenca | { usuarioId, presenca, motivo } |
alerta:som | { conversaId } |
Visitante do widget:
- Conexão: namespace
/webchat,auth: { token: <token do visitante> }. - Recebe
mensagem(pública),digitando { ativo }eencerrada. - Envia
digitando.
Configurações, perfil, fotos e integrações
Empresa (admin)
GET at/empresa→{ id, nome, marca:{ cor, logo_url }, config:{ inatividade } }PATCH at/empresa→{ nome?, marca?:{ cor? }, config?:{ inatividade? } }inatividade:{ ativo, aplicar_em:['aberta','pendente','bot'], lembretes:[{ minutos, texto }], encerrar_minutos, mensagem_encerramento }- Regra: depois da nossa última mensagem sem resposta do cliente, cada lembrete é enviado
minutosapós a mensagem anterior. Depois do último, passadosencerrar_minutos, a conversa é encerrada commensagem_encerramento, motivoinatividadee sem avaliação. Os textos aceitam.
POST at/empresa/logo { base64, mime }→{ logo_url }·DELETE at/empresa/logo
Meu perfil (qualquer pessoa logada)
GET at/equipe/eupassa a trazerfoto_url,assinaturaepreferencias:{ som, notificacoes_navegador, assinar_mensagens }.PATCH at/equipe/eu { nome?, assinatura?, preferencias? }- Com
assinar_mensagens, o WhatsApp recebe*Assinatura*na 1ª linha das mensagens da pessoa. O padrão da assinatura é o primeiro nome.
- Com
POST at/equipe/eu/foto { base64, mime }→{ foto_url }·DELETE at/equipe/eu/foto- A senha continua em
POST auth/senha { senha_atual, senha_nova }(rota já existente). - Fotos em listas e conversas:
foto_urlemat/equipe,atendente_foto_urlecontato_foto_urlna conversa,autor_foto_urlna mensagem efoto_urlno contato.- São URLs relativas, abertas direto em
<img>. A foto do contato do WhatsApp é buscada sozinha na Evolution.
Detalhe da conversa
GET at/conversas/:idtraz tambémsdr(ficha do agente de IA de prospecção do n8n, quando o telefone bate):{ etapa, status, negocio, necessidade, orcamento, oferta, objecoes, resumo, reuniao_em, ultima_nota, numero, link }, ounull.GET at/conversas/:id/integracoes→[{ integracao_id, nome, tipo:'whmcs', encontrado, erro?, cliente?, servicos[], faturas[], dominios[], tickets[], resumo?:{ faturas_abertas, valor_em_aberto } }], com:cliente:{ id, nome, empresa, email, telefone, status, criado_em, link }servicos:[{ id, produto, dominio, status, valor, ciclo, vencimento, link }]faturas:[{ id, numero, status, total, vencimento, link }]dominios:[{ dominio, status, vencimento }]tickets:[{ id, assunto, status, departamento, atualizado_em, link }]
Integrações (admin)
GET/POST at/integracoes,PATCH/DELETE at/integracoes/:id,POST at/integracoes/:id/testar→{ ok, mensagem }.- Criar:
{ tipo:'whmcs', nome, config:{ url }, segredos:{ identifier, secret, accesskey? } }urlé o endereço do WHMCS, por exemplohttps://www.inneed.com.br/clientes.- Os segredos voltam só como
true/false. - O WHMCS precisa liberar o IP do servidor do painel (177.11.55.67) em Setup → General → Security → API IP Access Restriction, ou usar
accesskey.
- Agentes de IA ganham
usar_integracoes: o resumo do cliente no WHMCS (serviços, faturas em aberto) entra no contexto do agente.
Chat do site (canal webchat): configuração
Em config do canal (todos opcionais):
| Campo | Valor |
|---|---|
titulo_boas_vindas | ex.: "Olá! Seja bem-vindo ao atendimento da In Need Cloud." |
subtitulo | frase abaixo do título |
tempo_resposta | minutos | hora | dia |
titulo_lancador | texto ao lado do botão, ex.: "Fale conosco no chat"; vazio = só o ícone |
mensagem_formulario | texto acima dos campos |
campos | [{ chave, tipo:'text'|'email'|'tel'|'textarea'|'select', rotulo, placeholder, obrigatorio, ativo, opcoes?:[texto] }]. Chaves padrão: nome, email, telefone; outras chaves (slug) vão para contato.campos e para conversa.formulario. Padrão: nome, email e telefone, todos obrigatórios |
mostrar_equipe | fotos da equipe online na tela inicial |
permitir_anexos | |
permitir_encerrar | |
cor, posicao, dominios, texto_privacidade, link_privacidade, whatsapp | já existentes |
GET at/webchat/:chave/configpassa a devolver esses campos e maisequipe:[{ nome, foto_url }](até 3 online) elogo.POST at/webchat/:chave/sessao { campos:{ chave: valor }, consentimento:true, pagina? }. Continua aceitandonome,telefoneeemailsoltos.POST at/webchat/encerrar(Bearer do visitante): o visitante encerra a conversa.
Nós de fluxo: lembrete antes do tempo esgotar
menu, pergunta e ia_agente aceitam lembrete_min e lembrete_texto. Se o cliente não responder em lembrete_min, o texto é enviado uma vez; em espera_min sai pela saída timeout (no agente, concluiu). Também aceitam msg_timeout, a mensagem de encerramento quando não há ligação na saída de tempo esgotado.
Canal Evolution: conexão automática
POST at/canais/:id/conectar(admin): aponta o webhook da instância (/webhook/set) para o painel, com os eventos MESSAGES_UPSERT, MESSAGES_UPDATE e CONNECTION_UPDATE e mídia em base64. Devolve o status.GET at/canais/:id/status→{ conectado, estado, webhook_ok, webhook_atual, qr (base64 PNG quando desconectado), mensagem }.