← Back to Articles

Webhook Integration Guide: HTTP Notifications for Trade Alerts

"Webhooks let you build integrations without maintaining a persistent connection. Your server just waits for us to call."

Webhooks provide an alternative to WebSocket connections for receiving trade alerts. Instead of you connecting to us, we send HTTP POST requests to your server whenever trades occur on wallets you're watching. This guide covers everything you need to set up, secure, and use webhooks effectively.

WebSocket vs Webhook: When to Use Each

Before diving into webhooks, understand when to use each approach:

WebSocket Alerts

  • Lowest latency (milliseconds)
  • Persistent connection required
  • Best for real-time trading bots
  • Requires connection management
  • Single connection for all alerts

Webhook Notifications

  • Low latency (seconds)
  • Stateless HTTP requests
  • Best for notifications & logging
  • No connection management
  • Multiple endpoints supported

Recommendation:

Use WebSockets for copy trading bots and latency-sensitive applications. Use Webhooks for Telegram/Discord notifications, logging systems, analytics pipelines, and as a backup notification method.

Prerequisites

What You'll Need:

Elite PerpsTracker subscription with API key
A publicly accessible HTTPS endpoint
Server that can receive HTTP POST requests
At least one wallet on your watchlist

Setting Up Webhooks

Step 1: Create a Webhook via API

Create a webhook by sending a POST request to the webhooks endpoint:

curl -X POST https://perpstracker.com:5001/api/webhooks \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://your-server.com/webhook", "events": ["trade.new", "trade.close", "trade.liquidation"] }'

Response:

{ "success": true, "webhook": { "id": "wh_abc123", "url": "https://your-server.com/webhook", "events": ["trade.new", "trade.close", "trade.liquidation"], "secret": "whsec_xxxxxxxxxxxxxxxxxxx" }, "message": "Webhook created. Save your secret - it will only be shown once!" }

Save Your Secret!

The webhook secret is only shown once when creating the webhook. Store it securely—you'll need it to verify webhook signatures. If you lose it, you can regenerate it, but you'll need to update your verification code.

Step 2: Available Event Types

Event Type Description
trade.new Triggered when a watched trader opens a new position
trade.close Triggered when a watched trader closes a position
trade.liquidation Triggered when a watched trader is liquidated

Webhook Payload Format

When a trade event occurs, we send a POST request to your webhook URL with the following format:

{ "id": "evt_abc123xyz", "type": "trade.new", "created": 1705312800, "data": { "owner": "DRVRvzf8pSiMJi5BY1NmZu5STahBYKMVcfkPp3vxPump", "marketSymbol": "SOL-PERP", "side": "long", "sizeUsd": 15000.50, "leverage": 5.2, "entryPrice": 185.42, "collateral": 2884.71, "timestamp": "2025-01-15T10:30:00Z" } }

Request Headers

Header Description
Content-Type application/json
X-Webhook-Signature HMAC-SHA256 signature for payload verification
X-Webhook-ID Your webhook ID
X-Event-Type The event type (e.g., trade.new)
X-Delivery-ID Unique ID for this delivery attempt

Verifying Webhook Signatures

Always verify webhook signatures to ensure requests are genuinely from PerpsTracker. We sign each payload using HMAC-SHA256 with your webhook secret.

Node.js Verification

const crypto = require('crypto'); function verifyWebhookSignature(payload, signature, secret) { const expectedSignature = crypto .createHmac('sha256', secret) .update(payload) .digest('hex'); return crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expectedSignature) ); } // Express middleware example app.post('/webhook', (req, res) => { const signature = req.headers['x-webhook-signature']; const payload = JSON.stringify(req.body); if (!verifyWebhookSignature(payload, signature, WEBHOOK_SECRET)) { return res.status(401).send('Invalid signature'); } // Process the verified webhook console.log('Verified webhook:', req.body); res.status(200).send('OK'); });

Python Verification

import hmac import hashlib def verify_webhook_signature(payload: str, signature: str, secret: str) -> bool: expected = hmac.new( secret.encode(), payload.encode(), hashlib.sha256 ).hexdigest() return hmac.compare_digest(signature, expected) # Flask example @app.route('/webhook', methods=['POST']) def handle_webhook(): signature = request.headers.get('X-Webhook-Signature') payload = request.get_data(as_text=True) if not verify_webhook_signature(payload, signature, WEBHOOK_SECRET): return 'Invalid signature', 401 data = request.get_json() print(f"Verified webhook: {data}") return 'OK', 200

Security Note:

Always use timing-safe comparison functions (timingSafeEqual in Node.js, hmac.compare_digest in Python) to prevent timing attacks.

Building a Webhook Server

Here's a complete Express.js server that receives webhooks and sends Discord notifications:

