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.