Skip to main content

Webhook Events

POST 

/webhooks/kraken/events

This endpoint should be implemented by the partner to receive real-time event notifications from Kraken Embed.

When specific events occur (such as a quote being executed), Kraken sends a POST request to your configured webhook URL with event details.

Security

All webhook requests include an X-Signature header containing a timestamped HMAC-SHA256 signature. Partners should verify this signature to ensure the authenticity and integrity of the request.

Signature Header Format

The signature header follows the format:

t={timestamp},v1={signature}

Where:

  • t - Unix timestamp (seconds) when the webhook was sent
  • v1 - Hex-encoded HMAC-SHA256 signature

Example header:

X-Signature: t=1734567890,v1=5f2b3c4d1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d

Signature Verification

To verify the webhook signature:

  1. Extract the timestamp (t) and signature (v1) from the X-Signature header
  2. Construct the signed payload by concatenating: {timestamp}.{raw_request_body}
  3. Base64-decode your webhook secret (received when registering the webhook)
  4. Compute HMAC-SHA256 of the signed payload using the decoded secret
  5. Compare the computed signature (hex-encoded) with the v1 value from the header
import crypto from 'crypto';

interface ParsedSignature {
timestamp: number;
signatures: string[];
}

// Parse the X-Signature header
function parseSignatureHeader(header: string): ParsedSignature | null {
let timestamp: number | null = null;
const signatures: string[] = [];

for (const part of header.split(',')) {
if (part.startsWith('t=')) {
timestamp = parseInt(part.slice(2), 10);
} else if (part.startsWith('v1=')) {
signatures.push(part.slice(3));
}
}

if (timestamp === null) return null;
return { timestamp, signatures };
}

// Verify the webhook signature
function verifyWebhookSignature(
signatureHeader: string,
body: string,
secretBase64: string
): boolean {
const parsed = parseSignatureHeader(signatureHeader);
if (!parsed || parsed.signatures.length === 0) {
return false;
}

// Construct signed payload: {timestamp}.{body}
const signedPayload = `${parsed.timestamp}.${body}`;

// Decode base64 secret
const secretBytes = Buffer.from(secretBase64, 'base64');

// Compute HMAC-SHA256
const computedSignature = crypto
.createHmac('sha256', secretBytes)
.update(signedPayload)
.digest('hex');

// Compare with provided signatures (constant-time comparison recommended)
return parsed.signatures.some(sig =>
crypto.timingSafeEqual(
Buffer.from(computedSignature),
Buffer.from(sig)
)
);
}

Timestamp Validation

The timestamp in the signature header indicates when the webhook was sent. To prevent replay attacks, you should:

  1. Parse the timestamp from the X-Signature header
  2. Compare it against the current time
  3. Reject webhooks with timestamps older than your tolerance window (e.g., 5 minutes)
function isTimestampValid(timestamp: number, toleranceSeconds: number = 300): boolean {
const currentTime = Math.floor(Date.now() / 1000);
return Math.abs(currentTime - timestamp) <= toleranceSeconds;
}

Event Types

Event TypeDescription
quote.executedA quote has been successfully executed
quote.execution_failedA quote execution has failed
quote.cancelledA quote was cancelled before execution
user.verifiedA user has been verified and can now trade on the platform
webhook.testA test event sent via the Test Webhook endpoint

Headers

All webhook requests include these headers:

HeaderDescription
Content-TypeAlways application/json
X-SignatureTimestamped HMAC-SHA256 signature (format: t={timestamp},v1={signature})
X-Webhook-EventThe event type (e.g., quote.executed)
X-Webhook-IDThe webhook configuration ID

Request

Responses

Webhook received and processed successfully. Return any 2xx status code to acknowledge receipt.