Skip to content

Webhooks

Webhooks avisam um sistema externo sempre que um agendamento é criado ou alterado no xLumen. A cada evento, o xLumen envia uma requisição HTTP para a URL cadastrada, com os dados do agendamento no corpo, e o sistema receptor não precisa consultar o xLumen periodicamente para saber o que mudou.

Os webhooks são complementares à API: o envio traz os dados principais do agendamento e, quando o receptor precisar de algo que não vem no corpo, consulta a API.

Os webhooks são disparados a partir de eventos do módulo de Agenda. No cadastro do webhook, o administrador escolhe quais eventos aquele webhook recebe; um mesmo webhook pode estar inscrito em um ou nos dois.

EventoQuando é enviado
AGENDAMENTO_CRIADOum agendamento é criado, seja pelo painel, pela API, pelo agente de IA ou como o novo agendamento gerado por um reagendamento
AGENDAMENTO_ATUALIZADOum agendamento é cancelado, concluído ou reagendado, ou tem título, descrição, valor ou a notificação por e-mail alterados

Para saber o que mudou em um AGENDAMENTO_ATUALIZADO, compare statusAnterior com status no corpo:

  • ATIVO → CANCELADO: cancelamento
  • ATIVO → CONCLUIDO: conclusão
  • ATIVO → REAGENDADO: reagendamento
  • ATIVO → ATIVO: edição de dados, sem mudança de status

O reagendamento gera dois envios:

  1. AGENDAMENTO_ATUALIZADO do agendamento original, com status REAGENDADO e reagendadoPara apontando para o novo agendamento
  2. AGENDAMENTO_CRIADO do novo agendamento, com reagendamentoDe apontando para o original

Alterar os valores de campos personalizados de agendamento não dispara webhook.

Cada webhook é cadastrado com:

  • nome ou descrição para identificação na lista e no log
  • URL de destino, que recebe as requisições
  • eventos que disparam o webhook
  • secret opcional, usado para assinar as requisições

O secret pode ser digitado ou criado pelo botão Gerar no cadastro, e só pode ser visto e copiado nesse momento: guarde o valor no sistema receptor antes de salvar. Depois de salvo, o secret não é exibido de novo. Se ele se perder, troque por um novo no cadastro do webhook e atualize o receptor.

Cada envio é uma requisição POST com corpo JSON para a URL cadastrada, com estes headers:

HeaderConteúdo
content-typeapplication/json
x-xlumen-eventnome do evento: AGENDAMENTO_CRIADO ou AGENDAMENTO_ATUALIZADO
x-xlumen-tenant-ididentificador numérico da conta no xLumen
x-xlumen-signatureassinatura HMAC-SHA256 do corpo, em hexadecimal; enviado só quando o webhook tem secret

Os dois eventos usam o mesmo formato. Toda chave é sempre enviada; quando não há valor, ela chega como null. Datas vêm em ISO 8601, em UTC.

CampoO que é
versionversão do formato do corpo; hoje, "v1"
eventonome do evento, igual ao header x-xlumen-event
dominiodomínio da conta no xLumen
occurredAtmomento em que o envio foi gerado
data.agendamento.idID do agendamento, o mesmo usado na API
data.agendamento.titulotítulo do agendamento
data.agendamento.dataInicialinício do agendamento
data.agendamento.dataFinalfim do agendamento
data.agendamento.tempoEmMinutosduração em minutos
data.agendamento.valorvalor do agendamento como texto decimal (ex.: "150.00"), ou null
data.agendamento.statusstatus atual: ATIVO, CANCELADO, REAGENDADO ou CONCLUIDO
data.agendamento.statusAnteriorstatus antes da alteração, com os mesmos valores de status; null em AGENDAMENTO_CRIADO
data.agendamento.descricaodescrição do agendamento
data.agendamento.observacoesobservações do agendamento
data.agendamento.motivoCancelamentomotivo informado no cancelamento
data.agendamento.agendadoPeloAgentetrue quando o agendamento foi criado pelo agente de IA
data.agendamento.canceladoPeloAgentetrue quando o agendamento foi cancelado pelo agente de IA
data.agendamento.agendadoPeloUsuariousuário que criou o agendamento, como { id, nome }
data.agendamento.canceladoPeloUsuariousuário que cancelou o agendamento, como { id, nome }
data.agendamento.prestadorprestador do agendamento, como { id, nome }
data.agendamento.clientecliente do agendamento, como { id, nome }
data.agendamento.servicoserviço do agendamento, como { id, nome }
data.agendamento.reagendamentoDeagendamento original, quando este foi gerado por um reagendamento, como { id, dataInicial, dataFinal, status }
data.agendamento.reagendadoParanovo agendamento, quando este foi reagendado, como { id, dataInicial, dataFinal, status }
data.agendamento.createdAtdata de criação do registro
data.agendamento.updatedAtdata da última alteração do registro

