kyve.dev

Webhooks

Event envelope, signature verification, and delivery semantics.

Webhooks are the authoritative way to receive verification results. Polling /v1/verifications/:id works, but webhooks are instant and cheaper.

Event envelope

interface WebhookEventEnvelope<T> {
  id: string;              // evt_...
  type: string;            // e.g. "verification.completed"
  api_version: "2025-01";  // pinned per endpoint
  created: number;         // unix seconds
  livemode: boolean;
  data: { object: T };
}

This mirrors Stripe's event shape. id is globally unique; type identifies the event; data.object is the full resource at the time the event fired.

Headers

Every delivery includes:

  • Content-Type: application/json
  • KYC-Signature: t=<unix_ts>,v1=<hex_hmac_sha256>
  • KYC-Event-Id: evt_... (idempotency key for your receiver)
  • User-Agent: KYC-Webhooks/1.0

Verifying the signature

  1. Read the KYC-Signature header and parse out t and v1.
  2. Compute HMAC-SHA256(secret, t + "." + raw_body).
  3. Compare the hex digest against v1 using a constant-time comparison.
  4. Reject if |now - t| > 300 seconds.
import crypto from 'node:crypto';
 
function verify(secret: string, header: string, rawBody: string) {
  const parts = Object.fromEntries(
    header.split(',').map((kv) => kv.split('=')),
  );
  const t = parts.t;
  const v1 = parts.v1;
  if (!t || !v1) return false;
 
  const skew = Math.abs(Date.now() / 1000 - Number(t));
  if (skew > 300) return false;
 
  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${t}.${rawBody}`)
    .digest('hex');
 
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}

Delivery guarantees

  • At-least-once. Expect occasional duplicates; dedupe on KYC-Event-Id.
  • Retries. Exponential backoff up to 24 hours.
  • Expected response. 2xx within 10 seconds. Anything else is a retry.

Event types (MVP)

TypeFires when
verification.createdPOST /v1/sessions succeeds.
verification.updatedAny status transition.
verification.completedTerminal success.
verification.failedTerminal failure.
verification.review_requiredManual review needed.
billing.subscription.updatedPlan or state change (fiat).
billing.wallet.updatedPrepaid wallet debit/credit.

On this page