Webhooks & HMAC Signatures
v1.0Technical reference for webhook event schemas, cryptographic signature validation, delivery retries, and endpoint management.
SyncNexa webhooks deliver secure, event-driven HTTP POST notifications directly to your application backend.
Webhook Delivery Headers
| Header | Format | Description |
|---|---|---|
X-SyncNexa-Signature | t=1724155200,v1=9a8b... | Timestamp and HMAC-SHA256 signature for payload verification |
X-SyncNexa-Event | verification.completed | The specific event identifier for routing |
X-SyncNexa-Delivery | del_9f8e7d6c5b4a | Unique delivery attempt UUID for idempotency tracking |
Content-Type | application/json | JSON payload encoding |
User-Agent | SyncNexa-Webhooks/1.0 | Standard webhook user agent string |
Event Schemas & Payloads
Sample verification.completed event payload:
verification.completed.jsonjson
| 1 | { |
| 2 | "id": "evt_0a1b2c3d4e5f", |
| 3 | "event": "verification.completed", |
| 4 | "createdAt": "2026-08-20T12:30:00.000Z", |
| 5 | "appId": "app_8a7b6c5d4e3f", |
| 6 | "environment": "live", |
| 7 | "data": { |
| 8 | "sessionId": "sess_9f8e7d6c5b4a", |
| 9 | "verified": true, |
| 10 | "university": { |
| 11 | "name": "University College London", |
| 12 | "domain": "ucl.ac.uk", |
| 13 | "country": "GB" |
| 14 | }, |
| 15 | "claims": { |
| 16 | "is_active_student": true, |
| 17 | "academic_level": "Undergraduate", |
| 18 | "valid_until": "2027-06-30T23:59:59Z" |
| 19 | } |
| 20 | } |
| 21 | } |
Verifying HMAC-SHA256 Signatures
Verify the raw request body with your secret key to prevent replay attacks and man-in-the-middle tampering:
typescript
| 1 | import crypto from 'crypto'; |
| 2 | |
| 3 | export function verifySyncNexaWebhook( |
| 4 | rawBody: string | Buffer, |
| 5 | signatureHeader: string, |
| 6 | secret: string, |
| 7 | toleranceSeconds: number = 300 |
| 8 | ): boolean { |
| 9 | const parts = signatureHeader.split(','); |
| 10 | const timestamp = parts.find((p) => p.startsWith('t='))?.split('=')[1]; |
| 11 | const signature = parts.find((p) => p.startsWith('v1='))?.split('=')[1]; |
| 12 | |
| 13 | if (!timestamp || !signature) return false; |
| 14 | |
| 15 | // Check timestamp freshness to prevent replay attacks |
| 16 | const now = Math.floor(Date.now() / 1000); |
| 17 | if (Math.abs(now - parseInt(timestamp, 10)) > toleranceSeconds) { |
| 18 | return false; |
| 19 | } |
| 20 | |
| 21 | const payload = `${timestamp}.${rawBody.toString()}`; |
| 22 | const computed = crypto |
| 23 | .createHmac('sha256', secret) |
| 24 | .update(payload) |
| 25 | .digest('hex'); |
| 26 | |
| 27 | return crypto.timingSafeEqual( |
| 28 | Buffer.from(computed), |
| 29 | Buffer.from(signature) |
| 30 | ); |
| 31 | } |
Managing Subscriptions via API
POST
/org/v1/webhooksBearer Auth
Create Webhook Subscription
Registers a new webhook URL and subscribes to specified event triggers.
Was this page helpful?