API reference
One keyed REST API over your guild's public data and management operations. Read your roster, events and DKP, or drive events, attendance, VODs and more from your own tools.
Overview
The Qutie API is a single, keyed REST API. Every endpoint lives under /api/v1, speaks JSON in and out, and requires a per-guild key. The guild is derived from the key, so no path contains a guild id.
There is no anonymous tier. The key is the gate: a read-only key reaches only the read endpoints for that guild's own data (exactly what a guild chooses to display publicly), while write scopes unlock the management endpoints. A guild that wants nothing public simply never issues a key.
API keys are a Premium feature. On a server without Premium its keys stay listed but every call is refused with 403 naming the plan, and they work again as soon as the server has Premium.
Authentication
Send your key as a bearer token on every request. Keys are prefixed qta_ and are stored hashed: the raw value is only ever shown to you once, at creation.
curl https://qutie.app/api/v1/guild \ -H "Authorization: Bearer qta_your_key_here"
A read-only key is effectively publishable: even if it leaks, it only reads data the guild already displays publicly. Keep write-scoped keys secret, on a server you control.
Call the API from a server, not from a visitor's browser: it does not answer cross-origin browser requests. A site's own backend (or a serverless function) holds the key and passes on what the page needs.
Scopes
Every key carries one or more scopes. A recruitment site uses a read key; a management integration is granted the write scopes it needs. A call without the required scope returns 403.
| Scope | Grants |
|---|---|
read | All GET endpoints (reads). |
event:write | Create, update, cancel events; set signups; mark attendance; finalize. |
dkp:write | Adjust DKP (single and bulk). |
vod:write | Add VODs. |
member:write | Set a member's game fields (IGN, class, ...). |
strikes:write | Add or remove a member's strikes. |
Pagination
Every list endpoint is paginated. Pass ?limit= (default 50, max 200) and ?offset= (default 0), and page by adding limit to offset until offset + limit ≥ total. A value out of range is a 400.
Lists that already nest under a named key (members, events) keep that key and add the paging metadata alongside it; all other lists use a data array:
{
"data": [ /* ... */ ],
"total": 842,
"limit": 50,
"offset": 0
}Rate limits
Limits are counted per key (a token that is not a valid key counts against your IP). Over a limit you get 429 with a Retry-After header and a { "error": "rate_limited", "retryAfter": N } body.
| Class | Limit |
|---|---|
| Reads (GET) | 600 / min |
| Database writes (fields, DKP adjust, VOD add, strikes) | 60 / min |
| Discord / DKP writes (events, signups, attendance, finalize) | 10 / min + 200 / day |
Conventions
- IDs are strings. Guild, game, event and user ids are Discord snowflakes that overflow a JavaScript number. Always send and read them as strings.
memberKey. An opaque, stable identifier for a member within one guild. It joins across endpoints and is useless in any other guild, so it is safe to publish on a public site in place of the raw Discord id.- Times. Times you send in are guild-local
yyyy-MM-ddTHH:mm(the events list'sfrom/toalso take an ISO time withZor an offset); times you get back are UTC ISO-8601, ending inZ. - Idempotency. Discord-mutating and DKP-moving writes require an
Idempotency-Keyheader (max 200 characters; without one the write is a400), scoped to your key. A successful result is replayed for 24 hours with anIdempotent-Replayed: trueheader, so a retry never posts two messages or awards DKP twice. A retry while the first is still running gets409; the same key on a different request gets422. A failed write keeps nothing, so a corrected retry with the same key runs. - Caching. Reads carry an
ETagandCache-Control: private, max-age=60. Send the ETag back asIf-None-Matchand an unchanged answer is a304with no body. - The server comes from the key. A request that also sends an
X-Guild-Idheader is refused with400. - Ids that do not resolve. A game or event id this server does not have is a
404, so an empty list means "none", never "wrong id".GET /membersandGET /members/{userId}cover only people still in the server, and a write that targets a member who left is a404. - Side-effects. Some writes do more than touch the database. The legend on those endpoints: Discord Ping DKP
Errors
Every error has a JSON body with a sentence you can show: { "error": "..." }.
| Status | Meaning |
|---|---|
400 | Invalid input (bad shape, value, range or length). |
401 / 403 | Missing or invalid key (401), or the key lacks the required scope or the server is not on Premium (403). |
404 | The id does not resolve inside this guild. |
409 | Valid request that conflicts with current state (e.g. event already completed or cancelled, game archived, a VOD already submitted, an Idempotency-Key still running). |
422 | An Idempotency-Key reused for a different request, or a video that is private or gone. |
429 | Rate limited; retry after the given delay. |
502 | Qutie's bot could not be reached for a Discord write; retry shortly with the same Idempotency-Key. |
Guild, games & applications
Read the guild card, its games, and the application forms anyone can apply to.
Members & roster
The guild roster, per-game rosters with fields and attendance, and setting a game field.
DKP
Read balances and every change behind them, and adjust them by a signed delta. Balances floor at 0.
Events
Read and fully manage events. Each event can have many VODs, so events carry a `vodCount`, not a single video URL. Fetch the VODs from the VODs endpoints.
Signups & attendance
Read the signup roster and drive signups, attendance and finalization.
VODs
VODs belong to an event (and a member) and have no title of their own: the event supplies the title and context. There is no flat "all VODs" endpoint and no delete (VODs are append-only).
Loot
Who got what: every piece of loot handed out, from auctions, rolls, loot council and sales.
Strikes
Add or remove a single strike on a member. There is no bulk and no clear-all, and strike history and counts are never returned: attendance % on the member payloads is the only reliability signal exposed.
What's never exposed
Some data is never returned on any key, whatever its scopes. Attendance % on the member payloads is the only reliability signal surfaced.