Webhooks
Register endpoints to receive real-time event notifications from TournamentSuite.
Webhooks let you receive notifications when events occur in your project — instead of polling the API repeatedly. When a subscribed event fires, TournamentSuite sends an HTTP POST to your endpoint with the event payload.
Set up a webhook
1. Register your endpoint
In your project's Settings → Developer → Webhooks, enter the URL of your endpoint and click Register. TournamentSuite will immediately send a verification request to confirm you own that endpoint.
Your endpoint must:
- Be publicly reachable over HTTPS
- Respond to the verification request with a
200 OKwithin 5 seconds - Echo back the
challengevalue from the verification payload
// Verification handler (Express example)
app.post('/webhooks/tournamentsuite', (req, res) => {
if (req.body.type === 'webhook.verify') {
return res.json({ challenge: req.body.challenge });
}
// handle real events below
res.sendStatus(200);
});2. Save the security token
After verification, TournamentSuite returns a signing secret. Store this secret securely — you will use it to verify the authenticity of every incoming webhook request.
3. Subscribe to events
Once registered, open the webhook details and select the events you want to receive. You can subscribe at the project level (all tournaments) or scope subscriptions to a specific tournament.
Verify webhook signatures
Every webhook request includes an X-TournamentSuite-Signature header. Verify it to ensure the payload came from TournamentSuite and was not tampered with:
import crypto from 'crypto';
function verifyWebhook(
payload: string,
signature: string,
secret: string
): boolean {
const expected = crypto
.createHmac('sha256', secret)
.update(payload, 'utf8')
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(`sha256=${expected}`)
);
}
app.post('/webhooks/tournamentsuite', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['x-tournamentsuite-signature'] as string;
if (!verifyWebhook(req.body.toString(), sig, process.env.WEBHOOK_SECRET!)) {
return res.sendStatus(401);
}
const event = JSON.parse(req.body.toString());
// process event
res.sendStatus(200);
});Event payload format
{
"id": "evt_01HXYZ",
"type": "tournament.started",
"createdAt": "2026-07-01T09:00:00.000Z",
"projectId": "proj_abc123",
"data": {
"tournamentId": "550e8400-e29b-41d4-a716-446655440000",
"name": "Summer Open 2026"
}
}Available events
Tournament events
| Event | When it fires |
|---|---|
tournament.created | A tournament is created |
tournament.published | A tournament is made public |
tournament.started | A tournament begins |
tournament.completed | A tournament concludes |
tournament.cancelled | A tournament is cancelled |
Participant events
| Event | When it fires |
|---|---|
participant.registered | A player or team registers |
participant.checked_in | A participant checks in |
participant.disqualified | A participant is removed |
Match events
| Event | When it fires |
|---|---|
match.created | A match is scheduled |
match.started | A match begins |
match.score_reported | A result is submitted |
match.completed | A match result is confirmed |
match.disputed | A result is disputed |
Circuit events
| Event | When it fires |
|---|---|
circuit.season_started | A circuit season begins |
circuit.season_ended | A circuit season concludes |
circuit.standings_updated | Standings change after a tournament |
Retries
If your endpoint returns a non-2xx status or does not respond within 10 seconds, TournamentSuite retries delivery using exponential backoff over the next 24 hours. You can inspect failed deliveries in the webhook log inside your project dashboard.
Best practices
Always respond with 200 OK immediately, then process the event asynchronously. This avoids timeouts and duplicate retries caused by slow processing.
app.post('/webhooks/tournamentsuite', async (req, res) => {
res.sendStatus(200); // acknowledge immediately
// process in the background
setImmediate(() => handleEvent(req.body));
});