
# Webhook-uri

Webhook-urile permit <span data-t="appName">Your AI Connector</span> să notifice automat celelalte instrumente de afaceri ori de câte ori se întâmplă ceva important — crearea unui contact nou, programarea unei întâlniri, primirea unui mesaj. În loc să verificați manual actualizările, sistemele conectate primesc o notificare instantanee în momentul în care are loc un eveniment.


---

## Ce sunt webhook-urile?

Gândiți-vă la un webhook ca la un mesaj text automat între două aplicații. Când se întâmplă ceva în <span data-t="appName">Your AI Connector</span> (cum ar fi înregistrarea unui contact nou), platforma trimite instantaneu o notificare către un alt sistem ales de dumneavoastră. Furnizați o adresă web (numită „URL webhook”) unde ar trebui trimise aceste notificări — aceasta este furnizată de obicei de CRM-ul, platforma de automatizare sau dezvoltatorul dumneavoastră.

> **Webhook-urile trimit date DOAR în afara <span data-t="appName">Your AI Connector</span>.** Un webhook este o cale cu sens unic *de la* <span data-t="appName">Your AI Connector</span> *către* celelalte instrumente ale tale. **Nu există niciun URL de webhook care să trimită clienți potențiali, contacte sau mesaje ÎN platformă.** Pentru a introduce un client potențial nou — dintr-un formular de pe site, din CRM-ul tău sau din GoHighLevel — sistemul tău efectuează în schimb un **apel API**. Consultă [Acces API](api-access.md) (operațiunea *Create a Contact*) și [Funnel-uri](funnels.md). Singurul lucru de care ai nevoie pentru direcția de intrare este **cheia API**, care se află în propria sa secțiune — consultă [Acces API](api-access.md#generating-your-api-key). Pagina **Webhook-uri** descrisă aici este exclusiv pentru direcția de ieșire.

::: note
**Notă:** Configurarea webhook-urilor implică o anumită configurare tehnică. Dacă nu vă simțiți confortabil cu acest lucru, partajați această pagină cu dezvoltatorul dvs. sau utilizați o platformă de automatizare precum Zapier, Make sau Pabbly, care oferă URL-uri de webhook fără a fi nevoie de programare.
:::


Utilizările comune includ:

- Sincronizarea contactelor noi cu CRM-ul dumneavoastră.
- Declanșarea unui flux de lucru în Zapier, Make sau Pabbly atunci când este aplicată o etichetă.
- Notificarea echipei în Slack atunci când un om este alertat.
- Actualizarea sistemului de calendar atunci când este programată o întâlnire.
- Înregistrarea rezumatelor conversațiilor în baza de date.

---

## Configurarea webhook-urilor

1. În bara laterală din stânga, faceți clic pe **Settings** (pictograma roată).
2. În bara laterală Settings, sub grupul **Integrations**, faceți clic pe **Webhooks**.


Într-un cont în care nu sunt configurate încă webhook-uri, pagina arată astfel:


3. Faceți clic pe **New webhook** (Webhook nou), în dreapta sus. Un formular se va deschide direct în pagină:


4. Completați:
   - **URL Endpoint** — adresa web către care <span data-t="appName">Your AI Connector</span> va trimite notificările despre evenimente. O obțineți de la sistemul extern (CRM, platformă de automatizare sau server personalizat).
   - **Nume** — o etichetă pe care o veți recunoaște ulterior (de exemplu, „Alerte Slack” sau „Sincronizare CRM”). Doar pentru referința dumneavoastră.

> **URL-ul dvs. de webhook trebuie să fie o adresă `https://` accesibilă public.** Adresele `http://` simple, `localhost` sau adresele de rețea privată și adresele interne ale platformei sunt respinse la salvare. Pentru a testa de pe propriul computer, utilizați un tunel public (webhook.site sau ngrok) în loc de localhost.

5. Sub **Evenimente**, faceți clic pe evenimentele pe care doriți să le primească acest webhook — toate cele 22 sunt listate în [Cele 22 de evenimente Webhook](#the-22-webhook-events).
6. *(Opțional)* Activați **Reîncercare livrări eșuate** dacă doriți ca <span data-t="appName">Your AI Connector</span> să continue să încerce în caz de eșec temporar — consultați [Reîncercarea livrărilor eșuate](#retrying-failed-deliveries).
7. Faceți clic pe **Creează webhook**. Acesta apare în lista de sub formular și puteți face clic pe **Test** în rândul său oricând pentru a trimite un payload de probă către endpoint-ul dvs.

> **Permisiune necesară.** Adăugarea, editarea sau testarea webhook-urilor necesită permisiunea „edit” pentru Integrations (membrii echipei cu acces doar pentru vizualizare vor vedea o notificare de tip read-only în locul formularului).

> **Semnarea unui webhook** necesită ca acesta să fie deja salvat — deschideți rândul unui webhook existent pentru a-l edita, iar panoul **Signing secret** va apărea în partea de jos a formularului de editare. O schiță nouă, nesalvată, nu are încă opțiune de semnare — consultați [Signed Payloads](#signed-payloads-verifying-a-webhook-really-came-from-us) mai jos.

---

## Un singur webhook pentru toate conturile clienților tăi (Agenții)

Dacă administrezi o agenție, nu trebuie să recreezi același webhook pentru fiecare cont de client. În contul de agenție, formularul pentru webhook are un comutator suplimentar: **Also fire for all client accounts**. Activează-l și acest webhook va primi și evenimentele care au loc în fiecare cont de client din cadrul agenției tale — un singur endpoint pentru întreaga agenție.

Cum funcționează:

- **Blocul `user` îți indică apartenența evenimentului la un anumit client.** Fiecare notificare conține deja un bloc `user` care identifică contul în care a avut loc evenimentul, astfel încât automatizarea ta să poată direcționa datele per client.
- **Setările proprii ale webhook-ului tău se aplică peste tot.** Evenimentele selectate, secretul de semnare și setarea de reîncercare sunt utilizate și pentru livrările către conturile clienților.
- **Fără livrări duble.** Dacă un cont de client are propriul său webhook care indică către același URL, acesta va fi utilizat în schimb pentru evenimentele acelui cont — același eveniment nu va ajunge niciodată de două ori la același endpoint.
- **Clienții nu îl văd.** Webhook-ul nu apare în pagina de Webhooks a contului clientului, iar clienții nu îl pot dezactiva — este sub controlul tău.
- **Fiabilitatea este monitorizată per cont de client.** Dacă endpoint-ul tău continuă să eșueze, acesta este dezactivat automat doar pentru contul ale cărui livrări au eșuat (vezi [Fiabilitatea Webhook-urilor](#webhook-reliability)), nu pentru întreaga agenție deodată.

Comutatorul apare doar în conturile de agenție. Configurarea acestuia prin API este, de asemenea, acceptată — vezi câmpul `apply_to_sub_accounts` din [Webhooks API](../api/webhooks.md#one-subscription-for-all-client-accounts-agencies).

---

## Evenimente de declanșare disponibile

Puteți activa sau dezactiva fiecare dintre cele 22 de evenimente webhook în mod independent. Când un eveniment este declanșat, <span data-t="appName">Your AI Connector</span> trimite o notificare către URL-ul webhook-ului dvs. cu datele relevante. Fiecare eveniment, semnificația acestuia și codul `event` pe care îl introduce în payload sunt listate împreună în [Cele 22 de evenimente Webhook](#the-22-webhook-events) mai jos pe această pagină.

> **Bine de știut:** **Task Created**, **Task Updated** și **Task Completed** sunt complet selectabile și se salvează corect. **Daily Summary Created** este, de asemenea, o adăugare recentă. Consultați [Webhook pentru Task Completed](#task-completed-webhook) mai jos pentru structura acelui payload.

---

## Declanșatoare de webhook bazate pe etichete

`subscribed_to_tags` nu limitează evenimentele unui webhook la o etichetă. Acesta doar restrânge etichetele care produc o notificare de rezumat al conversației. Pentru a primi o solicitare atunci când este aplicată o anumită etichetă, setați un URL de webhook pe acea etichetă în fila **Etichete** a agentului (sau campaniei).

Formularul de webhook în sine nu are un selector de etichete, nici la crearea unui webhook nou, nici la editarea unuia, deci `subscribed_to_tags` poate fi citit sau modificat doar prin [API-ul Webhooks](../api/webhooks.md) sau solicitând asistență.

> **Bine de știut:** editarea unui webhook existent care are o listă `subscribed_to_tags` (redenumirea acestuia, modificarea evenimentelor, activarea reîncercărilor) nu mai șterge acea listă — deoarece formularul nu are un selector de etichete de trimis înapoi, salvarea din această pagină lasă acum lista existentă neatinsă. (Aceasta a fost o eroare reală înainte de **21 iulie 2026**: salvarea din formularul de webhook obișnuia să șteargă lista deoarece trimitea întotdeauna o listă de etichete goală. Dacă un webhook și-a pierdut lista `subscribed_to_tags` înainte de acea dată, va trebui reconfigurat prin API.)

### Generarea unui rezumat pentru contactele etichetate

Acolo unde un webhook are o listă `subscribed_to_tags`, puteți activa **Generează rezumat**. Când este activat, <span data-t="appName">Your AI Connector</span> generează automat un rezumat al conversației pentru contact atunci când una dintre acele etichete este aplicată și îl include în datele webhook-ului — context complet fără o solicitare separată.

---

## Testarea webhook-ului dumneavoastră

1. Deschideți **Setări → Integrări → Webhook-uri**.
2. Pe rândul webhook-ului dvs., faceți clic pe **Test**.
3. Verificați sistemul extern pentru a confirma că a primit datele de test.
4. Examinați formatul datelor pentru a vă asigura că sistemul dvs. îl poate analiza corect.

Pentru un test complet cap-la-cap, trimiteți un mesaj care ar declanșa unul dintre evenimentele configurate (o difuzare sau un mesaj primit pe un canal conectat) și verificați dacă webhook-ul se declanșează cu datele reale.

::: tip
**Sfat:** Utilizați un instrument precum [webhook.site](https://webhook.site) sau [RequestBin](https://requestbin.com) în timpul dezvoltării pentru a inspecta datele brute ale webhook-ului înainte de a vă conecta sistemul de producție.
:::


### Ce este considerată o livrare reușită

Indiferent dacă dai clic pe **Test** sau dacă evenimentul este declanșat în mod real, trimitem același lucru:

- O solicitare **POST** (niciodată GET), cu corpul sub formă de JSON și `Content-Type: application/json`.
- Antetele listate sub [Payload-uri semnate](#signed-payloads-verifying-a-webhook-really-came-from-us). Antetele de semnătură sunt incluse doar după ce ați setat o cheie secretă de semnare.

Considerăm livrarea reușită atunci când:

- Endpoint-ul tău răspunde cu **orice cod de stare 2xx** (200, 201, 204 — toate sunt acceptate).
- Răspunde **în decurs de 30 de secunde**.

Câteva aspecte care îi surprind pe utilizatori:

- **Corpul răspunsului este ignorat.** Nu trebuie să returnați niciun JSON anume. Un răspuns 200 gol este suficient.
- **Redirecționările sunt considerate eșecuri.** Noi nu le urmăm, așa că un cod 301 sau 302 (inclusiv o redirecționare cu slash la final sau de la http la https) este înregistrat ca o livrare eșuată. Salvați URL-ul final, nu unul care redirecționează.
- **Șirurile de interogare (query strings) sunt pe deplin acceptate.** `https://your-app.com/hook?token=abc123` este trimis exact așa cum l-ați salvat, deci plasarea unui token în șirul de interogare funcționează la fel de bine ca plasarea lui în cale.
- **URL-ul dumneavoastră trebuie să fie `https://` și accesibil public.** Adresele care aparțin propriei infrastructuri <span data-t="appName">Your AI Connector</span> sunt respinse, dar propriile dumneavoastră endpoint-uri pe Google Cloud Functions, Cloud Run, App Engine, Firebase Hosting sau oriunde altundeva sunt în regulă.
- **Un firewall sau un strat de protecție împotriva boților din fața endpoint-ului dumneavoastră ne poate bloca.** Cel mai frecvent caz este Cloudflare: dacă zona dumneavoastră are activat „Bot Fight Mode” sau o provocare gestionată, cererea noastră primește o pagină de provocare „Just a moment...” cu un cod 403 în loc să ajungă la serverul dumneavoastră — iar o cerere server-la-server nu poate trece niciodată de o provocare de browser, așa că atât butonul **Test**, cât și evenimentele reale eșuează în același mod. Butonul Test vă va spune când se întâmplă acest lucru („Cloudflare is showing a bot challenge to our request”). Remediați problema în Cloudflare cu o regulă de Securitate / WAF care omite provocările pentru calea webhook-ului dumneavoastră (sau pentru agentul utilizator `Webhook-Delivery/1.0`), apoi faceți clic din nou pe **Test**.
- **Dacă firewall-ul dumneavoastră are nevoie de o listă de permisiuni IP în schimb** (de exemplu, planul gratuit Cloudflare, unde „Bot Fight Mode” simplu nu poate fi omis printr-o regulă WAF, dar o regulă de acces IP setată pe „Allow” rulează înaintea acestuia), vă putem ajuta: fiecare livrare, fie că provine de la butonul **Test** sau de la un eveniment live, este trimisă de la o singură adresă IPv4 fixă (fără intervale, fără IPv6, fără rotație). Contactați asistența și vă vom oferi adresa pentru a o adăuga în lista de permisiuni. Păstrați [verificarea semnăturii](#signed-payloads-verifying-a-webhook-really-came-from-us) ca verificare reală de încredere, deoarece aceasta validează fiecare sarcină utilă indiferent de locul din care provine.
- **Rezultatul testului vă spune exact ce a răspuns endpoint-ul dumneavoastră.** Un test eșuat arată acum motivul real (codul de stare HTTP pe care l-a returnat endpoint-ul dumneavoastră, un timeout sau faptul că nu am putut accesa deloc adresa) în loc de o eroare generică, iar un test pe un webhook salvat este trimis semnat atunci când semnarea este activată, exact ca un eveniment live.

### Utilizarea n8n, Make sau Zapier ("Test URL" vs "Production URL")

Platformele de automatizare îți oferă de obicei două adrese webhook diferite, iar acest lucru îi induce pe mulți în eroare:

- Un **URL de test** (în n8n conține `/webhook-test/`). Acesta primește date doar în timp ce monitorizați activ panoul și tocmai ați făcut clic pe **Ascultă evenimentul de test** (sau **Testare flux de lucru**). Acesta captează un singur eveniment și apoi încetează să mai asculte — deci, dacă faceți clic pe **Test** în <span data-t="appName">Your AI Connector</span> de mai multe ori la rând, se va capta doar primul, și doar dacă fereastra de ascultare este activă în acel moment exact. Pentru a testa: faceți clic mai întâi pe **Ascultă evenimentul de test** în n8n, apoi reveniți la <span data-t="appName">Your AI Connector</span> și faceți clic pe **Test** o singură dată.
- Un **URL de producție** (în n8n conține `/webhook/`, fără `-test`). Acesta este cel care trebuie lipit în <span data-t="appName">Your AI Connector</span> pentru evenimente live. Funcționează doar după ce fluxul de lucru este setat pe **Activ**. Dacă fluxul de lucru nu este activ, n8n respinge cererea cu o eroare "404 / webhook not registered", chiar dacă <span data-t="appName">Your AI Connector</span> a trimis datele corect.

Pe scurt: testează cu URL-ul de test în timp ce asculți, dar pentru ca webhook-ul să continue să funcționeze pentru contacte reale, salvează **URL-ul de producție** în <span data-t="appName">Your AI Connector</span> și asigură-te că fluxul de lucru este **Active**.

---

## Formatul datelor webhook

Când un webhook este declanșat, <span data-t="appName">Your AI Connector</span> trimite date structurate (JSON) către URL-ul webhook-ului dumneavoastră. Dacă utilizați o platformă de automatizare precum Zapier sau Make, aceasta analizează automat aceste date pentru dumneavoastră. Dacă construiți o integrare personalizată:

```json
{
  "event": "contactCreated",
  "contact": { "id": "<contact-id>", "first_name": "Jane", "...": "..." },
  "campaign": { "id": "<campaign-id>", "name": "AI Receptionist", "status": "Live" },
  "agent": { "id": "<agent-id>", "name": "Front Desk" },
  "user": { "id": "<account-id>", "email": "owner@example.com" }
}
```

| Câmp | Descriere |
|---|---|
| `event` | Șirul exact al evenimentului care a declanșat notificarea (de exemplu, `contactCreated`, `booked`). Aceasta **nu** este eticheta de afișare prezentată în lista de evenimente; fiecare etichetă și codul său corespondent se află în [Cele 22 de evenimente Webhook](#the-22-webhook-events). |
| `contact` | Contactul despre care este evenimentul sau `null` pentru evenimentele care nu sunt legate de un contact (cum ar fi `creditsRecharged`). |
| `campaign` | Campania din care face parte contactul sau `null` dacă nu există una. |
| `agent` | Agentul care gestionează conversația sau `null` dacă nu există unul. |
| `user` | Informații de identitate de bază pentru contul care deține datele. |

> **`campaign` sau `agent` — de obicei unul, nu ambele.** Dacă contul dvs. utilizează agenți, contactele sunt alocate unui agent, nu unei campanii, deci `campaign` apare ca `null`, iar `agent` vă indică cine a gestionat conversația. Conturile mai vechi, bazate pe campanii, văd situația invers. Citiți câmpul care este completat; nu presupuneți că `campaign` este întotdeauna prezent.

> **Blocul `agent` a fost introdus pe 15 august 2026.** Acesta se află alături de `campaign` în ceea ce privește evenimentele legate de o conversație — un chat încheiat, modul „nu deranjați”, o reluare, o dezarhivare, o pauză AI, un mesaj nou, un rezumat al conversației și webhook-ul pe care îl puteți seta pe o etichetă — și conține `id` și `name` ale agentului care gestionează conversația, sau `null` atunci când nu este implicat niciun agent. Este pur aditiv: fiecare câmp pe care îl primiți deja rămâne neschimbat, astfel încât un receptor pe care l-ați creat înainte de acea dată va continua să funcționeze fără a fi nevoie de actualizări.

Unele evenimente adaugă propriul lor bloc suplimentar de nivel superior. De exemplu, **Appointment Booked** adaugă un bloc `appointment` (vezi [Webhook-ul Appointment Booked](#appointment-booked-webhook)), **New Message** adaugă un bloc complet `message` cu textul (vezi [Webhook-ul New Message](#new-message-webhook)), iar **Deliveries** și **Reads** adaugă un bloc scurt `message` doar cu ID-ul și starea mesajului (vezi [Webhook-ul Deliveries and Reads](#deliveries-and-reads-webhook)).

> **Deliveries și Reads vă spun despre ce mesaj este vorba, dar nu și ce conținea acesta.** Acestea conțin un bloc `message` care include `id` și `status` ale mesajului — iar acel `id` este același `messageId` pe care îl returnează [endpoint-ul de trimitere a mesajelor](../api/messages.md#send-a-message), astfel încât să puteți potrivi o confirmare de livrare sau de citire cu mesajul exact pe care l-ați trimis — dar nu conțin corpul mesajului. **Replies** nu conține deloc un bloc `message`. Dacă aveți nevoie de cuvintele trimise sau primite, abonați-vă și la **New Message**.

> **Două lucruri de știut înainte de a scrie receptorul.** Nu există niciun câmp `timestamp` și niciun wrapper `data`. Fiecare bloc se află la nivelul superior al obiectului JSON, așa cum se arată mai sus.

### Cele 22 de evenimente Webhook

Cele 22 de evenimente webhook, cu eticheta de afișare pe care o bifați în aplicație și codul `event` trimis în payload. Codul `event` este un șir scurt care **nu** se potrivește cu eticheta de afișare, deci potriviți receptorul dvs. pe baza codului, nu a etichetei:

| Etichetă de afișare (în aplicație) | Cod `event` în payload | Ce înseamnă |
|---|---|---|
| Contact Created | `contactCreated` | Un contact nou este adăugat în contul dvs. (manual, prin import sau prin API). |
| Contact Paused | `contact_paused` | O conversație cu un contact este întreruptă (botul nu mai răspunde). |
| Contact Resumed | `contact_resumed` | O conversație întreruptă cu un contact este reluată. |
| Contact Do Not Disturb | `contact_do_not_disturb_changed` | Setarea „Nu deranja” a unui contact este activată. |
| Contact Unarchived | `contact_unarchived` | Un contact arhivat trimite un mesaj nou, readucându-l în inbox-ul dvs. activ. |
| New Message | `new_message` | Orice mesaj este adăugat la o conversație pe orice canal — atât mesajele pe care contactul vi le trimite, cât și mesajele pe care AI-ul sau echipa dvs. le trimite acestuia. Acesta este singurul eveniment care conține textul propriu-zis al mesajului (vezi [Webhook-ul New Message](#new-message-webhook)). |
| Replies | `replied` | Un contact răspunde la un mesaj. |
| Reads | `read` | Un contact citește un mesaj (pe canalele care acceptă confirmări de citire). Conține ID-ul mesajului care a fost citit — vezi [Webhook-ul Deliveries and Reads](#deliveries-and-reads-webhook). |
| Deliveries | `delivered` sau `undelivered` | Un mesaj este livrat cu succes unui contact (`undelivered` când livrarea eșuează). Conține ID-ul mesajului — vezi [Webhook-ul Deliveries and Reads](#deliveries-and-reads-webhook). |
| Human Alerted | `humanAlerted` | Botul AI determină că nu poate gestiona o conversație și o marchează pentru atenție umană. |
| Chat Concluded | `chat_concluded` | Botul AI decide că o conversație a ajuns la final (programare efectuată, lead descalificat etc.). |
| Appointment Booked | `booked` | Un contact programează o întâlnire prin sistemul de programări. |
| Credits Spent | `creditsSpent` | Creditele sunt deduse din contul dvs. |
| Credits Recharged | `creditsRecharged` | Creditele sunt adăugate în contul dvs. prin reîncărcare automată sau achiziție manuală. |
| Low Credit Balance | `lowCreditBalance` la o livrare **Test**, `Low Credit Balance` la una reală | Un avertisment timpuriu că soldul de credite a scăzut sub pragul de alertă (100 de credite, dacă nu ați setat altul). Destinat agențiilor ale căror sub-conturi consumă dintr-un fond comun. Conține `balance`, `threshold` și `account_email` în loc de un bloc de contact, este trimis cel mult o dată la 24 de ore cât timp soldul rămâne scăzut și se reactivează imediat ce soldul depășește din nou pragul. |
| Task Created | `taskCreated` | O sarcină este creată. |
| Task Updated | `taskUpdated` | O sarcină se modifică fără a trece într-o etapă de finalizare. |
| Task Completed | `taskCompleted` | O sarcină trece într-o etapă configurată ca etapă de finalizare. |
| Daily Summary Created | `dailySummaryCreated` | Raportul dvs. zilnic este generat. |
| Channel Connected | `channelConnected` | **Încă nu este trimis — selectabil, dar nu este emis momentan. Nu construiți pe baza acestuia.** Destinat momentului în care un canal de mesagerie termină conectarea. |
| Broadcast Started | `broadcastStarted` | O difuzare începe să fie trimisă (starea se schimbă în „Sending”). Se declanșează o dată per pornire, inclusiv când o difuzare întreruptă este reluată. Conține un bloc `broadcast` în loc de un bloc de contact: id, nume, canal, stare, stare anterioară, lista vizată (`list_id`, `list_name`, `is_smart_list`), `scheduled_at`, `total_contacts`. |
| Broadcast Completed | `broadcastCompleted` | O difuzare se termină (starea se schimbă în „Sent” sau „Failed”). Același bloc `broadcast` plus `completed_at` și, când este disponibil, `completion_summary` (`total_sent`, `permanently_failed`, `unique_replied`, `failure_rate`, `had_errors`). Folosiți-le pe ambele pentru a conecta o listă Smart Broadcast la instrumente externe. |

Încă două coduri nu apar niciodată în acea listă deoarece nu vă abonați la ele: `contact_tags_updated`, trimis de un URL de webhook setat pe o etichetă individuală, și `summary_generated`, trimis când un rezumat al chat-ului este scris pentru o etichetă din lista `subscribed_to_tags` a unui webhook.

> **Canal conectat nu este trimis încă.** Apare în lista de evenimente, dar nimic nu îl emite în prezent. Nu construiți funcționalități bazate pe acesta.

Notificările bazate pe etichete și sarcini folosesc propriile forme separate. Consultați [Contact Tags Updated](#contact-tags-updated-webhook) și [Task Completed](#task-completed-webhook).

---

## Webhook pentru Contact Creat

Trimis când se declanșează evenimentul **Contact creat** (un contact nou este adăugat manual, prin import sau prin API).

### Numele evenimentului

`contactCreated`

### Formatul sarcinii utile (payload)

```json
{
  "event": "contactCreated",
  "contact": {
    "id": "<contact-id>",
    "email": "jane@example.com",
    "phone_number": "+15551234567",
    "first_name": "Jane",
    "last_name": "Smith",
    "human_alerted": false,
    "human_alert_reason": null,
    "is_bot_active": true,
    "ad_referral": null
  },
  "campaign": {
    "id": "<campaign-id>",
    "name": "AI Receptionist",
    "status": "Live"
  },
  "agent": {
    "id": "<agent-id>",
    "name": "Front Desk"
  },
  "user": {
    "id": "<account-id>",
    "email": "owner@example.com",
    "first_name": "Alex",
    "last_name": "Doe"
  }
}
```

| Câmp | Descriere |
|---|---|
| `event` | Întotdeauna `contactCreated` pentru acest eveniment. |
| `contact.id` | ID-ul unic al noului contact. |
| `contact.email` / `contact.phone_number` | Adresa de e-mail și numărul de telefon ale contactului, dacă sunt cunoscute (oricare poate fi gol, în funcție de canal). |
| `contact.first_name` / `contact.last_name` | Numele contactului, dacă este cunoscut. |
| `contact.human_alerted` / `contact.human_alert_reason` | Dacă contactul este marcat pentru atenție umană și motivul. |
| `contact.is_bot_active` | Dacă botul AI este activ în prezent pentru acest contact. |
| `contact.ad_referral` | Atribuirea reclamei Meta Click-to-WhatsApp sau `null` — consultați [Atribuirea reclamelor Click-to-WhatsApp](click-to-whatsapp-attribution.md). |
| `campaign` | Campania sub care a fost creat contactul sau `null`. |
| `agent` | Agentul atribuit contactului sau `null`. |
| `user` | Informații de bază de identitate pentru contul care deține contactul. |

> **Eșantionul de "Test" și un eveniment real arată ușor diferit.** Butonul de test trimite date de substituent (John Doe, o campanie eșantion). Un eveniment real de Contact creat conține detaliile reale ale contactului, iar unele câmpuri pot fi goale în funcție de canal.

---

## Webhook Mesaj nou

Acest webhook se declanșează de fiecare dată când un mesaj este adăugat la o conversație, pe orice canal. Acesta acoperă ambele direcții: mesajele pe care contactul ți le trimite și mesajele pe care AI-ul, echipa ta sau o campanie le trimite acestuia. Este singurul webhook care include textul mesajului, deci acesta este cel pe care trebuie să îl folosești atunci când dorești să oglindești conversațiile într-un sistem extern.

### Numele evenimentului

`new_message`

### Formatul sarcinii utile (payload)

```json
{
  "event": "new_message",
  "contact": {
    "id": "<contact-id>",
    "email": "jane@example.com",
    "phone_number": "+15551234567",
    "first_name": "Jane",
    "last_name": "Smith",
    "human_alerted": false,
    "human_alert_reason": null,
    "is_bot_active": true,
    "ad_referral": null
  },
  "agent": {
    "id": "<agent-id>",
    "name": "Front Desk"
  },
  "user": {
    "id": "<account-id>",
    "email": "owner@example.com",
    "first_name": "Alex",
    "last_name": "Doe"
  },
  "message": {
    "id": "<message-id>",
    "body": "Hi, are you open on Saturday?",
    "direction": "inbound",
    "status": "received",
    "created_at": "2026-07-30T17:27:06.000Z",
    "channel": "whatsapp_web"
  }
}
```

| Câmp | Descriere |
|---|---|
| `event` | Întotdeauna `new_message` pentru acest eveniment. Rețineți că acesta este șirul exact trimis — nu este eticheta de afișare „New Message”. |
| `contact` | Contactul căruia îi aparține conversația mesajului. Aceeași formă ca în [Contact Created](#contact-created-webhook). |
| `agent` | Agentul care gestionează conversația (`id` și `name`) sau `null` dacă nu este implicat niciun agent. |
| `user` | Informații de identitate de bază pentru contul care deține conversația. |
| `message.id` | ID-ul unic al mesajului. |
| `message.body` | Textul mesajului. Gol pentru un mesaj care conține doar un atașament (imagine, notă vocală, document). |
| `message.direction` | `inbound` pentru un mesaj de la contact, `outbound` pentru unul trimis de AI-ul dvs. sau de echipa dvs. din inbox și `outbound-api` pentru unul trimis de o campanie, o difuzare, un șablon sau prin API. |
| `message.status` | Unde se află mesajul în ciclul său de viață: `received` pentru primire și `queued` / `sent` / `delivered` / `read` / `failed` / `undelivered` pentru trimitere. Aceasta este starea în momentul în care mesajul a fost creat, deci un mesaj trimis ajunge de obicei aici ca `queued` sau `sent` și ajunge la `delivered` ulterior — folosiți evenimentele **Deliveries** și **Reads** dacă aveți nevoie de acele tranziții ulterioare. Ele conțin același `message.id` ca acest bloc, astfel încât să puteți potrivi tranziția cu acest mesaj (vezi [Webhook-ul Deliveries and Reads](#deliveries-and-reads-webhook)). |
| `message.created_at` | Când a fost creat mesajul, în UTC (ISO 8601). |
| `message.channel` | Canalul prin care a trecut mesajul, de exemplu `whatsapp`, `whatsapp_web`, `sms`, `instagram`, `messenger`, `telegram`, `email` sau `custom`. |

> **Încă nu există un bloc `campaign` în acest payload.** Mesajul nou trimite `contact`, `agent`, `user` și `message`. Blocul `agent` a fost adăugat pe **15 august 2026** și vă indică ce agent gestionează conversația; dacă aveți nevoie și de contextul campaniei, căutați contactul prin API folosind `contact.id`.

> **Înregistrările interne ale AI-ului nu declanșează acest webhook.** Pe lângă mesajele reale, platforma își păstrează propriile rânduri de evidență într-o conversație (apelurile de instrumente ale AI-ului și înregistrările interne ale turnurilor). Acelea nu sunt trimise niciodată — primești doar mesajele care au fost trimise sau primite în mod autentic.

---

## Webhook-ul Deliveries and Reads

Aceste două evenimente raportează ce s-a întâmplat cu un mesaj după ce a părăsit <span data-t="appName">Your AI Connector</span>: **Deliveries** se declanșează când un mesaj ajunge la contact (sau nu reușește), iar **Reads** se declanșează când contactul îl deschide, pe canalele care acceptă confirmări de citire.

Ambele conțin un bloc `message` cu ID-ul mesajului la care se referă evenimentul, astfel încât să puteți potrivi actualizarea cu mesajul exact pe care l-ați trimis.

### Numele evenimentelor

`delivered` și `undelivered` pentru **Deliveries**, `read` pentru **Reads**.

### Formatul sarcinii utile (payload)

```json
{
  "event": "delivered",
  "contact": {
    "id": "<contact-id>",
    "email": "jane@example.com",
    "phone_number": "+15551234567",
    "first_name": "Jane",
    "last_name": "Smith",
    "ad_referral": null
  },
  "campaign": {
    "id": "<campaign-id>",
    "name": "AI Receptionist",
    "status": "Live"
  },
  "agent": {
    "id": "<agent-id>",
    "name": "Front Desk"
  },
  "user": {
    "id": "<account-id>",
    "email": "owner@example.com",
    "first_name": "Alex",
    "last_name": "Doe"
  },
  "message": {
    "id": "<message-id>",
    "status": "delivered"
  }
}
```

| Câmp | Descriere |
|---|---|
| `event` | `delivered` sau `undelivered` pentru **Deliveries**, `read` pentru **Reads**. |
| `contact` | Contactul căruia i-a fost trimis mesajul. |
| `campaign` | Campania din care face parte contactul sau `null`. |
| `agent` | Agentul care gestionează conversația sau `null`. |
| `user` | Informații de identitate de bază pentru contul care deține datele. |
| `message.id` | ID-ul mesajului la care se referă această actualizare. Este aceeași valoare pe care [endpoint-ul de trimitere a mesajelor](../api/messages.md#send-a-message) o returnează ca `messageId` și același `message.id` pe care îl conține o notificare [New Message](#new-message-webhook). |
| `message.status` | Noua stare, întotdeauna același șir ca `event` (`delivered`, `undelivered` sau `read`). |

> **Cum să potriviți o actualizare cu mesajul trimis.** Stocați `messageId` pe care îl primiți când trimiteți un mesaj prin API. Când sosește o notificare **Deliveries** sau **Reads**, căutați acel ID stocat în `message.id` din payload — aceasta este confirmarea de livrare sau de citire pentru acel mesaj exact.

> **Nu există text de mesaj aici.** Blocul `message` conține doar ID-ul și starea. Abonați-vă la [New Message](#new-message-webhook) dacă aveți nevoie și de corpul mesajului.

> **Blocul `message` este prezent doar atunci când știm despre ce mesaj este vorba.** În cazul rar al unei actualizări pe care nu o putem asocia cu un mesaj stocat, blocul este omis complet în loc să fie trimis gol — deci verificați dacă `message` există înainte de a citi `message.id`.

> **O notificare per schimbare de stare.** Un singur mesaj trimis produce în mod normal o notificare `delivered` și apoi, pe canalele cu confirmări de citire, una `read`. O trimitere eșuată produce `undelivered` în schimb.

---

## Appointment Booked Webhook

Se declanșează atunci când un contact programează o întâlnire. Se declanșează în același mod, indiferent dacă AI-ul a programat-o în timpul unei conversații, dacă ați programat-o manual sau dacă a venit prin API.

### Numele evenimentului

`booked`

### Formatul sarcinii utile (payload)

```json
{
  "event": "booked",
  "contact": {
    "id": "<contact-id>",
    "email": "jane@example.com",
    "phone_number": "+15551234567",
    "first_name": "Jane",
    "last_name": "Smith"
  },
  "campaign": {
    "id": "<campaign-id>",
    "name": "AI Receptionist",
    "status": "Live"
  },
  "user": {
    "id": "<account-id>",
    "email": "owner@example.com"
  },
  "appointment": {
    "appointment_id": "<appointment-id>",
    "start_time": "2026-07-20T15:00:00.000Z",
    "end_time": "2026-07-20T15:30:00.000Z",
    "status": "confirmed",
    "room_name": "Room 1",
    "description": "Discovery call",
    "summary": "30 min intro",
    "google_calendar_event_id": null,
    "event": {
      "id": "<service-id>",
      "event_name": "Intro Call",
      "slot_duration": 30,
      "location": "Zoom",
      "meeting_link": "https://...",
      "event_type": "online"
    }
  }
}
```

| Câmp | Descriere |
|---|---|
| `event` | Întotdeauna `booked` pentru acest eveniment. |
| `contact` | Persoana care a efectuat programarea. `email` și `phone_number` pot fi goale în funcție de canal. |
| `appointment.appointment_id` | ID-ul unic al programării. |
| `appointment.start_time` / `end_time` | Începutul și sfârșitul intervalului programat, în UTC (ISO 8601). |
| `appointment.status` | Starea curentă a programării. |
| `appointment.room_name` | Camera în care a fost plasată programarea, dacă este utilizată. |
| `appointment.description` / `summary` | Detalii textuale capturate odată cu programarea. |
| `appointment.google_calendar_event_id` | ID-ul Google Calendar pentru evenimentul sincronizat. Este adesea `null` în webhook-ul Appointment Booked, deoarece evenimentul din calendar este creat în același moment în care este trimisă notificarea — re-preluați programarea prin `appointment_id`-ul său puțin mai târziu dacă aveți nevoie și așteptați-vă la un `null` permanent pe conturile fără un Google Calendar conectat. |
| `appointment.event` | Serviciul care a fost programat: nume, lungimea intervalului, locație, link de întâlnire, tip. |

> **`google_calendar_event_id` este adesea `null` în acest webhook, și acest lucru este normal.** Evenimentul Google Calendar este creat în același moment în care este trimisă această notificare, deci ID-ul de obicei nu este încă gata. Regăsiți programarea după `appointment_id` puțin mai târziu dacă aveți nevoie de ea. Rămâne `null` permanent dacă contul nu are un Google Calendar conectat, deci nu așteptați la nesfârșit.

> **Butonul "Test" nu include blocul `appointment`.** Folosiți-l pentru a confirma că punctul final răspunde, apoi faceți o programare reală pentru a vedea sarcina utilă completă.

> **Două cazuri în care acest webhook nu se declanșează:** programările importate dintr-un calendar extern și rezervările care provin prin integrarea Formitable.

---

## Webhook pentru actualizarea etichetelor de contact

Se declanșează atunci când o etichetă este **aplicată** unui contact, iar acea etichetă are un URL de webhook configurat pentru agentul sau campania de care aparține contactul.

### Numele evenimentului

`contact_tags_updated`

### Când se declanșează

- O etichetă este aplicată unui contact care are un agent atribuit, o campanie atribuită sau ambele.
- Cel puțin una dintre etichetele aplicate are un URL de webhook setat în fila Etichete a acelui agent sau acelei campanii.

Dacă un contact le are pe ambele și etichetele campaniei conțin URL-uri de webhook, acelea au prioritate; în caz contrar, se folosesc cele ale agentului.

Dacă mai multe etichete cu URL-uri de webhook diferite sunt aplicate în aceeași actualizare, se trimite o cerere per URL, fiecare conținând doar etichetele care corespund acelui URL.

**Eliminarea unei etichete nu trimite niciodată o cerere.** Majoritatea utilizatorilor direcționează aceste URL-uri către o acțiune — colectarea unui depozit, rezervarea unui interval, alertarea unui reprezentant — astfel încât eliminarea unei etichete de pe un contact ar fi putut declanșa din nou acea acțiune. Acest lucru nu mai este posibil. O eliminare apare totuși în `removed_tags` atunci când are loc în aceeași actualizare cu o aplicare care merge către același URL, astfel încât o automatizare care citește ambele matrice păstrează imaginea completă; ceea ce nu va vedea niciodată este o cerere cauzată doar de o eliminare. (Modificat la **12 august 2026**. Înainte de această dată, eliminările trimiteau și ele o cerere.)

### Formatul sarcinii utile (payload)

```json
{
  "event": "contact_tags_updated",
  "contact": {
    "id": "<contact-id>",
    "email": "jane@example.com",
    "phone_number": "+15551234567",
    "first_name": "Jane",
    "last_name": "Smith",
    "human_alerted": false,
    "is_bot_active": true,
    "ad_referral": {
      "ctwa_clid": "ARAbc123...",
      "source_id": "120210000000000",
      "source_type": "ad",
      "source_url": "https://fb.me/xxxx",
      "headline": "Get 20% off today",
      "body": "Message us now to claim your discount",
      "channel": "whatsapp"
    }
  },
  "added_tags": ["qualified-lead"],
  "removed_tags": ["new-lead"],
  "agent": {
    "id": "<agent-id>",
    "name": "Front Desk"
  },
  "user": {
    "email": "owner@example.com",
    "first_name": "Alex",
    "last_name": "Doe"
  }
}
```

| Câmp | Descriere |
|---|---|
| `event` | Întotdeauna `contact_tags_updated` pentru acest webhook. |
| `contact.id` | ID-ul unic al contactului ale cărui etichete s-au modificat. |
| `contact.email` / `contact.phone_number` | E-mailul/telefonul contactului, dacă este cunoscut. |
| `contact.first_name` / `contact.last_name` | Numele contactului. |
| `contact.human_alerted` | Dacă contactul este marcat în prezent pentru atenția unui operator uman. |
| `contact.is_bot_active` | Dacă botul AI este activ în prezent în conversația acestui contact. |
| `contact.ad_referral` | Prezent doar atunci când contactul v-a contactat pentru prima dată printr-o reclamă sau postare Meta Click-to-WhatsApp (CTWA). `null` în caz contrar. |
| `added_tags` | Matrice de nume de etichete aplicate în această actualizare. Niciodată goală — o aplicare este cea care declanșează cererea. |
| `removed_tags` | Matrice de nume de etichete eliminate în aceeași actualizare, dacă există. O eliminare de una singură nu trimite nimic. |
| `agent` | Agentul care gestionează conversația contactului (`id` și `name`), sau `null` dacă nu este implicat niciun agent. Adăugat pe **15 august 2026**. |
| `user` | Informații de bază de identitate pentru contul care deține contactul. |

### Testarea unui webhook de etichetă

Lângă câmpul URL-ului webhook-ului din fila Etichete există un buton **Test**. Acesta trimite imediat un payload de probă către acel URL, astfel încât să puteți confirma că automatizarea dvs. îl primește înainte de a aștepta o conversație reală.

Testul trimite aceeași formă `contact_tags_updated` prezentată mai sus, folosind un contact substituent, cu eticheta pe care o testați în `added_tags` și un `removed_tags` gol. Ceea ce vede automatizarea dvs. în test este ceea ce va vedea în producție.

Două lucruri de știut:

- **Salvați mai întâi eticheta.** Testul caută eticheta după numele salvat, deci o etichetă nouă sau o redenumire nesalvată nu poate fi testată încă. Butonul rămâne gri până când numele de pe ecran corespunde cu cel salvat.
- **Un test eșuat nu afectează webhook-ul dvs.** Testele nu contribuie niciodată la oprirea automată după eșecuri repetate descrisă în [Fiabilitatea Webhook-urilor](#webhook-reliability).

Dacă testul eșuează, mesajul vă spune ce a răspuns endpoint-ul dvs. (de exemplu, un `404` sau `500`), ceea ce este de obicei suficient pentru a identifica un URL greșit sau un flux de lucru care nu este activat.

---

## Webhook pentru sarcină finalizată

> **Doar pentru referință.** Webhook-urile de sarcini (ca date) sunt documentate aici pentru dezvoltatori; evenimentele **Task Created**, **Task Updated** și **Task Completed** sunt selectabile în lista standard de evenimente din formularul de webhook ca oricare alt eveniment — consultați [Evenimente de declanșare disponibile](#available-trigger-events) și [Cele 22 de evenimente Webhook](#the-22-webhook-events).

Acest payload este trimis atunci când o sarcină trece într-o etapă marcată ca etapă de finalizare. O sarcină care se mută între etape care nu sunt de finalizare trimite în schimb formatul `taskUpdated`.

### Numele evenimentului

`taskCompleted`

### Când se declanșează

- O sarcină este actualizată.
- Valoarea `stage` a acesteia s-a modificat față de valoarea anterioară.
- Noua etapă este configurată ca etapă de finalizare în setările etapelor de sarcini ale contului.

### Formatul sarcinii utile (payload)

```json
{
  "event": "taskCompleted",
  "contact": {
    "email": "jane@example.com",
    "phone_number": "+15551234567",
    "first_name": "Jane",
    "last_name": "Smith",
    "human_alerted": false,
    "human_alert_reason": null
  },
  "user": {
    "email": "owner@example.com",
    "first_name": "Alex",
    "last_name": "Doe"
  },
  "message": {
    "id": "<task-id>",
    "title": "Follow up with Jane",
    "description": "Confirm pricing and send proposal",
    "type": "follow_up",
    "priority": "high",
    "stage": "<stage-id>",
    "due_date": "2026-01-20T15:00:00Z",
    "source": "ai",
    "source_detail": "<source-detail>",
    "campaign_id": "<campaign-id>",
    "linked_human_alert": "<human-alert-id>",
    "tags": ["qualified-lead"],
    "notes": "Customer requested a callback"
  }
}
```

| Câmp | Descriere |
|---|---|
| `event` | Întotdeauna `taskCompleted` pentru acest webhook. Același format de payload este trimis ca `taskUpdated` atunci când o sarcină se modifică fără a intra într-o etapă de finalizare. |
| `contact` | Contactul legat de sarcină, dacă există. `null` când nu este legat. |
| `contact.human_alert_reason` | Motivul pentru care contactul a fost marcat pentru atenție umană, dacă este cazul. |
| `user` | Informații de identitate de bază pentru contul care deține sarcina. |
| `message.id` | ID-ul unic al sarcinii. |
| `message.title` / `description` | Titlul și descrierea sarcinii. |
| `message.type` | Tipul sarcinii (de exemplu, `follow_up`, `call`, `custom`). |
| `message.priority` | Prioritatea sarcinii (`low`, `medium`, `high`). |
| `message.stage` | ID-ul etapei în care se află acum sarcina. |
| `message.due_date` | Data scadentă a sarcinii, dacă este setată. |
| `message.source` | Ce a creat sarcina (`ai`, `manual`, `api`). |
| `message.source_detail` | Detalii suplimentare despre sursă. |
| `message.campaign_id` | ID-ul campaniei legate sau `null`. |
| `message.linked_human_alert` | ID-ul alertei umane legate, dacă există. |
| `message.tags` | Etichete aplicate sarcinii. |
| `message.notes` | Note libere despre sarcină. |

---

## Dezactivarea (sau ștergerea) unui webhook

Fiecare webhook are un comutator pornit/oprit, chiar pe rândul său. Oprirea unuia (**off**) îl împiedică să primească evenimente, dar păstrează tot ce ați configurat — URL-ul, evenimentele, orice secret de semnare. Reporniți-l și va continua de unde a rămas; nimic din ce s-a întâmplat în timp ce era oprit nu va fi livrat ulterior.

Folosiți această opțiune atunci când doriți ca livrările să se oprească pentru o perioadă: punctul final este în curs de reconstrucție, depanați o integrare zgomotoasă sau întrerupeți o automatizare.

**Ștergerea** unui webhook (pictograma coș de gunoi de pe rândul său) îl elimină definitiv, inclusiv secretul său de semnare. Dacă doriți doar ca livrările să se oprească, opriți-l în schimb — ștergerea este pentru când ați terminat complet cu acel endpoint.

> **Acest lucru nu este același lucru cu dezactivarea automată a unui webhook.** Dacă dezactivăm webhook-ul dumneavoastră după eșecuri repetate (consultați [Fiabilitatea Webhook](#webhook-reliability)), comutatorul de mai sus nu îl va reactiva. Odată ce punctul final este remediat, editați webhook-ul și salvați-l cu o adresă URL modificată (orice modificare a URL-ului îl reactivează) sau apelați [punctul final de reactivare](../api/webhooks.md) prin API — sau contactați asistența și îl vom reactiva noi pentru dumneavoastră.

---

## Payload-uri semnate (Verificarea faptului că un webhook provine într-adevăr de la noi)

Oricine află URL-ul webhook-ului dvs. ar putea trimite o cerere falsă către acesta. Dacă acționați automat pe baza webhook-urilor — actualizarea facturării, crearea de înregistrări CRM — activarea **semnării** vă permite să verificați dacă fiecare cerere provine cu adevărat de la noi.

Semnarea este **opțională și dezactivată implicit**, și o activați pentru fiecare webhook, din vizualizarea de editare a acelui webhook (deschideți rândul unui webhook salvat).

### Activarea semnării

1. Deschideți webhook-ul (Setări → Integrări → Webhook-uri → faceți clic pe rândul webhook-ului dvs.).
2. În secțiunea **Secret de semnare**, faceți clic pe **Generare**.
3. Copiați secretul (începe cu `whsec_`) și stocați-l în sistemul dvs. de primire. Tratați-l ca pe o parolă.

Puteți reveni oricând pentru a dezvălui, copia, roti sau dezactiva secretul din același panou.

### Ce trimitem

Odată ce semnarea este activată, fiecare livrare pentru acel webhook conține aceste două anteturi HTTP suplimentare:

| Antet | Semnificație |
|---|---|
| `X-Webhook-Signature` | Semnătura, sub forma `v1=<hex>`. |
| `X-Webhook-Timestamp` | Când am trimis-o, sub formă de marcaj temporal Unix în secunde. |

Aceste trei sunt prezente la **fiecare** livrare, semnată sau nu:

| Antet | Semnificație |
|---|---|
| `X-Webhook-Delivery` | Un ID unic pentru acest eveniment. Rămâne același pe parcursul reîncercărilor, deci acesta este elementul după care puteți elimina duplicatele. |
| `X-Webhook-Attempt` | A câta încercare este aceasta (`1` este prima încercare). |
| `X-Webhook-Event` | Numele evenimentului, astfel încât să puteți direcționa fluxul fără a citi corpul mesajului. |

### Cum se verifică

Semnătura este un HMAC-SHA256 al șirului `<timestamp>.<raw request body>`, folosind secretul tău de semnare drept cheie.

**Verificați în raport cu corpul cererii brute — octeții exacți pe care i-ați primit.** Dacă framework-ul dvs. analizează JSON-ul și îl reserializează înainte de verificare, octeții se pot modifica și semnătura nu se va potrivi.

Exemplu Node.js:

```js
const crypto = require("crypto");

function verify(rawBody, headers, secret) {
  const timestamp = headers["x-webhook-timestamp"];
  const signature = headers["x-webhook-signature"]; // "v1=<hex>"

  // Reject anything older than 5 minutes so a captured request can't be replayed later.
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;

  const expected = crypto.createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex");

  return crypto.timingSafeEqual(Buffer.from(signature.replace("v1=", "")), Buffer.from(expected));
}
```

Exemplu Python:

```python
import hashlib, hmac, time

def verify(raw_body: bytes, headers, secret: str) -> bool:
    timestamp = headers["X-Webhook-Timestamp"]
    signature = headers["X-Webhook-Signature"].replace("v1=", "")

    # Reject anything older than 5 minutes so a captured request can't be replayed later.
    if abs(time.time() - int(timestamp)) > 300:
        return False

    expected = hmac.new(secret.encode(), f"{timestamp}.".encode() + raw_body, hashlib.sha256).hexdigest()

    return hmac.compare_digest(signature, expected)
```

> **Compară semnăturile cu o funcție sigură la sincronizare (timing-safe)** (`timingSafeEqual` / `compare_digest`), nu `==`. Nu costă nimic și evită o clasă subtilă de atacuri.

### Rotarea secretului

Dă clic pe **Rotește** pentru a înlocui secretul. Comutarea este imediată: următoarea livrare este semnată doar cu noul secret. Dacă endpoint-ul tău este activ, acceptă **atât** secretul vechi, cât și pe cel nou timp de câteva minute, în timp ce îl implementezi pe cel nou.

Dezactivarea semnării oprește pur și simplu trimiterea antetelor de semnătură.

---

## Reîncercarea livrărilor eșuate

În mod implicit, o livrare care eșuează nu este reîncercată — dacă sistemul dvs. este indisponibil în acel moment, evenimentul respectiv este pierdut.

Activează **Reîncearcă livrările eșuate** pentru un webhook (în formularul de creare/editare) și vom continua să încercăm:

| Încercare | Când |
|---|---|
| 1 | Imediat |
| 2 | 1 minut mai târziu |
| 3 | 5 minute mai târziu |
| 4 | 30 minute mai târziu |
| 5 | 2 ore mai târziu |

Acest interval însumează aproximativ **2 ore și 40 de minute**, astfel încât un webhook poate supraviețui unei ferestre de mentenanță sau unei întreruperi scurte din partea dvs.

**Ce se reîncearcă:** probleme temporare — serverul dvs. returnează o eroare 5xx, un timeout sau o eroare de conexiune.

**Ce nu facem:** dacă endpoint-ul tău respinge cererea (orice eroare 4xx), nu reîncercăm — trimiterea aceleiași cereri din nou ar produce doar aceeași respingere.

**Ce evenimente se reîncearcă:** webhook-urile de etichete (`contact_tags_updated`), cele trei evenimente de sarcină și rezumatul zilnic. Restul sunt trimise o singură dată, deci pentru acelea comutatorul nu are nicio acțiune. Fiecare eveniment conține în continuare `X-Webhook-Delivery`, deci o singură regulă de eliminare a duplicatelor le acoperă pe toate.

> **Activează reîncercările doar dacă endpoint-ul tău este idempotent.** Reîncercările înseamnă că același eveniment poate ajunge de mai multe ori. Folosește antetul `X-Webhook-Delivery` pentru a recunoaște o repetiție: acesta rămâne același la fiecare încercare pentru un eveniment, astfel încât poți ignora în siguranță un ID pe care l-ai procesat deja.

Reîncercările interacționează cu oprirea automată după eșecuri repetate (consultați [Fiabilitatea webhook-urilor](#webhook-reliability)) exact așa cum v-ați dori: contorul de eșecuri numără o **livrare completă**, doar după ce fiecare reîncercare a fost epuizată — nu fiecare încercare individuală.

---

## Fiabilitatea Webhook-urilor

- <span data-t="appName">Your AI Connector</span> trimite webhook-uri printr-o conexiune securizată (HTTPS). Asigurați-vă că adresa web pe care o furnizați utilizează HTTPS.
- Dacă sistemul dumneavoastră returnează o eroare, livrarea este considerată eșuată.
- Monitorizați timpul de funcționare al sistemului de primire pentru a evita pierderea evenimentelor.
- Pentru fluxuri de lucru critice, activați [Reîncercarea livrărilor eșuate](#retrying-failed-deliveries) și luați în considerare și un mecanism de rezervă.

> **Webhook-urile sunt dezactivate automat după eșecuri repetate.** Dacă URL-ul webhook-ului dumneavoastră eșuează în mod repetat (aproximativ 5 erori consecutive sau 3 consecutive pentru erori de tip configurare), <span data-t="appName">Your AI Connector</span> nu mai trimite automat evenimente către acel URL. Pentru a-l reactiva odată ce punctul final este funcțional: editați webhook-ul și salvați-l cu o adresă URL modificată (orice modificare a URL-ului îl reactivează) sau utilizați [punctul final de reactivare](../api/webhooks.md) prin API — salvarea cu același URL nu este suficientă. Asistența îl poate reactiva, de asemenea, pentru dumneavoastră.

---

## Depanare

| Problemă | Soluție |
|---|---|
| Webhook-ul nu se declanșează | Mai întâi, verificați dacă webhook-ul nu este **oprit** pe rândul său. Apoi, confirmați că evenimentele corecte sunt selectate și că URL-ul dvs. este accesibil de pe internet. |
| Evenimentul de test funcționează, dar evenimentele reale nu | Asigurați-vă că tipul specific de eveniment este activat. Dacă ați așteptat o solicitare la aplicarea unei etichete, rețineți că `subscribed_to_tags` nu limitează evenimentele unui webhook la o etichetă — ci doar restrânge etichetele care generează o notificare de rezumat al conversației. Pentru a primi o solicitare când este aplicată o etichetă specifică, setați un URL de webhook pe acea etichetă în fila **Etichete** a agentului (sau campaniei) — consultați [Webhook pentru actualizarea etichetelor de contact](#contact-tags-updated-webhook). |
| Nu ajunge nimic în n8n / Make / Zapier | Probabil utilizați **URL-ul de test** al platformei, care ascultă doar un singur eveniment imediat după ce faceți clic pe „Ascultă evenimentul de test”. Pentru evenimente live, salvați **URL-ul de producție** și comutați fluxul de lucru pe **Activ**. |
| Primiți evenimente duplicate | Verificați dacă există mai multe webhook-uri care indică spre același URL. Dacă opțiunea **Reîncearcă livrările eșuate** este activată, o repetare este de așteptat ori de câte ori punctul final a acceptat un eveniment, dar nu a reușit să răspundă la timp — eliminați duplicatele pe `X-Webhook-Delivery`. |
| Verificarea semnăturii eșuează mereu | Aproape întotdeauna deoarece corpul a fost re-serializat înainte de verificare. Verificați în raport cu corpul **brut** al cererii, semnați `<timestamp>.<body>` și confirmați că utilizați secretul curent dacă l-ați rotit recent. |
| Reîncercările nu au loc | Reîncercările sunt dezactivate, cu excepția cazului în care sunt activate pentru acel webhook specific. Nu reîncercăm răspunsurile 4xx. |
| Blocul `campaign` este întotdeauna `null` | Este de așteptat dacă contul dvs. utilizează agenți: contactele sunt alocate unui agent, nu unei campanii. Citiți blocul `agent` în schimb — consultați [Formatul datelor webhook](#webhook-data-format). |
| Datele sunt goale sau malformate | Verificați dacă sistemul dvs. de primire acceptă JSON. Verificați jurnalele serverului pentru erori de parsare. |
| URL-ul webhook-ului returnează erori | Testați URL-ul cu un instrument precum Postman sau [webhook.site](https://webhook.site). |
| Webhook-ul a încetat să se mai declanșeze complet după o întrerupere | Eșecurile repetate dezactivează automat un webhook. Salvarea din nou nu îl reactivează — reparați punctul final, apoi contactați asistența. |
| Salvarea sau Testarea oferă o eroare de permisiune | Aveți nevoie de permisiunea „edit” pentru Integrări. Cereți proprietarului contului să v-o acorde. |
| Lista `subscribed_to_tags` a unui webhook a revenit goală | `subscribed_to_tags` nu limitează evenimentele unui webhook la o etichetă — ci doar restrânge etichetele care generează o notificare de rezumat al conversației. Editarea din formularul de webhook nu mai șterge acea listă (remediat la 21 iulie 2026). Dacă un webhook și-a pierdut lista înainte de acea dată, setați `subscribed_to_tags` din nou prin [API-ul Webhooks](../api/webhooks.md) — consultați [Declanșatoare webhook bazate pe etichete](#tag-based-webhook-triggers). |

---

## Pașii următori

- [Integrare GoHighLevel](ghl-integration.md) — utilizați webhook-uri pentru a integra <span data-t="appName">Your AI Connector</span> cu GHL.
- [Acces API](api-access.md) — combinați webhook-urile cu API-ul pentru automatizări puternice.
- [Utilizarea etichetelor pentru a marca contactele](../get-started/creating-tags.md) — configurați etichete care declanșează webhook-urile dumneavoastră.
