Documentation menu

Webhooks de saída

Webhooks enviam eventos do Manisma para um endpoint HTTPS público da sua organização.

Configurar o endpoint

Abra como Admin Administração → Integrações, crie um endpoint e escolha nome, URL e eventos. O signing secret é exibido uma única vez. Guarde-o com segurança. Na rotação, o novo secret passa a valer a partir do salvamento.

A URL deve:

  • usar HTTPS;
  • estar acessível publicamente via DNS;
  • usar a porta 443 ou 8443;
  • não conter usuário/senha na URL;
  • não apontar para localhost, IP privado, link-local ou metadados de cloud.

Use Teste para enviar um ping antes de ativar eventos reais.

Eventos

Evento Gatilho
ping Teste manual do endpoint.
client.created Cliente criado.
client.updated Cliente alterado.
job.created Ordem de serviço criada.
job.planned Status da ordem de serviço alterado para planejada.
job.completed Status da ordem de serviço alterado para concluída.
job.cancelled Status da ordem de serviço alterado para cancelada.
approval.approved Cliente aprovou uma proposta.
approval.declined Cliente recusou uma proposta.
workorder.completed Dados da ordem de execução prontos para faturamento na conclusão.

Uma alteração em outro campo de uma ordem de serviço já concluída não reenvia job.completed; é o próprio status que precisa mudar.

Envelope e cabeçalhos

{
  "event": "job.completed",
  "version": 1,
  "tenant_id": "tenant-uuid",
  "occurred_at": "2026-07-13T09:15:00.000Z",
  "data": {}
}

Cabeçalhos:

  • X-Manisma-Event: nome do evento;
  • X-Manisma-Delivery: id estável desta entrega lógica;
  • X-Manisma-Signature: sha256=<hex HMAC-SHA256> sobre o corpo bruto exato.

Verifique o HMAC antes de fazer o parse do JSON e use uma comparação timing-safe:

import { createHmac, timingSafeEqual } from 'node:crypto'

export function verify(rawBody, header, secret) {
  if (!header?.startsWith('sha256=')) return false
  const expected = createHmac('sha256', secret).update(rawBody).digest('hex')
  const supplied = header.slice(7)
  const a = Buffer.from(expected, 'hex')
  const b = Buffer.from(supplied, 'hex')
  return a.length === b.length && timingSafeEqual(a, b)
}

Payloads

Eventos de cliente contêm id, customer_number, name, client_type, email e phone. Notas internas do cliente não são incluídas.

Eventos de ordem de serviço contêm id/número da ordem de serviço, título, status, prioridade, planejamento e objetos compactos para cliente, endereço de serviço e técnico principal. Uma ordem de serviço não atribuída ou sem endereço usa null.

Eventos de aprovação contêm id da aprovação, job_id, status, janela escolhida e horário da resposta. chosen_slot pode ser uma string JSON ou texto livre antigo; faça o parse de forma defensiva.

workorder.completed contém:

  • ordem de serviço e cliente compactos;
  • a visita concluída mais recente, se disponível;
  • materiais utilizados com SKU, preço de venda, quantidade e total da linha;
  • duração do trabalho;
  • total de materiais em EUR.

O preço do material é o preço de venda no momento em que o payload do webhook é montado, não necessariamente um preço fixado historicamente.

Entrega e retries

Webhooks são at least once. Torne o seu receptor idempotente com base em X-Manisma-Delivery. Uma resposta bem-sucedida é qualquer resposta 2xx. Redirects não são seguidos e cada tentativa tem um timeout de 10 segundos.

Em caso de falha temporária, há no máximo três tentativas: imediata, após 60 segundos e novamente 300 segundos depois. O mesmo id de entrega é mantido. Os retries atualmente rodam no processo de serviço e não sobrevivem a um redeploy; por isso, verifique também o log de entrega em Integrações para entradas pending pendentes.

Guarde primeiro o id de entrega na mesma transação do seu processamento e responda rapidamente com 2xx em seguida. Mova o processamento lento para a sua própria fila.