Anleitung
Verbinden Sie jede beliebige Website per Webhook
Sie nutzen kein WordPress? Kein Problem. BlogTend sendet jeden fertigen Artikel per POST als signiertes JSON an einen Endpunkt unter Ihrer Kontrolle — mit jedem Technologiepaket, jedem CMS, mit etwa zwanzig Zeilen Code auf Ihrer Seite.
Was der Webhook-Konnektor ist
Sobald ein Artikel fertig generiert ist — oder sein geplanter Veröffentlichungszeitpunkt erreicht ist — sendet BlogTend ihn als eine JSON-POST-Anfrage an Ihren Endpunkt: Titel, vollständiges HTML, Auszug, SEO-Metadaten, Kategorie- und Schlagwortnamen, Sprache, Quellen, und eine öffentliche URL für das Beitragsbild. Ihr Endpunkt speichert ihn so, wie Ihre Website Inhalte speichert, und alles andere in BlogTend funktioniert genauso wie bei WordPress-Websites: Automatisierungen, Zeitplanung, Entwürfe, die Übersicht, Google-Indexierung.
Jede Anfrage wird mit einem Geheimnis signiert, das nur Sie und BlogTend kennen, sodass Ihr Endpunkt nachweisen kann, dass der Artikel wirklich von uns stammt und nicht manipuliert wurde.
Was Ihr Endpunkt leisten muss
- Nehmen Sie eine HTTPS-POST-Anfrage mit JSON-Inhalt unter einer öffentlichen URL entgegen.
- Antworten Sie innerhalb von 15 Sekunden mit einem 2xx-Statuscode. Führen Sie zeitaufwendige Vorgänge (Bilder herunterladen, Cache neu aufbauen) erst nach der Antwort aus, nicht davor.
- Beantworten Sie jeden Ping mit einem 2xx-Statuscode. Ein Ping enthält keinen Artikel und darf keine Schreibvorgänge auslösen, daher können Sie ihn vor (oder ohne) seiner Verifizierung beantworten: So kann Testen & verbinden erfolgreich abgeschlossen werden, während Ihr geheimer Schlüssel noch bereitgestellt wird.
- Prüfen Sie X-BlogTend-Signature bei jedem Artikelereignis (Code unten), und antworten Sie mit 401, wenn die Signatur nicht übereinstimmt. Wir erzwingen diese Prüfung nicht, aber ohne sie könnte jeder, der die URL findet, gefälschte Artikel an Ihre Website senden.
- Verwenden Sie article.id als Idempotenzschlüssel: Wenn bereits ein Beitrag mit dieser ID existiert, aktualisieren Sie ihn, statt ein Duplikat zu erstellen.
- Wenn Sie eine Anfrage ablehnen, geben Sie den Grund im Antworttext an (zum Beispiel {"error": "ungültige Signatur"}). Wir zeigen die ersten 300 Zeichen in Ihrem Zustellungsprotokoll an.
Verbindung herstellen
- Stellen Sie Ihren Empfangsendpunkt bereit (das Beispiel unten ist eine vollständige Implementierung).
- Gehen Sie in BlogTend zu Websites → Website verbinden → Webhook, geben Sie einen Namen und die Endpunkt-URL ein, und klicken Sie auf Weiter.
- Wir zeigen den Signaturschlüssel whsec_… an. Kopieren Sie ihn jetzt in die Umgebungsvariablen Ihres Servers und stellen Sie die Anwendung bereit: Der Schlüssel wird nur auf dieser Seite angezeigt.
- Klicken Sie auf Testen & verbinden. Wir senden einen mit diesem Geheimschlüssel signierten Ping; bei einem 2xx-Status wird die Verbindung gespeichert. Wenn Ihr Endpunkt ihn ablehnt, zeigen wir dessen Status und Fehlermeldung an, damit Sie das Problem beheben und erneut auf Testen & verbinden klicken können.
Wenn Sie dieselbe URL erneut verbinden (um die Website umzubenennen, oder nach einem Fehler), bleibt der geheime Schlüssel unverändert: Der Dialog zeigt den aktuellen Schlüssel an und die Testanfrage wird damit signiert. Um einen neuen geheimen Schlüssel zu erhalten, erneuern Sie ihn.
Von uns gesendete Header
Jede Anfrage, ob Ping oder Artikel, enthält diese Header:
| Header | Wert | Notizen |
|---|---|---|
| X-BlogTend-Signature | t=<unix seconds>,v1=<hex>[,v1=<hex>] | HMAC-SHA256-Signatur. Ein v1 pro aktivem geheimem Schlüssel: zwei nur während der 24-stündigen Übergangsfrist bei einer Rotation, der neueste zuerst. |
| X-BlogTend-Event | ping | article.published | article.updated | Identisch mit dem Feld event im Nachrichtenkörper. |
| X-BlogTend-Delivery | whd_… | Identisch mit delivery_id im Nachrichtenkörper. Bei jedem Versuch neu, auch bei Wiederholungen. |
| X-BlogTend-Test | 1 | Nur bei Pings (Testen & verbinden, Erneut verifizieren, Kategorien aktualisieren). Ein Ping enthält keinen Artikel und darf nichts schreiben. |
| Content-Type | application/json | Der Nachrichtenkörper enthält UTF-8-kodiertes JSON. |
| User-Agent | BlogTend-Webhook/1 | Hieß vor der Umbenennung YoDon-Webhook/1. |
| X-Yodon-Signature, X-Yodon-Event, X-Yodon-Delivery | deprecated | Wird für Empfänger gesendet, die vor der Umbenennung entwickelt wurden, mit denselben Werten, außer dass X-Yodon-Signature immer genau eine v1 enthält, die mit dem neuesten Geheimschlüssel erstellt wurde. Neue Empfänger sollten die X-BlogTend-* Header auslesen. |
Die X-Yodon-* Header sind veraltet. Sie funktionieren weiterhin für Empfänger, die vor der Umbenennung entwickelt wurden, aber nur X-BlogTend-Signature enthält während eines Schlüsselwechsels beide Signaturen, wechseln Sie daher bei der nächsten Anpassung Ihres Empfängers zu den neuen Namen.
Die Nutzdaten, Feld für Feld
Es gehen drei Ereignistypen ein, die sich anhand des Feldes event und des Headers X-BlogTend-Event unterscheiden: ping (Verbindungstest, kein Artikel), article.published (erste Übermittlung eines Artikels) und article.updated (derselbe Artikel erneut: nach einem von Ihnen ausgelösten erneuten Versuch, der Veröffentlichung eines Entwurfs oder der Aktualisierung eines bereits veröffentlichten Beitrags, wobei url den zu ersetzenden veröffentlichten Beitrag angibt und id die ID ist, die Sie dafür gespeichert haben). Die vollständige Struktur:
{
"event": "article.published",
"delivery_id": "whd_5f0c9c1e-…",
"site_id": "d2a41c3e-…",
"sent_at": "2026-08-28T15:04:05.000Z",
"article": {
"id": "a81f6a02-…",
"title": "How to Choose a Standing Desk",
"slug": "how-to-choose-a-standing-desk",
"status": "publish",
"html": "<h2>…</h2><p>…</p>",
"excerpt": "A practical buyer's guide…",
"seo": {
"title": "Standing Desk Buyer's Guide (2026)",
"description": "Everything to check before…",
"keyword": "standing desk"
},
"category": "Office Setup",
"categories": ["Office Setup", "Buying Guides"],
"tags": ["desks", "ergonomics"],
"language": "en",
"featured_image": {
"url": "https://www.blogtend.com/api/images/…?sig=…",
"mime": "image/webp"
},
"sources": [{ "title": "OSHA guidance", "url": "https://…" }],
"published_at": "2026-08-28T15:04:05.000Z",
"updated_at": "2026-08-28T15:04:05.000Z",
"url": "https://example.com/blog/how-to-choose-a-standing-desk"
}
}- article.status ist der von uns angeforderte Status: "publish" oder "draft". Geplante Artikel werden zum geplanten Zeitpunkt mit "publish" übermittelt.
- article.html enthält den vollständigen Artikelinhalt. Die darin enthaltenen Bildverweise zeigen bereits auf öffentlich zugängliche URLs.
- category und categories sind Namen, keine IDs — ordnen Sie sie Ihrer eigenen Taxonomie zu, oder ignorieren Sie sie.
- featured_image ist null, wenn der Artikel kein Bild enthält. Die URL ist signiert und dauerhaft gültig — rufen Sie sie jederzeit ab.
- delivery_id kennzeichnet diesen einzelnen Versuch und ändert sich bei jedem erneuten Versuch; article.id kennzeichnet den Artikel und bleibt immer gleich. Erkennen Sie Duplikate anhand von article.id.
- Der Nachrichteninhalt eines Pings enthält nur event, delivery_id, site_id und sent_at; keinen Artikel.
Signaturprüfung
Jede Anfrage enthält X-BlogTend-Signature: t=<unix seconds>,v1=<hex>. Jeder v1-Wert ist ein HMAC-SHA256 der Zeichenfolge "<t>.<raw body>" mit einem whsec_-Geheimnis als Schlüssel. Prüfen Sie die Signatur anhand des unverarbeiteten Anfrageinhalts, bevor Sie JSON parsen, lehnen Sie Zeitstempel mit mehr als 5 Minuten Abweichung ab, vergleichen Sie mit einer zeitkonstanten Funktion, und akzeptieren Sie die Anfrage, wenn einer der v1-Werte zu Ihrem Geheimnis passt: während einer Schlüsselrotation gibt es zwei.
Node.js
import crypto from "crypto";
// header: the X-BlogTend-Signature value, e.g. "t=1767225600,v1=ab12…,v1=cd34…".
// During a secret rotation it holds one v1 per live secret: accept if ANY matches.
export function verifyBlogTendSignature(secret, header, rawBody) {
const parts = (header || "").split(",").map((p) => p.trim());
const t = parts.find((p) => p.startsWith("t="))?.slice(2);
// Reject anything older than 5 minutes: replay protection.
if (!t || Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
const expected = Buffer.from(
crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex")
);
return parts
.filter((p) => p.startsWith("v1="))
.map((p) => Buffer.from(p.slice(3)))
.some((sig) => sig.length === expected.length && crypto.timingSafeEqual(sig, expected));
}PHP
function verify_blogtend_signature(string $secret, string $header, string $rawBody): bool {
$t = null;
$sigs = [];
foreach (explode(',', $header) as $part) {
[$k, $v] = array_pad(explode('=', trim($part), 2), 2, '');
if ($k === 't') $t = $v;
if ($k === 'v1') $sigs[] = $v; // one per live secret
}
if (!ctype_digit((string) $t) || abs(time() - (int) $t) > 300) return false; // replay window
$expected = hash_hmac('sha256', $t . '.' . $rawBody, $secret);
foreach ($sigs as $sig) {
if (hash_equals($expected, $sig)) return true; // timing-safe
}
return false;
}Python
import hashlib, hmac, time
def verify_blogtend_signature(secret: str, header: str, raw_body: bytes) -> bool:
parts = [p.strip().split("=", 1) for p in (header or "").split(",") if "=" in p]
t = next((v for k, v in parts if k == "t"), "")
if not t.isdigit() or abs(time.time() - int(t)) > 300: # replay window
return False
expected = hmac.new(
secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256
).hexdigest()
# One v1 per live secret during a rotation: accept if any matches.
return any(hmac.compare_digest(expected, v) for k, v in parts if k == "v1")Ein älterer Empfänger, der X-Yodon-Signature mit einem Muster für genau einen v1-Wert ausliest, funktioniert weiterhin: Dieser Header enthält nach wie vor genau einen v1-Wert, der mit dem neuesten geheimen Schlüssel erzeugt wird.
Geheimschlüssel erneuern
Haben Sie den geheimen Schlüssel verloren oder wechseln Sie ihn regelmäßig? Öffnen Sie auf der Seite Websites die Karte der Webhook-Website und klicken Sie auf Geheimen Schlüssel wechseln. Der neue geheime Schlüssel wird nur einmal angezeigt.
- In den nächsten 24 Stunden enthält jede Anfrage zwei Signaturen in X-BlogTend-Signature: eine mit dem neuen geheimen Schlüssel erstellte und eine mit dem alten. Der Empfänger kann die Anfrage mit jedem der beiden Schlüssel verifizieren, sodass nichts fehlschlägt, während Sie Ihren Server aktualisieren.
- Nach 24 Stunden wird nur noch mit dem neuen geheimen Schlüssel signiert. Die Karte zeigt an, wann der alte nicht mehr verwendet wird.
- Falls der alte geheime Schlüssel offengelegt wurde, aktivieren Sie beim Wechsel "sofort deaktivieren": Es gibt keine Übergangsfrist, und Zustellungen schlagen mit 401 fehl, bis Ihr Server den neuen geheimen Schlüssel hat.
- Empfänger, die noch den veralteten X-Yodon-Signature auslesen, sehen nur die Signatur des neuen geheimen Schlüssels, sodass die Umstellung für sie sofort erfolgt.
Antwortinhalt
Jeder 2xx-Status gilt als erfolgreiche Zustellung; der Antwortinhalt ist optional und darf leer sein. Antworten Sie mit JSON und BlogTend wird intelligenter:
{ "ok": true, "id": "123", "url": "https://example.com/blog/my-post" }- url — wird als Link zum veröffentlichten Artikel gespeichert: Die Schaltfläche „Ansehen“ im Dashboard öffnet ihn, und er wird zur Indexierung bei Google eingereicht.
- id — die Kennung Ihres Beitrags, die gespeichert wird, damit künftige article.updated-Ereignisse auch auf Ihrer Seite zugeordnet werden können.
- held — eine kurze Begründung, wenn Sie den Artikel angenommen, aber nicht auf Ihrer Website veröffentlicht haben (eine redaktionelle Warteschlange, eine interne Regel). BlogTend zeigt ihn dann mit dieser Begründung als Entwurf statt als veröffentlichten Beitrag an und versendet keine E-Mail mit "veröffentlicht". Eine Antwort mit status: "draft" bewirkt dasselbe ohne Begründung.
Auch bei einem Ping reicht jeder 2xx-Statuscode aus. Das einzige Feld, das wir aus einer Ping-Antwort auslesen, ist categories: Antworten Sie mit { ok: true, categories: ["Guides", "News"] } (einem Array mit bis zu 100 Namen), damit diese Namen in den Erstellungsformularen von BlogTend als auswählbare Kategorien erscheinen. Die Liste wird bei jeder erneuten Verifizierung und über die Aktualisierungsschaltfläche der Kategorieauswahl aktualisiert. Lassen Sie das Feld weg, schlägt die KI stattdessen einfach Kategorienamen vor.
Fehler, Wiederholungsversuche und Duplikate
So wertet BlogTend jede Antwort auf eine Artikelübermittlung aus:
| Ihre Antwort | Was es für uns bedeutet | Erneut gesendet? |
|---|---|---|
| 2xx | Zugestellt. Der Antwortinhalt ist optional; ein JSON-Antwortinhalt wird wie unter Was Sie zurückgeben sollten beschrieben ausgelesen. | Erledigt. |
| 3xx | Fehlgeschlagen. Weiterleitungen werden nicht verfolgt: Verbinden Sie stattdessen die endgültige URL. | Nicht sofort erneut gesendet. |
| 401 / 403 | Fehlgeschlagen: angezeigt als "Signatur abgelehnt". Der geheime Schlüssel auf Ihrem Server stimmt nicht überein. Fügen Sie ihn erneut ein oder ersetzen Sie ihn durch einen neuen. | Nicht sofort erneut gesendet. |
| 503 | Fehlgeschlagen: angezeigt als "noch nicht konfiguriert", die übliche Antwort eines Empfängers, für den kein geheimer Schlüssel festgelegt ist. | Einmal erneut gesendet, ohne Verzögerung. |
| Sonstige 4xx | Fehlgeschlagen: Sie haben die Anfrage verstanden und abgelehnt. | Nicht sofort erneut gesendet. |
| Sonstige 5xx | Fehlgeschlagen: Ihr Endpunkt ist abgestürzt oder nicht erreichbar. | Einmal erneut gesendet, ohne Verzögerung. |
| Keine Antwort innerhalb von 15 s, oder ein Netzwerkfehler | Fehlgeschlagen: angezeigt als "Ihr Endpunkt konnte nicht erreicht werden". | Einmal erneut gesendet, ohne Verzögerung. |
- Artikel, die BlogTend selbstständig veröffentlicht (Automatisierungen und geplante Beiträge): wenn eine Übermittlung weiterhin fehlschlägt, wird der gesamte Artikel in den nächsten zwei Durchläufen im Abstand von etwa einer Minute erneut übermittelt, unabhängig von der Antwort. Nach dem dritten fehlgeschlagenen Versuch stoppt BlogTend, behält den fertigen Artikel und teilt Ihnen den Grund per E-Mail mit. Veröffentlichen Sie ihn erneut, sobald Ihr Endpunkt wieder funktioniert.
- Artikel, die Sie manuell veröffentlichen: kein automatischer Wiederholungsversuch. Der Grund wird sofort beim Artikel angezeigt; klicken Sie erneut auf Veröffentlichen, oder auf Erneut versuchen unter Letzte Zustellungen.
- Kein Artikel geht verloren: Schlägt die Veröffentlichung fehl, bleibt er mit der Fehlerursache in BlogTend, bereit für einen erneuten Versuch.
- Hier finden Sie die Details: Auf der Seite Websites enthält die Karte einer Webhook-Website den Bereich Letzte Zustellungen. Jeder Eintrag zeigt den Statuscode, seine übliche Bedeutung und die ersten 300 Zeichen Ihrer Fehlermeldung; fehlgeschlagene Zustellungen haben eine Schaltfläche Erneut versuchen. Das erste Kilobyte jeder Antwort wird gespeichert.
- Durch Wiederholungsversuche kann Ihr Endpunkt denselben Artikel zweimal empfangen, jeweils mit einer neuen delivery_id. Deshalb ist es wichtig, anhand von article.id vorhandene Artikel zu aktualisieren und neue einzufügen.
Ein vollständiges Beispiel für einen Empfänger
Eine Route im Next.js App Router, die prüft, Duplikate entfernt und speichert. Ersetzen Sie savePost durch Ihre eigene Speicherlogik und sie ist produktionsreif:
// app/api/articles/webhook/route.ts — a complete Next.js receiver
import { NextResponse } from "next/server";
import { verifyBlogTendSignature } from "./verify"; // the Node.js function above
const SECRET = process.env.BLOGTEND_WEBHOOK_SECRET; // the whsec_… shown when you connected
export async function POST(req: Request) {
const body = await req.text(); // raw body — verify BEFORE parsing
let payload;
try {
payload = JSON.parse(body);
} catch {
return NextResponse.json({ error: "invalid JSON" }, { status: 400 });
}
// A ping carries no article and writes nothing, so it is answered without
// verifying: Test & connect then passes even before SECRET is deployed.
// Return nothing private here.
if (payload.event === "ping") {
// Optional: advertise your categories so BlogTend's forms can offer them.
return NextResponse.json({ ok: true, categories: await listMyCategories() });
}
// Every content event must be verified. Clear messages here show up in
// BlogTend's delivery log, so "wrong secret" never looks like "down".
if (!SECRET) {
return NextResponse.json({ error: "receiver not configured: BLOGTEND_WEBHOOK_SECRET is not set" }, { status: 503 });
}
const header =
req.headers.get("x-blogtend-signature") ?? req.headers.get("x-yodon-signature") ?? "";
if (!verifyBlogTendSignature(SECRET, header, body)) {
return NextResponse.json({ error: "invalid signature: check BLOGTEND_WEBHOOK_SECRET" }, { status: 401 });
}
const a = payload.article;
// Upsert by a.id — retries and updates must not create duplicates.
const post = await savePost({
externalId: a.id,
slug: a.slug,
title: a.title,
html: a.html,
excerpt: a.excerpt,
metaTitle: a.seo.title,
metaDescription: a.seo.description,
tags: a.tags,
category: a.category,
imageUrl: a.featured_image?.url ?? null,
published: a.status === "publish",
});
// Answer with the live URL so the BlogTend dashboard links to the post.
return NextResponse.json({ ok: true, id: post.id, url: post.url });
}Dieselbe Logik lässt sich in wenigen Minuten auf jedes Framework übertragen — die FAQ unten beantworten die häufigsten Fragen zu den Plattformen.
Fragen
Mit welchen Plattformen funktioniert das?
Ich habe den Signaturschlüssel verloren. Wo finde ich ihn wieder?
Funktioniert die zeitgesteuerte Veröffentlichung von Artikeln?
Was ist mit Entwürfen?
Wie werden Bilder übermittelt?
Werden die Nutzdaten bei Fehlern erneut gesendet? Erhalte ich Duplikate?
Werden SEO-Metadaten übertragen?
Stattdessen auf WordPress veröffentlichen? Lesen Sie die WordPress-Anleitung
Ihre Infrastruktur, unsere Artikel
Verbinden Sie einmal einen Endpunkt und jeder Artikel — manuell, per Massenerstellung oder automatisiert — gelangt direkt auf Ihre Website.