Webhook-Schnellstart
Verbinde GrowWriter mit deiner eigenen Website und lass dir dein erstes signiertes Testereignis zustellen, auf einer Seite, mit einer einzigen Code-Datei.
Zuletzt aktualisiert am
Diese Seite bringt dich von null bis Testereignis zugestellt. Ein Stack, eine Datei, keine Theorie. Sobald der Test klappt, zeigt dir die Anleitung, wie du die Artikel speicherst, die danach kommen, und die Payload-Referenz listet alles auf, was sie enthalten.
Diese Seite ist für die Person, die eine Datei in deine Website einbauen und sie deployen kann. Bist du das nicht, schick ihr diesen Link; es ist eine einzige Datei. Der Code ist für Next.js, das Setup hinter den meisten Vercel- und Netlify-Websites. Andere Stacks stehen in der Anleitung; die Schritte auf dieser Seite bleiben gleich.
Verbinde den Webhook in GrowWriter
Öffne die Seite Veröffentlichen, suche die Karte Webhook unter Automatisierung und klicke auf Verbinden.
Gib die Adresse ein, unter der der Code antworten wird. Für die Datei unten
ist das die Domain deiner Website plus /webhooks/growwriter, zum Beispiel
https://www.example.com/webhooks/growwriter. Nutze exakt die Adresse, die
deine Website ausliefert: Leitet sie example.com auf www.example.com
weiter, gib die mit www ein. Klicke auf Verbindung erstellen.
Der Dialog zeigt jetzt deinen Signaturschlüssel, einen String, der mit
whsec_ beginnt. Klicke auf Schlüssel kopieren. Er wird nur ein einziges
Mal angezeigt, lass den Dialog also offen oder füge ihn irgendwo sicher ein.
Füge die Empfänger-Datei hinzu
Lege diese Datei in deinem Projekt an. Sie liest jede Anfrage exakt so, wie sie angekommen ist, prüft, dass GrowWriter sie signiert hat, und antwortet auf das Testereignis.
import { createHmac, timingSafeEqual } from 'node:crypto';
// Eine signierte Anfrage, die älter ist als das hier, wird abgelehnt. So
// lässt sich eine abgefangene Anfrage später nicht wieder einspielen.
const MAX_AGE_SECONDS = 5 * 60;
function isSignedByGrowWriter(rawBody: string, header: string, secret: string) {
// Der Header sieht so aus: t=1756718400,v1=5257a869e7...
const parts = Object.fromEntries(
header.split(',').map((part) => part.split('='))
);
const age = Math.abs(Date.now() / 1000 - Number(parts.t));
if (!(age < MAX_AGE_SECONDS)) {
return false;
}
// Berechne den Fingerabdruck über "zeitstempel.body" nach, mit deinem
// KOMPLETTEN Schlüssel (whsec_-Präfix inklusive), und vergleiche in
// konstanter Zeit.
const expected = createHmac('sha256', secret)
.update(`${parts.t}.${rawBody}`)
.digest('hex');
const provided = Buffer.from(parts.v1 ?? '', 'hex');
const wanted = Buffer.from(expected, 'hex');
return provided.length === wanted.length && timingSafeEqual(provided, wanted);
}
export async function POST(request: Request) {
// Lies den Body zuerst als Text. Ihn vor der Signaturprüfung zu parsen
// ändert die Bytes, und die Prüfung schlägt fehl.
const rawBody = await request.text();
const signature = request.headers.get('x-growwriter-signature') ?? '';
const secret = process.env.GROWWRITER_WEBHOOK_SECRET ?? '';
if (!isSignedByGrowWriter(rawBody, signature, secret)) {
return new Response('Invalid signature', { status: 401 });
}
const payload = JSON.parse(rawBody);
// "Testereignis senden" schickt einen Ping ohne Artikel. Die 200-Antwort
// hier lässt den Test gelingen.
if (payload.event === 'ping') {
return new Response('pong');
}
// Ein echter Artikel (oder seine Entfernung: article.unpublished trägt nur
// die id). Für den Moment beweisen wir nur, dass er angekommen ist; die
// Anleitung zeigt, wie du ihn speicherst. Sobald du das tust, gibt
// Response.json({ url }) mit der öffentlichen URL des Beitrags dem
// Veröffentlichen-Dialog einen Link "Beitrag ansehen".
console.log(`${payload.event}: ${payload.article.id}`);
return new Response('OK');
}Gib deiner Website den Schlüssel und deploye
Dein Code liest den Schlüssel aus einer Umgebungsvariable: einer Einstellung, die dein Hosting-Anbieter außerhalb deines Codes aufbewahrt. Füge im Dashboard deines Hosts (Vercel: Settings, dann Environment Variables) hinzu:
GROWWRITER_WEBHOOK_SECRET=whsec_paste_your_secret_hereFüge den ganzen Wert ein, whsec_ inklusive. Deploye dann deine Website,
damit die neue Datei und die neue Variable live sind.
Sende das Testereignis
Zurück im GrowWriter-Dialog klickst du auf Testereignis senden. GrowWriter
signiert einen kleinen ping und schickt ihn per POST an deine Adresse.
Antwortet deine Datei, zeigt der Dialog Testereignis zugestellt. Klicke
auf Fertig.
Schlägt es fehl, nennt der Dialog den Grund. Die zwei, die du am wahrscheinlichsten siehst:
- Deine Endpoint-URL leitet weiter. Gib die Adresse ein, zu der sie
weiterleitet, und versuche es erneut: Deine Website antwortet unter einer
anderen Adresse (meist
wwwgegen keinwww), und die Meldung zeigt, welche du nutzen sollst. Klicke auf URL bearbeiten in der Webhook-Zeile, um sie zu ändern. - Dein Endpoint hat die Zustellung abgelehnt: Mit einem
401stimmt der Schlüssel bei deinem Host nicht überein; mit einem404liegt die Datei nicht unter der Adresse, die du eingegeben hast.
Alles andere steht auf der Seite zur Fehlerbehebung.
Beweise, dass die Prüfung funktioniert
Optional, aber die Minute lohnt sich. Dieses Skript signiert einen Ping genau so, wie GrowWriter es tut, und schickt ihn an deine Website:
SECRET="whsec_paste_your_secret_here"
URL="https://www.example.com/webhooks/growwriter"
BODY='{"version":"2026-09-06","event":"ping","deliveryId":"9d2f7c1e-6a3b-4c8d-b5e0-1f2a3b4c5d6e","sentAt":"2026-09-01T09:30:00.000Z"}'
TIMESTAMP=$(date +%s)
SIGNATURE=$(printf '%s.%s' "$TIMESTAMP" "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | sed 's/^.* //')
curl -X POST "$URL" \
-H "Content-Type: application/json" \
-H "X-GrowWriter-Event: ping" \
-H "X-GrowWriter-Delivery: 9d2f7c1e-6a3b-4c8d-b5e0-1f2a3b4c5d6e" \
-H "X-GrowWriter-Signature: t=$TIMESTAMP,v1=$SIGNATURE" \
--data "$BODY"Du solltest pong bekommen. Ändere jetzt ein einzelnes Zeichen in BODY
und führe es erneut aus: Du solltest ein 401 bekommen. Das ist deine
Signaturprüfung, die eine manipulierte Anfrage ablehnt, und genau darum geht
es.
Was du jetzt hast
Einen öffentlichen Endpoint, der nur Anfragen akzeptiert, die GrowWriter
signiert hat, und eine Verbindung, die auf der Seite Kanäle
Verbunden anzeigt. Einen Artikel zu veröffentlichen liefert ihn als
article.published-Ereignis an diese Datei, und die console.log-Zeile
zeigt seine id in den Logs deines Hosts.
Weiter: den Artikel speichern
Die Datei oben loggt den Artikel nur. Um ihn zu speichern, lies Speichere den Artikel in deinem CMS in der Anleitung: sechs kurze Regeln zu ids, Datumswerten, Veröffentlichungsstatus, Keywords, Bildern und Escaping, jede gelernt von einem echten Empfänger, der sie falsch gemacht hat. Die vollständigen Empfänger-Beispiele dort wenden alle sechs an.
Willst du wissen, warum es die Signatur und den Schlüssel überhaupt gibt? Die Anleitung erklärt es in vier einfachen Schritten.
In BaseHub veröffentlichen
Verbinde dein BaseHub-Repository, damit jeder Artikel zu einer Zeile in einer Collection wird, die deine Website schon ausliest. Kein Code nötig.
Mit Webhooks veröffentlichen
Sende jeden Artikel, den du in GrowWriter veröffentlichst, direkt an deine eigene Website. Richte die Webhook-Integration Schritt für Schritt ein, mit fertigem Code.