Skip to main content

Webhooks Guide

This guide covers setting up and handling webhooks in your TraderPal Connect integration.

Overview

Webhooks allow your application to receive real-time updates about various events:

  • Order status changes
  • Trade executions
  • Account updates
  • Payment processing
  • KYC status changes

Webhook Setup

Register Webhook Endpoint

const webhook = await client.webhooks.create({
url: 'https://your-app.com/webhooks',
events: ['order.executed', 'account.updated'],
description: 'Production webhook endpoint'
});

console.log(`Webhook ID: ${webhook.id}`);

Webhook Authentication

const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(JSON.stringify(payload))
.digest('hex');

return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}

Event Types

Order Events

{
"type": "order.executed",
"id": "evt_123456789",
"created": "2024-01-01T00:00:00Z",
"data": {
"orderId": "ord_123456789",
"status": "filled",
"symbol": "AAPL",
"quantity": 10,
"price": 150.00,
"side": "buy",
"executedAt": "2024-01-01T00:00:00Z"
}
}

Account Events

{
"type": "account.updated",
"id": "evt_123456789",
"created": "2024-01-01T00:00:00Z",
"data": {
"accountId": "acc_123456789",
"status": "active",
"balance": 10000.00,
"currency": "USD",
"updatedAt": "2024-01-01T00:00:00Z"
}
}

Payment Events

{
"type": "payment.processed",
"id": "evt_123456789",
"created": "2024-01-01T00:00:00Z",
"data": {
"paymentId": "pmt_123456789",
"status": "completed",
"amount": 1000.00,
"currency": "USD",
"type": "deposit",
"processedAt": "2024-01-01T00:00:00Z"
}
}

Webhook Handler

Express Implementation

const express = require('express');
const app = express();

app.post('/webhooks', express.raw({type: 'application/json'}), (req, res) => {
const signature = req.headers['x-tp-signature'];

if (!verifyWebhookSignature(req.body, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}

const event = JSON.parse(req.body);

// Process event based on type
switch (event.type) {
case 'order.executed':
handleOrderExecution(event.data);
break;
case 'account.updated':
handleAccountUpdate(event.data);
break;
case 'payment.processed':
handlePaymentProcessed(event.data);
break;
default:
console.log(`Unhandled event type: ${event.type}`);
}

res.status(200).send('Webhook processed');
});

Event Handlers

async function handleOrderExecution(data) {
try {
// Update local order status
await db.orders.update({
orderId: data.orderId,
status: data.status,
executedQuantity: data.quantity,
executedPrice: data.price
});

// Notify user
await notifyUser({
userId: data.userId,
type: 'order_executed',
message: `Order ${data.orderId} has been executed`
});

// Update positions
await updatePositions(data);

} catch (error) {
console.error('Error handling order execution:', error);
// Implement retry logic or alert system
}
}

async function handleAccountUpdate(data) {
try {
// Update account information
await db.accounts.update({
accountId: data.accountId,
status: data.status,
balance: data.balance
});

// Check for important changes
if (data.status === 'margin_call') {
await handleMarginCall(data);
}

} catch (error) {
console.error('Error handling account update:', error);
}
}

async function handlePaymentProcessed(data) {
try {
// Update payment status
await db.payments.update({
paymentId: data.paymentId,
status: data.status
});

// Handle successful payment
if (data.status === 'completed') {
await updateAccountBalance(data);
await notifyUser({
userId: data.userId,
type: 'payment_success',
message: `Payment of ${data.amount} ${data.currency} has been processed`
});
}

} catch (error) {
console.error('Error handling payment:', error);
}
}

Retry Logic

Webhook Retry Handler

class WebhookRetryHandler {
constructor(maxRetries = 3, initialDelay = 1000) {
this.maxRetries = maxRetries;
this.initialDelay = initialDelay;
}

async processWithRetry(eventId, handler) {
let attempts = 0;
let lastError;

while (attempts < this.maxRetries) {
try {
await handler();
return;
} catch (error) {
lastError = error;
attempts++;

if (attempts < this.maxRetries) {
await this.wait(this.getDelay(attempts));
}
}
}

// Log failed event for manual review
await this.logFailedEvent(eventId, lastError);
}

getDelay(attempt) {
// Exponential backoff
return this.initialDelay * Math.pow(2, attempt - 1);
}

wait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}

async logFailedEvent(eventId, error) {
await db.failedWebhooks.create({
eventId,
error: error.message,
timestamp: new Date()
});
}
}

Event Queue

Queue Implementation

const Queue = require('bull');

const webhookQueue = new Queue('webhooks', {
redis: {
port: 6379,
host: 'localhost'
}
});

// Add event to queue
app.post('/webhooks', async (req, res) => {
const event = req.body;

await webhookQueue.add(event, {
attempts: 3,
backoff: {
type: 'exponential',
delay: 1000
}
});

res.status(200).send('Event queued');
});

// Process events
webhookQueue.process(async (job) => {
const event = job.data;

switch (event.type) {
case 'order.executed':
await handleOrderExecution(event.data);
break;
// Handle other events
}
});

// Handle failed jobs
webhookQueue.on('failed', async (job, error) => {
console.error(`Job ${job.id} failed:`, error);
await logFailedJob(job, error);
});

Monitoring

Health Checks

function checkWebhookHealth() {
return {
queueSize: webhookQueue.count(),
failedJobs: webhookQueue.getFailedCount(),
processingTime: getAverageProcessingTime(),
lastProcessedEvent: getLastProcessedEvent()
};
}

// Expose health endpoint
app.get('/webhooks/health', async (req, res) => {
const health = await checkWebhookHealth();
res.json(health);
});

Best Practices

  1. Reliability

    • Implement retry logic
    • Use event queues
    • Handle failures gracefully
    • Monitor processing
  2. Security

    • Verify signatures
    • Use HTTPS
    • Validate payload
    • Secure secrets
  3. Performance

    • Process async
    • Use queues
    • Handle rate limits
    • Monitor latency
  4. Maintenance

    • Log events
    • Monitor health
    • Clean old data
    • Update endpoints

Webhook Testing

Test Event

const testEvent = {
type: 'order.executed',
id: 'evt_test_123',
created: new Date().toISOString(),
data: {
orderId: 'ord_test_123',
status: 'filled',
symbol: 'AAPL',
quantity: 10,
price: 150.00
}
};

// Sign test event
const signature = signPayload(testEvent, process.env.WEBHOOK_SECRET);

// Send test webhook
await axios.post('https://your-app.com/webhooks', testEvent, {
headers: {
'Content-Type': 'application/json',
'X-TP-Signature': signature
}
});

Next Steps

  1. Error Handling
  2. Security Guide
  3. Integration Overview