Skip to content
Documentation
Docs·Webhooks

Webhooks

Connect external services to your bot via HTTP API. Business tier required.

Est8 min
DifficultyAdvanced
Updated2026-04-30
For versionv1.4.0+
01Webhooks

Getting started

  1. Open your server's dashboard → Webhooks.
  2. Click Create webhook. Choose Outgoing to receive events, Incoming to push actions in.
  3. Copy the generated secret. It signs every request so you can verify us.
  4. Subscribe to the events (outgoing) or actions (incoming) you care about.
  5. Hit the Test button next to any outgoing webhook to fire a mock payload through the real retry pipeline.
02Webhooks

Authentication

Every delivery carries an HMAC-SHA256 signature in the X-Signature header (prefixed with sha256=) computed over the raw request body using your webhook secret. Always verify the signature before trusting the payload.

Delivery headers

  • X-Webhook-Idyour webhook's UUID.
  • X-Eventevent name (e.g. duel.finish).
  • X-Signaturesha256=<hex> HMAC of the body.
  • X-Attemptattempt number, starting at 1.
  • X-Idempotency-Keystable per delivery; dedupe on your side.
03Webhooks

Retry policy

A delivery is considered successful on HTTP 2xx. Anything else is a failure:

  • Up to 5 attempts per delivery.
  • Exponential backoff: 30s → 1m → 2m → 4m → 8m (~15 min total).
  • Use the X-Idempotency-Key header to dedupe if you receive the same delivery twice.
  • After the final failed attempt the log entry is marked DEAD; you can re-queue it from the dashboard.
  • If your endpoint is down for planned maintenance, use Pause on the dashboard instead of erroring — it prevents noise in the retry queue.
04Webhooks

Event catalogue

Every outgoing event with an example payload. These payloads are also what the dashboard "Test" button fires.

trade.complete

A P2P trade finished successfully and goods/funds settled.

Example payload
{
  "event": "trade.complete",
  "guildId": "123456789012345678",
  "timestamp": "2026-04-14T19:00:00.000Z",
  "data": {
    "tradeId": "trade_test",
    "creatorId": "111111111111111111",
    "counterpartyId": "222222222222222222",
    "currencyId": "cur_test",
    "amount": "500.00",
    "completedAt": "2026-04-14T19:00:00.000Z"
  }
}
duel.finish

A duel ended; winner is paid, loser refunded per rules.

Example payload
{
  "event": "duel.finish",
  "guildId": "123456789012345678",
  "timestamp": "2026-04-14T19:00:00.000Z",
  "data": {
    "duelId": "duel_test",
    "winnerId": "111111111111111111",
    "loserId": "222222222222222222",
    "stake": "100.00",
    "currencyId": "cur_test",
    "finishedAt": "2026-04-14T19:00:00.000Z"
  }
}
member.join

A member joined the guild.

Example payload
{
  "event": "member.join",
  "guildId": "123456789012345678",
  "timestamp": "2026-04-14T19:00:00.000Z",
  "data": {
    "userId": "333333333333333333",
    "username": "new_member",
    "joinedAt": "2026-04-14T19:00:00.000Z"
  }
}
member.leave

A member left (or was kicked from) the guild.

Example payload
{
  "event": "member.leave",
  "guildId": "123456789012345678",
  "timestamp": "2026-04-14T19:00:00.000Z",
  "data": {
    "userId": "333333333333333333",
    "username": "former_member",
    "leftAt": "2026-04-14T19:00:00.000Z"
  }
}
balance.change

A user’s balance delta that crossed an operator-configured threshold.

Example payload
{
  "event": "balance.change",
  "guildId": "123456789012345678",
  "timestamp": "2026-04-14T19:00:00.000Z",
  "data": {
    "userId": "111111111111111111",
    "currencyId": "cur_test",
    "delta": "+250.00",
    "newBalance": "1250.00",
    "reason": "DUEL_WIN",
    "occurredAt": "2026-04-14T19:00:00.000Z"
  }
}
moderation.action

Moderator took an action against a user (warn/mute/kick/ban).

Example payload
{
  "event": "moderation.action",
  "guildId": "123456789012345678",
  "timestamp": "2026-04-14T19:00:00.000Z",
  "data": {
    "action": "warn",
    "targetUserId": "222222222222222222",
    "moderatorId": "111111111111111111",
    "reason": "Test warning from /docs/webhooks",
    "occurredAt": "2026-04-14T19:00:00.000Z"
  }
}
05Webhooks

Code examples

Node.js// Node.js — verifying an incoming outgoing-webhook delivery
import express from 'express';
import crypto from 'node:crypto';

const app = express();
const SECRET = process.env.WEBHOOK_SECRET!;

app.post('/vektor-webhook', express.raw({type: 'application/json'}), (req, res) => {
  const sig = req.header('x-signature');
  const expected = 'sha256=' + crypto.createHmac('sha256', SECRET).update(req.body).digest('hex');
  if (!sig || sig !== expected) return res.status(401).end();

  const payload = JSON.parse(req.body.toString());
  console.log('event', payload.event, 'attempt', req.header('x-attempt'));
  // ... handle payload.data ...
  res.status(200).end();
});

app.listen(3000);
Python / Flask# Python / Flask — verifying an incoming outgoing-webhook delivery
from flask import Flask, request, abort
import hmac, hashlib, os

app = Flask(__name__)
SECRET = os.environ["WEBHOOK_SECRET"].encode()

@app.post("/vektor-webhook")
def handle():
    raw = request.get_data()
    sig = request.headers.get("X-Signature", "")
    expected = "sha256=" + hmac.new(SECRET, raw, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(sig, expected):
        abort(401)

    payload = request.get_json()
    print("event", payload["event"], "attempt", request.headers.get("X-Attempt"))
    # ... handle payload["data"] ...
    return "", 200
curl# Send a test payload manually (normally the dashboard "Test" button does this for you)
curl -X POST "https://your-server.example.com/vektor-webhook" \
  -H "Content-Type: application/json" \
  -H "X-Signature: sha256=$(echo -n '$BODY' | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')" \
  -H "X-Event: duel.finish" \
  -H "X-Webhook-Id: wh_abc123" \
  -H "X-Attempt: 1" \
  --data-raw "$BODY"
06Webhooks

Incoming webhooks

Incoming webhooks let your services trigger bot actions (add currency, assign a role, post a message) without maintaining a Discord bot of your own. See the allowed actions list in the dashboard; each action is rate-limited and requires the matching HMAC signature.

Next →
Troubleshooting

Common issues and fixes — bot not responding, missing perms, embeds not showing.

Webhooks — vektor Developer Docs