Overview
SPLEXA delivers events to your HTTPS endpoint as POST requests with a JSON body. Each request is signed with HMAC-SHA256 using your webhook secret so you can verify it really came from SPLEXA.
- You own the endpoint URL — anything internet-reachable over HTTPS works.
- Subscribe per category:
network_event,audit_event. - Respond 2xx within 8 seconds. Anything else is recorded as a failure.
- After 10 consecutive failures, the webhook is auto-disabled and you'll see it flagged in Settings.
Request signing
Every request includes the following headers:
| Header | Value |
|---|---|
| content-type | application/json |
| user-agent | splexa-universe/1.0 (+webhook) |
| x-splexa-signature | sha256=<hex(hmac_sha256(secret, raw_body))> |
Critical: compute the HMAC over the raw request body bytes — not a re-serialized JSON object. Any whitespace difference will produce a different signature. Use a constant-time comparison (crypto.timingSafeEqual, hmac.compare_digest, etc.) to avoid timing attacks.
Verify the signature
Drop-in receivers in your language of choice:
import crypto from 'crypto'
import express from 'express'
const SECRET = process.env.SPLEXA_WEBHOOK_SECRET!
// Body must be the RAW request body (Buffer or string).
// In Express: app.use('/webhooks/splexa', express.raw({ type: '*/*' }))
function verify(rawBody: Buffer, signatureHeader: string | undefined): boolean {
if (!signatureHeader) return false
const [scheme, hex] = signatureHeader.split('=')
if (scheme !== 'sha256' || !hex) return false
const expected = crypto.createHmac('sha256', SECRET).update(rawBody).digest('hex')
// Constant-time compare prevents timing attacks.
const a = Buffer.from(hex, 'hex')
const b = Buffer.from(expected, 'hex')
return a.length === b.length && crypto.timingSafeEqual(a, b)
}
const app = express()
app.post('/webhooks/splexa', express.raw({ type: '*/*' }), (req, res) => {
if (!verify(req.body, req.header('x-splexa-signature'))) {
return res.status(401).send('invalid signature')
}
const event = JSON.parse(req.body.toString('utf8'))
console.log('event:', event.event, 'category:', event.category)
// Respond 2xx within 8s or SPLEXA will mark the delivery failed.
res.status(204).end()
})Event payloads
All events share a top-level shape: event, category, timestamp, plus event-specific fields. Payloads are intentionally compact — fetch the full record from the REST API if you need more.
{
"event": "device.degraded",
"category": "network_event",
"timestamp": "2026-04-29T12:34:56.000Z",
"device": {
"id": "ckxyz...",
"name": "edge-router-04",
"networkId": "ckabc..."
},
"metrics": {
"lossPct": 6.4,
"avgLatencyMs": 215,
"peakLatencyMs": 540
}
}Retries & replay
- SPLEXA does not auto-retry failed deliveries. Treat the webhook as best-effort and reconcile via the REST API for critical workflows.
- Every delivery is recorded with status, duration, and the original payload. Open Settings → Webhooks → Deliveries to inspect history.
- Click Replay on any historical row to re-send the original payload byte-for-byte.
- Replays are recorded in history with category
replayso they're distinguishable from organic events.
Idempotency
For audit events, the audit.id field is unique and stable. For device events, dedupe on device.id + timestamp. If you replay a delivery, the same id is sent again — your handler should be idempotent.