Pular para o conteúdo principal

Batalhas

Sete rotas dirigem o lobby inteiro, e nenhuma delas é um início: uma batalha começa sozinha quando todos os assentos estão READY. Leia Ciclo de vida da batalha primeiro.

Abrir uma batalha

POST /battles escopo: battles:write → 201
{
"game_id": "sweet-bonanza",
"seats_total": 3,
"entry_amount": "25.50",
"currency": "BRL",
"creator_player_ref": "alice",
"creator_game_url": "https://provider.example/session/abc"
}

seats_total precisa ser de 2 a 8. O criador é sentado no índice 0 automaticamente.

creator_game_url decide se a batalha pode começar

Informe e o assento 0 fica READY. Omita e o assento 0 fica FILLED: reivindicado, mas incapaz de jogar. Uma batalha de dois assentos criada sem ele não vai começar quando o outro jogador entrar, porque o assento 0 continua fora de READY.

Omita apenas se você pretende chamar POST /battles/{id}/seats para o criador depois.

A resposta é a batalha inteira, incluindo um ws_token para o feed ao vivo:

{
"id": "btl_uCHSnowFVUXf73YaSH2j",
"status": "OPEN",
"entry_amount": "25.5",
"ws_url": "wss://slotbattle.example.com/ws?battle_id=btl_uCHSnowFVUXf73YaSH2j",
"ws_token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsImtpZCI6ImsxIn0...",
"ws_token_expires_at": "2026-08-03T03:55:42Z",
"ws_subprotocol": "slotbattle.jwt",
"seats": [
{ "seat_index": 0, "seat_ref": "usr_s0", "player_ref": "alice", "is_bot": false, "status": "READY" },
{ "seat_index": 1, "seat_ref": "usr_s1", "player_ref": "", "is_bot": false, "status": "EMPTY" },
{ "seat_index": 2, "seat_ref": "usr_s2", "player_ref": "", "is_bot": false, "status": "EMPTY" }
],
"expires_at": "2026-08-03T03:47:10Z"
}

"25.50" voltou como "25.5", porque valores são normalizados. expires_at é a janela do lobby: se não encher até lá, a batalha é cancelada.

ws_token pode estar ausente

Se a instância não tem chave de assinatura de viewer, a batalha é criada normalmente e o ws_token fica ausente. É um campo omitempty, então não há erro para capturar, e a visualização ao vivo fica indisponível até uma chave ser configurada. Cheque a presença dele em vez de presumir.

Falhas que vale tratar

StatusCódigoSignificado
400invalid_requestseats_total fora de 2..8, creator_player_ref ou game_id faltando, ou campo desconhecido
403game_not_allowedO jogo não está na sua allowlist
409conflictO criador já está em outra batalha ativa
503unavailableEm capacidade máxima. Retente com backoff

Sentar um jogador

POST /battles/{id}/seats escopo: battles:write → 200
{ "player_ref": "bob", "game_url": "https://provider.example/session/def" }

Ocupa o assento vazio de menor índice, ou promove o assento FILLED do chamador para READY. Os dois campos são obrigatórios, então o assento fica sempre READY depois, que é o que pode disparar o início automático.

Um jogador com assento READY ou PLAYING não pode entrar de novo (409). Um com assento FILLED está promovendo, o que é permitido e é como um criador que omitiu creator_game_url se torna pronto.

A resposta traz um ws_token novo, para o browser do jogador que entrou começar a assistir na hora.

Encher o resto com bots

POST /battles/{id}/bot-seats escopo: bots:write → 200

Preenche todos os assentos vazios restantes. Bots recebem sessões demo emitidas sob as credenciais de provedor do próprio casino, então caem em READY, que normalmente é o que inicia a batalha.

{}

Um corpo vazio nomeia todos os bots automaticamente. Para nomeá-los:

{
"player_ref": "alice",
"bots": [{ "player_ref": "bot_lucky" }, { "player_ref": "bot_swift" }]
}

Informar bots significa informar ao menos tantas entradas quantos assentos vazios. Menos é 409.

Dois portões valem aqui e em nenhum outro lugar:

  • Só o criador. Um player_ref que não é o criador é 403.
  • A allowlist é rechecada. Um jogo revogado enquanto o lobby estava aberto interrompe a batalha neste ponto, em vez de deixá-la rodar.

Um 502 significa que a emissão da sessão demo de um bot falhou no provedor, o que é problema upstream e não requisição ruim.

Sair

POST /battles/{id}/leave escopo: battles:write → 200
{ "player_ref": "bob" }

Libera o assento de volta para EMPTY, limpando jogador, URL de vídeo e qualquer resultado.

Se o criador sair, a batalha é cancelada

Sair não passa o lobby para outra pessoa. Cheque creator_player_ref antes de chamar isso em nome de um criador, e garanta que a sua UI diga "cancelar", não "sair".

Cancelar

POST /battles/{id}/cancel escopo: battles:write → 200

Só o criador, só em OPEN. Uma batalha que já começou não pode ser cancelada aqui, e responde 409.

O corpo é opcional. Envie {"player_ref": "alice"} quando a chamada vier de uma ação de jogador, para a checagem de criador rodar. Omita para um cancelamento iniciado por operador, que pula essa checagem.

Ler e listar

GET /battles/{id} escopo: battles:read
GET /battles?status=OPEN&limit=20 escopo: battles:read

limit tem padrão 20 e é limitado a 100: pedir mais é cortado em silêncio, não recusado. Os resultados vêm dos mais novos para os mais antigos.

Nenhuma das duas rotas emite ws_token. Emita um explicitamente com tokens de viewer quando um espectador precisar conectar numa batalha que ele não criou.

Uma batalha de outro casino responde 404, igual a uma que nunca existiu.

Não faça polling

Não fique dando polling em GET /battles/{id} para acompanhar uma batalha. Assine o feed WebSocket para estado ao vivo, e trate o webhook como autoridade sobre o resultado. Reserve as leituras para renderizar uma página que alguém acabou de abrir.

Próximo