Webhooks gør ethvert upstream-system til en udgiver. Signer en payload, POST den, og motoren mapper den til indhold og genopbygger — ingen nøgleudveksling i anmodningsstien, fordi signaturen er godkendelsen.
Slutpunktet
POST https://your-host/webhooks/{integration} {integration} er en slug, du vælger, når du opretter integrationen — ikke navnet på en mapper. Hver enkelt har sin egen slug, sit eget hemmelighed og én mapper bag sig, så et site kan have flere: newsroom, docs-sync, partner-feed. Slugs består af små bogstaver, cifre og bindestreger.
php bin/fastsite webhook:create newsroom --site=blog --mapper=autoseo Det udskriver den fulde URL og signeringshemmeligheden én gang. Du kan gøre det samme fra dashboardet, REST API'et eller create_webhook MCP-værktøjet — webhooks er site-scoped, så ethvert medlem af et site kan administrere dem uden at være administrator.
Signering af en anmodning
Den rå body læses ordret og verificeres, før noget andet kører. To headere er påkrævede:
X-Fastsite-Signature: t=<unix>,v1=<hmac_sha256("<t>.<body>", secret)>— den signerede streng er tidsstemplet, et bogstaveligt punktum og de præcise bytes, du sender. Sammenlignes i konstant tid inden for et ±300 sekunders vindue.X-Delivery-Id— unik pr. levering. Det er replay-beskyttelsen: et gentaget id accepteres med en200og ignoreres stille i stedet for at blive behandlet to gange.
Signer de bytes, du faktisk sender. Genserialising af JSON mellem signering og afsendelse er den sædvanlige årsag til en signatur, der ikke kan verificeres.
Et gennemarbejdet eksempel
# body.json er de præcise bytes, du signerer og sender
SECRET="whsec_…"
BODY=$(cat body.json)
TS=$(date +%s)
SIG=$(printf '%s.%s' "$TS" "$BODY" | \
openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')
curl -X POST https://your-host/webhooks/newsroom \
-H "X-Fastsite-Signature: t=$TS,v1=$SIG" \
-H "X-Delivery-Id: $(uuidgen)" \
-H "Content-Type: application/json" \
--data-binary @body.json Hvad du får tilbage
| Kode | Betydning |
|---|---|
| 202 | Accepteret. Registreret og sat i kø. |
| 200 | Duplikat leverings-id — allerede set, intet gjort. |
| 400 | Manglende X-Delivery-Id. |
| 401 | Manglende eller ugyldig signatur. |
| 404 | Ingen sådan integration, eller den er deaktiveret. |
Slutpunktet svarer på millisekunder, fordi det kun registrerer og sætter i kø. Mapping, opbygning og publicering sker efterfølgende på køen med genforsøg og backoff.
Webhooks publicerer; API'et gør ikke
Dette er den ene reelle forskel mellem de to ingest-stier. Oprettelse af indhold via REST eller MCP gemmer det og venter på, at du publicerer. En webhook-levering kører sin mapper og kæder derefter automatisk en opbygning og en publicering — et upstream-system, der skubber en artikel, forventer, at den er live, ikke kladde.
Indbyggede mappere
Mappere er kode, ikke konfigurationsstrenge. Hver tager en generisk payload og omdanner den til en motorhandling.
| Mapper | Payload | Gør |
|---|---|---|
autoseo | {id, title, body_html, slug?, excerpt?, featured_image_url?, author?, seo_title?, seo_description?, status?, published_at?} | Idempotent artikelingest, nøglet på id. |
create_post | {title, body_html, slug?, excerpt?, status?, author?, source_ref?} | Mapper en payload til et indlæg. |
create_page | Samme som ovenfor | Mapper en payload til en side. |
import_media | {url, alt?, decorative?} | Henter en fil via URL (SSRF-beskyttet) ind i biblioteket. |
upsert_component | {handle, type, source_code, css?} | Opretter eller opdaterer en komponent. |
autoseo er den, du bør bruge til en artikelpipeline. Den er idempotent på payloadens id, så genlevering opdaterer det eksisterende indlæg i stedet for at oprette et nyt. Den adopterer også billeder: det fremhævede billede og alle eksterne <img>-elementer i brødteksten hentes ind i dit mediebibliotek og omskrives til lokale referencer, med alternativ tekst afledt af titlen, når kilden ikke har nogen. Hvis et billede ikke kan hentes, droppes det pågældende billede, og artiklen publiceres alligevel — en defekt CDN upstream bør ikke koste dig historien.
For indholdsmapperne er status som standard published. Send source_ref, hvis du ønsker, at genlevering opdaterer frem for at duplikere.
Når noget går galt
Hver levering registreres med sine headere, body og resultat, så en fejl kan inspiceres frem for at gå tabt.
php bin/fastsite webhook:list
php bin/fastsite webhook:deliveries --limit=20
php bin/fastsite webhook:replay <delivery-id> En payload, som mapperen ikke kan bruge — et manglende id, ingen title, misdannet JSON — markeres som skipped og forsøges ikke igen, fordi genforsøg ikke vil løse det. Alt andet, der kaster en fejl, markeres som failed og forsøges igen med backoff. Replay kører en gemt levering igen fra dens registrerede bytes, så du kan rette en mapper eller et legitimationsoplysning og genbehandle uden at bede afsenderen om at prøve igen.
Hemmeligheder gemmes krypteret og kan vises igen fra dashboardet for at konfigurere en afsender. Foretrækker du at trække frem for at blive skubbet til? REST API'et og MCP-serveren dækker de samme operationer.