Skip to content
InfoHub
UpgradeSign in
Popular
LiquidationsFunding RatesDashboardNewsScreener
Shortcuts
ETF TrackerL/S Ratio
Developers

Custom webhooks

Receive HMAC-signed alert payloads on your own HTTPS endpoint. Set up in three curl commands. Whale tier only — see /pricing (free during launch).
Whale tier required — even during launch. Webhook endpoints return 403 for Free / Trader / Pro accounts. The launch unlock applies to in-app features, not this server-gated API endpoint, so an active Whale subscription is required to configure webhooks here (admin accounts are auto-Whale).
1

Register your webhook URL

Send a PUT /api/account/webhook with your HTTPS endpoint. You'll get back a secret used for signature verification — save it now, it won't be shown again.

bash
curl -X PUT https://info-hub.io/api/account/webhook \
  -H "Cookie: $YOUR_SESSION_COOKIE" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://my-bot.example.com/infohub-webhook"}'

Response:

json
{
  "url": "https://my-bot.example.com/infohub-webhook",
  "secret": "8f3a9d2b...64-char-hex-string...e1c7",
  "createdAt": "2026-05-20T13:42:11.000Z",
  "warning": "Save this secret now — it will not be shown again."
}
URL restrictions: must be HTTPS, cannot be localhost, private network ranges (10.x, 172.16-31.x, 192.168.x), or cloud metadata hosts (169.254.169.254). SSRF defense.
2

Send a test ping

Fire a synthetic alert.test event to verify your receiver accepts the payload and signature:

bash
curl -X POST https://info-hub.io/api/account/webhook/test \
  -H "Cookie: $YOUR_SESSION_COOKIE"

Returns { ok: true, message: "Test webhook delivered." } if your endpoint replied 2xx. Otherwise check the message + your server logs.

3

Enable "webhook" on your alerts

Add "webhook" to the channels array when creating or updating an alert rule:

bash
curl -X POST https://info-hub.io/api/account/alerts \
  -H "Cookie: $YOUR_SESSION_COOKIE" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "funding_flip",
    "enabled": true,
    "channels": ["telegram", "webhook"],
    "cooldownMin": 60
  }'
You can combine webhook with other channels (telegram, email, browser_push). Each delivers independently with its own cooldown.
4

Payload format

Every webhook delivery is a POST with Content-Type: application/json and an HMAC signature header.

http
POST /infohub-webhook HTTP/1.1
Host: my-bot.example.com
Content-Type: application/json
X-InfoHub-Signature: 4a72f0b8c2d...hex-sha256...e1
X-InfoHub-Event: alert.triggered
User-Agent: InfoHub-Webhook/1.0 (+https://info-hub.io)

{
  "timestamp": "2026-05-20T13:45:00.000Z",
  "version": "v1",
  "event": "alert.triggered",
  "alerts": [
    {
      "alertId": "abc-123",
      "symbol": "BTC",
      "metric": "fundingRate",
      "operator": "gt",
      "threshold": 0.0001,
      "actualValue": 0.00018,
      "exchange": "Binance"
    }
  ]
}
5

Verify the signature

Compute HMAC-SHA256(secret, raw_body) and compare to X-InfoHub-Signature. Use a constant-time comparison to avoid timing attacks. Same scheme as Stripe / GitHub webhooks.

Node.js (Express)

js
import { createHmac, timingSafeEqual } from 'crypto';
import express from 'express';

const app = express();
const SECRET = process.env.INFOHUB_WEBHOOK_SECRET;

// IMPORTANT: read raw body — JSON.parse before signature check changes the bytes
app.post('/infohub-webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.header('X-InfoHub-Signature') || '';
  const expected = createHmac('sha256', SECRET).update(req.body).digest('hex');

  const a = Buffer.from(sig, 'hex');
  const b = Buffer.from(expected, 'hex');
  if (a.length !== b.length || !timingSafeEqual(a, b)) {
    return res.status(401).json({ error: 'invalid signature' });
  }

  const payload = JSON.parse(req.body.toString('utf8'));
  console.log('verified webhook:', payload.event, payload.alerts.length, 'alerts');
  res.status(200).json({ ok: true });
});

app.listen(3000);

Python (Flask)

python
import hmac
import hashlib
import os
from flask import Flask, request, abort

app = Flask(__name__)
SECRET = os.environ['INFOHUB_WEBHOOK_SECRET'].encode()

@app.route('/infohub-webhook', methods=['POST'])
def webhook():
    sig = request.headers.get('X-InfoHub-Signature', '')
    expected = hmac.new(SECRET, request.data, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(sig, expected):
        abort(401)

    payload = request.get_json()
    print(f"verified webhook: {payload['event']} {len(payload['alerts'])} alerts")
    return {'ok': True}, 200

if __name__ == '__main__':
    app.run(port=3000)
6

Manage your webhook

GET /api/account/webhook — read current URL. Secret is never re-exposed after creation.
PUT /api/account/webhook — change URL + rotate secret. Previous secret becomes invalid immediately.
DELETE /api/account/webhook — clear config. Not tier-gated — downgrade-friendly.
POST /api/account/webhook/test — fire a synthetic alert.test event to your registered URL.
7

Delivery behaviour

  • 10-second request timeout per delivery.
  • Receiver must return 2xx to be considered successful.
  • No automatic retry today — if your endpoint is down, you miss that batch. Same per-channel cooldown as Discord/email (5-1440 min).
  • Multiple alerts firing together arrive in a single POST as alerts[] — process them as a batch.
  • X-InfoHub-Event header tells you the event kind without parsing the body — useful for routing alert.test vs alert.triggered differently.
Full API reference ·Pricing tiers ·Questions on Telegram