Documentation menu

Webhook in uscita

I webhook inviano eventi di Manisma a un endpoint HTTPS pubblico della tua organizzazione.

Configurare l’endpoint

Apri come Admin Gestione → Integrazioni, crea un endpoint e scegli nome, URL ed eventi. Il signing secret viene mostrato una sola volta. Conservalo in modo sicuro. In caso di rotazione, il nuovo secret è valido dal salvataggio.

L’URL deve:

  • usare HTTPS;
  • essere raggiungibile pubblicamente tramite DNS;
  • usare la porta 443 o 8443;
  • non contenere nome utente/password nell’URL;
  • non puntare a localhost, IP privato, link-local o metadata del cloud.

Usa Test per inviare un ping prima di attivare eventi reali.

Eventi

Evento Trigger
ping Test endpoint manuale.
client.created Cliente creato.
client.updated Cliente modificato.
job.created Commessa creata.
job.planned Stato commessa cambiato in pianificata.
job.completed Stato commessa cambiato in completata.
job.cancelled Stato commessa cambiato in annullata.
approval.approved Il cliente ha approvato una proposta.
approval.declined Il cliente ha rifiutato una proposta.
workorder.completed Dati della commessa di lavoro pronti per la fatturazione al completamento.

Una modifica a un altro campo di una commessa già completata non invia di nuovo `job.completed}; lo stato stesso deve cambiare.

Envelope e intestazioni

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

Intestazioni:

  • X-Manisma-Event: nome evento;
  • X-Manisma-Delivery: id stabile di questa consegna logica;
  • X-Manisma-Signature: sha256=<hex HMAC-SHA256> sul body grezzo esatto.

Verifica l’HMAC prima del parsing JSON e usa un confronto 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)
}

Payload

Gli eventi cliente contengono id, customer_number, name, client_type, email e phone. Le note interne del cliente sono assenti.

Gli eventi commessa contengono id/numero commessa, titolo, stato, priorità, pianificazione e oggetti compatti per cliente, indirizzo di servizio e tecnico principale. Una commessa non assegnata o senza indirizzo usa null.

Gli eventi di approvazione contengono id approvazione, job_id, stato, fascia scelta e orario di risposta. chosen_slot può essere una stringa JSON o testo libero più vecchio; effettua il parsing in modo difensivo.

workorder.completed contiene:

  • commessa e cliente compatti;
  • la visita completata più recente, se disponibile;
  • materiali utilizzati con SKU, prezzo di vendita, quantità e importo di riga;
  • durata del lavoro;
  • totale materiali in EUR.

Il prezzo del materiale è il prezzo di vendita nel momento in cui il payload del webhook viene costruito, non necessariamente un prezzo fissato storicamente.

Consegna e retry

I webhook sono at least once. Rendi idempotente il tuo ricevitore su X-Manisma-Delivery. Una ricezione riuscita è qualsiasi risposta 2xx. I redirect non vengono seguiti e ogni tentativo ha un timeout di 10 secondi.

In caso di errore temporaneo, ci sono al massimo tre tentativi: immediato, dopo 60 secondi e altri 300 secondi dopo. Lo stesso delivery-id viene mantenuto. I retry attualmente vengono eseguiti nel processo di servizio e non sopravvivono a un redeploy; controlla pertanto anche il log delle consegne in Integrazioni per le righe pending rimaste indietro.

Salva prima il delivery-id nella stessa transazione della tua elaborazione e rispondi poi rapidamente con 2xx. Sposta l’elaborazione lenta nella tua coda.