Webhook-uri emise
Webhook-urile trimit evenimente Manisma către un endpoint HTTPS public al organizației dumneavoastră.
Configurarea endpoint-ului
Deschideți ca Admin Administrare → Integrări, creați un endpoint și alegeți numele, URL-ul și evenimentele. Secretul de semnare este afișat o singură dată. Păstrați-l în siguranță. La rotire, noul secret este valabil de la salvare.
URL-ul trebuie:
- să utilizeze HTTPS;
- să fie accesibil public prin DNS;
- să utilizeze portul 443 sau 8443;
- să nu conțină nume de utilizator/parolă în URL;
- să nu trimită către localhost, IP privat, link-local sau metadata de cloud.
Utilizați Test pentru a trimite un ping înainte de a activa evenimente reale.
Evenimente
| Eveniment | Declanșator |
|---|---|
ping |
Test manual al endpoint-ului. |
client.created |
Client creat. |
client.updated |
Client modificat. |
job.created |
Sarcină creată. |
job.planned |
Starea sarcinii schimbată în planificată. |
job.completed |
Starea sarcinii schimbată în finalizată. |
job.cancelled |
Starea sarcinii schimbată în anulată. |
approval.approved |
Clientul a aprobat o propunere. |
approval.declined |
Clientul a respins o propunere. |
workorder.completed |
Date de bon de lucru pregătite pentru facturare la finalizare. |
O modificare a altui câmp al unei sarcini deja finalizate nu retrimite job.completed; trebuie să se schimbe chiar starea.
Envelope și headere
{
"event": "job.completed",
"version": 1,
"tenant_id": "tenant-uuid",
"occurred_at": "2026-07-13T09:15:00.000Z",
"data": {}
}
Headere:
X-Manisma-Event: numele evenimentului;X-Manisma-Delivery: id stabil al acestei livrări logice;X-Manisma-Signature:sha256=<hex HMAC-SHA256>peste exact body-ul brut.
Verificați HMAC înainte de parsarea JSON și utilizați o comparație 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-uri
Evenimentele de client conțin id, customer_number, name, client_type, email și phone. Notele interne ale clientului lipsesc.
Evenimentele de sarcină conțin id/numărul sarcinii, titlu, stare, prioritate, planificare și obiecte compacte pentru client, adresă de service și tehnician principal. O sarcină neatribuită sau fără adresă utilizează null.
Evenimentele de aprobare conțin id-ul aprobării, job_id, stare, slot ales și ora răspunsului. chosen_slot poate fi un șir JSON sau text liber mai vechi; parsați defensiv.
workorder.completed conține:
- sarcină și client compacte;
- cea mai recentă vizită finalizată, dacă este disponibilă;
- materialele utilizate cu SKU, preț de vânzare, cantitate și valoarea liniei;
- durata lucrului;
- total materiale în EUR.
Prețul materialului este prețul de vânzare din momentul construirii payload-ului webhook, nu neapărat un preț istoric fixat.
Livrare și retry-uri
Webhook-urile sunt at least once. Faceți receptorul idempotent pe `X-Manisma-Delivery**. O primire reușită este orice răspuns 2xx. Redirectările nu sunt urmate și fiecare încercare are un timeout de 10 secunde.
În caz de eroare temporară, există maximum trei încercări: imediat, după 60 de secunde și încă o dată după 300 de secunde. Același delivery-id este păstrat. În prezent, retry-urile rulează în procesul de serviciu și nu supraviețuiesc unui redeploy; verificați, așadar, și jurnalul de livrare din Integrări pentru rânduri pending rămase.
Salvați mai întâi delivery-id-ul în aceeași tranzacție cu procesarea dumneavoastră și răspundeți apoi rapid cu 2xx. Mutați procesarea lentă în propria coadă.