const express = require('express'); const crypto = require('crypto'); const app = express(); app.use(express.json()); const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET; const DISCORD_WEBHOOK = process.env.DISCORD_WEBHOOK_URL; function verifySignature(payload, signature) { const expected = crypto .createHmac('sha256', WEBHOOK_SECRET) .update(JSON.stringify(payload)) .digest('hex'); return signature === expected; } async function sendDiscordNotification(trade) { const emoji = trade.side === 'long' ? '🟢' : '🔴'; const message = { embeds: [{ title: `${emoji} ${trade.side.toUpperCase()} ${trade.marketSymbol}`, color: trade.side === 'long' ? 0x10b981 : 0xef4444, fields: [ { name: 'Size', value: `$${trade.sizeUsd.toLocaleString()}`, inline: true }, { name: 'Leverage', value: `${trade.leverage}x`, inline: true }, { name: 'Entry', value: `$${trade.entryPrice}`, inline: true }, { name: 'Trader', value: `\`${trade.owner.slice(0, 8)}...\`` } ], timestamp: trade.timestamp }] }; await fetch(DISCORD_WEBHOOK, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(message) }); } app.post('/webhook', async (req, res) => { const signature = req.headers['x-webhook-signature']; if (!verifySignature(req.body, signature)) { console.log('Invalid webhook signature'); return res.status(401).send('Unauthorized'); } const { type, data } = req.body; console.log(`Received ${type} event`); try { if (type === 'trade.new') { await sendDiscordNotification(data); } res.status(200).send('OK'); } catch (error) { console.error('Error processing webhook:', error); res.status(500).send('Error'); } }); app.listen(3000, () => { console.log('Webhook server running on port 3000'); });

Retry Policy

We automatically retry failed webhook deliveries with exponential backoff:

Attempt Delay Total Time Since First Attempt
1st (initial) Immediate 0 seconds
2nd retry 30 seconds 30 seconds
3rd retry 60 seconds ~1.5 minutes

What Counts as Success?

A delivery is successful when your endpoint returns a 2xx status code (200, 201, 202, etc.). Any other status code (or timeout after 10 seconds) triggers a retry.

Managing Webhooks

List Your Webhooks

curl -X GET https://perpstracker.com:5001/api/webhooks \ -H "X-API-Key: YOUR_API_KEY"

Update a Webhook

curl -X PUT https://perpstracker.com:5001/api/webhooks/wh_abc123 \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://new-server.com/webhook", "events": ["trade.new"], "is_active": true }'

Delete a Webhook

curl -X DELETE https://perpstracker.com:5001/api/webhooks/wh_abc123 \ -H "X-API-Key: YOUR_API_KEY"

Test a Webhook

Send a test event to verify your endpoint is working:

curl -X POST https://perpstracker.com:5001/api/webhooks/wh_abc123/test \ -H "X-API-Key: YOUR_API_KEY"

View Delivery History

Debug webhook issues by viewing recent deliveries:

curl -X GET "https://perpstracker.com:5001/api/webhooks/wh_abc123/deliveries?limit=20" \ -H "X-API-Key: YOUR_API_KEY"

Best Practices

Production Webhook Checklist:

Always verify signatures before processing webhooks
Respond quickly (within 5 seconds) to avoid timeouts
Process webhooks asynchronously if heavy work is needed
Implement idempotency using the delivery ID
Log all incoming webhooks for debugging
Use HTTPS endpoints only (HTTP will be rejected)
Handle retries gracefully—you may receive duplicates
Monitor your webhook delivery success rate

Handling Duplicates (Idempotency)

Due to retries, you may receive the same event multiple times. Use the delivery ID to ensure idempotent processing:

const processedDeliveries = new Set(); app.post('/webhook', (req, res) => { const deliveryId = req.headers['x-delivery-id']; // Check if we already processed this delivery if (processedDeliveries.has(deliveryId)) { console.log(`Duplicate delivery: ${deliveryId}`); return res.status(200).send('Already processed'); } // Process the webhook... processedDeliveries.add(deliveryId); // Clean up old entries periodically if (processedDeliveries.size > 10000) { const oldest = processedDeliveries.values().next().value; processedDeliveries.delete(oldest); } res.status(200).send('OK'); });

Common Issues

Issue Solution
Webhooks not arriving Check your endpoint is publicly accessible; verify firewall rules
Signature verification fails Ensure you're using the raw request body, not parsed JSON
Timeouts Respond within 10 seconds; process heavy work asynchronously
SSL certificate errors Use a valid SSL certificate from a trusted CA
Webhook disabled automatically Too many failures; fix your endpoint and re-enable

Need Help?

Check our API documentation for complete endpoint references. Join our Discord community for support.

Related Articles

← Back to Articles