Documentatie
Uitgaande webhooks: payload en handtekening
Ontvang AutoPSEO-meldingen en -rapporten als ondertekende JSON op elk HTTPS-eindpunt: de request headers, de velden in de body, de eventtypen en hoe je de handtekening controleert.
Een uitgaande webhook stuurt de meldingen, geplande rapporten, testberichten en rapportsamenvattingen van AutoPSEO naar een eindpunt dat jij beheert, als één JSON-POST per bericht, ondertekend met HMAC-SHA256. Gebruik hem om Zapier, Make, n8n, een incidenttool of je eigen code te voeden; er is geen Slack- of Teams-account nodig. Zoek je de webhook die artikelen op je site aflevert? Die hoort bij de contentengine: zie contentlevering.
Een webhook toevoegen
- Klik in het onderdeel Outbound webhooks op New webhook.
- Geef de webhook een Name en plak de URL van het eindpunt. Die moet openbaar HTTPS zijn: gewone
http,localhost, namen die eindigen op.localof.internal, hosts zonder punt en privé- of link-local-IP-adressen worden geweigerd. - Kopieer het signing secret dat na het opslaan verschijnt. Het wordt maar één keer getoond.
- Klik op Send test om een
test-event te versturen en koppel daarna geplande rapporten of meldingen aan de webhook.
Hoe een webhookverzoek eruitziet
Elk bericht is een POST met deze headers:
| Header | Waarde |
|---|---|
Content-Type | application/json |
User-Agent | AutoPSEO-Webhook/1.0 |
X-AutoPSEO-Event | Het eventtype, bijvoorbeeld traffic_drop |
X-AutoPSEO-Signature | sha256= gevolgd door 64 hexadecimale tekens in kleine letters |
Hieronder staat een verkeersdalingsmelding voor een domeinproperty uit de run van 06:00 UTC op 12 oktober 2026, opgemaakt om te lezen (over de lijn is het compacte JSON, en de waarden van id en site_id zijn verzonnen):
{
"id": "9b2e61f04c3a4d7e8f15a0b6c2d39e47",
"event_type": "traffic_drop",
"severity": "high",
"site_id": "4f0c1e9a2b7d4c58",
"site_url": "sc-domain:example.com",
"occurred_at": "2026-10-12T06:00:04.183Z",
"title": "Traffic drop: example.com",
"text": "Clicks 1,500 vs 2,000 in the previous window (-25.0%).",
"url": "https://app.autopseo.com/property/sc-domain%3Aexample.com",
"payload": {
"fields": [
{ "label": "Metric", "value": "clicks" },
{ "label": "Current", "value": "1,500" },
{ "label": "Baseline", "value": "2,000" },
{ "label": "Window", "value": "7 days (2026-10-05 → 2026-10-11 vs 2026-09-28 → 2026-10-04)" },
{ "label": "Change", "value": "-25.0%" }
],
"table": null,
"detector": "traffic_drop",
"window": 7,
"range": { "start": "2026-10-05", "end": "2026-10-11", "compStart": "2026-09-28", "compEnd": "2026-10-04" },
"result": {
"fired": true,
"metric": "clicks",
"value": 1500,
"baseline": 2000,
"change": -0.25,
"severity": "high",
"summary": "Clicks 1,500 vs 2,000 in the previous window (-25.0%)."
}
},
"source": "autopseo"
}
De velden op het hoogste niveau zijn voor elk event hetzelfde:
| Veld | Wat het bevat |
|---|---|
id | Een willekeurige hexadecimale string van 32 tekens, nieuw bij elk verzoek |
event_type | Wat er is gebeurd (zie de volgende sectie) |
severity | critical, high, medium of low voor meldingen; info voor rapporten, samenvattingen en tests |
site_id | De id van AutoPSEO voor de property, of null voor tests en de demo-property |
site_url | De Search Console-property, zoals sc-domain:example.com of https://www.example.com/, of null voor tests |
occurred_at | Het moment waarop het bericht is opgebouwd, als ISO 8601-tijdstempel in UTC |
title, text | De kop en de samenvattende zin die Slack en Teams tonen |
url | Het rapport in AutoPSEO, of null voor tests |
payload.fields | De gelabelde waarden die Slack als velden en Teams als feiten toont |
payload.table | columns en rows voor rapporten en samenvattingen, anders null |
source | Altijd autopseo |
Verhoudingen zijn breuken: een change van -0.25 is een daling van 25% en een ctr van 0.034 is 3,4%.
Eventtypen
event_type | Verzonden wanneer | Extra sleutels in payload |
|---|---|---|
traffic_drop, traffic_spike, position_drop, ctr_drop, new_lost_rankings, anomaly, index_flip, rank_drop, audit_regression | Een meldingsregel wordt geactiveerd, volgens schema of via Test rule | detector, window, range (de datums, of null voor index_flip, rank_drop en audit_regression), result |
scheduled_report | Een gepland rapport wordt verstuurd, volgens schema of via Send now | reportType (summary, queries, pages of rankings), range (datums plus een label), totals, previous |
report_summary | Iemand gebruikt Send to Slack op een rapportpagina | Geen: de cijfers staan in fields en table |
test | Iemand klikt op Send test bij de webhook | sample, altijd true |
result herhaalt wat de detector heeft gemeten: fired, metric, value, baseline, change (null als er niets is om mee te vergelijken), severity en summary, plus z, de gemodificeerde z-score, voor anomaly. totals en previous bevatten clicks, impressions, ctr en position voor de twee periodes van een rapport. Wat elke detector meet, staat beschreven onder Alerts.
De handtekening controleren
De handtekening is een HMAC-SHA256 van exact de bytes die AutoPSEO heeft verstuurd, met het signing secret van de webhook als sleutel, geschreven als sha256= plus hexadecimale tekens in kleine letters. Zo controleer je haar:
- Lees de ruwe request body voordat je JSON parset. Opnieuw parsen en serialiseren kan de bytes veranderen.
- Bereken HMAC-SHA256 over die bytes en gebruik het secret als tekst: de string van 64 tekens precies zoals getoond, niet gedecodeerd uit hex.
- Zet
sha256=ervoor en vergelijk het resultaat in constante tijd met de headerX-AutoPSEO-Signature. - Weiger het verzoek als ze verschillen. Antwoord anders meteen met een 2xx-status en doe het trage werk daarna.
Een ontvanger voor Node.js zonder afhankelijkheden:
import { createServer } from 'node:http';
import { createHmac, timingSafeEqual } from 'node:crypto';
const secret = process.env.AUTOPSEO_WEBHOOK_SECRET;
createServer((req, res) => {
const chunks = [];
req.on('data', (chunk) => chunks.push(chunk));
req.on('end', () => {
const raw = Buffer.concat(chunks);
const expected = Buffer.from('sha256=' + createHmac('sha256', secret).update(raw).digest('hex'));
const received = Buffer.from(String(req.headers['x-autopseo-signature'] || ''));
if (received.length !== expected.length || !timingSafeEqual(received, expected)) {
res.writeHead(401).end();
return;
}
res.writeHead(204).end();
const event = JSON.parse(raw.toString('utf8'));
console.log(event.event_type, event.severity, event.site_url);
});
}).listen(8080);
De lengtecontrole komt eerst, omdat timingSafeEqual in Node een fout geeft wanneer de twee buffers in lengte verschillen.
Afleverregels
- Elk bericht krijgt één poging met een timeout van 10 seconden. Elk antwoord anders dan 2xx geldt als mislukt en wordt bewaard in Delivery history, met de statuscode en het begin van je response body.
- Redirects worden niet gevolgd. Een 3xx-antwoord mislukt met de melding om de definitieve URL te gebruiken.
- Er wordt niets op een timer opnieuw geprobeerd. Een mislukte melding wordt niet als verzonden gemarkeerd, dus bij de volgende controle wordt ze opnieuw beoordeeld en geplaatst als ze nog steeds afgaat: de volgende ochtend voor de meeste detectors, het volgende uur voor indexflips. Een mislukt gepland rapport wordt in dezelfde periode niet opnieuw verstuurd, gebruik dus Send now nadat je het eindpunt hebt hersteld.
idwordt per verzoek aangemaakt en identificeert dus een aflevering, niet een event. Om meldingen te ontdubbelen combineer jeevent_type,site_urlen de datum vanoccurred_at: een regel plaatst maximaal één bericht per property per UTC-dag, tenzij iemand Test rule uitvoert.
Het signing secret
AutoPSEO toont het signing secret één keer, bij het opslaan van de webhook, en de lijst toont het daarna nooit meer. Bewaar het meteen bij de configuratie van je ontvanger, naast het eindpunt dat het beschermt.
De URL zelf wordt versleuteld opgeslagen en de lijst toont alleen de host en de laatste tekens. Als je een webhook verwijdert, verwijder je ook alle regels die erop posten. Wil je een leesbare kaart in een Teams-kanaal in plaats van JSON, gebruik dan Microsoft Teams.