O corpo não traz o idExterno do agendamento, os valores de campos personalizados nem o telefone ou o e-mail do cliente. Para esses dados, consulte a API com o id recebido.

Valores ilustrativos:

{
"version": "v1",
"evento": "AGENDAMENTO_CRIADO",
"dominio": "minhaclinica",
"occurredAt": "2026-10-13T14:30:02.118Z",
"data": {
"agendamento": {
"id": 4821,
"titulo": "Consulta de avaliação",
"dataInicial": "2026-10-20T13:00:00.000Z",
"dataFinal": "2026-10-20T13:50:00.000Z",
"tempoEmMinutos": 50,
"valor": "150.00",
"status": "ATIVO",
"statusAnterior": null,
"descricao": null,
"observacoes": "Primeira consulta",
"motivoCancelamento": null,
"agendadoPeloAgente": false,
"canceladoPeloAgente": false,
"agendadoPeloUsuario": { "id": 12, "nome": "Ana Souza" },
"canceladoPeloUsuario": null,
"prestador": { "id": 3, "nome": "Dra. Beatriz Lima" },
"cliente": { "id": 905, "nome": "Carlos Pereira" },
"servico": { "id": 7, "nome": "Avaliação" },
"reagendamentoDe": null,
"reagendadoPara": null,
"createdAt": "2026-10-13T14:30:01.954Z",
"updatedAt": "2026-10-13T14:30:01.954Z"
}
}
}

Exemplo: AGENDAMENTO_ATUALIZADO (cancelamento)

Section titled “Exemplo: AGENDAMENTO_ATUALIZADO (cancelamento)”

Valores ilustrativos:

{
"version": "v1",
"evento": "AGENDAMENTO_ATUALIZADO",
"dominio": "minhaclinica",
"occurredAt": "2026-10-15T18:12:45.507Z",
"data": {
"agendamento": {
"id": 4821,
"titulo": "Consulta de avaliação",
"dataInicial": "2026-10-20T13:00:00.000Z",
"dataFinal": "2026-10-20T13:50:00.000Z",
"tempoEmMinutos": 50,
"valor": "150.00",
"status": "CANCELADO",
"statusAnterior": "ATIVO",
"descricao": null,
"observacoes": "Primeira consulta",
"motivoCancelamento": "Cliente pediu para remarcar no próximo mês",
"agendadoPeloAgente": false,
"canceladoPeloAgente": true,
"agendadoPeloUsuario": { "id": 12, "nome": "Ana Souza" },
"canceladoPeloUsuario": null,
"prestador": { "id": 3, "nome": "Dra. Beatriz Lima" },
"cliente": { "id": 905, "nome": "Carlos Pereira" },
"servico": { "id": 7, "nome": "Avaliação" },
"reagendamentoDe": null,
"reagendadoPara": null,
"createdAt": "2026-10-13T14:30:01.954Z",
"updatedAt": "2026-10-15T18:12:45.391Z"
}
}
}

Quando o webhook tem secret, cada requisição traz o header x-xlumen-signature. Para confirmar que ela veio do xLumen e que o corpo não foi alterado:

  1. Leia o corpo bruto da requisição, exatamente como chegou, antes de qualquer conversão para objeto. Não reserialize o JSON: qualquer diferença de espaço ou ordem de chaves muda a assinatura.
  2. Calcule o HMAC-SHA256 desse corpo usando o secret como chave.
  3. Converta o resultado para hexadecimal minúsculo (64 caracteres).
  4. Compare com o valor do header x-xlumen-signature usando comparação em tempo constante.
  5. Se não conferir, ou se o header não vier, rejeite a requisição.

