evnxt.

Die Suche läuft in deinem Browser, es wird keine Eingabe an uns gesendet.

Dokumentation · Integrationen

Eigene Website anbinden (Webhook)

Beta. Diese Anbindung ist neu. Sie funktioniert, wird aber noch beobachtet — prüfe den ersten Beitrag bitte auf deiner Seite nach.

Noch nicht in der Auswahl? Diese Anbindung wird plattformweit freigeschaltet (Einstellung cms_family_enabled). Solange sie aus ist, zeigt die Seite CMS anbinden nur WordPress. Melde dich bei uns, wenn die Auswahl bei dir fehlt.

Wenn deine Website selbst gebaut ist — Next.js, Astro, Laravel, ein statischer Generator —, gibt es keine Schnittstelle, die wir kennen könnten. Also drehen wir es um: Du stellst eine Adresse bereit, wir schicken den fertigen Beitrag dorthin.

Einrichten

  1. Eine https-Adresse bereitstellen, die JSON per POST entgegennimmt.
  2. Ein Geheimnis ausdenken (mindestens 32 zufällige Zeichen) und an beiden Stellen hinterlegen: bei dir und hier im Formular.
  3. Verbindung testen klicken. Wir schicken ein ping; antwortet deine Adresse mit 2xx, steht die Verbindung.

Die Adresse muss öffentlich erreichbar sein. Adressen in privaten Netzen (localhost, 192.168.…, 10.…) lehnen wir ab — sie wären ein Weg in fremde Netze.

Die Signatur

Jede Nachricht trägt zwei Kopfzeilen:

X-Evnxt-Timestamp: 1770000000
X-Evnxt-Signature: sha256=4f3a…   (Präfix `sha256=` plus 64 Hex-Zeichen)

Die Signatur ist ein sha256-HMAC über Zeitstempel.Rumpf mit deinem Geheimnis:

signatur = "sha256=" + hex( hmac_sha256( timestamp + "." + roher_koerper, secret ) )

Bei einer GET-Abfrage (Standsabfrage) ist der Rumpf leer; signiert wird dann timestamp + ".".

Prüfe die Signatur immer — sonst kann jeder, der deine Adresse kennt, Beiträge auf deiner Seite anlegen. Vergleiche zeitkonstant (hash_equals, crypto.timingSafeEqual) und weise Nachrichten ab, deren Zeitstempel mehr als fünf Minuten alt ist.

Es ist dieselbe Regel wie bei den ausgehenden Ereignis-Webhooks der Plattform. Wer beides nutzt, baut nur einen Prüfer.

Die Ereignisse

Ereignis Bedeutung
ping Verbindungstest. Schreibt nichts. Darf ziele melden.
article.draft Beitrag anlegen, nicht öffentlich.
article.update Bestehenden Beitrag nachziehen.
article.publish Beitrag öffentlich schalten.
article.delete Entwurf entfernen (abgelehnter Beitrag).

Beispiel eines Rumpfes:

{
  "ereignis": "article.publish",
  "zeit": "2026-02-01T10:15:00+01:00",
  "website": { "id": 42, "name": "Magazin" },
  "remote_id": "kunde-42",
  "ziel": "ratgeber",
  "beitrag": {
    "id": 1234,
    "titel": "Ein Beitrag",
    "slug": "ein-beitrag",
    "teaser": "Kurz und knapp erklärt.",
    "body_html": "<p>Text</p>",
    "bild_url": "https://beispiel.de/portal-media/titel.webp",
    "meta_title": "Ein Beitrag",
    "meta_description": "Kurz und knapp erklärt.",
    "autor": "Redaktion",
    "kategorie": "Ratgeber",
    "veroeffentlicht_am": "2026-02-01T10:15:00+01:00"
  }
}

Die Antwort

Antworte mit 2xx und diesem JSON:

{
  "remote_id": "kunde-42",
  "remote_url": "https://kunde.example/blog/ein-beitrag",
  "remote_version": "2026-02-01T10:15:00Z"
}
  • remote_id ist Pflicht. Ohne sie können wir den Beitrag später nicht mehr ansprechen — wir lehnen die Antwort dann ab.
  • remote_url ist die öffentliche Adresse. Eine Empfangsbestätigung ist keine Veröffentlichung: Erst wenn du bei article.publish eine Adresse meldest, gilt der Beitrag bei uns als öffentlich.
  • remote_version ist dein eigener Änderungsstand (Zeitstempel oder Hash). Meldest du ihn, erkennen wir, wenn jemand den Beitrag bei dir geändert hat, und überschreiben ihn nicht.

Stand abfragen

Vor jedem Schreibvorgang fragen wir den aktuellen Stand ab:

GET https://kunde.example/evnxt-hook?remote_id=kunde-42

Antworte mit remote_id, remote_url, remote_version und optional entwurf: true.

Fehler und Wiederholungen

Wir unterscheiden deine Antworten:

Antwort Bedeutung bei uns
401 / 403 Zugangsfehler — wir fragen nach neuen Daten, keine Wiederholung.
409 Konflikt — bei dir steht etwas Neueres. Wir überschreiben nichts.
408, 429, 5xx, Zeitüberschreitung Vorübergehend — wir versuchen es später erneut.
andere 4xx Endgültig abgelehnt — der Beitrag bleibt bei uns stehen und trägt deine Meldung.

