Webhooks de saída
Os webhooks enviam eventos do Manisma para um endpoint HTTPS público da sua organização.
Configurar o endpoint
Abra como Admin Gestão → Integrações, crie um endpoint e escolha nome, URL e eventos. O signing secret é mostrado uma única vez. Guarde-o em segurança. Em caso de rotação, o novo secret passa a ser válido a partir da gravação.
O URL tem de:
- utilizar HTTPS;
- estar acessível publicamente via DNS;
- utilizar a porta 443 ou 8443;
- não conter nome de utilizador/palavra-passe no URL;
- não apontar para localhost, IP privado, link-local ou metadados da cloud.
Utilize Teste para enviar um ping antes de ativar eventos reais.
Eventos
| Evento | Acionador |
|---|---|
ping |
Teste manual do endpoint. |
client.created |
Cliente criado. |
client.updated |
Cliente alterado. |
job.created |
Ordem de serviço criada. |
job.planned |
Estado da ordem alterado para planeada. |
job.completed |
Estado da ordem alterado para concluída. |
job.cancelled |
Estado da ordem alterado para cancelada. |
approval.approved |
O cliente aprovou uma proposta. |
approval.declined |
O cliente recusou uma proposta. |
workorder.completed |
Dados da ordem de trabalho pronta para faturação na conclusão. |
Uma alteração noutro campo de uma ordem já concluída não volta a enviar job.completed; é o próprio estado que tem de 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 body em bruto exato.
Verifique o HMAC antes de fazer parsing do JSON e utilize 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
Os eventos de cliente contêm id, customer_number, name, client_type, email e phone. As notas internas do cliente estão ausentes.
Os eventos de ordem de serviço contêm id/número da ordem, título, estado, prioridade, planeamento e objetos compactos para cliente, morada de serviço e técnico principal. Uma ordem não atribuída ou sem morada utiliza null.
Os eventos de aprovação contêm id da aprovação, job_id, estado, janela escolhida e hora da resposta. chosen_slot pode ser uma string JSON ou texto livre mais antigo; faça parsing defensivamente.
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 é construído, não necessariamente um preço fixado historicamente.
Entrega e retries
Os webhooks são at least once. Torne o seu recetor idempotente em X-Manisma-Delivery. Uma receção bem-sucedida é qualquer resposta 2xx. Os redirecionamentos não são seguidos e cada tentativa tem um timeout de 10 segundos.
Em caso de erro temporário, há no máximo três tentativas: imediata, após 60 segundos e mais 300 segundos depois. O mesmo delivery-id é mantido. Os retries correm, de momento, no processo de serviço e não sobrevivem a um redeploy; por isso, verifique também o registo de entregas em Integrações quanto a linhas pending pendentes.
Guarde primeiro o delivery-id na mesma transação que o seu processamento e responda depois rapidamente com 2xx. Mova o processamento lento para a sua própria fila.