Artículos sobre: Captación de leads

Enviar clientes potenciales con Webhooks

Este es un artículo traducido automáticamente del inglés. Ver artículo original


Requisito: Un endpoint accesible públicamente que acepte solicitudes POST en JSON.


Spreadly envía una solicitud HTTP POST con un cuerpo JSON que contiene un único objeto de cliente potencial cada vez que se crea o actualiza uno. La autenticación utiliza una firma HMAC SHA-256 sobre el cuerpo de la solicitud en bruto.


Destinos típicos:

  • Plataformas de automatización (Zapier, Make, n8n)
  • Sin servidor (AWS Lambda, Vercel, Cloudflare)
  • APIs personalizadas/middleware



Configuración


1) En tu panel de control de Spreadly, ve a: Clientes potenciales → Integraciones con CRM → Webhook

2) Configura:

  • URL del Webhook: Tu endpoint accesible públicamente
  • Clave Secreta: Copia y guarda de forma segura (por ejemplo, SPREADLY_WEBHOOK_SECRET)

3) Guarda tu configuración

4) Enviar Prueba: Usa el botón «Probar webhook» para verificar tu endpoint


Una vez activado, Spreadly enviará el objeto de cliente potencial mediante POST al crearse o actualizarse.



Autenticación


Basada en firma (HMAC SHA-256, obligatorio):

  • Encabezado: X-Spreadly-Signature
  • Contenido: Firma HMAC SHA-256 del cuerpo de la solicitud en bruto, calculada con tu Clave Secreta del Webhook


Notas: Verifica siempre la firma utilizando el cuerpo de la solicitud en bruto sin modificar


Reglas de verificación:

  • Calcula HMAC SHA-256 con SPREADLY_WEBHOOK_SECRET
  • Usa los bytes del cuerpo de la solicitud en bruto (antes de cualquier análisis JSON)
  • Compara exactamente con el encabezado X-Spreadly-Signature



Ejemplo de verificación (Next.js/Node)


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

export const config = {
api: { bodyParser: false }, /// importante: usa el cuerpo en bruto para 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' });
}

/// El cuerpo es un único objeto de cliente potencial
const webhook = JSON.parse(rawBody);

/// Opcional: manejar cargas de prueba (si está activado)
if (webhook.data.is_test === true) {
return res.status(200).json({ message: 'Test webhook received successfully' });
}

/// Actualizar o insertar por 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);
});
}



Estructura de la carga útil: Cliente potencial (Objeto plano)


Ejemplo de cuerpo de la solicitud:

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


Notas sobre los campos:

  • id: ID interno del cliente potencial (estable en las actualizaciones; úsalo para actualizar o insertar)
  • given_name, family_name, job_title, department, company: campos de perfil
  • note: texto libre opcional
  • emails[], phones[], websites[], addresses[]: matrices de elementos relacionados
  • custom_fields[]: extensibilidad para tu esquema
  • event_id: referencia opcional de evento/captura
  • latitude, longitude: geolocalización opcional
  • locale: código de idioma/región
  • source: origen (por ejemplo, spreadly_card, scanner_app)
  • campos de enriquecimiento: enriched_at, is_ai_enrichment_completed
  • external_*: enlaces a sistemas externos si es aplicable
  • created_at, updated_at: marcas de tiempo en ISO 8601


Banderas opcionales (si están activadas en tu espacio de trabajo):

  • is_test: true puede incluirse en la raíz para marcar cargas de prueba



Pruebas


  • Ve a Clientes potenciales → Integraciones con CRM → Webhook y haz clic en «Probar Webhook»
  • Espera HTTP 200 si se maneja correctamente
  • Si está activado, las cargas de prueba incluyen is_test: true en el mismo objeto plano de cliente potencial
  • Trata is_test: true como no persistente y simplemente devuelve 200



Solución de problemas


El webhook no se activa:

  • Asegúrate de que el endpoint sea accesible públicamente
  • Devuelve HTTP 200 al tener éxito
  • Verifica que el webhook esté activado en Clientes potenciales → Integraciones con CRM → Webhook
  • Usa «Probar Webhook» para validar


La prueba de conexión falló:

  • Acepta POST
  • Verifica la URL y la disponibilidad
  • Confirma que el servidor/función esté en ejecución
  • Revisa los registros del servidor

Actualizado el: 18/06/2026

¿Este artículo te resultó útil?

Comparte tu opinión

Cancelar

¡Gracias!