> ## Documentation Index
> Fetch the complete documentation index at: https://docs.enrow.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Come funzionano i webhook

> Ricevi notifiche webhook in tempo reale al completamento delle ricerche e delle verifiche di Enrow, invece di interrogare ripetutamente l'API

export const PollingVsWebhook = () => {
  const [pollingStep, setPollingStep] = useState(0);
  const [webhookPhase, setWebhookPhase] = useState("idle");
  useEffect(() => {
    const timers = [];
    const addTimer = (fn, ms) => {
      const id = setTimeout(fn, ms);
      timers.push(id);
    };
    const runCycle = () => {
      setPollingStep(0);
      for (let i = 0; i < 5; i++) {
        addTimer(() => setPollingStep(i + 1), (i + 1) * 900);
      }
      addTimer(() => setPollingStep(0), 6500);
      setWebhookPhase("idle");
      addTimer(() => setWebhookPhase("sent"), 400);
      addTimer(() => setWebhookPhase("processing"), 1200);
      addTimer(() => setWebhookPhase("callback"), 2800);
      addTimer(() => setWebhookPhase("idle"), 6500);
    };
    runCycle();
    const loop = setInterval(runCycle, 7200);
    return () => {
      clearInterval(loop);
      timers.forEach(clearTimeout);
    };
  }, []);
  const POLLING_STEPS = [{
    response: "pending",
    success: false
  }, {
    response: "pending",
    success: false
  }, {
    response: "pending",
    success: false
  }, {
    response: "pending",
    success: false
  }, {
    response: "complete",
    success: true
  }];
  const svgIcon = ({className, circle, paths}) => <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}>
      {circle ? <circle cx={circle.cx} cy={circle.cy} r={circle.r} /> : null}
      {paths.map((d, i) => <path key={i} d={d} />)}
    </svg>;
  const icons = {
    arrowRight: className => svgIcon({
      className,
      paths: ["M5 12h14", "m12 5 7 7-7 7"]
    }),
    bell: className => svgIcon({
      className,
      paths: ["M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9", "M10.3 21a1.94 1.94 0 0 0 3.4 0"]
    }),
    checkCircle: className => svgIcon({
      className,
      circle: {
        cx: 12,
        cy: 12,
        r: 10
      },
      paths: ["m9 12 2 2 4-4"]
    }),
    xCircle: className => svgIcon({
      className,
      circle: {
        cx: 12,
        cy: 12,
        r: 10
      },
      paths: ["m15 9-6 6", "m9 9 6 6"]
    }),
    loader2: className => svgIcon({
      className,
      paths: ["M21 12a9 9 0 1 1-6.219-8.56"]
    })
  };
  return <div className="my-6 grid grid-cols-1 sm:grid-cols-2 gap-4 not-prose">
      {}
      <div className="rounded-xl border border-rose-500/20 bg-zinc-50/50 dark:bg-zinc-900/40 p-4">
        <div className="flex items-start gap-2.5 mb-3">
          <div className="size-8 rounded-lg bg-rose-500/10 flex items-center justify-center shrink-0">
            {icons.xCircle("size-4 text-rose-600 dark:text-rose-400")}
          </div>
          <div className="min-w-0">
            <h3 className="text-sm font-semibold text-zinc-900 dark:text-zinc-100 m-0 leading-8">
              Polling
            </h3>
            <div className="text-[11px] text-zinc-500 dark:text-zinc-400" style={{
    lineHeight: 1.5
  }}>
              Your app repeatedly asks "is it done yet?" until the result is ready.
            </div>
          </div>
        </div>

        <div className="space-y-1.5 mb-3 min-h-[140px]">
          {POLLING_STEPS.map((step, i) => <div key={i} className="flex items-center gap-2 text-xs font-mono transition-all duration-300" style={{
    opacity: i < pollingStep ? 1 : 0.2,
    transform: i < pollingStep ? "translateX(0)" : "translateX(-4px)"
  }}>
              <span className="text-zinc-500 dark:text-zinc-400 w-20 shrink-0">
                GET /status
              </span>
              {icons.arrowRight("size-3 text-zinc-400/70 shrink-0")}
              {step.success ? <span className="flex items-center gap-1 text-emerald-600 dark:text-emerald-400">
                  {icons.checkCircle("size-3")}
                  {step.response}
                </span> : <span className="flex items-center gap-1 text-rose-600 dark:text-rose-400">
                  {icons.xCircle("size-3")}
                  {step.response}
                </span>}
            </div>)}
        </div>

        <div className="border-t border-zinc-200 dark:border-zinc-800 pt-3 space-y-1">
          {["5+ requests per result", "Wasted bandwidth", "Delayed results"].map(stat => <div key={stat} className="flex items-center gap-2 text-xs text-zinc-500 dark:text-zinc-400">
              {icons.xCircle("size-3 text-rose-500/60 shrink-0")}
              {stat}
            </div>)}
        </div>
      </div>

      {}
      <div className="rounded-xl border border-emerald-500/20 bg-zinc-50/50 dark:bg-zinc-900/40 p-4">
        <div className="flex items-start gap-2.5 mb-3">
          <div className="size-8 rounded-lg bg-emerald-500/10 flex items-center justify-center shrink-0">
            {icons.checkCircle("size-4 text-emerald-600 dark:text-emerald-400")}
          </div>
          <div className="min-w-0">
            <h3 className="text-sm font-semibold text-zinc-900 dark:text-zinc-100 m-0 leading-8">
              Webhook
            </h3>
            <div className="text-[11px] text-zinc-500 dark:text-zinc-400" style={{
    lineHeight: 1.5
  }}>
              Enrow notifies your server the instant the result is ready.
            </div>
          </div>
        </div>

        <div className="space-y-1.5 mb-3 min-h-[140px]">
          <div className="flex items-center justify-between text-xs font-mono transition-all duration-500" style={{
    opacity: webhookPhase !== "idle" ? 1 : 0.2,
    transform: webhookPhase !== "idle" ? "translateX(0)" : "translateX(-4px)"
  }}>
            <span className="text-emerald-700 dark:text-emerald-400 font-semibold whitespace-nowrap">
              POST /email/find/single
            </span>
            <span className="text-zinc-500 dark:text-zinc-400 shrink-0 ml-2">
              {webhookPhase === "sent" && "sent ✓"}
              {webhookPhase === "processing" && <span className="inline-flex items-center gap-1">
                  {icons.loader2("size-3 animate-spin")}
                  Processing...
                </span>}
              {webhookPhase === "callback" && <span className="text-emerald-600 dark:text-emerald-400">✓ done</span>}
            </span>
          </div>

          {webhookPhase === "callback" && <div className="mt-3 rounded-md border border-emerald-400/30 bg-emerald-500/5 px-3 py-2 font-mono text-xs">
              <div className="flex items-center gap-1.5 mb-1">
                {icons.bell("size-3 text-emerald-500")}
                <span className="text-emerald-600 dark:text-emerald-400 font-semibold text-[10px] uppercase tracking-wider">
                  Webhook callback
                </span>
              </div>
              <span className="text-zinc-600 dark:text-zinc-400">
                {'{"email":"found","status":"valid"}'}
              </span>
            </div>}
        </div>

        <div className="border-t border-zinc-200 dark:border-zinc-800 pt-3 space-y-1">
          {["1 request, 1 callback", "Zero waste", "Real-time delivery"].map(stat => <div key={stat} className="flex items-center gap-2 text-xs text-zinc-500 dark:text-zinc-400">
              {icons.checkCircle("size-3 text-emerald-500/70 shrink-0")}
              {stat}
            </div>)}
        </div>
      </div>
    </div>;
};

