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.