Documentation menu

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ă.