Webhooks & Events
Receive real-time notifications about important events in your Quub Exchange account through webhooks. This guide covers webhook setup, event types, security, and best practices.
Overview
Webhooks allow you to:
- Get instant notifications when events occur
- Automate workflows based on trading activity
- Monitor account changes in real-time
- Build event-driven applications
How Webhooks Work
sequenceDiagram
participant Quub
participant Your Server
Note over Quub: Event occurs (e.g., order filled)
Quub->>Your Server: POST /webhook (Event payload)
Your Server->>Quub: 200 OK
Note over Quub: If no response
Quub->>Your Server: Retry (exponential backoff)
Your Server->>Quub: 200 OK
Setting Up Webhooks
1. Create Webhook Endpoint
curl -X POST https://api.quub.exchange/events/v1/webhooks \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "X-Tenant-ID: your-org-id" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-server.com/webhooks/quub",
"events": [
"order.filled",
"order.created",
"deposit.completed"
],
"secret": "your-webhook-secret",
"active": true
}'
Response
{
"webhookId": "wh_abc123",
"url": "https://your-server.com/webhooks/quub",
"events": ["order.filled", "order.created", "deposit.completed"],
"secret": "whsec_xyz789",
"active": true,
"createdAt": "2025-11-03T10:00:00Z"
}
Event Types
Trading Events
| Event | Description | Payload |
|---|---|---|
order.created |
New order placed | Order object |
order.filled |
Order fully executed | Order + fills |
order.partially_filled |
Order partially executed | Order + fills |
order.canceled |
Order canceled | Order object |
order.expired |
Order expired | Order object |
trade.executed |
Trade executed | Trade object |
Account Events
| Event | Description | Payload |
|---|---|---|
deposit.pending |
Deposit initiated | Deposit object |
deposit.completed |
Deposit confirmed | Deposit object |
withdrawal.pending |
Withdrawal requested | Withdrawal object |
withdrawal.completed |
Withdrawal processed | Withdrawal object |
balance.updated |
Balance changed | Balance object |
Compliance Events
| Event | Description | Payload |
|---|---|---|
kyc.approved |
KYC verification approved | User object |
kyc.rejected |
KYC verification rejected | User + reason |
risk.alert |
Risk threshold exceeded | Alert object |
compliance.flagged |
Transaction flagged | Transaction object |
Market Events
| Event | Description | Payload |
|---|---|---|
price.updated |
Price feed updated | Price object |
market.halted |
Trading halted | Market object |
market.resumed |
Trading resumed | Market object |
Webhook Payload Structure
Standard Format
{
"eventId": "evt_abc123xyz",
"eventType": "order.filled",
"timestamp": "2025-11-03T10:30:00Z",
"orgId": "org_456",
"data": {
"orderId": "ord_789",
"symbol": "BTC-USD",
"side": "buy",
"type": "limit",
"status": "filled",
"quantity": 0.1,
"price": 45000.0,
"filled": 0.1,
"averagePrice": 45000.0,
"fills": [
{
"fillId": "fill_111",
"quantity": 0.1,
"price": 45000.0,
"timestamp": "2025-11-03T10:30:00Z"
}
]
}
}
Event Examples
Order Filled
{
"eventId": "evt_order_filled_123",
"eventType": "order.filled",
"timestamp": "2025-11-03T10:30:00Z",
"orgId": "org_456",
"data": {
"orderId": "ord_789",
"symbol": "BTC-USD",
"side": "buy",
"status": "filled",
"quantity": 0.5,
"price": 45000.0,
"filled": 0.5,
"averagePrice": 44995.0,
"totalValue": 22497.5
}
}
Deposit Completed
{
"eventId": "evt_deposit_456",
"eventType": "deposit.completed",
"timestamp": "2025-11-03T11:00:00Z",
"orgId": "org_456",
"data": {
"depositId": "dep_789",
"asset": "USDC",
"amount": 10000.0,
"network": "ethereum",
"txHash": "0x1234...abcd",
"confirmations": 12,
"status": "completed"
}
}
Webhook Security
Signature Verification
Every webhook includes a signature in the X-Quub-Signature header:
X-Quub-Signature: t=1730628000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
Verify Signature (Node.js)
const crypto = require("crypto");
function verifyWebhookSignature(payload, signature, secret) {
const parts = signature.split(",");
const timestamp = parts[0].split("=")[1];
const signatureHash = parts[1].split("=")[1];
// Check timestamp (prevent replay attacks)
const now = Math.floor(Date.now() / 1000);
if (Math.abs(now - timestamp) > 300) {
// 5 minutes
throw new Error("Webhook timestamp too old");
}
// Compute expected signature
const signedPayload = `${timestamp}.${JSON.stringify(payload)}`;
const expectedHash = crypto
.createHmac("sha256", secret)
.update(signedPayload)
.digest("hex");
// Compare signatures (constant-time comparison)
if (
!crypto.timingSafeEqual(
Buffer.from(signatureHash, "hex"),
Buffer.from(expectedHash, "hex")
)
) {
throw new Error("Invalid webhook signature");
}
return true;
}
// Express.js example
app.post("/webhooks/quub", express.json(), (req, res) => {
const signature = req.headers["x-quub-signature"];
const secret = process.env.WEBHOOK_SECRET;
try {
verifyWebhookSignature(req.body, signature, secret);
// Process webhook
const { eventType, data } = req.body;
handleWebhookEvent(eventType, data);
res.status(200).json({ received: true });
} catch (error) {
console.error("Webhook verification failed:", error);
res.status(400).json({ error: "Invalid signature" });
}
});
Verify Signature (Python)
import hmac
import hashlib
import time
import json
def verify_webhook_signature(payload, signature, secret):
parts = signature.split(',')
timestamp = int(parts[0].split('=')[1])
signature_hash = parts[1].split('=')[1]
# Check timestamp
now = int(time.time())
if abs(now - timestamp) > 300: # 5 minutes
raise ValueError('Webhook timestamp too old')
# Compute expected signature
signed_payload = f"{timestamp}.{json.dumps(payload, separators=(',', ':'))}"
expected_hash = hmac.new(
secret.encode(),
signed_payload.encode(),
hashlib.sha256
).hexdigest()
# Compare signatures
if not hmac.compare_digest(signature_hash, expected_hash):
raise ValueError('Invalid webhook signature')
return True
# Flask example
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/webhooks/quub', methods=['POST'])
def handle_webhook():
signature = request.headers.get('X-Quub-Signature')
secret = os.environ['WEBHOOK_SECRET']
try:
verify_webhook_signature(request.json, signature, secret)
# Process webhook
event_type = request.json['eventType']
data = request.json['data']
handle_webhook_event(event_type, data)
return jsonify({'received': True}), 200
except Exception as e:
return jsonify({'error': str(e)}), 400
Handling Webhooks
Best Practices
- Return 200 Quickly
app.post("/webhooks/quub", async (req, res) => {
// Immediately acknowledge receipt
res.status(200).json({ received: true });
// Process asynchronously
processWebhookAsync(req.body);
});
- Idempotency
const processedEvents = new Set();
async function processWebhook(event) {
// Check if already processed
if (processedEvents.has(event.eventId)) {
console.log("Event already processed:", event.eventId);
return;
}
// Process event
await handleEvent(event);
// Mark as processed
processedEvents.add(event.eventId);
}
- Error Handling
async function handleWebhookEvent(eventType, data) {
try {
switch (eventType) {
case "order.filled":
await handleOrderFilled(data);
break;
case "deposit.completed":
await handleDepositCompleted(data);
break;
default:
console.log("Unhandled event type:", eventType);
}
} catch (error) {
console.error("Error processing webhook:", error);
// Log to monitoring service
await logError(error);
}
}
Retry Policy
If your endpoint doesnβt respond with 2xx:
- Immediate Retry: After 1 second
-
Subsequent Retries: Exponential backoff
- 5 seconds
- 25 seconds
- 2 minutes
- 10 minutes
- 1 hour
- Max Attempts: 5 retries over 24 hours
- After Max: Webhook disabled, notification sent
Testing Webhooks
Local Testing with ngrok
# Start ngrok
ngrok http 3000
# Use ngrok URL in webhook config
curl -X POST https://api.quub.exchange/events/v1/webhooks \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "X-Tenant-ID: your-org-id" \
-H "Content-Type: application/json" \
-d '{
"url": "https://abc123.ngrok.io/webhooks/quub",
"events": ["order.filled"]
}'
Trigger Test Event
curl -X POST https://api.quub.exchange/events/v1/webhooks/wh_abc123/test \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "X-Tenant-ID: your-org-id" \
-H "Content-Type: application/json" \
-d '{
"eventType": "order.filled"
}'
Managing Webhooks
List Webhooks
curl -X GET https://api.quub.exchange/events/v1/webhooks \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "X-Tenant-ID: your-org-id"
Update Webhook
curl -X PATCH https://api.quub.exchange/events/v1/webhooks/wh_abc123 \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "X-Tenant-ID: your-org-id" \
-H "Content-Type: application/json" \
-d '{
"events": ["order.filled", "order.created", "trade.executed"],
"active": true
}'
Delete Webhook
curl -X DELETE https://api.quub.exchange/events/v1/webhooks/wh_abc123 \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "X-Tenant-ID: your-org-id"
Monitoring & Logs
View Webhook Logs
curl -X GET https://api.quub.exchange/events/v1/webhooks/wh_abc123/logs \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "X-Tenant-ID: your-org-id"
Response
{
"webhookId": "wh_abc123",
"logs": [
{
"logId": "log_001",
"eventId": "evt_789",
"eventType": "order.filled",
"timestamp": "2025-11-03T10:30:00Z",
"httpStatus": 200,
"responseTime": 142,
"attempts": 1,
"success": true
},
{
"logId": "log_002",
"eventId": "evt_790",
"eventType": "deposit.completed",
"timestamp": "2025-11-03T11:00:00Z",
"httpStatus": 500,
"responseTime": 5000,
"attempts": 3,
"success": false,
"error": "Internal server error"
}
]
}
Alternative: WebSocket Events
For lower latency, use WebSocket connections:
const ws = new WebSocket("wss://api.quub.exchange/events/v1/stream");
ws.onopen = () => {
// Authenticate
ws.send(
JSON.stringify({
type: "auth",
token: "YOUR_ACCESS_TOKEN",
orgId: "org_456",
})
);
// Subscribe to events
ws.send(
JSON.stringify({
type: "subscribe",
events: ["order.filled", "trade.executed"],
})
);
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log("Event received:", data);
};
See Events API Reference for full WebSocket documentation.
Related Resources
- π‘ Events API Reference - Complete API documentation
- π Notifications Service - Email/SMS notifications
- π Observability - Monitoring and metrics
Support
Need help with webhooks?
- π¬ Community Forum
- π§ Email: support@quub.exchange
- π API Reference