Developers
Read-only score/fixes/citations, one write action (trigger an audit), and outbound webhook subscriptions — all under /api/v1/public/*, authenticated by API key rather than your portal login.
Quickstart
4 steps1. Create a key
In the portal, go to Settings → API Keys and mint one with the
readscope, thetriggerscope, or both. The raw key is shown once — store it like a password.2. Make your first call
Fetch the latest GIST Score for a site:
curl https://api.gistai.app/api/v1/public/sites/SITE_ID/score \ -H "Authorization: Bearer YOUR_API_KEY"
3. Register a webhook
Pick one or more event types:
audit_completed,fix_verified,score_changed. Subscriptions are account-wide (not per-site).curl -X POST https://api.gistai.app/api/v1/public/webhooks \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"url": "https://example.com/gist-hook", "eventTypes": ["audit_completed"]}' # => { "data": { "secret": "…shown once, store it…", "webhook": { "id": "wh_…", … } } }4. Verify each delivery
Every delivery carries
X-GIST-Signature: t=<unix_seconds>,v1=<hmac_sha256>(Stripe-style — the timestamp is signed alongside the raw body, so reject anything older than a few minutes as a possible replay).const crypto = require("crypto"); function verifyGistSignature(secret, rawBody, header) { const match = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(header || ""); if (!match) return false; const [, timestamp, signature] = match; const expected = crypto .createHmac("sha256", secret) .update(`${timestamp}.${rawBody}`) .digest("hex"); const ageSeconds = Math.abs(Date.now() / 1000 - Number(timestamp)); const expectedBuf = Buffer.from(expected, "utf8"); const actualBuf = Buffer.from(signature, "utf8"); return ( ageSeconds <= 300 && expectedBuf.length === actualBuf.length && crypto.timingSafeEqual(expectedBuf, actualBuf) ); }Sign over the exact raw request bytes you received — re-serializing a parsed JSON body can produce different bytes and fail verification even for a genuine delivery.