OptimoCMSDocs
Guides

Sync webhook con CRM

Mantén tu CRM sincronizado automáticamente con OptimoCMS mediante webhooks. Aprende a configurar webhooks, procesar payloads y verificar firmas.

Sync webhook con tu CRM

Los webhooks envían notificaciones en tiempo real a tu servidor cuando algo sucede en OptimoCMS — se envía un formulario, se realiza un pedido o se publica una página.

En este tutorial:

  • Crear un webhook mediante el SDK
  • Construir un servidor Express para recibir payloads
  • Verificación criptográfica de firmas
  • Procesar payloads y reenviarlos a un CRM

Requisitos previos

  • Node.js 18+
  • npm install @optimocms/sdk express
  • Un endpoint públicamente accesible (usa ngrok para pruebas locales)

Paso 1 — Crear un webhook

SDK

import { OptimoCMS } from '@optimocms/sdk';

const client = new OptimoCMS({
  apiKey: process.env.OPTIMOCMS_API_KEY!,
});

const webhook = await client.webhooks.create('site_abc123', {
  url: 'https://tu-servidor.es/webhooks/optimocms',
  events: ['form.submitted', 'order.created', 'booking.created'],
});

console.log(`Webhook creado: ${webhook.id}`);
console.log(`Secret: ${webhook.secret}`);

REST

curl -X POST https://api.optimocms.com/v1/sites/$SITE_ID/webhooks \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://tu-servidor.es/webhooks/optimocms",
    "events": ["form.submitted", "order.created", "booking.created"]
  }'

Paso 2 — Construir un endpoint webhook

Crea un servidor Express que recibe y procesa los payloads webhook:

import express from 'express';
import crypto from 'node:crypto';

const app = express();
const WEBHOOK_SECRET = process.env.OPTIMOCMS_WEBHOOK_SECRET!;

app.post(
  '/webhooks/optimocms',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const signature = req.headers['x-optimocms-signature'] as string;
    const timestamp = req.headers['x-optimocms-timestamp'] as string;

    if (!verifySignature(req.body, signature, timestamp)) {
      console.error('Firma de webhook inválida');
      return res.status(401).json({ error: 'Invalid signature' });
    }

    const event = JSON.parse(req.body.toString());

    switch (event.type) {
      case 'form.submitted':
        handleFormSubmission(event.data);
        break;
      case 'order.created':
        handleNewOrder(event.data);
        break;
      case 'booking.created':
        handleNewBooking(event.data);
        break;
      default:
        console.log(`Tipo de evento desconocido: ${event.type}`);
    }

    res.status(200).json({ received: true });
  }
);

app.listen(3000, () => {
  console.log('Servidor webhook ejecutándose en el puerto 3000');
});

Paso 3 — Verificación de firmas

OptimoCMS firma cada webhook con HMAC-SHA256. Verifica siempre antes de procesar:

function verifySignature(
  body: Buffer,
  signature: string,
  timestamp: string
): boolean {
  if (!signature || !timestamp) return false;

  const timestampMs = parseInt(timestamp, 10);
  const ageMs = Date.now() - timestampMs;
  if (ageMs > 5 * 60 * 1000) {
    console.error(`Webhook demasiado antiguo: ${ageMs}ms`);
    return false;
  }

  const payload = `${timestamp}.${body.toString()}`;
  const expected = crypto
    .createHmac('sha256', WEBHOOK_SECRET)
    .update(payload)
    .digest('hex');

  const expectedBuffer = Buffer.from(expected, 'hex');
  const receivedBuffer = Buffer.from(signature, 'hex');

  if (expectedBuffer.length !== receivedBuffer.length) return false;

  return crypto.timingSafeEqual(expectedBuffer, receivedBuffer);
}

Paso 4 — Reenviar payloads al CRM

Ejemplo: reenviar datos de formulario a HubSpot CRM:

interface FormSubmission {
  formId: string;
  siteId: string;
  fields: Record<string, string>;
  submittedAt: string;
}

async function handleFormSubmission(data: FormSubmission): Promise<void> {
  const { fields } = data;

  const response = await fetch('https://api.hubapi.com/crm/v3/objects/contacts', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.HUBSPOT_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      properties: {
        firstname: fields.firstName || fields.name?.split(' ')[0] || '',
        lastname: fields.lastName || fields.name?.split(' ').slice(1).join(' ') || '',
        email: fields.email,
        phone: fields.phone || '',
        company: fields.company || '',
        hs_lead_status: 'NEW',
        source: `OptimoCMS form: ${data.formId}`,
      },
    }),
  });

  if (!response.ok) {
    const error = await response.text();
    console.error(`Sync HubSpot fallida: ${error}`);
    throw new Error(`CRM sync failed: ${response.status}`);
  }

  console.log(`✓ Contacto creado en HubSpot para ${fields.email}`);
}

Eventos webhook disponibles

EventoDisparador
form.submittedUn visitante envía un formulario
order.createdNuevo pedido en la tienda
order.updatedCambio de estado del pedido
booking.createdNueva reserva
booking.cancelledReserva cancelada
page.createdNueva página creada
page.updatedContenido de página modificado
page.deletedPágina eliminada
site.publishedSitio publicado en producción

Buenas prácticas

  1. Siempre verificar la firma — Nunca confíes ciegamente en las peticiones entrantes
  2. Responder rápido con 200 — Procesa el payload de forma asíncrona si tarda
  3. Idempotencia — Usa el campo id para detectar duplicados
  4. Tolerante a retries — OptimoCMS reintenta entregas fallidas automáticamente (máx. 5x)
  5. Logging — Registra cada webhook entrante para depuración

Siguientes pasos

On this page