Authorization
How Tournament Suite controls access to resources through projects, scopes, and API key restrictions.
Authentication tells Tournament Suite who you are. Authorization determines what you can do.
Project-level isolation
All resources — tournaments, matches, and webhooks — belong to a project. Your API key can only access resources within the project it was created in. There is no way to read or write data across projects using a single key.
This means you can safely use separate keys for each environment (development, staging, production) without any risk of cross-contamination.
Scope-based access control
Every API key carries one or more scopes that limit the operations it can perform. A key with read:tournaments can list and retrieve tournaments but cannot access match or player data unless it also carries the matching read scope.
When you generate a key, you choose which scopes to grant. Request only the scopes your integration actually needs — this reduces the blast radius if a key is ever compromised.
See Scopes for the full list of available permissions.
What the public API surface covers
The public Data API is read-only. Every route requires an x-api-key header and a specific scope:
| Endpoint | Required scope |
|---|---|
GET /tournaments | read:tournaments |
GET /tournaments/:id | read:tournaments |
GET /tournaments/:id/bracket | read:tournaments |
GET /tournaments/:id/matches | read:matches |
GET /matches/:id | read:matches |
GET /matches/:id/events | read:matches |
GET /players/:id/stats | read:player_stats |
GET /teams/:id | read:teams |
Creating or updating tournaments, registering participants, and reporting match results are organizer actions performed from the dashboard — they are not exposed to API keys.
The one part of the public API that does accept writes is the anti-cheat integration surface, which uses its own dedicated scopes:
| Scope | Grants |
|---|---|
anticheat:session.write | Start and end anti-cheat sessions |
anticheat:evidence.write | Upload evidence for a session |
anticheat:detections.read | Read detection results |
Checking permissions at runtime
If your integration receives a 403 Forbidden, inspect the response body's error.code field. The API always returns FORBIDDEN for authorization failures, regardless of the underlying cause:
{
"success": false,
"error": {
"code": "FORBIDDEN",
"message": "API key is missing required scope(s): read:matches"
}
}
A 403 on a request made with an API key can come from any of the following. The message field tells you which one applies:
- Missing scope — the key doesn't carry a scope the endpoint requires.
- Plan entitlement — the project's current subscription plan doesn't include the capability the endpoint needs (for example, API access above your plan's tier), independent of the key's scopes.
- IP not allowed — the request came from an address outside the key's configured IP allow list.
To resolve a scope or IP restriction, go to your project's developer settings in the organizer dashboard and generate a new key (or rotate the existing one) with the correct scopes or allow list. To resolve a plan-entitlement error, upgrade the project's plan.
Was this helpful?