## Perché usare i webhook?

I webhook fanno arrivare i risultati direttamente a te, invece di richiederli ripetutamente. Anziché interrogare un endpoint GET finché una ricerca non è terminata, Enrow invia ogni risultato al tuo server nel momento in cui è pronto, risparmiando richieste, riducendo la latenza e mantenendo il codice semplice.

**Smetti di sprecare richieste: lascia che siano i risultati a raggiungerti.**

<PollingVsWebhook />

Poiché ogni endpoint di Enrow è **asincrono**, i webhook sono il metodo consigliato per ricevere i risultati di [Email Finder](/it/api-reference/email-finder/find-single), [Email Verifier](/it/api-reference/email-verifier/verify-single) e [Phone Finder](/it/api-reference/phone/find-single). I webhook inoltre aggirano completamente i [limiti di frequenza](/it/rate-limits), poiché è Enrow a chiamare te e non il contrario.

## Come funziona un flusso webhook?

Un flusso webhook trasforma una singola richiesta di ricerca in una consegna automatica. Indichi a Enrow dove inviare i risultati ed Enrow si occupa del resto:

1. Invii una richiesta di ricerca in **POST** con un URL `webhook` nelle `settings`
2. Enrow restituisce immediatamente un ID di ricerca
3. Enrow elabora la ricerca in background
4. Al completamento, Enrow invia i risultati in **POST** al tuo URL webhook

## Come configuro un webhook?

Puoi registrare un webhook in due modi, a seconda che lo desideri per una singola ricerca o per ogni ricerca:

1. **Per richiesta**: includi un URL `webhook` nell'oggetto `settings` di qualsiasi chiamata API
2. **Globale**: configura un webhook predefinito dalla [pagina delle integrazioni](https://app.enrow.io/integrations) nella dashboard

```json theme={null}
{
  "fullname": "Dwight Schrute",
  "company_domain": "dundermifflin.com",
  "settings": {
    "webhook": "https://your-app.com/webhooks/enrow"
  }
}
```

<Warning>
  Il tuo URL webhook deve essere un endpoint **HTTPS** valido che restituisce un codice di stato `200`.
</Warning>

## Quali eventi attivano una chiamata webhook?

Sei tipi di eventi possono attivare una chiamata webhook, uno per ogni endpoint e tipo di ricerca:

| Evento                         | Descrizione                                  |
| ------------------------------ | -------------------------------------------- |
| `single_search_finished`       | Una singola ricerca email è terminata        |
| `bulk_search_finished`         | Una ricerca email in blocco è terminata      |
| `verification_finished`        | Una singola verifica email è terminata       |
| `bulk_verification_finished`   | Una verifica email in blocco è terminata     |
| `single_phone_search_finished` | Una singola ricerca telefonica è terminata   |
| `bulk_phone_search_finished`   | Una ricerca telefonica in blocco è terminata |

## Che aspetto ha un payload webhook?

Il payload del webhook dipende dall'endpoint e dal fatto che la ricerca sia singola o in blocco. Le ricerche singole consegnano direttamente il risultato completo, mentre le ricerche in blocco consegnano una notifica di completamento a cui rispondi con una richiesta GET.

### Email Finder — Singolo

Per le ricerche singole, ricevi il **risultato completo direttamente** nella notifica webhook. Questo elimina la necessità di eseguire una richiesta GET.

```json theme={null}
{
  "event": "single_search_finished",
  "id": "910f3e13-b2bf-442d-ab0b-4cf44dfrij84fjrt",
  "credits": {
    "cost": 1
  },
  "result": {
    "email": "dwight.schrute@dundermifflin.com",
    "qualification": "valid",
    "custom": "your_custom_data",
    "info": {
      "company_domain": "dundermifflin.com",
      "fullname": "Dwight Schrute",
      "firstname": "Dwight",
      "lastname": "Schrute"
    }
  }
}
```

### Email Finder — In blocco

Per le ricerche in blocco, ricevi una notifica che il batch è terminato. Poi chiama l'endpoint [GET /email/find/bulk](/it/api-reference/email-finder/get-bulk-results) con l'`id` per recuperare i risultati.

```json theme={null}
{
  "event": "bulk_search_finished",
  "id": "910f3e13-b2bf-442d-ab0b-4cf44dfrij84fjrt",
  "credits": {
    "cost": 2284
  }
}
```

### Email Verifier — Singolo

Il risultato completo è incluso direttamente: nessuna richiesta GET necessaria.

```json theme={null}
{
  "event": "verification_finished",
  "id": "910f3e13-b2bf-442d-ab0b-4cf44dfrij84fjrt",
  "email": "pam.beesly@dundermifflin.com",
  "qualification": "valid",
  "custom": "your_custom_data"
}
```

### Email Verifier — In blocco

Questa è solo una notifica. Chiama [GET /email/verify/bulk](/it/api-reference/email-verifier/get-bulk-verifications) con l'`id` per recuperare i risultati.

```json theme={null}
{
  "event": "bulk_verification_finished",
  "id": "910f3e13-b2bf-442d-ab0b-4cf44dfrij84fjrt",
  "credits": {
    "cost": 386.25
  }
}
```

### Phone Finder — Singolo

Il risultato completo è incluso direttamente: nessuna richiesta GET necessaria.

```json theme={null}
{
  "event": "single_phone_search_finished",
  "id": "910f3e13-b2bf-442d-ab0b-4cf44dfrij84fjrt",
  "credits": {
    "cost": 50
  },
  "result": {
    "number": "+15705551234",
    "params": {
      "linkedin_url": "https://www.linkedin.com/in/michael-scott"
    },
    "qualification": "found"
  }
}
```

### Phone Finder — In blocco

Questa è solo una notifica. Chiama [GET /phone/bulk](/it/api-reference/phone/get-bulk-results) con l'`id` per recuperare i risultati.

```json theme={null}
{
  "event": "bulk_phone_search_finished",
  "id": "910f3e13-b2bf-442d-ab0b-4cf44dfrij84fjrt"
}
```

## In cosa differiscono i webhook singoli e in blocco?

I webhook delle ricerche singole contengono il risultato completo, quindi non è necessaria alcuna chiamata aggiuntiva. I webhook delle ricerche in blocco segnalano solo che il batch è terminato: a quel punto recuperi i risultati con l'endpoint GET corrispondente.

| Tipo                | Ricerche singole           | Ricerche in blocco                 |
| ------------------- | -------------------------- | ---------------------------------- |
| **Payload**         | Risultato completo incluso | Solo notifica (ID + crediti)       |
| **GET necessario?** | No                         | Sì — usa l'endpoint GET con l'`id` |

<Info>
  Per le ricerche singole, il webhook contiene tutto ciò di cui hai bisogno. Per le ricerche in blocco, il webhook ti informa che il batch è terminato: poi recuperi i risultati.
</Info>

## Quali sono le best practice per gli endpoint webhook?

Un endpoint webhook affidabile risponde rapidamente, accetta solo HTTPS e tollera l'occasionale duplicato. Segui queste pratiche per mantenere consegne affidabili:

<AccordionGroup>
  <Accordion title="Restituisci 200 rapidamente">
    Elabora i payload dei webhook in modo asincrono. Restituisci immediatamente un `200`, poi gestisci i dati in un job in background.
  </Accordion>

  <Accordion title="Usa HTTPS">
    Usa sempre endpoint HTTPS. I webhook HTTP verranno rifiutati.
  </Accordion>

  <Accordion title="Gestisci i duplicati">
    In rari casi, i webhook possono essere recapitati più di una volta. Usa il campo `id` per deduplicare.
  </Accordion>

  <Accordion title="Usa i campi personalizzati">
    Passa i dati `custom` nelle tue richieste per identificare a quale record appartiene il risultato di un webhook:

    ```json theme={null}
    {
      "fullname": "Dwight Schrute",
      "company_domain": "dundermifflin.com",
      "custom": { "crm_id": "lead_001" }
    }
    ```

    Il campo `custom` viene restituito così com'è nel payload del webhook.
  </Accordion>
</AccordionGroup>

## Conviene usare i webhook o il polling?

Usa i webhook in produzione e il polling solo per prototipazione rapida o debug. I webhook consegnano i risultati in tempo reale senza consumare la tua quota di richieste, mentre il polling effettua chiamate GET ripetute che incidono sui tuoi [limiti di frequenza](/it/rate-limits).

|                            | Webhook                                   | Polling (GET)                      |
| -------------------------- | ----------------------------------------- | ---------------------------------- |
| **Latenza**                | Tempo reale                               | Dipende dall'intervallo di polling |
| **Chiamate API**           | 0 (è Enrow a chiamarti)                   | Più chiamate per ricerca           |
| **Impatto sul rate limit** | Nessuno                                   | Consuma la quota                   |
| **Complessità**            | Richiede la configurazione di un endpoint | Più semplice da implementare       |

<Note>
  Consigliamo i webhook per l'uso in produzione. Usa il polling solo per la prototipazione rapida o il debug.
</Note>

## FAQ

<AccordionGroup>
  <Accordion title="I webhook costano crediti aggiuntivi?">
    No. I webhook non consumano crediti aggiuntivi: paghi solo per la ricerca stessa. Il costo in crediti è riportato nel campo `credits.cost` del payload. Consulta [Crediti e fatturazione](/it/credits-billing) per i costi per endpoint.
  </Accordion>

  <Accordion title="Cosa succede se il mio endpoint non restituisce un 200?">
    Il tuo URL webhook deve essere un endpoint HTTPS valido che restituisce un codice di stato `200`. Se il tuo server è irraggiungibile o risponde con un altro stato, la consegna viene considerata fallita. Come ripiego, puoi sempre recuperare i risultati interrogando il relativo endpoint GET con l'`id` della ricerca.
  </Accordion>

  <Accordion title="Come associo un webhook alla richiesta originale?">
    Usa l'`id` della risposta di ricerca, oppure passa un oggetto `custom` nella tua richiesta: viene restituito così com'è nel payload del webhook, così puoi ricollegare i risultati ai tuoi record, come un ID lead del CRM.
  </Accordion>

  <Accordion title="Perché non ho ricevuto un webhook?">
    Le cause più comuni sono un URL non HTTPS, un endpoint che non restituisce `200` o un server che va in timeout. Verifica che il tuo endpoint sia pubblicamente raggiungibile tramite HTTPS. Per una risoluzione dei problemi più approfondita, consulta [Gestione degli errori](/it/error-handling) e [Codici di stato](/it/status-codes).
  </Accordion>
</AccordionGroup>

## Prossimi passi

<CardGroup cols={2}>
  <Card title="Trova un'email" icon="envelope" href="/it/api-reference/email-finder/find-single">
    Passa un URL webhook nelle settings per ricevere il risultato automaticamente.
  </Card>

  <Card title="Recupera risultati in blocco" icon="layer-group" href="/it/api-reference/email-finder/get-bulk-results">
    Recupera i risultati del batch dopo che si attiva un webhook bulk\_search\_finished.
  </Card>

  <Card title="Autenticazione" icon="key" href="/it/authentication">
    Come passare la tua chiave API nell'header x-api-key.
  </Card>

  <Card title="Limiti di frequenza" icon="gauge-high" href="/it/rate-limits">
    Scopri perché i webhook evitano la quota di richieste consumata dal polling.
  </Card>
</CardGroup>