Exemplo em Node.js:

const crypto = require('node:crypto');
function assinaturaValida(corpoBruto, assinaturaRecebida, secret) {
const esperada = crypto.createHmac('sha256', secret).update(corpoBruto).digest('hex');
const a = Buffer.from(esperada, 'utf8');
const b = Buffer.from(String(assinaturaRecebida || ''), 'utf8');
return a.length === b.length && crypto.timingSafeEqual(a, b);
}

corpoBruto é o corpo recebido como Buffer ou texto, sem passar por JSON.parse. No Express, por exemplo, use express.raw({ type: 'application/json' }) na rota do webhook e faça o JSON.parse só depois de validar.

  • O envio é considerado bem-sucedido quando o receptor responde com status HTTP 2xx em até 10 segundos. Qualquer outra resposta, erro de conexão ou tempo esgotado conta como falha.
  • Redirecionamentos (3xx) não são seguidos e contam como falha. Cadastre a URL final do receptor.
  • Não há reenvio: um envio que falhou não é repetido. Para recuperar o que se perdeu, consulte o log ou a API.
  • Após 10 falhas seguidas, o webhook é desativado e deixa de receber envios. Um envio bem-sucedido zera essa contagem. Para voltar a receber, o administrador reativa o webhook no cadastro.
  • Não há garantia de ordem entre envios. Use occurredAt, updatedAt e status para tratar eventos que cheguem fora de ordem.

Para não estourar o tempo de resposta, o receptor pode responder 2xx assim que validar a assinatura e processar o conteúdo depois.

O xLumen mantém um log com o histórico de envios de cada webhook, visível para administradores. A lista mostra, para cada envio:

  • o evento que originou o disparo
  • o status do envio (sucesso ou erro)
  • o código HTTP retornado pelo receptor
  • a data do envio

Ao abrir um envio, o detalhe traz o payload enviado, a resposta do receptor e a mensagem de erro, quando houve falha.

Apenas eventos de agendamento: AGENDAMENTO_CRIADO na criação e AGENDAMENTO_ATUALIZADO em cancelamento, conclusão, reagendamento e edição de dados. Alterar campos personalizados não dispara webhook.

Como sei se um AGENDAMENTO_ATUALIZADO foi um cancelamento?

Section titled “Como sei se um AGENDAMENTO_ATUALIZADO foi um cancelamento?”

Compare statusAnterior com status: ATIVO → CANCELADO indica cancelamento. O motivo, quando informado, vem em motivoCancelamento.

O reagendamento gera um AGENDAMENTO_ATUALIZADO do agendamento original, com status REAGENDADO e reagendadoPara, e um AGENDAMENTO_CRIADO do novo agendamento, com reagendamentoDe. Os dois campos ligam um envio ao outro.

O webhook envia todos os dados do agendamento?

Section titled “O webhook envia todos os dados do agendamento?”

Envia os campos listados em Corpo. idExterno, valores de campos personalizados e contato do cliente não vêm no envio; o receptor consulta a API com o id recebido.

Como saber se o webhook recebido veio mesmo do xLumen?

Section titled “Como saber se o webhook recebido veio mesmo do xLumen?”

Cadastre o webhook com secret e valide o header x-xlumen-signature: calcule o HMAC-SHA256 do corpo bruto com o secret, em hexadecimal, e compare em tempo constante. Sem secret, o envio chega sem assinatura.

Não é possível: depois de salvo, o secret não é exibido de novo. Troque por um novo no cadastro do webhook e atualize o sistema receptor.

O que acontece quando o endpoint receptor fica fora do ar?

Section titled “O que acontece quando o endpoint receptor fica fora do ar?”

O envio que falhou não é repetido. Após 10 falhas seguidas, o webhook é desativado e precisa ser reativado pelo administrador no cadastro.

No log de envios do webhook, visível para administradores, com evento, status, código HTTP, data e, no detalhe, o payload enviado, a resposta do receptor e o erro.