V-ENT for developers
Build on V-ENT
Two things live here. A read API over tournaments, events, teams, players and rankings, authenticated with a key. And Sign in with V-ENT, so people can use their V-ENT account on your site.
Apply for accessGetting access
- Apply on the partners page, saying what you are building and which scopes you need.
- A V-ENT admin reviews it and approves the scopes you may have. They can approve fewer than you asked for.
- Issue a key from your partner page. The secret is shown once and never again, so store it before you close the tab.
A key can never carry a scope your organisation was not approved for, whatever the request asks for. Suspending a partner stops every key it owns immediately, without waiting for them to expire.
Authentication
Send the key as a bearer token on every request.
curl https://api.v-ent.co/api/v1/whoami/ \
-H "Authorization: Bearer vent_pk_<key id>.<secret>"whoami is the endpoint to call first: it answers with the partner the key belongs to and the exact scopes it carries, which is the quickest way to tell a misconfigured key from a missing permission.
Scopes
Every endpoint needs one scope. Ask only for what you use: an application requesting everything takes longer to approve.
events:read- Read events, their schedule and their venues
events:tickets:read- Read ticket types and remaining capacity for events
tournaments:read- Read tournaments, formats, prize pools and schedules
tournaments:participants:read- Read who is registered for a tournament
tournaments:brackets:read- Read brackets, matches and results
teams:read- Read team profiles and rosters
players:read- Read public player profiles
players:stats:read- Read player win and loss records
rankings:read- Read platform rankings
Endpoints
All read-only, all under /api/v1/. Lists are paginated and answer with results, count and the page you asked for.
| What | Path |
|---|---|
| events | /api/v1/events/ |
| event | /api/v1/events/<id>/ |
| tournaments | /api/v1/tournaments/ |
| tournament | /api/v1/tournaments/<id>/ |
| participants | /api/v1/tournaments/<id>/participants/ |
| bracket | /api/v1/tournaments/<id>/bracket/ |
| stats | /api/v1/tournaments/<id>/stats/ |
| teams | /api/v1/teams/ |
| team | /api/v1/teams/<id>/ |
| player | /api/v1/players/<username>/ |
| rankings | /api/v1/rankings/ |
| whoami | /api/v1/whoami/ |
curl "https://api.v-ent.co/api/v1/tournaments/?page=1" \
-H "Authorization: Bearer vent_pk_<key id>.<secret>"Every response has the same shape
Success or failure, there are the same keys at the top level and nothing else. Branch on code, never on message: the message is written for a person and may be reworded, the code is the contract.
{ "status": "success", "data": { }, "message": "Tournaments" }{ "status": "error", "code": "SCOPE_REQUIRED", "message": "…", "data": null }Paging
Every list pages the same way. page starts at 1, page_size defaults to 25 and stops at 100. Page with has_more rather than by counting: it comes from the same query as the rows, so it cannot disagree with them.
GET /api/v1/tournaments/?page=2&page_size=50
{ "results": [ … ], "page": 2, "page_size": 50, "total": 137, "has_more": true }Showing that the data came from V-ENT
GET /api/v1/ carries a brand block with the marks and the one line of guidance, so you do not have to go and take a logo off the website at whatever size you find it. No key is needed to read it, because somebody deciding whether to integrate has not got one yet.
"brand": {
"name": "V-ENT",
"logo": "https://v-ent.co/images/logo_mark_red.png",
"logo_svg": "https://v-ent.co/images/logo_mark_red.svg",
"colour": "#ED1C24",
"attribution": "Data from V-ENT"
}Use the mark to say where the data came from, at its own proportions and no smaller than 24px tall. Do not recolour it, stretch it, or use it in a way that suggests V-ENT endorses your product. Prefer the SVG; it stays sharp at every size, which the PNG will not.
Rate limit
60 requests a minute per key. Ask if you need more, and say what for. Rankings and finished brackets change rarely, so a minute of caching costs you nothing and keeps you well inside it.
Four things worth knowing before you build
- Address records by slug
- Every V-ENT address a person sees uses the slug, and a renamed record keeps its old addresses working. It is what you want in a link back to us.
- Money is a decimal string
- "220000.00", not a number. Parse it as a decimal. A prize pool that has been through a float is one you will eventually show wrong.
- Times are UTC, ISO 8601
- With the Z on the end. Convert for your own readers; do not assume Lagos.
- Only public records
- Drafts, private tournaments and anything belonging to a suspended account are never returned, and no scope opens them.
Sign in with V-ENT
Standard authorization-code flow with PKCE. If you have integrated "Sign in with Google" you already know this shape. Ask a V-ENT admin to approve SSO for your partner account and you get a client id and a secret, shown once.
Identity scopes
identityidentity:emailidentity:teams
Make a PKCE pair first
Keep the verifier in the visitor session on your server; it never leaves it. Send only the challenge. If you send a challenge, that is what is checked and your client secret is not consulted, which is why a browser app never needs one.
const verifier = base64url(randomBytes(32)); // keep this
const challenge = base64url(sha256(verifier)); // send this1. Send them to V-ENT
https://v-ent.co/partners/authorize
?client_id=vent_sso_<yours>
&redirect_uri=https://your-site.example/callback
&scope=identity identity:email
&state=<random, checked when they come back>
&code_challenge=<base64url(sha256(verifier))>
&code_challenge_method=S2562. Swap the code, on your server
curl -X POST https://api.v-ent.co/partners/sso/token/ \
-H "Content-Type: application/json" \
-d '{
"grant_type": "authorization_code",
"code": "<the code on your callback>",
"client_id": "vent_sso_<yours>",
"client_secret": "<yours>",
"redirect_uri": "https://your-site.example/callback",
"code_verifier": "<the verifier you hashed>"
}'3. Read who they are
curl https://api.v-ent.co/partners/sso/userinfo/ \
-H "Authorization: Bearer <access_token>"The code is single use and short lived, and the wrong PKCE verifier is refused. Swap it from your server, never from the browser: the client secret must not reach a page anybody can read.
Key the account on sub
Not on the username. A person can change their username; sub is stable for the life of the account and is the only field here that is safe as a primary key on your side.
Anybody who has signed in with V-ENT can remove the connection from their account, and existing tokens stop working. Handle BAD_TOKEN by sending them through the flow again, not by holding a dead session.
There is no scope that reads a wallet, a balance, a transaction, a payout or an identity document, and there will not be one. It is written into the code rather than into a policy.
When something is wrong
Every failure answers with a machine-readable code as well as a sentence. Branch on the code; the sentence may be reworded.
{ "status": "error", "code": "INVALID_KEY", "message": "That key is not valid.", "data": null }MISSING_KEY- No Authorization header, or not a bearer token.
MALFORMED_KEY- Not a V-ENT key. Check the vent_pk_ prefix and the dot before the secret. 401.
INVALID_KEY- Unknown key id, wrong secret, or the key was revoked.
PARTNER_INACTIVE- The partner account is suspended or not yet approved. Keys stop working the moment that happens, not at the next issue. 401.
SCOPE_REQUIRED- The key is good but was not approved for this endpoint.
RATE_LIMITED- Too many requests this minute. Back off and retry.
EVENT_NOT_FOUND- No such record, or it is not public. A private record answers 404 rather than 403, because saying it exists is itself a disclosure. Also TOURNAMENT_NOT_FOUND, TEAM_NOT_FOUND, PLAYER_NOT_FOUND. 404.