Webhooks

Receive real-time events from SPLEXA Universe

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:

HeaderValue
content-typeapplication/json
user-agentsplexa-universe/1.0 (+webhook)
x-splexa-signaturesha256=<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 replay so 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.