DNZ Digita
← Back to Voice docs

DNZ Voice Docs

Webhooks & Events

Set webhook URL per app in the console. DNZ POSTs JSON events to your endpoint.

Configure

Console → Apps → select app → Webhook URL.

PATCH /v1/apps/{appId}/webhook with JWT from developer login.

The response returns webhookSecret (dnz_whsec_…) — save it, you need it to verify signatures. It is also returned by GET /v1/apps.

PATCH https://voice-api.dnzteam.online/v1/apps/app_xxxx/webhook
Authorization: Bearer <developer_jwt>

{ "webhookUrl": "https://your-server.com/voice-events" }

→ { "ok": true, "appId": "app_xxxx", "webhookUrl": "...", "webhookSecret": "dnz_whsec_..." }

Event payload

room.started — { roomId }

room.ended — { roomId }

peer.joined / peer.left — { roomId, peerId, uid }

{
  "event": "room.started",
  "appId": "app_abc",
  "payload": { "roomId": "class_101" },
  "ts": "2026-07-01T12:00:00.000Z"
}

Verify the signature (required in production)

Every request includes X-DNZ-Timestamp and X-DNZ-Signature: sha256=<hex>.

Compute HMAC-SHA256 of `${timestamp}.${rawBody}` using your webhookSecret and compare with the signature (constant-time compare).

Reject requests where the timestamp is more than a few minutes old to prevent replay.

import crypto from "crypto";

function verifyDnzWebhook(rawBody, timestamp, signatureHeader, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");
  const provided = signatureHeader.replace("sha256=", "");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(provided));
}

Related content

Support