Wiederholungen sind ungefährlich: Wir schicken denselben Beitrag mit derselben remote_id. Solange du danach handelst statt blind anzulegen, entsteht kein Doppelpost.

Fertige Rezepte

Die Regeln auf dieser Seite gelten für jede Gegenstelle. Für drei häufige Fälle gibt es eine ausführliche Anleitung:

  • Next.js anbinden — vollständiger Route Handler für den App Router, mit MDX- und Datenbank-Variante
  • Framer anbinden — nur über einen Vermittlerdienst; was Framer heute erlaubt und was nicht
  • WordPress.com anbinden — braucht diesen Weg nicht: ab Business läuft es über die native WordPress-Anbindung

Beispiel-Empfänger in PHP

<?php
$secret = getenv('EVNXT_SECRET');
$body   = file_get_contents('php://input');
$zeit   = $_SERVER['HTTP_X_EVNXT_TIMESTAMP'] ?? '';
$sig    = $_SERVER['HTTP_X_EVNXT_SIGNATURE'] ?? '';

// 1. Zeitfenster
if (! ctype_digit($zeit) || abs(time() - (int) $zeit) > 300) {
    http_response_code(401);
    exit;
}

// 2. Signatur — zeitkonstant vergleichen
$erwartet = 'sha256=' . hash_hmac('sha256', $zeit . '.' . $body, $secret);
if (! hash_equals($erwartet, $sig)) {
    http_response_code(401);
    exit;
}

// 3. Verarbeiten
$daten   = json_decode($body, true);
$beitrag = $daten['beitrag'] ?? [];

switch ($daten['ereignis'] ?? '') {
    case 'ping':
        echo json_encode(['ok' => true, 'ziele' => [['id' => 'ratgeber', 'title' => 'Ratgeber']]]);
        exit;

    case 'article.draft':
    case 'article.update':
        $id = speichere($beitrag, oeffentlich: false);
        echo json_encode(['remote_id' => $id, 'remote_version' => gmdate('c')]);
        exit;

    case 'article.publish':
        $id = speichere($beitrag, oeffentlich: true);
        echo json_encode([
            'remote_id'      => $id,
            'remote_url'     => 'https://kunde.example/blog/' . $beitrag['slug'],
            'remote_version' => gmdate('c'),
        ]);
        exit;

    case 'article.delete':
        entferne($daten['remote_id']);
        echo json_encode(['remote_id' => $daten['remote_id']]);
        exit;
}

http_response_code(422);
echo json_encode(['message' => 'Unbekanntes Ereignis']);

Beispiel-Empfänger in Node (Express)

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

const app = express();
const secret = process.env.EVNXT_SECRET;

// Wichtig: den ROHEN Rumpf behalten — die Signatur gilt fuer genau diese Bytes.
app.use(express.raw({ type: 'application/json' }));

app.post('/evnxt-hook', (req, res) => {
  const zeit = req.get('X-Evnxt-Timestamp') ?? '';
  const sig  = req.get('X-Evnxt-Signature') ?? '';
  const body = req.body.toString('utf8');

  if (!/^\d+$/.test(zeit) || Math.abs(Date.now() / 1000 - Number(zeit)) > 300) {
    return res.sendStatus(401);
  }

  const erwartet = 'sha256=' + crypto.createHmac('sha256', secret).update(`${zeit}.${body}`).digest('hex');
  const a = Buffer.from(erwartet, 'utf8');
  const b = Buffer.from(sig, 'utf8');
  if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
    return res.sendStatus(401);
  }

  const daten = JSON.parse(body);

  switch (daten.ereignis) {
    case 'ping':
      return res.json({ ok: true, ziele: [{ id: 'ratgeber', title: 'Ratgeber' }] });

    case 'article.draft':
    case 'article.update': {
      const id = speichere(daten.beitrag, false);
      return res.json({ remote_id: id, remote_version: new Date().toISOString() });
    }

    case 'article.publish': {
      const id = speichere(daten.beitrag, true);
      return res.json({
        remote_id: id,
        remote_url: `https://kunde.example/blog/${daten.beitrag.slug}`,
        remote_version: new Date().toISOString(),
      });
    }

    case 'article.delete':
      entferne(daten.remote_id);
      return res.json({ remote_id: daten.remote_id });
  }

  return res.status(422).json({ message: 'Unbekanntes Ereignis' });
});

app.listen(3000);

Was diese Anbindung kann

Fähigkeit Webhook
Bilder in die Mediathek laden nein — die Bildadressen zeigen auf uns; hol sie dir beim Speichern ab
Titel und Beschreibung für Suchmaschinen ja — sie stehen in der Nachricht
Autorennamen setzen ja — er steht in der Nachricht
Nicht öffentliche Entwürfe jaarticle.draft
Bestehende Beiträge ändern jaarticle.update
Fremde Änderungen erkennen ja, sobald du remote_version meldest

Zu den Bildern: Wir laden nichts zu dir hoch. Im body_html stehen Adressen unter /portal-media/…. Lade die Dateien beim Verarbeiten einmal herunter und lege sie bei dir ab — sonst hängt deine Seite dauerhaft an unserer.

Zur Übersicht Kostenlos starten Etwas fehlt oder stimmt nicht? Schreib an anfrage@wojcik.de.