This endpoint should be implemented by the partner to receive real-time event notifications from Payward Embed.
When specific events occur (such as a quote being executed), Payward sends a POST request to your configured webhook URL with event details.
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.
The signature header follows the format:
t={timestamp},v1={signature}
Where:
t - Unix timestamp (seconds) when the webhook was sentv1 - Hex-encoded HMAC-SHA256 signatureExample header:
X-Signature: t=1734567890,v1=5f2b3c4d1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d
To verify the webhook signature:
t) and signature (v1) from the X-Signature header{timestamp}.{raw_request_body}v1 value from the headerimport 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)
)
);
}
The timestamp in the signature header indicates when the webhook was sent. To prevent replay attacks, you should:
X-Signature headerfunction isTimestampValid(timestamp: number, toleranceSeconds: number = 300): boolean {
const currentTime = Math.floor(Date.now() / 1000);
return Math.abs(currentTime - timestamp) <= toleranceSeconds;
}
| Event Type | Description |
|---|---|
custom_order.executed | A custom order has been successfully executed |
custom_order.execution_failed | A custom order execution has failed |
custom_order.cancelled | A custom order was cancelled |
quote.executed | A quote has been successfully executed |
quote.execution_failed | A quote execution has failed |
quote.cancelled | A quote was cancelled before execution |
deposit.status_updated | The status of a deposit has been updated |
withdrawal.status_updated | The status of a withdrawal has been updated |
user.verified | A user has been verified and can now trade on the platform |
user.closed | A user’s account has been permanently closed |
user.disabled | A user’s account has been disabled (e.g., due to a trade lock or account suspension) |
reward.paid | An Earn reward has been paid to a user |
webhook.test | A test event sent via the Test Webhook endpoint |
All webhook requests include these headers:
| Header | Description |
|---|---|
Content-Type | Always application/json |
X-Signature | Timestamped HMAC-SHA256 signature (format: t={timestamp},v1={signature}) |
X-Webhook-Event | The event type (e.g., quote.executed) |
X-Webhook-ID | The webhook configuration ID |
curl --request POST \
--url https://your-webhook-endpoint.com/webhooks/payward/events \
--header 'Content-Type: <content-type>' \
--header 'X-Signature: <x-signature>' \
--header 'X-Webhook-Event: <x-webhook-event>' \
--header 'X-Webhook-ID: <x-webhook-id>' \
--data '
{
"event_type": "custom_order.executed",
"iiban": "AA00 TEST EXAM PLE1",
"id": "AEGXGV-JZ4I2-6QXEJS",
"timestamp": "2025-01-15T10:30:00.000000000Z",
"action": {
"amount": {
"asset": "USD",
"amount": "100.00",
"asset_class": "currency"
},
"quote": {
"asset": "BTC"
},
"type": "spend"
},
"trigger": {
"type": "price",
"base_asset": "BTC",
"quote_asset": "USD",
"target_price": "50000.00",
"condition": "lte"
}
}
'Documentation Index
Fetch the complete documentation index at: https://kraken-sandbox.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
Timestamped HMAC-SHA256 signature in the format t={timestamp},v1={signature}.
Use this to verify the webhook authenticity and prevent replay attacks.
"t=1734567890,v1=5f2b3c4d1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d"
The type of event that triggered this webhook.
"quote.executed"
The ID of the webhook configuration that received this event.
"WHTEST-XXXXX-EXAMPLE"
application/json Sent when a custom order has been successfully executed. The order's trade action was triggered by the configured price condition and completed without error.
The event type identifier.
custom_order.executed "custom_order.executed"
The Internet International Bank Account Number (IIBAN) of the user whose custom order was executed.
"AA00 TEST EXAM PLE1"
The unique identifier of the custom order (scheduled action ID).
"AEGXGV-JZ4I2-6QXEJS"
The timestamp when the order was executed (ISO-8601 format).
"2025-01-15T10:30:00.000000000Z"
The trade action configured for the custom order.
Show child attributes
The price trigger that initiated the custom order execution.
Show child attributes
Webhook received and processed successfully. Return any 2xx status code to acknowledge receipt.
curl --request POST \
--url https://your-webhook-endpoint.com/webhooks/payward/events \
--header 'Content-Type: <content-type>' \
--header 'X-Signature: <x-signature>' \
--header 'X-Webhook-Event: <x-webhook-event>' \
--header 'X-Webhook-ID: <x-webhook-id>' \
--data '
{
"event_type": "custom_order.executed",
"iiban": "AA00 TEST EXAM PLE1",
"id": "AEGXGV-JZ4I2-6QXEJS",
"timestamp": "2025-01-15T10:30:00.000000000Z",
"action": {
"amount": {
"asset": "USD",
"amount": "100.00",
"asset_class": "currency"
},
"quote": {
"asset": "BTC"
},
"type": "spend"
},
"trigger": {
"type": "price",
"base_asset": "BTC",
"quote_asset": "USD",
"target_price": "50000.00",
"condition": "lte"
}
}
'