Create a lobby
When a match is ready to play, BluMint POSTs your create-lobby URL. On a best-of series, BluMint sends one create per match, and on single game sessions, one create per game session. Either way, each create carries anIdempotency-Key header that names it (Idempotency):
{ "inGameId": "emb1_example" }, holding the id your lookup answered (Player lookup).
By the accounts players link on BluMint. Each player carries one identity for each account kind you chose in the setup form that they have linked, { "provider": "discord", "inGameId": "274220342558756145" }. A player can carry more than one identity: admit them if any one matches, and name them in a report by any one of them.
You receive only one of the two forms, so you never need to tell them apart. A create on a Discord integration looks like this:
teamslists every team in the match, within the range of teams per match you gave in the setup form, which is exactly two on a best-of series. Admit only the players it names.seriesis present on every best-of series create, andgamesToWinis the number of game sessions a team must win to take the match. It is absent on single game sessions.- On a best-of series,
startingWinsis on every team when BluMint asks you for a new lobby for a match already under way: after you abort withretryable: true, and after the host resets the match. It is the number of game sessions of this match BluMint has already recorded for that team, and0after a reset. It is absent on the first create for a match, including BluMint’s retries of it, and on single game sessions. Honour it: count it into every progress report, result and abort for the new lobby. A report that gives a team fewer wins than itsstartingWinsanswers400 invalid_wins. spectatorslists the tournament’s host, streamers and moderators who chose to watch, at most 16, each by the same kind of identity a player carries. It never lists a player in the match or anyone registered in the tournament. It is always present and may be empty, and it can list more spectators than your lobby admits. If your game supports watching, admit listed identities as spectators, in list order, until your lobby holds as many as it admits; a spectator can never play or report a result. Otherwise ignore it. Never refuse a create because of its spectators.matchSettingscarries the settings the host chose for this match (Match settings).readyWithinSecondsis the tournament’s hold length in seconds, always present (Which answer to send when gives its range). BluMint tells every player to be ready this long after their match starts, or be disqualified, and BluMint times that itself. If your game ends a lobby on its own when a player doesn’t show up, let that happen no earlier than this after you answer the create. Otherwise ignore it. The host can lengthen the hold while a match waits, so a repeat of oneIdempotency-Keycan carry a larger value: leave it out when you compare a repeat’s body.- Ignore any field you don’t recognise in a body BluMint sends you. BluMint can add a field you may ignore at any time, so a server that refuses an unknown field stops working when one is added.
Keep every value in the type the contract gives
Keep each value in the JSON type this guide and the API reference give it, in your storage as much as on the wire, and send it back in that type. EveryinGameId is a string, so store it as a string and send it as one in every report: an id sent as a number is refused with invalid_request. A string id that happens to hold digits shows why this matters. A Discord id is longer than a JSON number can hold exactly, so an id parsed into a number comes back as a different id, and BluMint can no longer tell which player you mean.
Match settings
Match settings let a host run the tournament they have in mind, rather than only your default match.matchSettings passes the host’s choices to you unchanged on every create, so hosts can run fun, custom events of your game: a tournament on one map, a most-kills-wins night, a short time limit for a fast bracket, or whatever rules make their community show up.
- You define the settings. In the setup form, list the settings of your game a host should be able to configure for a match, and link the API or JSON spec that defines them if you have one (Before you start). The keys in
matchSettingsare the ones you list, with the values the host chose. - Apply what the host chose. Set up the lobby with each setting the create carries. An empty object is valid and means the host chose none, so use your defaults.
- Refuse a setting you can’t run. Answer
400 match_unsupported, and the host starts the match manually (Which answer to send when).
{ "clock": { "limitSeconds": 180, "incrementSeconds": 2 } } as a three-minute game with a two-second increment, so a host can run a blitz tournament as easily as a classical one.
Answer the create
Answer200 with your lobby’s id, matchId, and one of three ways for players to get in. Your integration uses the one you declared in the setup form:
- A join link,
matchUrl. Players open the link to join. It is https, carries nouser:pass@, is at most 2048 characters, and its hostname is exactly one of the join-link hostnames you gave BluMint. It can open a page on your site or your game client directly. A lobby code in the fragment after#is fine, since BluMint sends the link byte for byte. - A lobby code,
lobbyCode. Players type the code into your game. It is 1 to 32 characters using only ASCII letters, digits, spaces,_,*,., the backtick and-. Because BluMint shows the code in chat, it must not be wrapped in*,_or backticks, and must not contain a link or an email address. - Neither, because your own game client puts players into the lobby. Send no
matchUrlorlobbyCode. SendspectateUrlinstead: an https watch link on one of the watch-link hostnames you gave BluMint, which BluMint offers to the host so they can follow the match.
matchIdis your lobby’s id, 1 to 256 characters. BluMint treats it as opaque and sends it back unchanged.- An answer that breaks these rules, or carries a way in your integration did not declare, fails the create for good.
Who gets the way in
BluMint tells every player the create names inteams that their match is ready, by push notification and in the app:
With a join link or a lobby code, BluMint sends your matchUrl or lobbyCode unchanged to those players, on their match card:
Let only those players play. Anyone else who has the link or code must not, because a player could otherwise pass it to a friend who joins their team to help them win.
When your own game client puts players into their lobbies, BluMint sends players nothing to open and offers your spectateUrl only to the host:
Anyone viewing the tournament can still read that link, so it must only let its holder watch. It must never let them play, control the match, or get the access you give the spectators listed in spectators.
Which answer to send when
While BluMint waits on your server or on a player, it holds the match and retries every 60 seconds for the tournament’s hold length, which the host sets between 5 and 30 minutes (10 by default). What happens when the hold runs out depends on what BluMint was waiting for:- A player, such as one you answered
player_busyorplayer_unlinkedfor: that player is disqualified, since fixing it was theirs to do. - Your server, after outage answers: nobody is disqualified, and the host takes the match and sets it up manually.
Retry-After, in seconds or as an HTTP date, BluMint waits at least that long before it sends again, up to 5 minutes. A wait reaching past the end of the hold is cut short, so BluMint still sends one last create before the host takes the match.
match_unsupported, invalid_request, idempotency_key_reused, a 500 and any answer this table does not list are final: BluMint stops, and the host starts the match manually. BluMint reads a 500 as your code failing on this request, which sending it again would only repeat. When a problem will clear on its own, such as a deploy, a restart or a full queue, answer 503, not 500.
- Send
player_unlinkedfor any player you can’t match to one of yours, naming inplayersevery id from the request you don’t recognise. When you name players by one kind of linked account, the player can replace that account with another in the app, and the next retry names the new one. - With a close URL registered, BluMint waits for your
200to each lobby’s close before it asks you for a new lobby for any of its players, so a create never names a player who is still in one of your lobbies (Close a lobby). If one arrives anyway, for example because you registered no close URL or because of a close BluMint gave up on, answer423 player_busy, naming inplayersevery id from the request that another of your lobbies holds. Never answer a busy player with a5xx, which BluMint reads as your server being down. - Treat a refused create as if it never happened. When you answer
player_unlinkedorplayer_busy, create nothing and don’t mark theIdempotency-Keyas used. Handle each retry under that key, possibly with corrected player ids, like a first request.
code and, where the table names it, players. BluMint ignores any other text you add:
Timing
Answer every create within 30 seconds. BluMint waits 40 seconds for each request, counted on the wall clock from the start of the request and including DNS lookup and connection time. A request with no answer by then, or whose DNS lookup fails for a moment (EAI_AGAIN), is an outage answer, and BluMint sends it again under the same key.
Players are waiting on the match while your server creates its lobby, so BluMint stops waiting on one request after 40 seconds and holds the match instead, rather than let one stuck request hold up the players. The 10 seconds between your 30 and BluMint’s 40 cover the network, so an answer you send in time is never mistaken for an outage, and by the time BluMint sends the request again, your work on the first one has finished rather than still running beside the repeat.
If a request arrives for a key you are still creating a lobby for, answer with that lobby once it exists, or answer 503 so BluMint sends the same key again. Never answer 409 here: BluMint treats 409 as final and closes the lobby your first request is still creating.
Idempotency
BluMint can send you the same create more than once. An answer can be lost on the network or arrive after BluMint’s deadline, and BluMint retries a create through an outage or a hold. When an answer is lost, BluMint can’t tell whether you made the lobby, so it sends the request again. If your server made a new lobby for every request, one match could end up with two lobbies, and its players could split between them. TheIdempotency-Key header tells you which requests are the same create.
A repeated create carrying the same Idempotency-Key must return the same lobby, on a best-of series and on single game sessions alike. The key is at most 128 characters of ASCII letters, digits and -, matching ^[\dA-Za-z-]{1,128}$, so it always fits as a matchId. BluMint builds it from its own ids, so treat it as opaque and never parse it.
BluMint closes a lobby by its key (Close a lobby), and every report you send names it (Report what happened), so keep each key for as long as you keep the lobby it made. A key whose create made no lobby, such as one a close named first, needs keeping only for at least 3 hours, the longest BluMint can go on sending that key’s create and its close, so a close can mark it closed and a later create under it answers 409 idempotency_key_reused. Honour the key one of two ways:
- Store the key with the lobby, and answer a repeat with the stored lobby.
- Derive your
matchIdfrom the key, the key itself or a UUIDv5 of it, so your own uniqueness on lobby ids finds the existing lobby on a repeat, with no extra table. The key then lives exactly as long as its lobby, which meets both rules above.
- BluMint sends a new key each time it needs a new lobby from you: after you abort with
retryable: true, after the host resets the match, and after the host undoes the start of the tournament and starts it again. On single game sessions, each game session also has its own key. AmatchIdyou derive from the key is therefore new each time, so you never return a lobby that has already ended. - Every other repeat carries the same key, whether it is one of BluMint’s retries or an occasional duplicate of the same request. Answer each one with the same lobby.
- Two requests with one key can arrive at the same moment. Serialize on the key, for example with a unique reservation row, so one key yields one lobby. A close takes the same reservation as a create: a close that finds a create under its key still building records the key closed and answers
200, and before that create commits its lobby, it re-reads the mark under the reservation, tears down what it built and answers409 idempotency_key_reused, which BluMint expects for a match it moved past.
Bursts
When a round starts, BluMint sends the creates for all of its matches at the same moment, one per match in the round. Tournaments of your game that start together add to that, and BluMint does not limit how many creates it sends at once. A 256-team single-elimination tournament, for example, sends about 128 creates at once. Handle the burst instead of answering429: BluMint reads a 429 as an outage answer and holds the match, as the table’s outage row says.
When a create fails
When a create fails, the host starts the match manually, and BluMint tells them so: What BluMint does about a lobby on your side depends on how you answered:- After
match_unsupported,invalid_requestorplayer_unlinked, BluMint assumes you made no lobby under the key and never sends a close for it. Check the request before you create anything. - After a
500,idempotency_key_reused, an answer the table does not list, a200that breaks the rules above, or an outage answer to BluMint’s last try, you may hold a lobby no player will ever join. If you registered a close URL, BluMint closes that lobby by its key (Close a lobby). If you did not, expire any lobby that none of its players joins within a time you choose. A later report on such amatchIdanswersunknown_match, ormatch_closedif BluMint recorded the lobby before moving past it.