På denne side
Webhooks
Registrér et HTTPS-endpoint, abonnér på hændelser, og Grundfast POST’er en signeret JSON-payload til dig, når de sker. Hver leverance signeres med HMAC-SHA256, så du kan verificere, at den kom fra Grundfast, og leveringen genforsøges med backoff, hvis dit endpoint er nede. Webhooks administreres med din session (i dashboardet eller på /v1/me/webhooks) — ikke med en API-nøgle.
1 · Registrér et endpoint
Opret et endpoint med mindst én event.
POST /v1/me/webhooks # session-cookie-auth (mint den i dashboardet) Content-Type: application/json { "url": "https://example.com/hooks/grundfast", // skal være public https "events": ["key.created", "usage.threshold"], "description": "prod alerting" // valgfri, max 200 tegn } → 201 { "webhook": { "id": "webhook_…", "url": "https://example.com/hooks/grundfast", "events": ["key.created", "usage.threshold"], "enabled": true, "description": "prod alerting", "secretPrefix": "whsec_a1b2c", "secret": "whsec_…", // ← VISES KUN HER. Gem den; den kan ikke hentes igen. "createdAt": "2026-07-03T10:00:00.000Z" } }
Hele administrations-fladen er session-only. En gf_live_/gf_test_-nøgle kan læse data, men ikke administrere webhooks:
/v1/me/webhooksList dine endpoints (uden secrets).
/v1/me/webhooksRegistrér et endpoint. Signing-secret returneres ÉN gang.
/v1/me/webhooks/:idOpdatér url / events / description / enabled.
/v1/me/webhooks/:idFjern et endpoint.
/v1/me/webhooks/:id/deliveriesSeneste leveringsforsøg — audit-loggen.
/v1/me/webhooks/:id/testSend en engangs-signeret ping (event "ping") og se resultatet med det samme.
2 · Event-katalog
Kataloget holdes bevidst lille og stabilt. Hver event bærer et data-objekt med netop disse felter:
key.created{ key_id, name, permission, prefix }En API-nøgle blev oprettet.
key.revoked{ key_id }En API-nøgle blev tilbagekaldt.
subscription.updated{ tier }Abonnementets plan (tier) ændrede sig.
usage.threshold{ tier, threshold_pct, used, allowance, month }Månedsforbruget krydsede 80% eller 100% af planens kvote (deduplikeret pr. måned+tærskel).
Test-ping’en fra /test bruger event-navnet ping og tælles ikke som en rigtig event.
3 · Payload & headers
Body’en er altid { id, event, created, data } — created er ISO-8601 (UTC). Ved siden af signaturen sender vi event-navnet og leverings-id’et som headers, så du kan route og deduplikere uden at parse body’en:
POST https://example.com/hooks/grundfast Content-Type: application/json User-Agent: Grundfast-Webhooks/1.0 X-Grundfast-Event: key.created X-Grundfast-Delivery: webhookDelivery_… X-Grundfast-Signature: sha256=<hex> # HMAC-SHA256 af den rå body, keyed med din secret { "id": "webhookDelivery_…", // matcher X-Grundfast-Delivery "event": "key.created", "created": "2026-07-03T10:00:00.000Z", "data": { "key_id": 42, "name": "prod", "permission": "read_write", "prefix": "gf_live_a1b2" } }
4 · Verificér signaturen
X-Grundfast-Signature er sha256= efterfulgt af den hex-kodede HMAC-SHA256 af den rå request-body, keyed med din whsec_-secret. Genberegn den over de eksakte bytes (før JSON.parse) og sammenlign i konstant tid. Copy-paste:
import { createHmac, timingSafeEqual } from 'node:crypto'; /** * Verificér en Grundfast-webhook-leverance. * @param rawBody De EKSAKTE bytes fra request-body'en (verificér FØR JSON.parse). * @param header Værdien af 'X-Grundfast-Signature' (formen "sha256=<hex>"). * @param secret whsec_-secret'en fra da du oprettede endpointet. */ export function verifyGrundfastWebhook( rawBody: string, header: string | null, secret: string, ): boolean { if (!header) return false; const expected = 'sha256=' + createHmac('sha256', secret).update(rawBody, 'utf8').digest('hex'); const a = Buffer.from(header); const b = Buffer.from(expected); // Konstant-tids-sammenligning (undgå timing-læk); længde-tjek først, ellers kaster timingSafeEqual. return a.length === b.length && timingSafeEqual(a, b); } // Express-eksempel — bemærk: læs den RÅ body, ikke et re-serialiseret objekt. import express from 'express'; const app = express(); app.post('/hooks/grundfast', express.raw({ type: 'application/json' }), (req, res) => { const rawBody = req.body.toString('utf8'); const sig = req.header('X-Grundfast-Signature'); if (!verifyGrundfastWebhook(rawBody, sig, process.env.GRUNDFAST_WEBHOOK_SECRET!)) { return res.status(401).send('bad signature'); } const event = JSON.parse(rawBody); // …håndtér event.event / event.data. Svar 2xx hurtigt; gør det tunge arbejde async. res.sendStatus(200); });
5 · Retry & levering
En leverance regnes som lykkedes ved en 2xx inden for et 10-sekunders timeout. Alt andet (non-2xx, timeout, netværksfejl) genforsøges med voksende backoff — op til 5 forsøg i alt:
forsøg 1 → (fejl) → vent 1 min forsøg 2 → (fejl) → vent 5 min forsøg 3 → (fejl) → vent 30 min forsøg 4 → (fejl) → vent 2 timer forsøg 5 → (fejl) → status "failed" (opgives)
Redirects følges ikke (et 30x fejler leverancen — en ekstra SSRF-værn). Hver leverance står i loggen som pending, success eller failed med antal forsøg og seneste HTTP-status — hent den på GET /v1/me/webhooks/:id/deliveries. Dit endpoint bør være idempotent: svar hurtigt 2xx og lav det tunge arbejde asynkront.
Tilbage til API-dokumentationen eller se den fulde SDK-reference.