NEWSADA Title II web deadlines: April 24, 2026 (50k+ pop) · April 26, 2027 (under 50k) — Is your site compliant?ADA Title II: April 2026 & 2027 deadlinesLearn more →
Webhooks
Angstroma can send HTTP POST requests to your endpoint when events occur in your account — scan completions, profile updates, plan changes, and more. Webhooks are configured in the portal under Settings → Organization.
Delivery
Angstroma uses an outbox pattern — each event is persisted before delivery is attempted. If your endpoint returns a non-2xx status, delivery is retried with exponential backoff. Failed deliveries can be retried or replayed from the Webhooks page in the portal.
Your endpoint must respond within 10 seconds or the delivery is treated as failed. Return 200 OK as quickly as possible — process the payload asynchronously.
Tip
Use the Send test webhook button in the portal to verify your endpoint before going live. The test event type is webhook.test.
Payload format
All webhook payloads are JSON with a consistent structure:
Angstroma signs each webhook payload with an HMAC-SHA256 signature using your webhook secret. The signature is sent in the X-Angstroma-Signature header.
Important
Always verify the signature before processing a webhook. This prevents attackers from sending fake events to your endpoint.
webhook-handler.ts
import crypto from 'crypto'
export function verifyWebhook(
payload: string, // raw request body (string, not parsed)
signature: string, // X-Angstroma-Signature header value
secret: string // your webhook secret from portal settings
): boolean {
const expected = crypto
.createHmac('sha256', secret)
.update(payload, 'utf8')
.digest('hex')
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
)
}
// In your route handler (Next.js example):
export async function POST(req: Request) {
const payload = await req.text()
const signature = req.headers.get('x-angstroma-signature') ?? ''
if (!verifyWebhook(payload, signature, process.env.ANGSTROMA_WEBHOOK_SECRET!)) {
return new Response('Unauthorized', { status: 401 })
}
const event = JSON.parse(payload)
// process event...
return new Response('OK')
}