Articles sur : Capture de prospects

Envoyer des leads avec des Webhooks

Cet article a été traduit automatiquement depuis l'anglais. View original article


Exigence : Un point de terminaison accessible publiquement qui accepte les requêtes POST JSON.


Spreadly envoie une requête HTTP POST avec un corps JSON contenant un seul objet lead chaque fois qu'un lead est créé ou mis à jour. L'authentification utilise une signature HMAC SHA-256 sur le corps brut de la requête.


Destinations typiques :

  • Plateformes d'automatisation (Zapier, Make, n8n)
  • Serverless (AWS Lambda, Vercel, Cloudflare)
  • API personnalisées/middleware



Configuration


1) Dans ton tableau de bord Spreadly, va dans : Leads → Intégrations CRM → Webhook

2) Configure :

  • URL du Webhook : Ton point de terminaison accessible publiquement
  • Clé secrète : Copie et stocke-la en sécurité (par exemple, SPREADLY_WEBHOOK_SECRET)

3) Enregistre ta configuration

4) Envoyer un test : Utilise le bouton « Test webhook » pour vérifier ton point de terminaison


Une fois activé, Spreadly enverra l'objet lead par POST à la création et à la mise à jour.



Authentification


Basée sur une signature (HMAC SHA-256, obligatoire) :

  • En-tête : X-Spreadly-Signature
  • Contenu : Signature HMAC SHA-256 du corps brut de la requête, calculée avec ta Clé Secrète du Webhook


Remarque : Vérifie toujours la signature en utilisant le corps brut non modifié de la requête


Règles de vérification :

  • Calcule le HMAC SHA-256 avec SPREADLY_WEBHOOK_SECRET
  • Utilise les octets bruts du corps de la requête (avant tout parsing JSON)
  • Compare exactement avec l'en-tête X-Spreadly-Signature



Exemple de vérification (Next.js/Node)


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

export const config = {
api: { bodyParser: false }, // important: use raw body for 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' });
}

// Body is a single lead object
const webhook = JSON.parse(rawBody);

// Optional: handle test payloads (if enabled)
if (webhook.data.is_test === true) {
return res.status(200).json({ message: 'Test webhook received successfully' });
}

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

return res.status(200).json({ message: 'Webhook received successfully' });
}

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);
});
}



Structure de la charge utile : Lead (Objet plat)


Exemple de corps de requête :

{
"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"
}
}


Remarques sur les champs :

  • id : ID interne du lead (stable lors des mises à jour ; à utiliser pour l'upsert)
  • given_name, family_name, job_title, department, company : champs de profil
  • note : texte libre optionnel
  • emails[], phones[], websites[], addresses[] : tableaux d'éléments associés
  • custom_fields[] : extensibilité pour ton schéma
  • event_id : référence optionnelle à un événement/capture
  • latitude, longitude : géolocalisation optionnelle
  • locale : code de langue/région
  • source : origine (par exemple, spreadly_card, scanner_app)
  • champs d'enrichissement : enriched_at, is_ai_enrichment_completed
  • external_* : liens vers des systèmes externes si applicable
  • created_at, updated_at : horodatages ISO 8601


Drapeaux optionnels (si activés dans ton espace de travail) :

  • is_test : true peut être inclus à la racine pour marquer les charges utiles de test



Test


  • Va dans Leads → Intégrations CRM → Webhook et clique sur « Test Webhook »
  • Attends-toi à un HTTP 200 si le traitement est réussi
  • Si activé, les charges utiles de test incluent is_test: true sur le même objet lead plat
  • Traite is_test: true comme non persistant et renvoie simplement 200



Résolution des problèmes


Le webhook ne se déclenche pas :

  • Assure-toi que le point de terminaison est accessible publiquement
  • Renvoie HTTP 200 en cas de succès
  • Vérifie que le webhook est activé dans Leads → Intégrations CRM → Webhook
  • Utilise « Test Webhook » pour valider


Échec du test de connexion :

  • Accepte POST
  • Vérifie l'URL et la disponibilité
  • Confirme que le serveur/fonction est en cours d'exécution
  • Consulte les journaux du serveur

Mis à jour le : 18/06/2026

Cet article a-t-il répondu à vos questions ?

Partagez vos commentaires

Annuler

Merci !