Webhooks
Receba avisos da OctoSolve no seu sistema quando algo acontecer, com assinatura e reenvio automático.
Webhooks
Com a API você pergunta para a OctoSolve. Com webhook, a OctoSolve avisa você: toda vez que algo acontece na sua conta, mandamos uma requisição POST para o endereço que você cadastrar.
Serve para coisas como registrar o agendamento no seu sistema, avisar o time num canal interno quando cair um lead, ou disparar uma automação no Make ou no n8n quando um pagamento for confirmado.
Você cadastra os endereços em Configurações, aba Integrações.
Eventos disponíveis
Cada endereço cadastrado escolhe quais eventos quer receber. Não é tudo ou nada.
| Evento | Quando dispara |
|---|---|
contact.created | Um contato novo foi criado |
contact.updated | Um contato foi alterado |
appointment.created | Um agendamento foi criado |
appointment.updated | Um agendamento mudou data, duração, profissional, serviço, valor ou status sem cancelamento |
appointment.cancelled | Um agendamento foi cancelado por qualquer fluxo |
payment.confirmed | Um pagamento foi confirmado |
conversation.handoff | Uma conversa passou do agente para uma pessoa do seu time |
Mensagens de conversa não geram evento nesta versão, de propósito: o volume é alto e o conteúdo é sensível.
Formato do envio
Todo evento chega com a mesma casca:
O data muda conforme o tipo. Campos podem vir null.
contact.created e contact.updated
Mesmo formato do contato na API: id, telefone, nome, email, cidade, data_nascimento, observacoes, tags, status, ig_username, telegram_username, updated_at.
O telefone pode vir null: contato que chegou pelo Instagram ou pelo Telegram se identifica por usuário.
O CPF não vai no evento, de propósito. O aviso sai da nossa casa e entra no seu sistema, e no caminho ele passa por lugares que ninguém controla depois: o log do seu servidor, o log do seu gateway e ferramentas de integração no meio (Make, n8n, Zapier). O CPF é do cliente final, não da sua empresa, então quanto menos lugares ele visitar, menor o estrago se um desses lugares vazar.
Se você precisa do CPF, ele está na API. Pegue o id que veio no evento e consulte:
Esse caminho é diferente do aviso: a consulta sai da sua chave, fica registrada, e você escolhe a hora. Vale a mesma ideia para qualquer dado que não esteja no evento.
appointment.created, appointment.updated e appointment.cancelled
| Campo | Tipo |
|---|---|
id | uuid |
contact_id | uuid |
data_hora | datetime |
status | string |
duracao_minutos | int ou null |
servico_id | uuid ou null |
profissional_id | uuid ou null |
valor | número ou null |
pago | booleano ou null |
motivo_cancelamento | string ou null |
created_at | datetime ou null |
appointment.updated é enviado quando muda data_hora, duracao_minutos, profissional_id, servico_id, valor ou status, desde que o novo status não seja um cancelamento.
Alterar somente notas não envia webhook. A nota não faz parte do payload, de propósito: é texto livre do dono e pode conter dado sensível, inclusive clínico. Enviar um evento sem mostrar o que mudou também criaria ruído para a integração.
appointment.cancelled é enviado em qualquer cancelamento, inclusive no cancelamento automático por pagamento não feito. Os estados cancelado, cancelado_por_nao_pagamento, cancelado_estornado e cancelado_lgpd pertencem a essa família.
payment.confirmed
| Campo | Tipo |
|---|---|
id | uuid |
contact_id | uuid ou null |
appointment_id | uuid ou null |
valor_brl | número |
tipo | string |
status | string |
paid_at | datetime ou null |
created_at | datetime |
conversation.handoff
| Campo | Tipo |
|---|---|
conversation_id | uuid |
contact_id | uuid |
canal | string |
motivo | string |
ocorrido_em | datetime |
Nenhum trecho da conversa é enviado.
Cabeçalhos
| Cabeçalho | Para que serve |
|---|---|
X-Octo-Event | Tipo do evento |
X-Octo-Timestamp | Momento do envio, em segundos Unix |
X-Octo-Signature | Assinatura no formato sha256=<hex> |
X-Octo-Delivery | Identificador desta tentativa de envio |
Verificar a assinatura
Verifique sempre. Sem isso, qualquer um que descubra a sua URL pode fingir ser a OctoSolve.
A assinatura é um HMAC SHA256 do texto {timestamp}.{corpo}, usando o secret do endereço como chave. O corpo é o texto cru recebido, antes de virar objeto.
Rejeite envios com X-Octo-Timestamp de mais de 5 minutos atrás, para bloquear reenvio malicioso.
Python
Node
No Express, guarde o corpo cru com express.json({ verify: (req, _res, buf) => { req.rawBody = buf; } }). Se você assinar em cima do JSON já convertido e convertido de volta, a assinatura não vai bater.
O mesmo evento pode chegar duas vezes
Esta é a parte mais importante desta página.
A entrega é pelo menos uma vez. Em falha de rede ou reenvio, o mesmo evento pode chegar repetido. O seu sistema precisa aguentar isso.
Como resolver: guarde o campo id do evento (o evt_...) e ignore o que você já processou. Esse id é estável, inclusive quando você clica em Reenviar no painel.
A ordem também não é garantida. Uma tentativa que falhou e foi reenviada pode chegar depois de um evento mais novo. Se a ordem importa para você, use o created_at do evento, não a ordem de chegada.
Se o seu endereço não responder
Consideramos entregue quando a resposta é 2xx. Qualquer outra coisa é falha.
Tentamos de novo, com espaço crescente entre as tentativas:
| Tentativa | Depois de |
|---|---|
| 2ª | 1 minuto |
| 3ª | 5 minutos |
| 4ª | 30 minutos |
| 5ª | 2 horas |
| 6ª | 6 horas |
| última | 24 horas |
Algumas respostas não são tentadas de novo, porque insistir não adiantaria:
3xx(redirecionamento): não seguimos redirecionamento, por segurança. Cadastre o endereço final.4xx, com exceção de408e429: quer dizer que o endereço existe mas recusou. Corrija e reenvie pelo painel.
Respondemos ao Retry-After quando ele pede um tempo maior que o nosso, até o limite de 24 horas.
Endereço desligado automaticamente
Depois de 20 tentativas seguidas com falha, desligamos o endereço e avisamos você no painel. É proteção contra endereço abandonado gerando fila infinita.
Ele não é apagado: você conserta o seu lado e clica em reativar.
Requisitos do endereço
| Regra | Motivo |
|---|---|
Precisa ser https:// na porta 443 | Evento trafega com dado do seu cliente |
| Não pode apontar para rede interna | Proteção contra uso da OctoSolve para atacar redes privadas |
| Não pode ter usuário e senha na URL | Formato usado para enganar leitor humano |
| Responda rápido, em até 10 segundos | Passou disso, tratamos como falha e tentamos de novo |
Responda 200 assim que receber e processe depois, em segundo plano. Não deixe o processamento inteiro dentro da requisição.
Limites por plano
| Plano | Webhooks | Endereços |
|---|---|---|
| Starter | não | 0 |
| Plus | sim | 5 |
| Pro | sim | 10 |
| Enterprise | sim | sob medida |
O limite de requisições por minuto da página de Limites vale para a API. Envio de webhook é nosso, não consome a sua cota.
Receita para Make ou n8n
- Crie um cenário (Make) ou workflow (n8n) começando com Webhook ou Webhook Trigger.
- Copie a URL que a ferramenta gerar.
- Na OctoSolve, vá em Configurações, aba Integrações, e cadastre essa URL escolhendo os eventos.
- Copie o secret que aparece uma única vez e guarde.
- No fluxo, adicione um passo que confira a assinatura (as duas ferramentas têm nó de código; use o exemplo em Node acima).
- Adicione um passo que ignore evento com
idjá visto, guardando os ids num Data Store (Make) ou numa tabela (n8n). - Só depois disso, ligue o passo que faz o trabalho de verdade.
Os passos 5 e 6 parecem burocracia, mas são o que separa uma automação que funciona de uma que duplica cobrança no fim de semana.
Testar
Enquanto você monta, aponte para um coletor público (como o webhook.site) para ver o formato real do evento chegando. Depois troque para o seu endereço definitivo e rotacione o secret, porque o antigo passou por um serviço de terceiro.