Flows

Webhooks

Open in app

Receive real-time events instead of polling. Register an HTTPS endpoint, subscribe to the events you care about, and verify the signature on every delivery.

Available events

payment.succeededeventoptionalA checkout payment cleared; an account is being provisioned
payment.failedeventoptionalA checkout payment failed
kyc.updatedeventoptionalA trader's KYC status changed
*wildcardoptionalSubscribe to every event type

Register an endpoint

POST/v2/webhook-endpoints
User session
Request body
urlstring
required
Your HTTPS receiver
eventsstring[]
required
e.g. ["payment.succeeded","kyc.updated"] or ["*"]
descriptionstringoptionalInternal label
curl
curl -X POST http://localhost:8000/v2/webhook-endpoints \
  -H "Authorization: Bearer <app_access_token>" \
  -H "X-Session-Token: <user_session_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yourapp.com/api/webhooks",
    "events": ["payment.succeeded", "kyc.updated"],
    "description": "Production receiver"
  }'
200 OK — secret shown once
{
  "id": "whe_...",
  "url": "https://yourapp.com/api/webhooks",
  "events": ["payment.succeeded", "kyc.updated"],
  "active": true,
  "description": "Production receiver",
  "secret": "whsec_...   // store it — used to verify signatures"
}

Delivery format

Each delivery is a POST with a JSON envelope and signed headers.

POST https://yourapp.com/api/webhooks
Content-Type: application/json
X-Hyperscaled-Event: payment.succeeded
X-Hyperscaled-Delivery: whd_...
X-Hyperscaled-Timestamp: 1750640000
X-Hyperscaled-Signature: t=1750640000,v1=9f86d081...

{
  "type": "payment.succeeded",
  "data": { "payment_id": "pay_...", "user_id": "usr_...", "stripe_payment_intent_id": "pi_..." },
  "timestamp": "2026-06-22T23:13:20+00:00"
}

Verify the signature

HMAC-SHA256 over `{timestamp}.{raw_body}` using your endpoint secret. Reject deliveries older than ~5 minutes.

app/api/webhooks/route.ts
import crypto from "node:crypto";

export async function POST(req: Request) {
  const raw = await req.text();
  const header = req.headers.get("x-hyperscaled-signature") ?? "";
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const ts = Number(parts.t);

  // Reject stale deliveries (replay protection).
  if (Math.abs(Date.now() / 1000 - ts) > 300) return new Response("stale", { status: 400 });

  const expected = crypto
    .createHmac("sha256", process.env.HSC_WEBHOOK_SECRET!)
    .update(`${ts}.${raw}`)
    .digest("hex");

  if (!crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected))) {
    return new Response("bad signature", { status: 401 });
  }

  const event = JSON.parse(raw); // { type, data, timestamp }
  // ...handle event.type
  return new Response("ok");
}

This app ships a receiver

See app/api/webhooks/route.ts in this repo for a working verifier wired to HSC_WEBHOOK_SECRET.

List & remove endpoints

GET/v2/webhook-endpoints
User session
200 OK
[
  {
    "id": "whe_...",
    "url": "https://yourapp.com/api/webhooks",
    "events": ["payment.succeeded", "kyc.updated"],
    "active": true,
    "description": "Production receiver"
  }
]
GET
/v2/webhook-endpoints

Runs live against your environment using the app's server-side credentials and your session. Sign in to the dashboard first for authenticated reads.

Deactivate with DELETE /v2/webhook-endpoints/{id}.