Flows
Webhooks
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.succeeded | event | optional | A checkout payment cleared; an account is being provisioned |
payment.failed | event | optional | A checkout payment failed |
kyc.updated | event | optional | A trader's KYC status changed |
* | wildcard | optional | Subscribe to every event type |
Register an endpoint
POST
/v2/webhook-endpointsUser session
Request body
url | string | required | Your HTTPS receiver |
events | string[] | required | e.g. ["payment.succeeded","kyc.updated"] or ["*"] |
description | string | optional | Internal 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-endpointsUser session
200 OK
[
{
"id": "whe_...",
"url": "https://yourapp.com/api/webhooks",
"events": ["payment.succeeded", "kyc.updated"],
"active": true,
"description": "Production receiver"
}
]GET
/v2/webhook-endpointsRuns 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}.