> ## Knowledge Base Index
> Fetch the complete knowledge base index at: https://help.spreadly.app/sitemap.xml
> Use this file to discover available pages before exploring further.
> Pure-Markdown content can be obtained by appending a '.md' suffix to the content URLs listed in the sitemap (without the trailing slash).

# Invia Lead tramite Webhook

*Questo articolo è stato tradotto automaticamente dall'inglese. [Visualizza l'articolo originale](https://help.spreadly.app/en/article/send-leads-with-a-webhooks-1sff2sq/)*

|| Requisito: Un endpoint accessibile pubblicamente che accetta richieste POST JSON.

Spreadly invia una richiesta HTTP POST con un body JSON contenente un singolo oggetto lead ogni volta che un lead viene creato o aggiornato. L'autenticazione utilizza una firma HMAC SHA-256 sul body grezzo della richiesta.

Destinazioni tipiche:
* Piattaforme di automazione (Zapier, Make, n8n)
* Serverless (AWS Lambda, Vercel, Cloudflare)
* API personalizzate/middleware

---

### Configurazione

1) Nel tuo pannello di controllo Spreadly, vai su: Lead → Integrazioni CRM → Webhook
2) Configura:
* URL Webhook: Il tuo endpoint accessibile pubblicamente
* Chiave Segreta: Copia e conserva in modo sicuro (es. `SPREADLY_WEBHOOK_SECRET`)
3) Salva la configurazione
4) Invia Test: Usa il pulsante "Testa webhook" per verificare il tuo endpoint

Una volta attivato, Spreadly invierà l'oggetto lead tramite POST alla creazione e all'aggiornamento.

---

### Autenticazione

Basata su firma (HMAC SHA-256, obbligatoria):
* Intestazione: `X-Spreadly-Signature`
* Contenuto: Firma HMAC SHA-256 del body grezzo della richiesta, calcolata con la tua Chiave Segreta del Webhook

|| Nota: Verifica sempre la firma utilizzando il body grezzo della richiesta non modificato

Regole di verifica:
* Calcola HMAC SHA-256 con `SPREADLY_WEBHOOK_SECRET`
* Usa i byte del body grezzo della richiesta (prima di qualsiasi parsing JSON)
* Confronta esattamente con l'intestazione `X-Spreadly-Signature`

---

### Esempio di Verifica (Next.js/Node)

```javascript
/// /pages/api/spreadly-webhook.js
import crypto from 'crypto';

export const config = {
  api: { bodyParser: false }, /// importante: usa il body grezzo per HMAC
};

export default async function handler(req, res) {
  if (req.method !== 'POST') {
    return res.status(405).json({ message: 'Method Not Allowed' });
  }

  const rawBody = await getRawBody(req);
  const secret = process.env.SPREADLY_WEBHOOK_SECRET;

  const signature = req.headers['x-spreadly-signature'];
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');

  if (!signature || signature !== expectedSignature) {
    return res.status(401).json({ message: 'Invalid signature' });
  }

  /// Il body è un singolo oggetto lead
  const webhook = JSON.parse(rawBody);

  /// Opzionale: gestisci payload di test (se abilitati)
  if (webhook.data.is_test === true) {
    return res.status(200).json({ message: 'Test webhook ricevuto correttamente' });
  }

  /// Upsert per lead.id
  /// await upsertLead(lead);

  return res.status(200).json({ message: 'Webhook ricevuto correttamente' });
}

function getRawBody(req) {
  return new Promise((resolve, reject) => {
    const chunks = [];
    req.on('data', chunk => chunks.push(chunk));
    req.on('end', () => resolve(Buffer.concat(chunks)));
    req.on('error', reject);
  });
}
```

---

### Struttura del Payload: Lead (Oggetto Piatto)

Esempio di body della richiesta:
```json
{
  "type": "lead.created",
  "timestamp": "2025-10-22T19:51:54.000000Z",
  "data": {
    "id": 0,
    "avatar_url": "https://localhost/img/assets/default_avatar.jpg",
    "given_name": "John",
    "family_name": "Doe",
    "job_title": "Sales Manager",
    "department": null,
    "company": "Acme, Inc.",
    "note": "This is a test note",
    "emails": [
      { "id": 0, "email": "j.smith@acme.com" }
    ],
    "phones": [
      { "id": 0, "number": "+39 58273 83572", "type": "mobile" }
    ],
    "websites": [
      { "id": 0, "url": "https://acme.com" }
    ],
    "addresses": [
      {
        "id": 0,
        "line1": "Via Roma 12",
        "line2": null,
        "line3": null,
        "city": "Roma",
        "state": null,
        "postal_code": null,
        "country": "Italy"
      }
    ],
    "custom_fields": [],
    "event_id": null,
    "latitude": null,
    "longitude": null,
    "locale": "en",
    "source": null,
    "enriched_at": null,
    "is_ai_enrichment_completed": true,
    "external_id": null,
    "external_system": null,
    "external_url": null,
    "created_at": "2025-10-22T19:51:54.000000Z",
    "updated_at": "2025-10-22T19:51:54.000000Z"
  }
}
```

Note sui campi:
* id: ID interno del lead (stabile tra gli aggiornamenti; usa per upsert)
* given_name, family_name, job_title, department, company: campi del profilo
* note: testo libero opzionale
* emails[], phones[], websites[], addresses[]: array di elementi correlati
* custom_fields[]: estendibilità per il tuo schema
* event_id: riferimento opzionale all'evento/cattura
* latitude, longitude: geolocalizzazione opzionale
* locale: codice lingua/regione
* source: origine (es. spreadly_card, scanner_app)
* campi di arricchimento: enriched_at, is_ai_enrichment_completed
* external_*: collegamenti a sistemi esterni, se applicabile
* created_at, updated_at: timestamp ISO 8601

Flag opzionali (se abilitati nel tuo spazio di lavoro):
* is_test: true può essere incluso alla radice per contrassegnare i payload di test

---

### Test

* Vai su Lead → Integrazioni CRM → Webhook e clicca su "Testa Webhook"
* Aspetta HTTP 200 se gestito correttamente
* Se abilitati, i payload di test includono `is_test: true` sullo stesso oggetto lead piatto
* Tratta `is_test: true` come non persistente e restituisci semplicemente 200

---

### Risoluzione dei Problemi

Webhook non attivato:
* Assicurati che l'endpoint sia accessibile pubblicamente
* Restituisci HTTP 200 in caso di successo
* Verifica che il webhook sia abilitato in Lead → Integrazioni CRM → Webhook
* Usa "Testa Webhook" per validare

Test di connessione fallito:
* Accetta POST
* Verifica URL e disponibilità
* Conferma che il server/funzione sia in esecuzione
* Controlla i log del server