Webhooks
INCLUXA 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
INCLUXA 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.
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:
{
"event": "scan.completed",
"timestamp": "2025-04-12T14:23:00Z",
"tenantSlug": "your-org",
"data": {
// event-specific data
}
}Event types
| Event | When it fires |
|---|---|
| webhook.test | Manual test from the portal |
| scan.completed | A WCAG scan job has finished |
| scan.failed | A WCAG scan job failed |
| profile.updated | A user accessibility profile was updated |
| preset.applied | A preset was applied to a user profile |
| iep.processed | An IEP document has been parsed and mapped |
| iep.failed | IEP processing failed |
| plan.upgraded | Tenant plan was upgraded |
| plan.downgraded | Tenant plan was downgraded |
| api_key.revoked | An API key was revoked |
| member.invited | A new team member was invited |
| member.removed | A team member was removed |
Example payloads
scan.completed
{
"event": "scan.completed",
"timestamp": "2025-04-12T14:23:00Z",
"tenantSlug": "your-org",
"data": {
"jobId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"url": "https://example.com",
"pagesScanned": 12,
"issuesFound": 47,
"criticalIssues": 3,
"wcagLevel": "AA",
"completedAt": "2025-04-12T14:22:58Z"
}
}iep.processed
{
"event": "iep.processed",
"timestamp": "2025-04-12T09:00:00Z",
"tenantSlug": "your-org",
"data": {
"documentId": "8a1b2c3d-...",
"studentId": "stu_456",
"accommodationsFound": 8,
"featuresActivated": 5
}
}Verifying webhook signatures
INCLUXA signs each webhook payload with an HMAC-SHA256 signature using your webhook secret. The signature is sent in the X-INCLUXA-Signature header.
Always verify the signature before processing a webhook. This prevents attackers from sending fake events to your endpoint.
import crypto from 'crypto'
export function verifyWebhook(
payload: string, // raw request body (string, not parsed)
signature: string, // X-INCLUXA-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-incluxa-signature') ?? ''
if (!verifyWebhook(payload, signature, process.env.INCLUXA_WEBHOOK_SECRET!)) {
return new Response('Unauthorized', { status: 401 })
}
const event = JSON.parse(payload)
// process event...
return new Response('OK')
}
Wire up your endpoint
Configure a webhook in the portal, send a test event, then verify signatures before you go live.