OptimoCMSDocs
Guides

Webhook-Sync mit CRM

Halte dein CRM automatisch mit OptimoCMS über Webhooks synchron. Lerne, wie du Webhooks einrichtest, Payloads verarbeitest und Signaturen verifizierst.

Webhook-Sync mit deinem CRM

Webhooks senden Echtzeit-Benachrichtigungen an deinen Server, wenn etwas in OptimoCMS passiert — ein Formular wird eingereicht, eine Bestellung aufgegeben oder eine Seite veröffentlicht.

In diesem Tutorial:

  • Webhook über das SDK erstellen
  • Express-Server zum Empfangen von Payloads
  • Kryptographische Signaturverifizierung
  • Payloads verarbeiten und an ein CRM weiterleiten

Voraussetzungen

  • Node.js 18+
  • npm install @optimocms/sdk express
  • Ein öffentlich erreichbarer Endpoint (verwende ngrok für lokales Testen)

Schritt 1 — Webhook erstellen

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://dein-server.de/webhooks/optimocms',
  events: ['form.submitted', 'order.created', 'booking.created'],
});

console.log(`Webhook erstellt: ${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://dein-server.de/webhooks/optimocms",
    "events": ["form.submitted", "order.created", "booking.created"]
  }'

Antwort:

{
  "id": "wh_abc123",
  "url": "https://dein-server.de/webhooks/optimocms",
  "events": ["form.submitted", "order.created", "booking.created"],
  "secret": "whsec_k7x9m2p4q8r1t5w3",
  "active": true,
  "createdAt": "2026-05-26T14:00:00Z",
  "failCount": 0,
  "lastDeliveredAt": null
}

Schritt 2 — Webhook-Endpoint bauen

Erstelle einen Express-Server, der Webhook-Payloads empfängt und verarbeitet:

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('Ungültige Webhook-Signatur');
      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(`Unbekannter Event-Typ: ${event.type}`);
    }

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

app.listen(3000, () => {
  console.log('Webhook-Server läuft auf Port 3000');
});

Schritt 3 — Signaturverifizierung

OptimoCMS signiert jeden Webhook mit HMAC-SHA256. Verifiziere dies immer vor der Verarbeitung:

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 zu alt: ${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);
}

Schritt 4 — Payloads an CRM weiterleiten

Beispiel: Formulardaten an HubSpot CRM weiterleiten:

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(`HubSpot-Sync fehlgeschlagen: ${error}`);
    throw new Error(`CRM sync failed: ${response.status}`);
  }

  console.log(`✓ Kontakt in HubSpot erstellt für ${fields.email}`);
}

async function handleNewOrder(data: { orderId: string; customer: { email: string; name: string }; totalCents: number }) {
  console.log(`Neue Bestellung: ${data.orderId} von ${data.customer.name} (€${(data.totalCents / 100).toFixed(2)})`);
}

async function handleNewBooking(data: { bookingId: string; customer: { name: string; email: string }; date: string; startTime: string }) {
  console.log(`Neue Reservierung: ${data.bookingId} - ${data.customer.name} am ${data.date} um ${data.startTime}`);
}

Schritt 5 — Webhook-Zustellungen überwachen

Überprüfe, ob Webhooks erfolgreich zugestellt werden:

SDK

for await (const delivery of client.webhooks.listDeliveries('site_abc123', 'wh_abc123')) {
  const status = delivery.success ? '✓' : '✗';
  console.log(`${status} ${delivery.event} → ${delivery.statusCode} (${delivery.durationMs}ms)`);
}

const replay = await client.webhooks.replay('site_abc123', 'wh_abc123', {
  since: '2026-05-25T00:00:00Z',
  eventTypes: ['form.submitted'],
});

console.log(`${replay.replayed} Events erneut gesendet`);

REST

curl https://api.optimocms.com/v1/sites/$SITE_ID/webhooks/$WEBHOOK_ID/deliveries \
  -H "Authorization: Bearer $API_KEY"

curl -X POST https://api.optimocms.com/v1/sites/$SITE_ID/webhooks/$WEBHOOK_ID/replay \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "since": "2026-05-25T00:00:00Z" }'

Verfügbare Webhook-Events

EventAuslöser
form.submittedBesucher reicht ein Formular ein
order.createdNeue Webshop-Bestellung
order.updatedBestellstatus ändert sich
booking.createdNeue Reservierung
booking.cancelledReservierung storniert
page.createdNeue Seite erstellt
page.updatedSeiteninhalt geändert
page.deletedSeite gelöscht
site.publishedSite in Produktion veröffentlicht

Best Practices

  1. Signatur immer verifizieren — Vertraue eingehenden Requests niemals blind
  2. Schnell mit 200 antworten — Verarbeite die Payload asynchron, wenn es länger dauert
  3. Idempotenz — Verwende das id-Feld zur Duplikat-Erkennung
  4. Retry-tolerant — OptimoCMS wiederholt fehlgeschlagene Zustellungen automatisch (max 5x)
  5. Logging — Logge jeden eingehenden Webhook für Debugging

Nächste Schritte

On this page