Clínica Lindomar Delgado · Guia operacional
Serviço que liga o atendimento de WhatsApp (SymplesDesk) ao CRM (Pipedrive) e, desde 26/08, o CRM ao prontuário (Clínica nas Nuvens) — gravando tudo em um banco de dados próprio, em tempo real. Esta página explica o que cada parte faz e o que é preciso configurar para cada uma funcionar — sem precisar abrir o repositório.
Atualizado em 17/09/2026 · versão em produção, validada ponta a ponta
A configuração de cada médico (funil, etapa, responsável, canal de WhatsApp) mora em três
tabelas do banco (integration_channels, integration_pipelines e
integration_pipeline_steps) — trocar de ambiente de teste para produção é editar
essas linhas e o .env, nunca o código.
Além dos 4 fluxos, o serviço mantém o quadro de Oportunidades do SymplesDesk espelhando o CRM nos dois sentidos — hoje em dois funis: o de Leads, das SDRs, e o de Consulta, das Concierges — e encerra a conversa 24h depois de um lead ser dado como perdido. Essas partes não são "Fluxo 5 e 6": nasceram depois e funcionam em paralelo aos quatro.
Desde 26/08 o serviço também cuida do prontuário no Clínica nas Nuvens quando uma Consulta é ganha: procura o paciente lá, liga ao contato se já existir e cria o cadastro se não existir. Detalhe na seção Cadastro no Clínica nas Nuvens.
A regra mais importante de todo o sistema:
+55 (32) 99811-7881 e 3298117881 são a mesma pessoa.Quando um paciente inicia conversa no WhatsApp, o SymplesDesk abre um ticket e avisa o serviço, que cria o lead no funil certo do médico certo — sem duplicar paciente nem ciclo.
55+DDD+número.telefone - sigla,
responsável, etapa e etiqueta vindos da config.<URL-do-serviço>/webhooks/symplesdesk. Importante: esse evento deve apontar
para uma URL só — dois destinos criariam deal duplicado.integration_channels no banco (canal → médico →
funil/etapa/responsável) — o SQL pronto está em
Como adicionar um novo canal.PIPEDRIVE_BASE_URL e PIPEDRIVE_API_TOKEN.Quando a equipe marca um deal como perdido ou ganho no Pipedrive, o serviço reage:
Deal perdido no Pipedrive: [título] / Motivo: [motivo da perda] — e o
ciclo vira closed_lost no banco. A conversa NÃO fecha neste momento: ela
fecha 24 horas depois, se o paciente não voltar (ver
Fechamento da conversa).closed_won) e a conversa até o momento do
ganho entra no deal na hora (ver Fluxo 4). Não fecha a conversa — o atendimento
pós-ganho continua no WhatsApp (decisão da clínica).deal, ações create + change, e adicionar a URL da
API: <URL-do-serviço>/webhooks/pipedrive, preenchendo usuário e senha (Basic
Auth) com os mesmos valores do ambiente.person, ação change, mesma URL e mesma Basic Auth. Sempre que
alguém editar o nome de uma pessoa no Pipedrive, o serviço atualiza o nome no banco
(só o nome — o telefone é a identidade do contato e nunca muda por aqui).SYMPLESDESK_TOKEN_T e SYMPLESDESK_TOKEN_A) — já validados contra a
produção em 22/07/2026.DISCORD_ERROR_WEBHOOK_URL para os alertas de falha.A automação só cria deal no funil de Leads (Fluxo 1). Nos funis de Consulta e Procedimentos, os deals são criados à mão pela equipe Concierge — este fluxo os captura para o banco, rastreando a jornada completa do paciente.
integration_pipelines com os funis mapeados
(Leads, Consulta e Procedimentos por médico) — o SQL pronto está em
Como adicionar um novo canal.Todas as mensagens trocadas no WhatsApp aparecem dentro do deal do paciente no Pipedrive, como uma atividade por dia de conversa. São duas partes:
symplesdesk_messages, vinculada ao contato certo
(pelo ticket ou pelo telefone). Mensagens repetidas (reenvio) e apagadas não duplicam nem entram.CONVERSATION_SYNC_CRON, sem mexer em código: o job agrupa as mensagens pendentes
por paciente e por dia (incluindo o próprio dia até o horário do disparo) e cria uma
Activity "Whatsapp - DD/MM/AAAA" por dia de conversa — somente em deals ABERTOS dos
funis de Leads e Consulta. Paciente sem deal aberto: as mensagens aguardam (nada se perde) e
entram no próximo deal.Formato da nota (mensagens do mesmo minuto são agrupadas, com o horário uma vez no fim; atendente aparece pelo nome):
Oi, tudo bem? Queria marcar. 15:07 Gabriel: Oi! Claro, vou te passar as opções. 15:07
<URL-do-serviço>/webhooks/symplesdesk-newmessage, colando no campo "Token de
autenticação" o mesmo valor de SYMPLESDESK_WEBHOOK_TOKEN do ambiente.PIPEDRIVE_ACTIVITY_TYPE_WHATSAPP_KEY — a chave do
tipo de atividade "Whatsapp" na conta do Pipedrive (sem ela o job falha de propósito, para
nunca criar atividade de tipo errado).As SDRs trabalham no quadro de Oportunidades do SymplesDesk, sem precisar abrir o Pipedrive. Cada deal de Leads tem um card espelho, e os dois lados se acompanham nos dois sentidos: mover o card move o deal, e mover o deal move o card.
| A SDR faz no card | Acontece no Pipedrive |
|---|---|
| Arrasta para outra etapa | o deal muda de etapa no funil |
| Marca como ganho | o deal vira ganho |
| Marca como perdido | o deal vira perdido, com o motivo |
| Renomeia o card | o nome da pessoa é corrigido no CRM |
| Alguém faz no Pipedrive | Acontece no quadro |
|---|---|
| Move o deal de etapa | o card acompanha |
| Ganha ou perde o deal | o card fecha com o mesmo resultado |
| Corrige o nome da pessoa | o card é renomeado |
| Reabre um deal fechado | o card volta a abrir |
<URL-do-serviço>/webhooks/oportunidade e colando no campo "Token de
autenticação" o valor de SYMPLESDESK_OPORTUNIDADE_WEBHOOK_SECRET.integration_pipeline_steps) — ver a seção
Mapa das etapas. Sem a tradução, o movimento é ignorado com registro:
o sistema nunca chuta uma etapa.symplesdesk_responsible_user_id): é por esse campo que cada uma filtra o quadro
compartilhado para ver só os cards dela.symplesdesk_lost_reason_id): o fornecedor recusa fechar card como perdido
sem motivo. O motivo real continua no Pipedrive e na nota — este é só o exigido pela tela.Quando um lead é ganho, o Pipedrive cria sozinho o negócio no funil de Consulta — isso já acontecia e continua igual. A novidade é que esse negócio agora também ganha um card, no funil "Funil de Consulta - Concierge" do quadro, com a Concierge do médico como dona. A Concierge passa a trabalhar no mesmo lugar que a SDR, sem abrir o Pipedrive.
| Card de Leads (SDR) | Card de Consulta (Concierge) | |
|---|---|---|
| Dona do card | a SDR do médico | a Concierge do médico |
| Nome do card | montado pelo sistema: nome - sigla | cópia exata do nome do negócio: CONSULTA DE nome |
| Etapas | as do funil de Leads | as do funil de Consulta |
| Renomear o card | corrige o nome da pessoa no CRM | não propaga nada — ver abaixo |
Mover o card de etapa move o negócio no funil de Consulta, e vice-versa. Ganhar ou perder o card fecha o negócio com o mesmo resultado, e reabrir também acompanha. É a mesma mão dupla dos cards de Leads — só o renomear ficou de fora.
symplesdesk_concierge_user_id): é o campo que liga esta parte. Vazio,
nenhum card de Consulta é criado — e o resto do sistema segue normal.integration_pipeline_steps com pipeline_type = 'consulta') — ver a
seção Mapa das etapas.Quando um lead é dado como perdido, a conversa dele no WhatsApp não fecha na hora: fecha 24 horas depois. A janela existe para o paciente ter tempo de responder — muita reversão acontece nesse intervalo.
Ao fechar, o sistema deixa uma nota interna no atendimento: "Conversa encerrada automaticamente — lead perdido há 24h." O paciente não recebe nada.
Se alguém encerra o atendimento manualmente no SymplesDesk, o sistema fica sabendo e registra — e por isso não tenta fechar de novo o que já está fechado.
<URL-do-serviço>/webhooks/atendimento-finalizado, com o valor
de SYMPLESDESK_ATENDIMENTO_WEBHOOK_SECRET no campo "Token de autenticação".CLOSE_LOST_CRON (horário) e CLOSE_LOST_MAX_PER_RUN
(teto) — ver Ligar e desligar.Quando uma Consulta é ganha — ou seja, o paciente passou de "Aguardando pagamento" e o negócio foi ganho no Pipedrive — o serviço procura essa pessoa no Clínica nas Nuvens. Já é paciente? Só registra a ligação. Não é? Cria o prontuário na hora, com os dados que já estão no Pipedrive.
O Clínica nas Nuvens tem 42.126 pacientes e é a base da clínica há anos. Medimos a população exata deste gatilho: de cada 10 pessoas que ganham uma Consulta, 9 já são pacientes de lá. Criar sem procurar duplicaria prontuário em 9 de cada 10 vezes — e prontuário duplicado quebra histórico clínico, agenda e faturamento.
Por isso a busca vem primeiro, e ela é dupla: por CPF e, se não achar, por nome. A segunda existe porque só 20% dos prontuários do CnN têm CPF preenchido — "não achei pelo CPF" quase nunca significa "não existe". Na medição, sem a busca por nome o sistema teria criado 7 pacientes que já existiam, todos com prontuário antigo e sem CPF.
Quando as duas buscas não acham ninguém, o paciente é criado. Sem espera e sem fila.
Rodado contra a base real, sobre 70 Consultas ganhas:
| Resultado | Quantos | O que significa |
|---|---|---|
| Ligados automaticamente | 61 | já eram pacientes; a ligação ficou registrada |
| Prontuários criados | 0 | ninguém precisou ser criado nessa carteira — todos os 70 já existiam no CnN. Daqui para a frente, quem for novo é criado |
| Foram para revisão | 9 | parecidos demais para decidir sozinho |
A fila não é onde o paciente novo espera — esse é criado direto. Ela guarda só o que o
sistema não pode decidir sozinho, na tabela cnn_match_reviews, com o
motivo de cada caso, e serve de relatório para a clínica:
| Motivo | O que houve |
|---|---|
ambiguo | dois ou mais pacientes parecidos — precisa de gente para escolher |
sem_cpf | a pessoa não tem CPF válido no Pipedrive, então nem dá para procurar |
sem_nascimento | falta a data de nascimento — é obrigatória para criar no CnN |
sem_nome | o "nome" no Pipedrive é o próprio telefone; viraria um prontuário chamado "5532991997896" |
sem_pessoa | o contato não tem pessoa no Pipedrive |
Todo dia às 18h, o serviço lê a agenda de amanhã no Clínica nas Nuvens e manda, por WhatsApp, a lista para o grupo da clínica e, para cada médico configurado, a mesma lista no WhatsApp dele — sem passar pelo SymplesDesk nem pela Meta, e sem que a paciente veja nada.
📅 *Agenda de <dia da semana>, <dd/mm>* —
<N> atendimentos no total e, logo abaixo, os atendimentos agrupados por profissional:
o nome do profissional em negrito, com 🩺 na frente e quantos atendimentos ele tem no dia, e, embaixo dele, uma linha por atendimento no formato
HH:MM Nome, por horário. Quem atende mais cedo aparece primeiro. Se o profissional
de um atendimento não for encontrado, a linha sai assim mesmo, sob
(profissional indisponível).AGENDA_DESTINO: o grupo Agenda Pacientes Clínica
Lindomar Delgado (ou um telefone).AGENDA_MEDICOS, manda ao WhatsApp dele a mesma lista
completa que foi para o grupo — tenha ele atendimento no dia ou não. A leitura da agenda é uma
só para todos.📅 *Agenda de quarta, 16/09* — 3 atendimentos no total 🩺 *Dr. Fulano de Tal* - 2 atendimentos 08:00 Maria Silva 11:00 Ana Souza 🩺 *Beltrana Fisioterapeuta* - 1 atendimento 08:15 Joana LimaOs médicos de
AGENDA_MEDICOS recebem exatamente esse mesmo texto.
(no WhatsApp, o texto entre asteriscos aparece em negrito)
AGENDA_DESTINO (quem recebe a lista geral: o grupo
120363411914071233@g.us, ou um telefone só com dígitos e DDI) e
TOKEN_FZAP (o token da instância) — o job só é agendado quando os dois
existem (ver Ligar e desligar).AGENDA_CRON muda o horário do disparo (padrão 0 18 * * *,
18h) e FZAP_BASE_URL muda o endereço da instância, se um dia precisar trocar.AGENDA_MEDICOS manda a lista completa também ao WhatsApp de cada médico. Formato
idpessoa:telefone, separados por vírgula — por exemplo
20689417:5532999999999. Par escrito errado é ignorado (e avisado no log), sem
derrubar nada. Para incluir um médico: acrescentar o par e fazer redeploy. Os códigos:
| idpessoa | Profissional |
|---|---|
20689417 | Dr. Thiago Ferreira Delgado |
20741719 | Dr. Augusto Cesar de Melo Almeida |
22509355 | Dr. Lindomar Delgado |
22647395 | Dra Larissa |
16918290 | Melissa Monica de Castro Teixeira |
27210466 | Riviane Marques da Costa Alves |
Os dois sistemas nomeiam as etapas de formas diferentes e usam números próprios. A tabela
integration_pipeline_steps é a tradução entre eles — e é ela que faz o card
acompanhar o deal e vice-versa.
| Etapa | No quadro (SymplesDesk) | Dr. Thiago | Dr. Augusto |
|---|---|---|---|
| Cliente Potencial | 52 | 46 | 37 |
| Em conversa | 50 | 27 | 36 |
| Aguardando Pagamento | 48 | 30 | 39 |
| Etapa | No quadro (SymplesDesk) | Dr. Thiago | Dr. Augusto |
|---|---|---|---|
| Consulta Agendada | 57 | 47 | 55 |
| Reagendamento | 58 | 49 | 56 |
| Consulta Confirmada | 59 | 48 | 57 |
O quadro é um só para os dois médicos — por isso a mesma etapa (52, por exemplo) traduz para funis diferentes conforme o médico do deal. Quem separa o trabalho de cada SDR e de cada Concierge é o filtro por responsável, não o quadro.
Toda automação pode ser desligada sem mexer em código e sem derrubar o resto. Útil para manutenção, teste, ou quando a clínica quer pausar um comportamento.
| Desligar | Como | Efeito |
|---|---|---|
| Um médico inteiro | integration_channels.is_active = false |
o canal some da configuração; nada daquele médico é processado |
| Criação de cards de Leads | symplesdesk_responsible_user_id = NULL |
nenhum card novo de Leads é criado; os existentes seguem funcionando |
| Criação de cards de Consulta | symplesdesk_concierge_user_id = NULL |
nenhum card novo de Consulta é criado; o funil de Leads não é afetado |
| Fechar card na perda | symplesdesk_lost_reason_id = NULL |
o deal é perdido normalmente, o card fica aberto |
| Fechamento de 24h | CLOSE_LOST_MAX_PER_RUN=0 |
a verificação roda e não fecha nada |
| Conversa diária no deal | CONVERSATION_SYNC_CRON=0 0 30 2 * |
30 de fevereiro nunca chega — o job nunca dispara |
| Criação de paciente no CnN | CNN_CADASTRO_MODE=link |
continua procurando e ligando quem já é paciente, mas para de criar |
| Cadastro no CnN, por inteiro | CNN_CADASTRO_MODE=off |
o ganho da Consulta segue normal; nada é procurado, ligado nem criado lá |
| Tarefas de NPS | remover PIPEDRIVE_SURGERY_DATE_FIELD_KEY |
o job roda sem fazer nada |
| Agenda do dia seguinte | deixar AGENDA_DESTINO ou TOKEN_FZAP vazio |
o job nem é agendado — nenhuma lista sai |
npx tsx scripts/agenda-dia-seguinte.ts
mostra a mensagem que sairia hoje, sem mandar nada e sem gravar evento. Só envia de verdade com a
flag --enviar.
Quem vai apenas integrar com a API não precisa desta seção — toda interação de fora é pelos endpoints e webhooks. Os blocos abaixo são de quem opera o serviço: variáveis de ambiente (segredos) e os webhooks apontados para a URL pública.
.env)Os valores reais nunca ficam no código nem nesta página — vivem só no .env do servidor.
| Variável | Obrigatória? | Para quê |
|---|---|---|
DATABASE_URL | Sim | Conexão com o Postgres (fonte da verdade) |
PIPEDRIVE_BASE_URL | Sim | Domínio da conta Pipedrive (teste ou produção) |
PIPEDRIVE_API_TOKEN | Sim | Token da API do Pipedrive |
PIPEDRIVE_WEBHOOK_BASIC_USER / _PASS | Sim | Usuário/senha que o webhook do Pipedrive usa para se autenticar |
SYMPLESDESK_TOKEN_T / _A | Fluxo 2 | Tokens da API de fechar conversa, um por canal/médico |
SYMPLESDESK_WEBHOOK_TOKEN | Fluxo 4 | Token que autentica o webhook de mensagem (sem ele a rota recusa tudo) |
PIPEDRIVE_ACTIVITY_TYPE_WHATSAPP_KEY | Fluxo 4 | Chave do tipo de atividade "Whatsapp" na conta |
PIPEDRIVE_PROCEDURE_FIELD_KEY | Produção | Campo personalizado "Procedimento" do deal (não existe na conta de teste) |
SYMPLESDESK_OPORTUNIDADE_WEBHOOK_SECRET | Cards | Token que autentica o webhook de Oportunidade (sem ele a rota recusa tudo) |
SYMPLESDESK_ATENDIMENTO_WEBHOOK_SECRET | Fechamento | Token do webhook "Atendimento finalizado" |
PIPEDRIVE_SURGERY_DATE_FIELD_KEY | NPS | Campo "Data da cirurgia"; ausente = tarefas de NPS desligadas |
CONVERSATION_SYNC_CRON | Opcional | Horário do job da conversa (cron, fuso Brasília; padrão 0 19 * * * = 19h) |
CLOSE_LOST_CRON | Opcional | Quando checar conversas a fechar (padrão 15 * * * * = de hora em hora, aos :15) |
CLOSE_LOST_MAX_PER_RUN | Opcional | Teto de fechamentos por rodada (padrão 25; 0 desliga) |
NPS_SYNC_CRON | Opcional | Horário do job de NPS (padrão 0 7 * * * = 7h) |
CNN_CADASTRO_MODE | Cadastro CnN | Nível do cadastro: off (padrão) · link (só reconhece) · full (reconhece e cria). Qualquer outro valor vira off — inclusive LINK em maiúscula |
CNN_CLIENT_ID / CNN_CLIENT_SECRET / CNN_CID | Cadastro CnN | Credenciais da API do Clínica nas Nuvens. Faltando qualquer uma, todo ganho de Consulta vira evento de erro |
PIPEDRIVE_CPF_FIELD_KEY | Cadastro CnN | Campo "CPF" da pessoa. Sem ele, tudo cai em revisão por falta de CPF |
PIPEDRIVE_BIRTH_DATE_FIELD_KEY | Cadastro CnN | Campo "Data de Nascimento" da pessoa |
PIPEDRIVE_ADDRESS_FIELD_KEY | Opcional | Campo "Endereço" da pessoa (preenchido em ~5% da base; o cadastro funciona sem) |
TOKEN_FZAP | Agenda | Token da instância FZAP (WhatsApp conectado por QR code) que envia a lista do dia seguinte |
FZAP_BASE_URL | Opcional | Endereço da instância FZAP; tem valor padrão de fábrica, só precisa ser definida se o endereço mudar |
AGENDA_DESTINO | Agenda | Quem recebe a lista geral: o JID do grupo (…@g.us) ou um telefone (só dígitos, com DDI). Sem ele ou sem TOKEN_FZAP, o job nem é agendado |
AGENDA_MEDICOS | Opcional | Médicos que também recebem a lista completa no próprio WhatsApp: idpessoa:telefone separados por vírgula (ver a seção da agenda) |
AGENDA_CRON | Opcional | Horário do envio (cron, fuso Brasília; padrão 0 18 * * * = 18h) |
DISCORD_ERROR_WEBHOOK_URL | Opcional | Alertas operacionais de erro |
PORT | Opcional | Porta HTTP (padrão 3000) |
npx tsx scripts/sonda-cnn-prontidao.ts, que é somente leitura.
O banco Postgres é parte interna do serviço e é imutável para terceiros: somente o administrador da integração cria as tabelas e mantém a configuração de canais e funis. Quem consome a API não precisa — nem consegue — acessar o banco; tudo acontece pelos endpoints. (Administrador: o passo a passo do banco está em Adicionar um médico e em Colocando no ar.)
São 3 webhooks — cada um é criar na tela do sistema de origem e adicionar a URL da API. O passo
a passo campo a campo de cada tela está no guia docs/WEBHOOKS.md do repositório.
| Onde criar | Evento | URL da API | Autenticação |
|---|---|---|---|
| SymplesDesk (2 canais) | Mensagem criada | /webhooks/symplesdesk-newmessage | Token de autenticação |
| Pipedrive (Webhooks v2) | deal: create + change | /webhooks/pipedrive | Usuário e senha (Basic Auth) |
| Pipedrive (Webhooks v2) | person: change (sync de nome) | /webhooks/pipedrive | Usuário e senha (Basic Auth) |
| SymplesDesk (2 canais) | Novo atendimento | /webhooks/symplesdesk | — |
| SymplesDesk (2 canais) | Oportunidade (card) | /webhooks/oportunidade | Token de autenticação |
| SymplesDesk (2 canais) | Atendimento finalizado | /webhooks/atendimento-finalizado | Token de autenticação |
Somente o administrador da integração executa estes passos — o banco é imutável para qualquer outra pessoa. Toda a configuração por médico é dado, não código: adicionar um médico novo é inserir linhas em duas tabelas, guardar um token no ambiente e criar os webhooks do canal. Nenhum deploy de código novo.
public.integration_channelsTroque os valores em MAIÚSCULAS pelos reais (a tabela abaixo do SQL explica onde encontrar cada um):
INSERT INTO integration_channels
(doctor_code, doctor_name, symplesdesk_account_id, symplesdesk_whatsapp_id,
symplesdesk_base_url, symplesdesk_api_id, symplesdesk_token_secret_name,
pipedrive_owner_id, pipedrive_pipeline_id, pipedrive_stage_id,
pipedrive_label_id, is_active)
VALUES
('SIGLA', 'NOME DO MÉDICO', 'ACCOUNT_ID_SYMPLESDESK', 'WHATSAPP_ID',
'https://serv2api.simplesdesk.com.br', 'API_ID_DA_CONFIG_DE_API',
'SYMPLESDESK_TOKEN_SIGLA', 'OWNER_ID_PIPEDRIVE', 'ID_FUNIL_LEADS',
'ID_ETAPA_ENTRADA', 'ID_ETIQUETA', true);
| Campo | O que é / onde encontrar |
|---|---|
doctor_code | Sigla única do médico (1 letra, ex.: 'T'). Aparece no título do deal (telefone - SIGLA) e amarra todas as outras configs. |
doctor_name | Nome de exibição (ex.: 'Dr. Thiago Delgado'). |
symplesdesk_account_id | ID da conta do canal no SymplesDesk (vem no payload dos webhooks do canal). |
symplesdesk_whatsapp_id | ID do canal de WhatsApp no SymplesDesk (ex.: '139'). É por ele que o Fluxo 1 descobre de qual médico é o lead. |
symplesdesk_base_url | Sempre 'https://serv2api.simplesdesk.com.br' (base da API externa). |
symplesdesk_api_id | ID (uuid) da configuração de API criada no SymplesDesk para este canal (tela APIs → a config de fechamento de conversa). Usado pelo Fluxo 2. |
symplesdesk_token_secret_name | Nome da variável de ambiente que guarda o token JWT dessa config — padrão 'SYMPLESDESK_TOKEN_SIGLA'. O token em si nunca vai no banco (só o nome da variável). |
pipedrive_owner_id | ID do usuário do Pipedrive que será o dono dos deals criados (Configurações → Usuários). |
pipedrive_pipeline_id / pipedrive_stage_id | ID do funil de Leads do médico e da etapa de entrada (aparecem na URL do Pipedrive ao abrir o funil/etapa). |
pipedrive_label_id | ID da etiqueta aplicada à pessoa (produção usa a etiqueta "WhatsApp"). Pode ser NULL se não houver. |
is_active | true para ligar o canal; false desliga sem apagar a linha. |
public.integration_pipelinesTrês linhas por médico — Leads (o funil onde o Fluxo 1 cria), Consulta e Procedimentos (funis que os Fluxos 2 e 3 rastreiam). Só o funil de Leads precisa de etapa:
INSERT INTO integration_pipelines
(doctor_code, pipeline_type, pipedrive_pipeline_id, pipedrive_stage_id, is_active)
VALUES
('SIGLA', 'leads', 'ID_FUNIL_LEADS', 'ID_ETAPA_ENTRADA', true),
('SIGLA', 'consulta', 'ID_FUNIL_CONSULTA', NULL, true),
('SIGLA', 'procedimentos', 'ID_FUNIL_PROCEDIMENTOS', NULL, true);
public.integration_pipeline_stepsSem esta tradução o card não acompanha o deal: o movimento é ignorado com registro, em silêncio. Uma linha por etapa, em cada funil que o médico novo vai usar (ver Mapa das etapas):
INSERT INTO integration_pipeline_steps
(doctor_code, pipeline_type, step_name, symplesdesk_step_id, pipedrive_stage_id, step_order, is_active)
VALUES
-- funil de Leads (SDR)
('SIGLA', 'leads', 'Cliente Potencial', '52', 'ID_ETAPA_ENTRADA', 1, true),
('SIGLA', 'leads', 'Em conversa', '50', 'ID_ETAPA_CONVERSA', 2, true),
('SIGLA', 'leads', 'Aguardando Pagamento', '48', 'ID_ETAPA_PAGAMENTO', 3, true),
-- funil de Consulta (Concierge)
('SIGLA', 'consulta', 'Consulta Agendada', '57', 'ID_ETAPA_AGENDADA', 1, true),
('SIGLA', 'consulta', 'Reagendamento', '58', 'ID_ETAPA_REAGENDA', 2, true),
('SIGLA', 'consulta', 'Consulta Confirmada', '59', 'ID_ETAPA_CONFIRMADA', 3, true);
As etapas do quadro (52, 50, 48 em Leads; 57, 58, 59 em Consulta) são as mesmas para
todos os médicos — o quadro é único. O que muda por médico são os ids das etapas do funil no
Pipedrive. A coluna pipeline_type é o que impede os números de se confundirem
entre um funil e outro — nunca deixe de preenchê-la.
No .env do serviço, crie a variável com o mesmo nome usado em
symplesdesk_token_secret_name, com o token JWT gerado na config de API do canal:
SYMPLESDESK_TOKEN_SIGLA=<token JWT gerado na tela de APIs do SymplesDesk>
<URL-do-serviço>/webhooks/symplesdesk-newmessage (com o token do ambiente), o
evento de novo atendimento → <URL-do-serviço>/webhooks/symplesdesk, o de
Oportunidade → <URL-do-serviço>/webhooks/oportunidade e o de
Atendimento finalizado → <URL-do-serviço>/webhooks/atendimento-finalizado.A configuração de canais e funis é carregada na inicialização (fica em memória para não consultar o banco a cada mensagem). Depois de inserir as linhas, reinicie o serviço para o canal novo valer.
telefone - SIGLA. Qualquer
erro fica registrado em integration_events.Se você vai integrar com a API, esta é a sua seção. Toda interação de fora é por HTTP — ninguém além do administrador acessa o banco.
| Endpoint | Autenticação | O que faz |
|---|---|---|
GET /health | — | Confirma que o serviço está no ar ({"ok":true}) |
POST /webhooks/symplesdesk | — (limitação da tela antiga) | Fluxo 1: entrada de lead |
POST /webhooks/pipedrive | Basic Auth | Fluxos 2, 3 e 5-B: status, captura e movimentação de deals + sync de nome do contato (person) |
POST /webhooks/symplesdesk-newmessage | Token no header | Fluxo 4: ingestão de cada mensagem |
POST /webhooks/oportunidade | Token no header | Cards: movimento, ganho, perda e renome feitos no quadro |
POST /webhooks/atendimento-finalizado | Token no header | Registra que alguém encerrou o atendimento na mão |
GET /docs | — | Esta página |
Todos os webhooks respondem 200 mesmo em erro interno (o erro fica registrado no banco,
em integration_events): se respondessem erro, o remetente reenviaria o evento e criaria
duplicatas. A exceção é autenticação inválida, que responde 401 — ou 404 nas duas
rotas mais novas, que não confirmam sequer a própria existência para quem não tem o token.
Cada resposta traz um outcome dizendo o que aconteceu (ex.: created,
duplicate_ticket, active_session_exists, ignored_event) — a
lista completa por endpoint está no docs/API.md do repositório.
.env completo → gera a URL pública fixa da API.db/schema.sql + db/seed_prod.sql
(limpo — os dados atuais são de teste).npm run backfill, com --dry-run antes para conferir os números).integration_events e os
alertas do Discord.