Skip to main content
Version: v2

Webhooks

Receive push notifications when an order changes status, instead of (or in addition to) polling.

Setup

Webhook endpoints are configured per partner — contact support@n.exchange with:

  1. your HTTPS endpoint URL,
  2. the event type you want: order status changes and/or KYC status changes,
  3. the payload API version — request v2 so payloads match the v2 REST responses (v1 remains the default for legacy configurations),
  4. optionally, a signing secret for HMAC verification (recommended).

Delivery

  • POST to your URL with Content-Type: application/json.
  • One delivery attempt per status change with a 5-second timeout — respond 2xx fast (enqueue internally; don't process synchronously).
  • No automatic retry on failure. Webhooks are a latency optimization, not a source of truth: keep a reconciliation sweep via GET /orders/{unique_reference}/ to catch missed deliveries.
  • Endpoints failing persistently (error rate ≥ 50% over the monitoring window) are auto-disabled; re-enabling is manual via support. Monitor your endpoint's health.

Payload

For order-status events with v2 payload version, the body is the same shape as GET /orders/{unique_reference}/ (the OrderV2 detail representation), plus a top-level delivery-deduplication id:

{
"id": "wh_1f4a9c2e7b3d48f0a1c2e7b3d48f0a1c",
"unique_reference": "ABCDEF123456",
"created_on": "2026-05-18T10:15:30Z",
"side": "BUY",
"status": "PAID",
"deposit_amount": "500.00000000",
"withdraw_amount": "0.01061111",
"deposit_currency": "USDTERC",
"withdraw_currency": "BTC",
"deposit_address": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
"deposit_address_extra_id": "",
"withdraw_address": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
"withdraw_address_extra_id": "",
"refund_address": "",
"refund_address_extra_id": "",
"payment_window_minutes": 60,
"fixed_rate_deadline": "2026-05-18T11:15:30Z",
"rate": "45000.00000000",
"deposit_transaction": "8a3f4c2d9e1b6a5f8c2d9e1b6a5f8c2d9e1b6a5f8c2d9e1b6a5f8c2d9e1b6a5f",
"withdraw_transaction": ""
}
  • id is stable per (order, status change) — use it to deduplicate; the same event redelivered carries the same id.
  • With the legacy v1 payload version, the body is the v1 order representation instead (nested pair, numeric status via status_name, amount_base/amount_quote, different extra-ID field names). If you consume v1 webhooks today, prefer migrating the webhook config to v2 together with your REST migration — mixing v1 webhooks with v2 REST means reconciling two field vocabularies.
Field truth

Treat the webhook payload as a notification; on receipt, confirm current state with GET /orders/{unique_reference}/ before acting on money-moving logic. This also makes your handler robust to payload-version differences.

Signature verification

When a signing secret is configured, each request carries:

HeaderValue
X-Webhook-TimestampUnix seconds at signing time
X-Webhook-Signaturesha256= + hex HMAC-SHA256 over "{timestamp}." + raw_body, keyed with your secret

Verification steps:

  1. Reject if X-Webhook-Timestamp is older than ~5 minutes (replay protection).
  2. Compute HMAC_SHA256(secret, timestamp + "." + raw_body) over the raw request body (before any JSON parsing).
  3. Constant-time-compare against the header value after the sha256= prefix.
Node.js verification
import { createHmac, timingSafeEqual } from 'node:crypto';

function verifyWebhook(rawBody, headers, secret) {
const ts = headers['x-webhook-timestamp'];
const sig = headers['x-webhook-signature'] ?? '';
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
const expected = 'sha256=' + createHmac('sha256', secret).update(`${ts}.${rawBody}`).digest('hex');
const a = Buffer.from(sig);
const b = Buffer.from(expected);
return a.length === b.length && timingSafeEqual(a, b);
}

Handler checklist

  • Respond 2xx within 5 seconds; process asynchronously
  • Deduplicate on id
  • Verify the HMAC signature and timestamp window
  • Confirm state via GET /orders/{unique_reference}/ before side effects
  • Keep the polling reconciliation sweep as backstop (no automatic redelivery)

Next steps