openapi: 3.1.0 info: title: Gamers pelo Brasil — troféus, identidade e API legada version: 0.3.0-community.1 description: 'Contrato journal-v1. Sessão, nomes, partidas, diário, resultado oficial e ranking na API. GAME_ORIGIN habilita CORS com credenciais somente nessas rotas para a origem exata do Defende Brasil. O jogo usa credentials include; o cookie Secure/HttpOnly permanece exclusivo do host da API. Repertório br-test-v1 não aprovado editorialmente: uso interno de teste. Origin exata em mutações, exatamente um Content-Type application/json e nenhum Content-Encoding, nem vazio. Cookie de sessão repetido é rejeitado com 401 nas rotas /api/, inclusive sessão e ranking. Campos desconhecidos, JSON duplicado em qualquer nível, null em campos obrigatórios e JSON ambíguo são rejeitados. Nomes usam corpo máximo de 4096 bytes; lotes, 131072 bytes. Respostas não armazenáveis (Cache-Control: no-store). Commit/timeout significa resultado desconhecido: consultar e reenviar idêntico. No contrato legado não há engine autoritativa nem prova física de partida. A operação de engajamento usa ENGAGEMENT_ORIGIN e não cria identidade. Troféus: emissão pelo SDK sem sessão, com chave pública por jogo e origem cadastrada; resgate central por cookie. As rotas journal-v1 continuam disponíveis como legado. Na emissão, cookies são ignorados, inclusive repetidos.' license: name: Uso interno do projeto servers: - url: https://api.gamerspelobrasil.tech tags: - name: Arena description: Partida cooperativa com conta, avatares do hub e moderação individual. - name: Comunidade description: Recebimento por link e HTML/ZIP para revisão privada; ativação pública explícita pelo operador. - name: Identidade description: Sessão e nome do próprio jogador. - name: Saúde description: Processo e disponibilidade do banco/schema. - name: Engajamento description: Visitas e cliques sem identificação de visitantes. - name: Partidas description: Diário consistente, cálculo oficial e publicação única. - name: Contas description: Login com Google pelo hub, sessão de conta e nome#número. - name: Troféus description: Conquistas declaradas pelos jogos e coleção central; versão de testes. security: - session: [] paths: /api/v1/arena/me: get: tags: - Arena operationId: getArenaParticipation summary: Consultar evento, participação própria e função description: Desativado sem ARENA_MODE. Exige conta Google do hub; identidade de teste é recusada. Respostas no-store. Ensaio e evento oficial usam IDs diferentes. Pontos da Arena não alteram o ranking legado. security: - session: [] responses: '200': description: Estado autorizado da Arena. Nenhum e-mail é exposto no estado coletivo. content: application/json: schema: type: object required: - state - role properties: state: $ref: '#/components/schemas/ArenaState' role: type: string enum: - '' - moderator - coordinator player: anyOf: - $ref: '#/components/schemas/ArenaPlayer' - type: 'null' checks: type: array items: type: object blocks: type: array items: type: object example: state: &arena_id001 t: state event: debate-2026-10-23-ensaio rehearsal: true phase: waiting hp: 10000 max_hp: 10000 defeats: 0 resolved: 0 online: 0 ranking: [] checked: 0 source_status: waiting source_updated: 0 server_time: 1792770000000 stun_until: 0 combo_until: 0 revision: 0 player: null role: '' '400': description: ARENA_INVALID, INVALID_JSON, INVALID_QUERY content: application/json: schema: $ref: '#/components/schemas/Error' examples: ARENA_INVALID: value: code: ARENA_INVALID message: 'Exemplo sintético: ARENA_INVALID' INVALID_JSON: value: code: INVALID_JSON message: 'Exemplo sintético: INVALID_JSON' INVALID_QUERY: value: code: INVALID_QUERY message: 'Exemplo sintético: INVALID_QUERY' '401': description: UNAUTHENTICATED content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNAUTHENTICATED: value: code: UNAUTHENTICATED message: 'Exemplo sintético: UNAUTHENTICATED' '404': description: ACCOUNT_NOT_FOUND; recurso desativado também retorna 404 sem JSON. content: application/json: schema: $ref: '#/components/schemas/Error' examples: ACCOUNT_NOT_FOUND: value: code: ACCOUNT_NOT_FOUND message: 'Exemplo sintético: ACCOUNT_NOT_FOUND' '409': description: ARENA_CLOSED, ARENA_CHARACTER_REQUIRED content: application/json: schema: $ref: '#/components/schemas/Error' examples: ARENA_CLOSED: value: code: ARENA_CLOSED message: 'Exemplo sintético: ARENA_CLOSED' ARENA_CHARACTER_REQUIRED: value: code: ARENA_CHARACTER_REQUIRED message: 'Exemplo sintético: ARENA_CHARACTER_REQUIRED' '429': description: RATE_LIMITED content: application/json: schema: $ref: '#/components/schemas/Error' examples: RATE_LIMITED: value: code: RATE_LIMITED message: 'Exemplo sintético: RATE_LIMITED' '503': description: ARENA_UNAVAILABLE, DEPENDENCY_UNAVAILABLE, UNAVAILABLE content: application/json: schema: $ref: '#/components/schemas/Error' examples: ARENA_UNAVAILABLE: value: code: ARENA_UNAVAILABLE message: 'Exemplo sintético: ARENA_UNAVAILABLE' DEPENDENCY_UNAVAILABLE: value: code: DEPENDENCY_UNAVAILABLE message: 'Exemplo sintético: DEPENDENCY_UNAVAILABLE' UNAVAILABLE: value: code: UNAVAILABLE message: 'Exemplo sintético: UNAVAILABLE' '403': description: ORIGIN_NOT_ALLOWED content: application/json: schema: $ref: '#/components/schemas/Error' examples: ORIGIN_NOT_ALLOWED: value: code: ORIGIN_NOT_ALLOWED message: 'Exemplo sintético: ORIGIN_NOT_ALLOWED' /api/v1/arena/join: post: tags: - Arena operationId: joinArena summary: Participar com a conta e UF do evento description: Desativado sem ARENA_MODE. Exige conta Google do hub; identidade de teste é recusada. Respostas no-store. Ensaio e evento oficial usam IDs diferentes. Pontos da Arena não alteram o ranking legado. security: - session: [] responses: '200': description: Estado autorizado da Arena. Nenhum e-mail é exposto no estado coletivo. content: application/json: schema: $ref: '#/components/schemas/ArenaPlayer' example: uf: PE damage: 0 points: 0 answers: {} last_hit: 0 tokens: 0 '400': description: ARENA_INVALID, INVALID_JSON, INVALID_QUERY content: application/json: schema: $ref: '#/components/schemas/Error' examples: ARENA_INVALID: value: code: ARENA_INVALID message: 'Exemplo sintético: ARENA_INVALID' INVALID_JSON: value: code: INVALID_JSON message: 'Exemplo sintético: INVALID_JSON' INVALID_QUERY: value: code: INVALID_QUERY message: 'Exemplo sintético: INVALID_QUERY' '401': description: UNAUTHENTICATED content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNAUTHENTICATED: value: code: UNAUTHENTICATED message: 'Exemplo sintético: UNAUTHENTICATED' '404': description: ACCOUNT_NOT_FOUND; recurso desativado também retorna 404 sem JSON. content: application/json: schema: $ref: '#/components/schemas/Error' examples: ACCOUNT_NOT_FOUND: value: code: ACCOUNT_NOT_FOUND message: 'Exemplo sintético: ACCOUNT_NOT_FOUND' '409': description: ARENA_CLOSED, ARENA_CHARACTER_REQUIRED content: application/json: schema: $ref: '#/components/schemas/Error' examples: ARENA_CLOSED: value: code: ARENA_CLOSED message: 'Exemplo sintético: ARENA_CLOSED' ARENA_CHARACTER_REQUIRED: value: code: ARENA_CHARACTER_REQUIRED message: 'Exemplo sintético: ARENA_CHARACTER_REQUIRED' '429': description: RATE_LIMITED content: application/json: schema: $ref: '#/components/schemas/Error' examples: RATE_LIMITED: value: code: RATE_LIMITED message: 'Exemplo sintético: RATE_LIMITED' '503': description: ARENA_UNAVAILABLE, DEPENDENCY_UNAVAILABLE, UNAVAILABLE content: application/json: schema: $ref: '#/components/schemas/Error' examples: ARENA_UNAVAILABLE: value: code: ARENA_UNAVAILABLE message: 'Exemplo sintético: ARENA_UNAVAILABLE' DEPENDENCY_UNAVAILABLE: value: code: DEPENDENCY_UNAVAILABLE message: 'Exemplo sintético: DEPENDENCY_UNAVAILABLE' UNAVAILABLE: value: code: UNAVAILABLE message: 'Exemplo sintético: UNAVAILABLE' '403': description: ORIGIN_NOT_ALLOWED content: application/json: schema: $ref: '#/components/schemas/Error' examples: ORIGIN_NOT_ALLOWED: value: code: ORIGIN_NOT_ALLOWED message: 'Exemplo sintético: ORIGIN_NOT_ALLOWED' '413': description: BODY_TOO_LARGE content: application/json: schema: $ref: '#/components/schemas/Error' examples: BODY_TOO_LARGE: value: code: BODY_TOO_LARGE message: 'Exemplo sintético: BODY_TOO_LARGE' '415': description: UNSUPPORTED_MEDIA_TYPE content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNSUPPORTED_MEDIA_TYPE: value: code: UNSUPPORTED_MEDIA_TYPE message: 'Exemplo sintético: UNSUPPORTED_MEDIA_TYPE' parameters: - &arena_id002 name: Origin in: header required: true schema: type: string example: https://gamerspelobrasil.tech description: Origem exata do hub; credenciais permanecem no cookie host-only da API. requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: - uf properties: uf: type: string enum: - AC - AL - AP - AM - BA - CE - DF - ES - GO - MA - MT - MS - MG - PA - PB - PR - PE - PI - RJ - RN - RS - RO - RR - SC - SP - SE - TO - BR example: uf: PE /api/v1/arena/admin: get: tags: - Arena operationId: getArenaModeration summary: Consultar fila e blocos retidos para revisão description: Desativado sem ARENA_MODE. Exige conta Google do hub; identidade de teste é recusada. Respostas no-store. Ensaio e evento oficial usam IDs diferentes. Pontos da Arena não alteram o ranking legado. security: - session: [] responses: '200': description: Estado autorizado da Arena. Nenhum e-mail é exposto no estado coletivo. content: application/json: schema: type: object required: - state - role properties: state: $ref: '#/components/schemas/ArenaState' role: type: string enum: - '' - moderator - coordinator player: anyOf: - $ref: '#/components/schemas/ArenaPlayer' - type: 'null' checks: type: array items: type: object blocks: type: array items: type: object example: state: *arena_id001 checks: [] blocks: [] role: moderator '400': description: ARENA_INVALID, INVALID_JSON, INVALID_QUERY content: application/json: schema: $ref: '#/components/schemas/Error' examples: ARENA_INVALID: value: code: ARENA_INVALID message: 'Exemplo sintético: ARENA_INVALID' INVALID_JSON: value: code: INVALID_JSON message: 'Exemplo sintético: INVALID_JSON' INVALID_QUERY: value: code: INVALID_QUERY message: 'Exemplo sintético: INVALID_QUERY' '401': description: UNAUTHENTICATED content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNAUTHENTICATED: value: code: UNAUTHENTICATED message: 'Exemplo sintético: UNAUTHENTICATED' '404': description: ACCOUNT_NOT_FOUND; recurso desativado também retorna 404 sem JSON. content: application/json: schema: $ref: '#/components/schemas/Error' examples: ACCOUNT_NOT_FOUND: value: code: ACCOUNT_NOT_FOUND message: 'Exemplo sintético: ACCOUNT_NOT_FOUND' '409': description: ARENA_CLOSED, ARENA_CHARACTER_REQUIRED content: application/json: schema: $ref: '#/components/schemas/Error' examples: ARENA_CLOSED: value: code: ARENA_CLOSED message: 'Exemplo sintético: ARENA_CLOSED' ARENA_CHARACTER_REQUIRED: value: code: ARENA_CHARACTER_REQUIRED message: 'Exemplo sintético: ARENA_CHARACTER_REQUIRED' '429': description: RATE_LIMITED content: application/json: schema: $ref: '#/components/schemas/Error' examples: RATE_LIMITED: value: code: RATE_LIMITED message: 'Exemplo sintético: RATE_LIMITED' '503': description: ARENA_UNAVAILABLE, DEPENDENCY_UNAVAILABLE, UNAVAILABLE content: application/json: schema: $ref: '#/components/schemas/Error' examples: ARENA_UNAVAILABLE: value: code: ARENA_UNAVAILABLE message: 'Exemplo sintético: ARENA_UNAVAILABLE' DEPENDENCY_UNAVAILABLE: value: code: DEPENDENCY_UNAVAILABLE message: 'Exemplo sintético: DEPENDENCY_UNAVAILABLE' UNAVAILABLE: value: code: UNAVAILABLE message: 'Exemplo sintético: UNAVAILABLE' '403': description: FORBIDDEN, ORIGIN_NOT_ALLOWED content: application/json: schema: $ref: '#/components/schemas/Error' examples: FORBIDDEN: value: code: FORBIDDEN message: 'Exemplo sintético: FORBIDDEN' ORIGIN_NOT_ALLOWED: value: code: ORIGIN_NOT_ALLOWED message: 'Exemplo sintético: ORIGIN_NOT_ALLOWED' /api/v1/arena/admin/action: post: tags: - Arena operationId: moderateArena summary: Operar fila ou coordenar o evento description: Desativado sem ARENA_MODE. Exige conta Google do hub; identidade de teste é recusada. Respostas no-store. Ensaio e evento oficial usam IDs diferentes. Pontos da Arena não alteram o ranking legado. security: - session: [] responses: '200': description: Estado autorizado da Arena. Nenhum e-mail é exposto no estado coletivo. content: application/json: schema: $ref: '#/components/schemas/ArenaState' example: *arena_id001 '400': description: ARENA_INVALID, INVALID_JSON, INVALID_QUERY content: application/json: schema: $ref: '#/components/schemas/Error' examples: ARENA_INVALID: value: code: ARENA_INVALID message: 'Exemplo sintético: ARENA_INVALID' INVALID_JSON: value: code: INVALID_JSON message: 'Exemplo sintético: INVALID_JSON' INVALID_QUERY: value: code: INVALID_QUERY message: 'Exemplo sintético: INVALID_QUERY' '401': description: UNAUTHENTICATED content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNAUTHENTICATED: value: code: UNAUTHENTICATED message: 'Exemplo sintético: UNAUTHENTICATED' '404': description: ACCOUNT_NOT_FOUND; recurso desativado também retorna 404 sem JSON. content: application/json: schema: $ref: '#/components/schemas/Error' examples: ACCOUNT_NOT_FOUND: value: code: ACCOUNT_NOT_FOUND message: 'Exemplo sintético: ACCOUNT_NOT_FOUND' '409': description: ARENA_CLOSED, ARENA_CHARACTER_REQUIRED content: application/json: schema: $ref: '#/components/schemas/Error' examples: ARENA_CLOSED: value: code: ARENA_CLOSED message: 'Exemplo sintético: ARENA_CLOSED' ARENA_CHARACTER_REQUIRED: value: code: ARENA_CHARACTER_REQUIRED message: 'Exemplo sintético: ARENA_CHARACTER_REQUIRED' '429': description: RATE_LIMITED content: application/json: schema: $ref: '#/components/schemas/Error' examples: RATE_LIMITED: value: code: RATE_LIMITED message: 'Exemplo sintético: RATE_LIMITED' '503': description: ARENA_UNAVAILABLE, DEPENDENCY_UNAVAILABLE, UNAVAILABLE content: application/json: schema: $ref: '#/components/schemas/Error' examples: ARENA_UNAVAILABLE: value: code: ARENA_UNAVAILABLE message: 'Exemplo sintético: ARENA_UNAVAILABLE' DEPENDENCY_UNAVAILABLE: value: code: DEPENDENCY_UNAVAILABLE message: 'Exemplo sintético: DEPENDENCY_UNAVAILABLE' UNAVAILABLE: value: code: UNAVAILABLE message: 'Exemplo sintético: UNAVAILABLE' '403': description: FORBIDDEN, ORIGIN_NOT_ALLOWED content: application/json: schema: $ref: '#/components/schemas/Error' examples: FORBIDDEN: value: code: FORBIDDEN message: 'Exemplo sintético: FORBIDDEN' ORIGIN_NOT_ALLOWED: value: code: ORIGIN_NOT_ALLOWED message: 'Exemplo sintético: ORIGIN_NOT_ALLOWED' '413': description: BODY_TOO_LARGE content: application/json: schema: $ref: '#/components/schemas/Error' examples: BODY_TOO_LARGE: value: code: BODY_TOO_LARGE message: 'Exemplo sintético: BODY_TOO_LARGE' '415': description: UNSUPPORTED_MEDIA_TYPE content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNSUPPORTED_MEDIA_TYPE: value: code: UNSUPPORTED_MEDIA_TYPE message: 'Exemplo sintético: UNSUPPORTED_MEDIA_TYPE' parameters: - *arena_id002 requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: - action - key - value properties: action: type: string enum: - publish - veto - shorten - withdraw - simulate - start - pause - resume - end - reset - hp key: type: string maxLength: 220 value: type: integer example: action: publish key: ensaio-1:1 value: 0 /api/v1/arena/ws: get: tags: - Arena operationId: connectArena summary: Conectar à Arena por WebSocket autenticado description: 'Handshake exige Origin exato do hub, cookie de conta e participação criada por joinArena. Até 2 sockets por conta, limite agregado configurado (128 por padrão). 1024 bytes por mensagem; até 8 mensagens/s com burst 8; cliques limitados a 15/s por conta. Cliente envia {t: hit, n: 1..30} ou {t: answer, key: id:revision, value: categoria}. Servidor envia welcome com state/player, state a cada 200 ms, hit com dano/pontos, answer com resultado privado, ou error. State omite o veredito até reveal. Uma resposta por conta/evento/poder, inclusive após reconexão. Logout revoga sockets da sessão; revalidação a cada 5 s. Estado substituível pode ser descartado para cliente lento; welcome recupera poder, prazos, pontos e respostas. Pontos são confirmados após commit SQL. Sem login ou estado em querystring.' security: - session: [] parameters: - *arena_id002 responses: '101': description: Upgrade para WebSocket; mensagens JSON conforme descrição. '403': description: Origem/handshake não autorizado. '404': description: Arena desligada. '429': description: Admissão ou limite de conexões atingido. /api/v1/submissions: post: tags: - Comunidade operationId: submitCommunityLink summary: Receber indicação HTTPS para revisão privada security: [] description: Desativado sem SUBMISSIONS_ORIGIN. O modo piloto exige loopback. O modo público exige HTTPS e ponte autenticada do hub; o navegador chama o mesmo caminho no hub, sem acesso direto à API. Sem conta, cookies ou CORS. Sem fetch de URL ou publicação. Origem de exemplo só para o ensaio local; no hub o caminho é encaminhado pela mesma origem. Até 16384 bytes; texto UTF-8 sem espaços nas extremidades, limites em caracteres Unicode. Cliente gera chave de idempotência e segredo independentes de 256 bits com CSPRNG antes de enviar; guarda os mesmos para retry. Ambos ficam somente como hash no banco. Corpo idêntico recupera protocolo/estado atual; mudar qualquer campo/segredo com a mesma chave retorna 409 sem protocolo. Protocolo aleatório de 128 bits. Até 20 novos/minuto e 500 registros no piloto; retry existente precede essas cotas. Envio e consulta compartilham admissão persistida de 120 tentativas/minuto, inclusive JSON inválido e retry; 429 inclui Retry-After 60. Contadores de admissão com mais de 24h são limpos ao tentar. Sem conta ou identificador escolhido pelo visitante; a admissão usa o solicitante da ponte confiável. Campos desconhecidos, null e duplicatas são recusados. Respostas no-store e no-referrer. Sem endpoint de fila/admin; CLI exige acesso local ao banco. parameters: - name: Origin in: header required: true description: Uma única origem exata configurada em SUBMISSIONS_ORIGIN, loopback no piloto ou HTTPS no modo público por ponte do hub. schema: type: string example: http://localhost:8091 requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SubmissionInput' examples: pilot: value: idempotency_key: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa tracking_secret: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb title: Jogo de teste url: https://example.invalid/jogo description: Indicação sintética. public_credit: Equipe de teste private_contact: contato@example.invalid relationship: recommendation responses: '200': description: Recebimento confirmado ou retry idêntico; consulta retorna somente estado e mensagem ao remetente. content: application/json: schema: $ref: '#/components/schemas/SubmissionReceipt' examples: received: value: &id001 protocol: dddddddddddddddddddddddddddddddd status: received public_message: '' created_at: '2026-10-18T15:00:00Z' updated_at: '2026-10-18T15:00:00Z' '400': description: INVALID_JSON, INVALID_QUERY, INVALID_SUBMISSION. content: application/json: schema: $ref: '#/components/schemas/Error' examples: INVALID_JSON: value: code: INVALID_JSON message: 'Exemplo sintético: INVALID_JSON' INVALID_QUERY: value: code: INVALID_QUERY message: 'Exemplo sintético: INVALID_QUERY' INVALID_SUBMISSION: value: code: INVALID_SUBMISSION message: 'Exemplo sintético: INVALID_SUBMISSION' '403': description: ORIGIN_NOT_ALLOWED. content: application/json: schema: $ref: '#/components/schemas/Error' examples: ORIGIN_NOT_ALLOWED: value: code: ORIGIN_NOT_ALLOWED message: 'Exemplo sintético: ORIGIN_NOT_ALLOWED' '413': description: BODY_TOO_LARGE. content: application/json: schema: $ref: '#/components/schemas/Error' examples: BODY_TOO_LARGE: value: code: BODY_TOO_LARGE message: 'Exemplo sintético: BODY_TOO_LARGE' '415': description: UNSUPPORTED_MEDIA_TYPE. content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNSUPPORTED_MEDIA_TYPE: value: code: UNSUPPORTED_MEDIA_TYPE message: 'Exemplo sintético: UNSUPPORTED_MEDIA_TYPE' '429': description: RATE_LIMITED. content: application/json: schema: $ref: '#/components/schemas/Error' examples: RATE_LIMITED: value: code: RATE_LIMITED message: 'Exemplo sintético: RATE_LIMITED' headers: Retry-After: description: Segundos até tentar de novo. schema: type: integer const: 60 '503': description: DEPENDENCY_UNAVAILABLE, UNAVAILABLE, SUBMISSION_CAPACITY. content: application/json: schema: $ref: '#/components/schemas/Error' examples: DEPENDENCY_UNAVAILABLE: value: code: DEPENDENCY_UNAVAILABLE message: 'Exemplo sintético: DEPENDENCY_UNAVAILABLE' UNAVAILABLE: value: code: UNAVAILABLE message: 'Exemplo sintético: UNAVAILABLE' SUBMISSION_CAPACITY: value: code: SUBMISSION_CAPACITY message: 'Exemplo sintético: SUBMISSION_CAPACITY' '409': description: SUBMISSION_CONFLICT. Conteúdo divergente para a chave, ou versão anterior indisponível, segredo incorreto, modalidade diferente ou sucessora já existente. content: application/json: schema: $ref: '#/components/schemas/Error' examples: SUBMISSION_CONFLICT: value: code: SUBMISSION_CONFLICT message: 'Exemplo sintético: SUBMISSION_CONFLICT' '404': description: Piloto desativado. content: text/plain: schema: type: string example: '404 page not found ' /api/v1/submissions/lookup: post: tags: - Comunidade operationId: lookupCommunitySubmission summary: Consultar estado por protocolo e segredo security: [] description: Desativado sem SUBMISSIONS_ORIGIN. O modo piloto exige loopback. O modo público exige HTTPS e ponte autenticada do hub; o navegador chama o mesmo caminho no hub, sem acesso direto à API. Sem conta, cookies ou CORS. Sem fetch de URL ou publicação. Origem de exemplo só para o ensaio local; no hub o caminho é encaminhado pela mesma origem. Até 4096 bytes. Protocolo e segredo vão no corpo POST, nunca na URL. Segredo errado e protocolo ausente têm o mesmo 404. Mesmo com segredo correto, contato, conteúdo submetido e nota interna não são devolvidos. Aprovado não significa publicado. Não há recuperação do segredo pelo operador. Envio e consulta compartilham admissão persistida de 120 tentativas/minuto, inclusive JSON inválido e retry; 429 inclui Retry-After 60. Contadores de admissão com mais de 24h são limpos ao tentar. Sem conta ou identificador escolhido pelo visitante; a admissão usa o solicitante da ponte confiável. Campos desconhecidos, null e duplicatas são recusados. Respostas no-store e no-referrer. Sem endpoint de fila/admin; CLI exige acesso local ao banco. parameters: - name: Origin in: header required: true description: Uma única origem exata configurada em SUBMISSIONS_ORIGIN, loopback no piloto ou HTTPS no modo público por ponte do hub. schema: type: string example: http://localhost:8091 requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SubmissionLookup' examples: pilot: value: protocol: dddddddddddddddddddddddddddddddd tracking_secret: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb responses: '200': description: Recebimento confirmado ou retry idêntico; consulta retorna somente estado e mensagem ao remetente. content: application/json: schema: $ref: '#/components/schemas/SubmissionReceipt' examples: received: value: *id001 '400': description: INVALID_JSON, INVALID_QUERY. content: application/json: schema: $ref: '#/components/schemas/Error' examples: INVALID_JSON: value: code: INVALID_JSON message: 'Exemplo sintético: INVALID_JSON' INVALID_QUERY: value: code: INVALID_QUERY message: 'Exemplo sintético: INVALID_QUERY' '403': description: ORIGIN_NOT_ALLOWED. content: application/json: schema: $ref: '#/components/schemas/Error' examples: ORIGIN_NOT_ALLOWED: value: code: ORIGIN_NOT_ALLOWED message: 'Exemplo sintético: ORIGIN_NOT_ALLOWED' '413': description: BODY_TOO_LARGE. content: application/json: schema: $ref: '#/components/schemas/Error' examples: BODY_TOO_LARGE: value: code: BODY_TOO_LARGE message: 'Exemplo sintético: BODY_TOO_LARGE' '415': description: UNSUPPORTED_MEDIA_TYPE. content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNSUPPORTED_MEDIA_TYPE: value: code: UNSUPPORTED_MEDIA_TYPE message: 'Exemplo sintético: UNSUPPORTED_MEDIA_TYPE' '429': description: RATE_LIMITED. content: application/json: schema: $ref: '#/components/schemas/Error' examples: RATE_LIMITED: value: code: RATE_LIMITED message: 'Exemplo sintético: RATE_LIMITED' headers: Retry-After: description: Segundos até tentar de novo. schema: type: integer const: 60 '503': description: DEPENDENCY_UNAVAILABLE, UNAVAILABLE. content: application/json: schema: $ref: '#/components/schemas/Error' examples: DEPENDENCY_UNAVAILABLE: value: code: DEPENDENCY_UNAVAILABLE message: 'Exemplo sintético: DEPENDENCY_UNAVAILABLE' UNAVAILABLE: value: code: UNAVAILABLE message: 'Exemplo sintético: UNAVAILABLE' '404': description: SUBMISSION_NOT_FOUND. content: application/json: schema: $ref: '#/components/schemas/Error' examples: SUBMISSION_NOT_FOUND: value: code: SUBMISSION_NOT_FOUND message: 'Exemplo sintético: SUBMISSION_NOT_FOUND' text/plain: schema: type: string example: '404 page not found ' /api/v1/engagement: post: tags: - Engajamento summary: Registrar visita ou clique em Jogar sem identificar visitantes operationId: recordEngagement security: [] description: Canal do portal, habilitado somente quando ENGAGEMENT_ORIGIN é configurada. Origin exata independente de PUBLIC_ORIGIN; sem CORS, sessão ou cookie novo. Campos extras, null, duplicatas e IDs fora do formato UUID v4 são rejeitados. ID representa uma ação, sem vínculo com visitante; repetir ID/corpo não duplica e mudar os campos retorna 409. Horário atribuído no servidor. Limite de 600 pedidos por minuto por processo, sem usar IP. Eventos dos últimos 30 dias no relatório; limpeza no boot e a cada hora. Não recebe scores nem cria ranking. Origin é controle do navegador, não autenticação de máquinas; contagens públicas podem conter automação. parameters: - name: Origin in: header required: true description: Origem exata do portal configurada em ENGAGEMENT_ORIGIN. Não autoriza essa origem nas mutações de jogadores. schema: type: string example: https://jogos.gamerspelobrasil.tech requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: - event_id - game - kind properties: event_id: type: string pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ game: type: string enum: - genius-6x1 - vire-a-carta - corrida-presidencial - baralho-interativo - brasil-melhor - desafio-do-brasil - brasil-em-jogo - super-lula-world - qual-seu-flavio - super-flavio-world - salve-a-padroeira - vira-voto - antes-de-votar - vira-voto-conversa - levante-13 - em-quem-jesus-votaria - asas-da-memoria - uno-companheiro - defende-brasil - soberania-em-jogo - placar-da-verdade - o-gabinete - divida-publica-simulator - herois-da-democracia - debate-arena kind: type: string enum: - visit - play examples: visit: summary: Visualização da página value: event_id: 12345678-1234-4123-8123-123456789abc game: genius-6x1 kind: visit play: summary: Clique em Jogar value: event_id: 12345678-1234-4123-8123-123456789abd game: genius-6x1 kind: play responses: '204': description: Evento persistido ou reenvio idêntico já persistido. Sem corpo e sem cookie. '400': description: INVALID_JSON, INVALID_QUERY, INVALID_ENGAGEMENT content: application/json: schema: $ref: '#/components/schemas/Error' examples: INVALID_JSON: summary: INVALID JSON value: code: INVALID_JSON message: 'Exemplo sintético: INVALID_JSON' INVALID_QUERY: summary: INVALID QUERY value: code: INVALID_QUERY message: 'Exemplo sintético: INVALID_QUERY' INVALID_ENGAGEMENT: summary: INVALID ENGAGEMENT value: code: INVALID_ENGAGEMENT message: 'Exemplo sintético: INVALID_ENGAGEMENT' '401': description: UNAUTHENTICATED content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNAUTHENTICATED: summary: UNAUTHENTICATED value: code: UNAUTHENTICATED message: 'Exemplo sintético: UNAUTHENTICATED' '403': description: ORIGIN_NOT_ALLOWED content: application/json: schema: $ref: '#/components/schemas/Error' examples: ORIGIN_NOT_ALLOWED: summary: ORIGIN NOT ALLOWED value: code: ORIGIN_NOT_ALLOWED message: 'Exemplo sintético: ORIGIN_NOT_ALLOWED' '409': description: ENGAGEMENT_CONFLICT content: application/json: schema: $ref: '#/components/schemas/Error' examples: ENGAGEMENT_CONFLICT: summary: ENGAGEMENT CONFLICT value: code: ENGAGEMENT_CONFLICT message: 'Exemplo sintético: ENGAGEMENT_CONFLICT' '413': description: BODY_TOO_LARGE content: application/json: schema: $ref: '#/components/schemas/Error' examples: BODY_TOO_LARGE: summary: BODY TOO LARGE value: code: BODY_TOO_LARGE message: 'Exemplo sintético: BODY_TOO_LARGE' '415': description: UNSUPPORTED_MEDIA_TYPE content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNSUPPORTED_MEDIA_TYPE: summary: UNSUPPORTED MEDIA TYPE value: code: UNSUPPORTED_MEDIA_TYPE message: 'Exemplo sintético: UNSUPPORTED_MEDIA_TYPE' '429': description: RATE_LIMITED content: application/json: schema: $ref: '#/components/schemas/Error' examples: RATE_LIMITED: summary: RATE LIMITED value: code: RATE_LIMITED message: 'Exemplo sintético: RATE_LIMITED' headers: Retry-After: schema: type: string const: '60' '503': description: DEPENDENCY_UNAVAILABLE, UNAVAILABLE content: application/json: schema: $ref: '#/components/schemas/Error' examples: DEPENDENCY_UNAVAILABLE: summary: DEPENDENCY UNAVAILABLE value: code: DEPENDENCY_UNAVAILABLE message: 'Exemplo sintético: DEPENDENCY_UNAVAILABLE' UNAVAILABLE: summary: UNAVAILABLE value: code: UNAVAILABLE message: 'Exemplo sintético: UNAVAILABLE' /api/v1/sessions: post: tags: - Identidade summary: Criar ou recuperar sessão operationId: createSession security: [] description: Cookie válido recupera a mesma identidade sem renovar prazo ou cookie. Ausente/inválido/expirado cria outra identidade; cookie de sessão duplicado retorna 401 sem criar identidade. Validade padrão 24h; SESSION_TTL admite 1m a 168h. Limite complementar de 60 pedidos por minuto por processo, incluindo recuperações; não é controle distribuído por IP. parameters: - $ref: '#/components/parameters/Origin' requestBody: required: true content: application/json: schema: type: object additionalProperties: false examples: empty: summary: empty value: {} responses: '200': description: Identidade persistida do chamador. content: application/json: schema: $ref: '#/components/schemas/Player' examples: reused: summary: reused value: player_id: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa display_name: Arara Cívica Azul repertoire_version: br-test-v1 expires_at: '2026-10-17T12:00:00Z' open_run_id: null open_run: summary: open run value: player_id: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa display_name: Arara Cívica Azul repertoire_version: br-test-v1 expires_at: '2026-10-17T12:00:00Z' open_run_id: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb '201': description: Nova sessão persistida; cookie opaco, somente hash no banco. headers: Set-Cookie: description: defende_brasil_session; Path=/; HttpOnly; SameSite=Lax; sem Domain. Secure se PUBLIC_ORIGIN usa HTTPS. Expires/Max-Age fixam validade. schema: type: string content: application/json: schema: $ref: '#/components/schemas/Player' examples: created_unnamed: summary: created unnamed value: player_id: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa display_name: null repertoire_version: null expires_at: '2026-10-17T12:00:00Z' open_run_id: null '400': description: INVALID_JSON para corpo inválido; INVALID_QUERY para consulta inválida. content: application/json: schema: $ref: '#/components/schemas/Error' examples: INVALID_JSON: summary: INVALID JSON value: code: INVALID_JSON message: 'Exemplo sintético: INVALID_JSON' '401': description: UNAUTHENTICATED — credencial ausente, inválida, expirada ou cookie de sessão duplicado. content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNAUTHENTICATED: summary: UNAUTHENTICATED value: code: UNAUTHENTICATED message: 'Exemplo sintético: UNAUTHENTICATED' '403': description: ORIGIN_NOT_ALLOWED — origem ausente, null, múltipla ou diferente. content: application/json: schema: $ref: '#/components/schemas/Error' examples: ORIGIN_NOT_ALLOWED: summary: ORIGIN NOT ALLOWED value: code: ORIGIN_NOT_ALLOWED message: 'Exemplo sintético: ORIGIN_NOT_ALLOWED' '413': description: BODY_TOO_LARGE — corpo excede limite da rota (4096 ou 131072 bytes). content: application/json: schema: $ref: '#/components/schemas/Error' examples: BODY_TOO_LARGE: summary: BODY TOO LARGE value: code: BODY_TOO_LARGE message: 'Exemplo sintético: BODY_TOO_LARGE' '415': description: UNSUPPORTED_MEDIA_TYPE — exatamente um Content-Type application/json e ausência de Content-Encoding, inclusive vazio. Headers duplicados são rejeitados. content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNSUPPORTED_MEDIA_TYPE: summary: UNSUPPORTED MEDIA TYPE value: code: UNSUPPORTED_MEDIA_TYPE message: 'Exemplo sintético: UNSUPPORTED_MEDIA_TYPE' '429': description: RATE_LIMITED — aguardar Retry-After antes de repetir. headers: Retry-After: schema: type: integer const: 60 content: application/json: schema: $ref: '#/components/schemas/Error' examples: RATE_LIMITED: summary: RATE LIMITED value: code: RATE_LIMITED message: 'Exemplo sintético: RATE_LIMITED' '503': description: DEPENDENCY_UNAVAILABLE ou UNAVAILABLE; sem SQL/credenciais. Resultado de commit pode ser desconhecido; consultar identidade antes de nova escolha. content: application/json: schema: $ref: '#/components/schemas/Error' examples: DEPENDENCY_UNAVAILABLE: summary: DEPENDENCY UNAVAILABLE value: code: DEPENDENCY_UNAVAILABLE message: 'Exemplo sintético: DEPENDENCY_UNAVAILABLE' UNAVAILABLE: summary: UNAVAILABLE value: code: UNAVAILABLE message: 'Exemplo sintético: UNAVAILABLE' /api/v1/players/me: get: tags: - Identidade summary: Recuperar identidade e nome confirmado operationId: getPlayer responses: '200': description: Identidade persistida do chamador. content: application/json: schema: $ref: '#/components/schemas/Player' examples: named: summary: named value: player_id: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa display_name: Arara Cívica Azul repertoire_version: br-test-v1 expires_at: '2026-10-17T12:00:00Z' open_run_id: null unnamed: summary: unnamed value: player_id: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa display_name: null repertoire_version: null expires_at: '2026-10-17T12:00:00Z' open_run_id: null '401': description: UNAUTHENTICATED — credencial ausente, inválida, expirada ou cookie de sessão duplicado. content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNAUTHENTICATED: summary: UNAUTHENTICATED value: code: UNAUTHENTICATED message: 'Exemplo sintético: UNAUTHENTICATED' '503': description: DEPENDENCY_UNAVAILABLE ou UNAVAILABLE; sem SQL/credenciais. Resultado de commit pode ser desconhecido; consultar identidade antes de nova escolha. content: application/json: schema: $ref: '#/components/schemas/Error' examples: DEPENDENCY_UNAVAILABLE: summary: DEPENDENCY UNAVAILABLE value: code: DEPENDENCY_UNAVAILABLE message: 'Exemplo sintético: DEPENDENCY_UNAVAILABLE' UNAVAILABLE: summary: UNAVAILABLE value: code: UNAVAILABLE message: 'Exemplo sintético: UNAVAILABLE' /api/v1/players/me/name-options: post: tags: - Identidade summary: Emitir três opções distintas operationId: createNameOptions description: Novo conjunto substitui os anteriores, sem alterar nome confirmado. Validade de 10 minutos, limitada pela sessão. Exatamente no vencimento a opção é inválida. Limite atômico no PostgreSQL de 20 tentativas autenticadas por jogador por minuto UTC; compartilhado entre processos. A cota é persistida antes da transação de negócio, inclusive em conflitos; erros de middleware ou parsing não consomem cota. Falhas de negócio não persistem conjuntos ou alterações de nome. Após confirmar um nome retorna NAME_ALREADY_SET. Nomes podem repetir entre jogadores. parameters: - $ref: '#/components/parameters/Origin' requestBody: required: true content: application/json: schema: type: object additionalProperties: false examples: empty: summary: empty value: {} responses: '200': description: Conjunto commitado; referências aleatórias vinculadas ao jogador. content: application/json: schema: $ref: '#/components/schemas/Options' examples: issued: $ref: '#/components/examples/createNameOptions_issued' '400': description: INVALID_JSON para corpo inválido; INVALID_QUERY para consulta inválida. content: application/json: schema: $ref: '#/components/schemas/Error' examples: INVALID_JSON: summary: INVALID JSON value: code: INVALID_JSON message: 'Exemplo sintético: INVALID_JSON' '401': description: UNAUTHENTICATED — credencial ausente, inválida, expirada ou cookie de sessão duplicado. content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNAUTHENTICATED: summary: UNAUTHENTICATED value: code: UNAUTHENTICATED message: 'Exemplo sintético: UNAUTHENTICATED' '403': description: ORIGIN_NOT_ALLOWED — origem ausente, null, múltipla ou diferente. content: application/json: schema: $ref: '#/components/schemas/Error' examples: ORIGIN_NOT_ALLOWED: summary: ORIGIN NOT ALLOWED value: code: ORIGIN_NOT_ALLOWED message: 'Exemplo sintético: ORIGIN_NOT_ALLOWED' '409': description: NAME_ALREADY_SET ou NAME_OPTIONS_EXPIRED conforme a operação. content: application/json: schema: $ref: '#/components/schemas/Error' examples: NAME_ALREADY_SET: summary: NAME ALREADY SET value: code: NAME_ALREADY_SET message: 'Exemplo sintético: NAME_ALREADY_SET' '413': description: BODY_TOO_LARGE — corpo excede limite da rota (4096 ou 131072 bytes). content: application/json: schema: $ref: '#/components/schemas/Error' examples: BODY_TOO_LARGE: summary: BODY TOO LARGE value: code: BODY_TOO_LARGE message: 'Exemplo sintético: BODY_TOO_LARGE' '415': description: UNSUPPORTED_MEDIA_TYPE — exatamente um Content-Type application/json e ausência de Content-Encoding, inclusive vazio. Headers duplicados são rejeitados. content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNSUPPORTED_MEDIA_TYPE: summary: UNSUPPORTED MEDIA TYPE value: code: UNSUPPORTED_MEDIA_TYPE message: 'Exemplo sintético: UNSUPPORTED_MEDIA_TYPE' '429': description: RATE_LIMITED — aguardar Retry-After antes de repetir. headers: Retry-After: schema: type: integer const: 60 content: application/json: schema: $ref: '#/components/schemas/Error' examples: RATE_LIMITED: summary: RATE LIMITED value: code: RATE_LIMITED message: 'Exemplo sintético: RATE_LIMITED' '503': description: DEPENDENCY_UNAVAILABLE ou UNAVAILABLE; sem SQL/credenciais. Resultado de commit pode ser desconhecido; consultar identidade antes de nova escolha. content: application/json: schema: $ref: '#/components/schemas/Error' examples: DEPENDENCY_UNAVAILABLE: summary: DEPENDENCY UNAVAILABLE value: code: DEPENDENCY_UNAVAILABLE message: 'Exemplo sintético: DEPENDENCY_UNAVAILABLE' UNAVAILABLE: summary: UNAVAILABLE value: code: UNAVAILABLE message: 'Exemplo sintético: UNAVAILABLE' /api/v1/players/me/name: post: tags: - Identidade summary: Confirmar uma opção emitida operationId: chooseName description: A primeira confirmação fixa o nome nesta identidade. Repetir a mesma referência devolve a escolha salva mesmo após vencer a opção, desde que a sessão seja válida. Outra referência após confirmar gera NAME_ALREADY_SET. Referência própria vencida/substituída gera NAME_OPTIONS_EXPIRED. Referência alheia ou adulterada gera INVALID_NAME_OPTION sem revelar dono. Geração e escolha concorrentes são serializadas pelo jogador no banco. parameters: - $ref: '#/components/parameters/Origin' requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: - option_id properties: option_id: $ref: '#/components/schemas/ID' examples: issued_option: summary: issued option value: option_id: cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc responses: '200': description: Identidade persistida do chamador. content: application/json: schema: $ref: '#/components/schemas/Player' examples: named: summary: named value: player_id: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa display_name: Arara Cívica Azul repertoire_version: br-test-v1 expires_at: '2026-10-17T12:00:00Z' open_run_id: null '400': description: INVALID_JSON para corpo inválido; INVALID_QUERY para consulta inválida. content: application/json: schema: $ref: '#/components/schemas/Error' examples: INVALID_JSON: summary: INVALID JSON value: code: INVALID_JSON message: 'Exemplo sintético: INVALID_JSON' '401': description: UNAUTHENTICATED — credencial ausente, inválida, expirada ou cookie de sessão duplicado. content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNAUTHENTICATED: summary: UNAUTHENTICATED value: code: UNAUTHENTICATED message: 'Exemplo sintético: UNAUTHENTICATED' '403': description: ORIGIN_NOT_ALLOWED — origem ausente, null, múltipla ou diferente. content: application/json: schema: $ref: '#/components/schemas/Error' examples: ORIGIN_NOT_ALLOWED: summary: ORIGIN NOT ALLOWED value: code: ORIGIN_NOT_ALLOWED message: 'Exemplo sintético: ORIGIN_NOT_ALLOWED' '409': description: NAME_ALREADY_SET ou NAME_OPTIONS_EXPIRED conforme a operação. content: application/json: schema: $ref: '#/components/schemas/Error' examples: NAME_ALREADY_SET: summary: NAME ALREADY SET value: code: NAME_ALREADY_SET message: 'Exemplo sintético: NAME_ALREADY_SET' NAME_OPTIONS_EXPIRED: summary: NAME OPTIONS EXPIRED value: code: NAME_OPTIONS_EXPIRED message: 'Exemplo sintético: NAME_OPTIONS_EXPIRED' '413': description: BODY_TOO_LARGE — corpo excede limite da rota (4096 ou 131072 bytes). content: application/json: schema: $ref: '#/components/schemas/Error' examples: BODY_TOO_LARGE: summary: BODY TOO LARGE value: code: BODY_TOO_LARGE message: 'Exemplo sintético: BODY_TOO_LARGE' '415': description: UNSUPPORTED_MEDIA_TYPE — exatamente um Content-Type application/json e ausência de Content-Encoding, inclusive vazio. Headers duplicados são rejeitados. content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNSUPPORTED_MEDIA_TYPE: summary: UNSUPPORTED MEDIA TYPE value: code: UNSUPPORTED_MEDIA_TYPE message: 'Exemplo sintético: UNSUPPORTED_MEDIA_TYPE' '422': description: INVALID_NAME_OPTION — referência inválida, alheia ou adulterada. content: application/json: schema: $ref: '#/components/schemas/Error' examples: INVALID_NAME_OPTION: summary: INVALID NAME OPTION value: code: INVALID_NAME_OPTION message: 'Exemplo sintético: INVALID_NAME_OPTION' '503': description: DEPENDENCY_UNAVAILABLE ou UNAVAILABLE; sem SQL/credenciais. Resultado de commit pode ser desconhecido; consultar identidade antes de nova escolha. content: application/json: schema: $ref: '#/components/schemas/Error' examples: DEPENDENCY_UNAVAILABLE: summary: DEPENDENCY UNAVAILABLE value: code: DEPENDENCY_UNAVAILABLE message: 'Exemplo sintético: DEPENDENCY_UNAVAILABLE' UNAVAILABLE: summary: UNAVAILABLE value: code: UNAVAILABLE message: 'Exemplo sintético: UNAVAILABLE' HANDLE_UNAVAILABLE: summary: HANDLE UNAVAILABLE value: code: HANDLE_UNAVAILABLE message: 'Exemplo sintético: HANDLE_UNAVAILABLE' /saude: get: tags: - Saúde summary: Readiness do schema e banco operationId: readiness security: [] description: Verifica checksum da migration pelo banco com prazo de 1s. Inicialização aplica migrations antes de abrir HTTP. Retorna 503 durante drain/falha. responses: '200': description: Vivo ou pronto, conforme o endpoint; Cache-Control no-store. content: application/json: schema: $ref: '#/components/schemas/Health' examples: ready: summary: ready value: status: ready '503': description: Banco/schema indisponível ou processo em encerramento. content: application/json: schema: oneOf: - $ref: '#/components/schemas/Health' - $ref: '#/components/schemas/Error' examples: UNAVAILABLE: summary: UNAVAILABLE value: code: UNAVAILABLE message: 'Exemplo sintético: UNAVAILABLE' schema_unavailable: summary: schema unavailable value: status: unavailable /vivo: get: tags: - Saúde summary: Liveness do processo operationId: liveness security: [] responses: '200': description: Vivo ou pronto, conforme o endpoint; Cache-Control no-store. content: application/json: schema: $ref: '#/components/schemas/Health' examples: alive: summary: alive value: status: alive '503': description: DEPENDENCY_UNAVAILABLE ou UNAVAILABLE; sem SQL/credenciais. Resultado de commit pode ser desconhecido; consultar identidade antes de nova escolha. content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNAVAILABLE: summary: UNAVAILABLE value: code: UNAVAILABLE message: 'Exemplo sintético: UNAVAILABLE' /api/v1/runs: post: tags: - Partidas summary: Abrir partida com snapshot operationId: openRun description: Nome obrigatório (NAME_REQUIRED). Uma vaga active/ready_to_finalize por sessão, protegida por lock e índice parcial. RUN_ALREADY_OPEN informa details.run_id. Regra corrente persistida no banco; boot não reativa versões. Máximo de 10 tentativas autenticadas de abertura por minuto UTC por jogador, inclusive conflitos e versões inválidas; cota independente do rollback de negócio. Erros de middleware ou parsing não consomem cota. Inatividade inicial de 30min, limitada pela sessão; now >= expires_at vence. parameters: - $ref: '#/components/parameters/Origin' requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: - stage_id - contract_version properties: stage_id: type: string const: missao-merenda-transparente contract_version: type: string const: journal-v1 examples: new_run: summary: new run value: stage_id: missao-merenda-transparente contract_version: journal-v1 responses: '400': description: INVALID_JSON para corpo inválido; INVALID_QUERY para consulta inválida. content: application/json: schema: $ref: '#/components/schemas/Error' examples: INVALID_JSON: summary: INVALID JSON value: code: INVALID_JSON message: 'Exemplo sintético: INVALID_JSON' '401': description: UNAUTHENTICATED — credencial ausente, inválida, expirada ou cookie de sessão duplicado. content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNAUTHENTICATED: summary: UNAUTHENTICATED value: code: UNAUTHENTICATED message: 'Exemplo sintético: UNAUTHENTICATED' '403': description: ORIGIN_NOT_ALLOWED — origem ausente, null, múltipla ou diferente. content: application/json: schema: $ref: '#/components/schemas/Error' examples: ORIGIN_NOT_ALLOWED: summary: ORIGIN NOT ALLOWED value: code: ORIGIN_NOT_ALLOWED message: 'Exemplo sintético: ORIGIN_NOT_ALLOWED' '413': description: BODY_TOO_LARGE — corpo excede limite da rota (4096 ou 131072 bytes). content: application/json: schema: $ref: '#/components/schemas/Error' examples: BODY_TOO_LARGE: summary: BODY TOO LARGE value: code: BODY_TOO_LARGE message: 'Exemplo sintético: BODY_TOO_LARGE' '415': description: UNSUPPORTED_MEDIA_TYPE — exatamente um Content-Type application/json e ausência de Content-Encoding, inclusive vazio. Headers duplicados são rejeitados. content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNSUPPORTED_MEDIA_TYPE: summary: UNSUPPORTED MEDIA TYPE value: code: UNSUPPORTED_MEDIA_TYPE message: 'Exemplo sintético: UNSUPPORTED_MEDIA_TYPE' '503': description: DEPENDENCY_UNAVAILABLE ou UNAVAILABLE; sem SQL/credenciais. Resultado de commit pode ser desconhecido; consultar identidade antes de nova escolha. content: application/json: schema: $ref: '#/components/schemas/Error' examples: DEPENDENCY_UNAVAILABLE: summary: DEPENDENCY UNAVAILABLE value: code: DEPENDENCY_UNAVAILABLE message: 'Exemplo sintético: DEPENDENCY_UNAVAILABLE' UNAVAILABLE: summary: UNAVAILABLE value: code: UNAVAILABLE message: 'Exemplo sintético: UNAVAILABLE' '201': description: Estado e confirmação commitados; sem bônus final na prévia ativa. content: application/json: schema: $ref: '#/components/schemas/Run' examples: active: $ref: '#/components/examples/openRun_active' '409': description: RUN_ALREADY_OPEN, MISSING_EVENTS, EVENT_CONFLICT, RUN_TERMINAL, RUN_NOT_TERMINAL ou FINISH_CONFLICT. content: application/json: schema: $ref: '#/components/schemas/Error' examples: NAME_REQUIRED: summary: NAME REQUIRED value: code: NAME_REQUIRED message: 'Exemplo sintético: NAME_REQUIRED' RUN_ALREADY_OPEN: summary: RUN ALREADY OPEN value: code: RUN_ALREADY_OPEN message: 'Exemplo sintético: RUN_ALREADY_OPEN' details: run_id: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb '422': description: UNSUPPORTED_VERSION, INVALID_EVENT, CLOCK_AHEAD, EVENT_LIMIT_EXCEEDED ou INVALID_FINISH. content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNSUPPORTED_VERSION: summary: UNSUPPORTED VERSION value: code: UNSUPPORTED_VERSION message: 'Exemplo sintético: UNSUPPORTED_VERSION' '429': description: RATE_LIMITED — aguardar Retry-After antes de repetir. headers: Retry-After: schema: type: integer const: 60 content: application/json: schema: $ref: '#/components/schemas/Error' examples: RATE_LIMITED: summary: RATE LIMITED value: code: RATE_LIMITED message: 'Exemplo sintético: RATE_LIMITED' /api/v1/runs/{run_id}: parameters: - name: run_id in: path required: true schema: $ref: '#/components/schemas/ID' example: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb get: tags: - Partidas summary: Consultar confirmação e resultado operationId: getRun description: Sem renovar inatividade. Resultado salvo permanece consultável após expirar partida, enquanto sessão for válida. responses: '200': description: Estado e confirmação commitados; sem bônus final na prévia ativa. content: application/json: schema: $ref: '#/components/schemas/Run' examples: active: $ref: '#/components/examples/openRun_active' terminal: $ref: '#/components/examples/getRun_terminal' finalized: $ref: '#/components/examples/getRun_finalized' '401': description: UNAUTHENTICATED — credencial ausente, inválida, expirada ou cookie de sessão duplicado. content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNAUTHENTICATED: summary: UNAUTHENTICATED value: code: UNAUTHENTICATED message: 'Exemplo sintético: UNAUTHENTICATED' '404': description: RUN_NOT_FOUND — inexistente ou de outro jogador, sem revelar proprietário. content: application/json: schema: $ref: '#/components/schemas/Error' examples: RUN_NOT_FOUND: summary: RUN NOT FOUND value: code: RUN_NOT_FOUND message: 'Exemplo sintético: RUN_NOT_FOUND' '410': description: RUN_EXPIRED — sessão ainda válida, partida não finalizada vencida. content: application/json: schema: $ref: '#/components/schemas/Error' examples: RUN_EXPIRED: summary: RUN EXPIRED value: code: RUN_EXPIRED message: 'Exemplo sintético: RUN_EXPIRED' '503': description: DEPENDENCY_UNAVAILABLE ou UNAVAILABLE; sem SQL/credenciais. Resultado de commit pode ser desconhecido; consultar identidade antes de nova escolha. content: application/json: schema: $ref: '#/components/schemas/Error' examples: DEPENDENCY_UNAVAILABLE: summary: DEPENDENCY UNAVAILABLE value: code: DEPENDENCY_UNAVAILABLE message: 'Exemplo sintético: DEPENDENCY_UNAVAILABLE' UNAVAILABLE: summary: UNAVAILABLE value: code: UNAVAILABLE message: 'Exemplo sintético: UNAVAILABLE' /api/v1/runs/{run_id}/events: parameters: - name: run_id in: path required: true schema: $ref: '#/components/schemas/ID' example: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb post: tags: - Partidas summary: Confirmar lote atomicamente operationId: appendEvents description: Sequências contíguas; prefixo idêntico normalizado pode repetir antes de sufixo novo. Mesmo seq alterado conflita; lacuna retorna expected_seq/last_accepted_seq. Lote inteiro reverte na primeira falha, inclusive ação após terminal intermediário. Tempos não decrescentes, avanço máximo 2000ms sobre relógio do servidor desde abertura. Apenas eventos novos renovam 30min (limitado pela sessão). Até 16384 eventos por partida e 120 tentativas autenticadas de lote por minuto UTC por jogador, incluindo reenvios e rejeições do caso de uso; contadores PostgreSQL compartilhados e persistidos independentemente do rollback do lote. Erros de middleware ou parsing não consomem cota. Finalizada aceita só repetições, mesmo após expiração com sessão válida. Nenhum ponto/dano vem do cliente. Em enemy_eliminated laser não admite target_id; player_contact pontua, gera filhos se splitter e causa 15 de dano se fora da invulnerabilidade de 1200ms (target_id obrigatório então; proibido durante). enemy_breached remove sem pontos/filhos e causa 20. projectile_fired usa ID p/, atirador vivo e no máximo 50 projéteis; primeiro disparo após intervalo da onda (2500/2000/1600ms), seguintes no mínimo 80% dele. projectile_hit consome projétil conhecido e causa 10 no alvo ou jogador; no jogador, target_id só fora da invulnerabilidade. Alvo deve estar vivo; não se verifica proximidade geométrica. wave_advanced requer todos removidos e 1000ms desde último, limpa projéteis; terceira transição vence. manual_exit termina com bônus proporcional e publica ao finalizar. Defesa zero termina no mesmo evento causador. parameters: - $ref: '#/components/parameters/Origin' requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: - events properties: events: type: array minItems: 1 maxItems: 256 items: $ref: '#/components/schemas/Event' examples: manual: summary: Partida nova, onda 1, integridade inicial. Tempos desde a abertura; aguardar antes de enviar. value: events: - seq: 1 elapsed_ms: 0 type: enemy_eliminated payload: entity_id: w1/swarm/0 cause: laser - seq: 2 elapsed_ms: 0 type: manual_exit payload: {} splitter: summary: Partida nova, onda 1, integridade inicial. Tempos desde a abertura; aguardar antes de enviar. value: events: - seq: 1 elapsed_ms: 0 type: enemy_eliminated payload: entity_id: w1/splitter/0 cause: laser - seq: 2 elapsed_ms: 0 type: enemy_eliminated payload: entity_id: w1/splitter/0/c0 cause: laser - seq: 3 elapsed_ms: 0 type: enemy_eliminated payload: entity_id: w1/splitter/0/c1 cause: laser - seq: 4 elapsed_ms: 0 type: manual_exit payload: {} partial: summary: Partida nova, onda 1, integridade inicial. Tempos desde a abertura; aguardar antes de enviar. value: events: - seq: 1 elapsed_ms: 0 type: enemy_breached payload: entity_id: w1/swarm/0 target_id: target-merenda - seq: 2 elapsed_ms: 0 type: enemy_breached payload: entity_id: w1/swarm/1 target_id: target-merenda - seq: 3 elapsed_ms: 0 type: enemy_breached payload: entity_id: w1/swarm/2 target_id: target-merenda - seq: 4 elapsed_ms: 0 type: manual_exit payload: {} depleted: summary: Partida nova, onda 1, integridade inicial. Tempos desde a abertura; aguardar antes de enviar. value: events: - seq: 1 elapsed_ms: 0 type: enemy_breached payload: entity_id: w1/swarm/0 target_id: target-merenda - seq: 2 elapsed_ms: 0 type: enemy_breached payload: entity_id: w1/swarm/1 target_id: target-merenda - seq: 3 elapsed_ms: 0 type: enemy_breached payload: entity_id: w1/swarm/2 target_id: target-merenda - seq: 4 elapsed_ms: 0 type: enemy_breached payload: entity_id: w1/swarm/3 target_id: target-merenda - seq: 5 elapsed_ms: 0 type: enemy_breached payload: entity_id: w1/swarm/4 target_id: target-merenda - seq: 6 elapsed_ms: 0 type: enemy_breached payload: entity_id: w1/swarm/5 target_id: target-saude - seq: 7 elapsed_ms: 0 type: enemy_breached payload: entity_id: w1/swarm/6 target_id: target-saude - seq: 8 elapsed_ms: 0 type: enemy_breached payload: entity_id: w1/swarm/7 target_id: target-saude - seq: 9 elapsed_ms: 0 type: enemy_breached payload: entity_id: w1/swarm/8 target_id: target-saude - seq: 10 elapsed_ms: 0 type: enemy_breached payload: entity_id: w1/swarm/9 target_id: target-saude - seq: 11 elapsed_ms: 0 type: enemy_breached payload: entity_id: w1/swarm/10 target_id: target-urna - seq: 12 elapsed_ms: 0 type: enemy_breached payload: entity_id: w1/swarm/11 target_id: target-urna - seq: 13 elapsed_ms: 0 type: enemy_breached payload: entity_id: w1/splitter/0 target_id: target-urna - seq: 14 elapsed_ms: 0 type: enemy_breached payload: entity_id: w1/splitter/1 target_id: target-urna - seq: 15 elapsed_ms: 0 type: enemy_breached payload: entity_id: w1/splitter/2 target_id: target-urna projectile: summary: Partida nova, onda 1, integridade inicial. Tempos desde a abertura; aguardar antes de enviar. value: events: - seq: 1 elapsed_ms: 2500 type: projectile_fired payload: entity_id: w1/swarm/0 projectile_id: p/1 - seq: 2 elapsed_ms: 2500 type: projectile_hit payload: projectile_id: p/1 cause: target target_id: target-merenda - seq: 3 elapsed_ms: 2500 type: manual_exit payload: {} contact: summary: Partida nova, onda 1, integridade inicial. Tempos desde a abertura; aguardar antes de enviar. value: events: - seq: 1 elapsed_ms: 0 type: enemy_eliminated payload: entity_id: w1/swarm/0 cause: player_contact target_id: target-merenda - seq: 2 elapsed_ms: 0 type: enemy_eliminated payload: entity_id: w1/swarm/1 cause: player_contact - seq: 3 elapsed_ms: 1200 type: enemy_eliminated payload: entity_id: w1/swarm/2 cause: player_contact target_id: target-merenda - seq: 4 elapsed_ms: 1200 type: manual_exit payload: {} victory: summary: Partida nova, onda 1, integridade inicial. Tempos desde a abertura; aguardar antes de enviar. value: events: - seq: 1 elapsed_ms: 0 type: enemy_eliminated payload: entity_id: w1/swarm/0 cause: laser - seq: 2 elapsed_ms: 0 type: enemy_eliminated payload: entity_id: w1/swarm/1 cause: laser - seq: 3 elapsed_ms: 0 type: enemy_eliminated payload: entity_id: w1/swarm/2 cause: laser - seq: 4 elapsed_ms: 0 type: enemy_eliminated payload: entity_id: w1/swarm/3 cause: laser - seq: 5 elapsed_ms: 0 type: enemy_eliminated payload: entity_id: w1/swarm/4 cause: laser - seq: 6 elapsed_ms: 0 type: enemy_eliminated payload: entity_id: w1/swarm/5 cause: laser - seq: 7 elapsed_ms: 0 type: enemy_eliminated payload: entity_id: w1/swarm/6 cause: laser - seq: 8 elapsed_ms: 0 type: enemy_eliminated payload: entity_id: w1/swarm/7 cause: laser - seq: 9 elapsed_ms: 0 type: enemy_eliminated payload: entity_id: w1/swarm/8 cause: laser - seq: 10 elapsed_ms: 0 type: enemy_eliminated payload: entity_id: w1/swarm/9 cause: laser - seq: 11 elapsed_ms: 0 type: enemy_eliminated payload: entity_id: w1/swarm/10 cause: laser - seq: 12 elapsed_ms: 0 type: enemy_eliminated payload: entity_id: w1/swarm/11 cause: laser - seq: 13 elapsed_ms: 0 type: enemy_eliminated payload: entity_id: w1/splitter/0 cause: laser - seq: 14 elapsed_ms: 0 type: enemy_eliminated payload: entity_id: w1/splitter/0/c0 cause: laser - seq: 15 elapsed_ms: 0 type: enemy_eliminated payload: entity_id: w1/splitter/0/c1 cause: laser - seq: 16 elapsed_ms: 0 type: enemy_eliminated payload: entity_id: w1/splitter/1 cause: laser - seq: 17 elapsed_ms: 0 type: enemy_eliminated payload: entity_id: w1/splitter/1/c0 cause: laser - seq: 18 elapsed_ms: 0 type: enemy_eliminated payload: entity_id: w1/splitter/1/c1 cause: laser - seq: 19 elapsed_ms: 0 type: enemy_eliminated payload: entity_id: w1/splitter/2 cause: laser - seq: 20 elapsed_ms: 0 type: enemy_eliminated payload: entity_id: w1/splitter/2/c0 cause: laser - seq: 21 elapsed_ms: 0 type: enemy_eliminated payload: entity_id: w1/splitter/2/c1 cause: laser - seq: 22 elapsed_ms: 1000 type: wave_advanced payload: {} - seq: 23 elapsed_ms: 1000 type: enemy_eliminated payload: entity_id: w2/swarm/0 cause: laser - seq: 24 elapsed_ms: 1000 type: enemy_eliminated payload: entity_id: w2/swarm/1 cause: laser - seq: 25 elapsed_ms: 1000 type: enemy_eliminated payload: entity_id: w2/swarm/2 cause: laser - seq: 26 elapsed_ms: 1000 type: enemy_eliminated payload: entity_id: w2/swarm/3 cause: laser - seq: 27 elapsed_ms: 1000 type: enemy_eliminated payload: entity_id: w2/swarm/4 cause: laser - seq: 28 elapsed_ms: 1000 type: enemy_eliminated payload: entity_id: w2/swarm/5 cause: laser - seq: 29 elapsed_ms: 1000 type: enemy_eliminated payload: entity_id: w2/swarm/6 cause: laser - seq: 30 elapsed_ms: 1000 type: enemy_eliminated payload: entity_id: w2/swarm/7 cause: laser - seq: 31 elapsed_ms: 1000 type: enemy_eliminated payload: entity_id: w2/splitter/0 cause: laser - seq: 32 elapsed_ms: 1000 type: enemy_eliminated payload: entity_id: w2/splitter/0/c0 cause: laser - seq: 33 elapsed_ms: 1000 type: enemy_eliminated payload: entity_id: w2/splitter/0/c1 cause: laser - seq: 34 elapsed_ms: 1000 type: enemy_eliminated payload: entity_id: w2/splitter/1 cause: laser - seq: 35 elapsed_ms: 1000 type: enemy_eliminated payload: entity_id: w2/splitter/1/c0 cause: laser - seq: 36 elapsed_ms: 1000 type: enemy_eliminated payload: entity_id: w2/splitter/1/c1 cause: laser - seq: 37 elapsed_ms: 1000 type: enemy_eliminated payload: entity_id: w2/splitter/2 cause: laser - seq: 38 elapsed_ms: 1000 type: enemy_eliminated payload: entity_id: w2/splitter/2/c0 cause: laser - seq: 39 elapsed_ms: 1000 type: enemy_eliminated payload: entity_id: w2/splitter/2/c1 cause: laser - seq: 40 elapsed_ms: 1000 type: enemy_eliminated payload: entity_id: w2/splitter/3 cause: laser - seq: 41 elapsed_ms: 1000 type: enemy_eliminated payload: entity_id: w2/splitter/3/c0 cause: laser - seq: 42 elapsed_ms: 1000 type: enemy_eliminated payload: entity_id: w2/splitter/3/c1 cause: laser - seq: 43 elapsed_ms: 1000 type: enemy_eliminated payload: entity_id: w2/splitter/4 cause: laser - seq: 44 elapsed_ms: 1000 type: enemy_eliminated payload: entity_id: w2/splitter/4/c0 cause: laser - seq: 45 elapsed_ms: 1000 type: enemy_eliminated payload: entity_id: w2/splitter/4/c1 cause: laser - seq: 46 elapsed_ms: 1000 type: enemy_eliminated payload: entity_id: w2/splitter/5 cause: laser - seq: 47 elapsed_ms: 1000 type: enemy_eliminated payload: entity_id: w2/splitter/5/c0 cause: laser - seq: 48 elapsed_ms: 1000 type: enemy_eliminated payload: entity_id: w2/splitter/5/c1 cause: laser - seq: 49 elapsed_ms: 1000 type: enemy_eliminated payload: entity_id: w2/stealer/0 cause: laser - seq: 50 elapsed_ms: 1000 type: enemy_eliminated payload: entity_id: w2/stealer/1 cause: laser - seq: 51 elapsed_ms: 1000 type: enemy_eliminated payload: entity_id: w2/stealer/2 cause: laser - seq: 52 elapsed_ms: 1000 type: enemy_eliminated payload: entity_id: w2/stealer/3 cause: laser - seq: 53 elapsed_ms: 2000 type: wave_advanced payload: {} - seq: 54 elapsed_ms: 2000 type: enemy_eliminated payload: entity_id: w3/swarm/0 cause: laser - seq: 55 elapsed_ms: 2000 type: enemy_eliminated payload: entity_id: w3/swarm/1 cause: laser - seq: 56 elapsed_ms: 2000 type: enemy_eliminated payload: entity_id: w3/swarm/2 cause: laser - seq: 57 elapsed_ms: 2000 type: enemy_eliminated payload: entity_id: w3/swarm/3 cause: laser - seq: 58 elapsed_ms: 2000 type: enemy_eliminated payload: entity_id: w3/swarm/4 cause: laser - seq: 59 elapsed_ms: 2000 type: enemy_eliminated payload: entity_id: w3/swarm/5 cause: laser - seq: 60 elapsed_ms: 2000 type: enemy_eliminated payload: entity_id: w3/swarm/6 cause: laser - seq: 61 elapsed_ms: 2000 type: enemy_eliminated payload: entity_id: w3/swarm/7 cause: laser - seq: 62 elapsed_ms: 2000 type: enemy_eliminated payload: entity_id: w3/tank/0 cause: laser - seq: 63 elapsed_ms: 2000 type: enemy_eliminated payload: entity_id: w3/tank/1 cause: laser - seq: 64 elapsed_ms: 2000 type: enemy_eliminated payload: entity_id: w3/tank/2 cause: laser - seq: 65 elapsed_ms: 2000 type: enemy_eliminated payload: entity_id: w3/tank/3 cause: laser - seq: 66 elapsed_ms: 2000 type: enemy_eliminated payload: entity_id: w3/splitter/0 cause: laser - seq: 67 elapsed_ms: 2000 type: enemy_eliminated payload: entity_id: w3/splitter/0/c0 cause: laser - seq: 68 elapsed_ms: 2000 type: enemy_eliminated payload: entity_id: w3/splitter/0/c1 cause: laser - seq: 69 elapsed_ms: 2000 type: enemy_eliminated payload: entity_id: w3/splitter/1 cause: laser - seq: 70 elapsed_ms: 2000 type: enemy_eliminated payload: entity_id: w3/splitter/1/c0 cause: laser - seq: 71 elapsed_ms: 2000 type: enemy_eliminated payload: entity_id: w3/splitter/1/c1 cause: laser - seq: 72 elapsed_ms: 2000 type: enemy_eliminated payload: entity_id: w3/splitter/2 cause: laser - seq: 73 elapsed_ms: 2000 type: enemy_eliminated payload: entity_id: w3/splitter/2/c0 cause: laser - seq: 74 elapsed_ms: 2000 type: enemy_eliminated payload: entity_id: w3/splitter/2/c1 cause: laser - seq: 75 elapsed_ms: 2000 type: enemy_eliminated payload: entity_id: w3/splitter/3 cause: laser - seq: 76 elapsed_ms: 2000 type: enemy_eliminated payload: entity_id: w3/splitter/3/c0 cause: laser - seq: 77 elapsed_ms: 2000 type: enemy_eliminated payload: entity_id: w3/splitter/3/c1 cause: laser - seq: 78 elapsed_ms: 2000 type: enemy_eliminated payload: entity_id: w3/splitter/4 cause: laser - seq: 79 elapsed_ms: 2000 type: enemy_eliminated payload: entity_id: w3/splitter/4/c0 cause: laser - seq: 80 elapsed_ms: 2000 type: enemy_eliminated payload: entity_id: w3/splitter/4/c1 cause: laser - seq: 81 elapsed_ms: 2000 type: enemy_eliminated payload: entity_id: w3/splitter/5 cause: laser - seq: 82 elapsed_ms: 2000 type: enemy_eliminated payload: entity_id: w3/splitter/5/c0 cause: laser - seq: 83 elapsed_ms: 2000 type: enemy_eliminated payload: entity_id: w3/splitter/5/c1 cause: laser - seq: 84 elapsed_ms: 2000 type: enemy_eliminated payload: entity_id: w3/stealer/0 cause: laser - seq: 85 elapsed_ms: 2000 type: enemy_eliminated payload: entity_id: w3/stealer/1 cause: laser - seq: 86 elapsed_ms: 2000 type: enemy_eliminated payload: entity_id: w3/stealer/2 cause: laser - seq: 87 elapsed_ms: 2000 type: enemy_eliminated payload: entity_id: w3/stealer/3 cause: laser - seq: 88 elapsed_ms: 2000 type: enemy_eliminated payload: entity_id: w3/stealer/4 cause: laser - seq: 89 elapsed_ms: 2000 type: enemy_eliminated payload: entity_id: w3/stealer/5 cause: laser - seq: 90 elapsed_ms: 3000 type: wave_advanced payload: {} responses: '400': description: INVALID_JSON para corpo inválido; INVALID_QUERY para consulta inválida. content: application/json: schema: $ref: '#/components/schemas/Error' examples: INVALID_JSON: summary: INVALID JSON value: code: INVALID_JSON message: 'Exemplo sintético: INVALID_JSON' '401': description: UNAUTHENTICATED — credencial ausente, inválida, expirada ou cookie de sessão duplicado. content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNAUTHENTICATED: summary: UNAUTHENTICATED value: code: UNAUTHENTICATED message: 'Exemplo sintético: UNAUTHENTICATED' '403': description: ORIGIN_NOT_ALLOWED — origem ausente, null, múltipla ou diferente. content: application/json: schema: $ref: '#/components/schemas/Error' examples: ORIGIN_NOT_ALLOWED: summary: ORIGIN NOT ALLOWED value: code: ORIGIN_NOT_ALLOWED message: 'Exemplo sintético: ORIGIN_NOT_ALLOWED' '413': description: BODY_TOO_LARGE — corpo excede limite da rota (4096 ou 131072 bytes). content: application/json: schema: $ref: '#/components/schemas/Error' examples: BODY_TOO_LARGE: summary: BODY TOO LARGE value: code: BODY_TOO_LARGE message: 'Exemplo sintético: BODY_TOO_LARGE' '415': description: UNSUPPORTED_MEDIA_TYPE — exatamente um Content-Type application/json e ausência de Content-Encoding, inclusive vazio. Headers duplicados são rejeitados. content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNSUPPORTED_MEDIA_TYPE: summary: UNSUPPORTED MEDIA TYPE value: code: UNSUPPORTED_MEDIA_TYPE message: 'Exemplo sintético: UNSUPPORTED_MEDIA_TYPE' '503': description: DEPENDENCY_UNAVAILABLE ou UNAVAILABLE; sem SQL/credenciais. Resultado de commit pode ser desconhecido; consultar identidade antes de nova escolha. content: application/json: schema: $ref: '#/components/schemas/Error' examples: DEPENDENCY_UNAVAILABLE: summary: DEPENDENCY UNAVAILABLE value: code: DEPENDENCY_UNAVAILABLE message: 'Exemplo sintético: DEPENDENCY_UNAVAILABLE' UNAVAILABLE: summary: UNAVAILABLE value: code: UNAVAILABLE message: 'Exemplo sintético: UNAVAILABLE' '200': description: Estado e confirmação commitados; sem bônus final na prévia ativa. content: application/json: schema: $ref: '#/components/schemas/Run' examples: active: $ref: '#/components/examples/appendEvents_active' terminal: $ref: '#/components/examples/getRun_terminal' finalized: $ref: '#/components/examples/getRun_finalized' '404': description: RUN_NOT_FOUND — inexistente ou de outro jogador, sem revelar proprietário. content: application/json: schema: $ref: '#/components/schemas/Error' examples: RUN_NOT_FOUND: summary: RUN NOT FOUND value: code: RUN_NOT_FOUND message: 'Exemplo sintético: RUN_NOT_FOUND' '409': description: RUN_ALREADY_OPEN, MISSING_EVENTS, EVENT_CONFLICT, RUN_TERMINAL, RUN_NOT_TERMINAL ou FINISH_CONFLICT. content: application/json: schema: $ref: '#/components/schemas/Error' examples: MISSING_EVENTS: summary: MISSING EVENTS value: code: MISSING_EVENTS message: 'Exemplo sintético: MISSING_EVENTS' details: expected_seq: 1 last_accepted_seq: 0 EVENT_CONFLICT: summary: EVENT CONFLICT value: code: EVENT_CONFLICT message: 'Exemplo sintético: EVENT_CONFLICT' RUN_TERMINAL: summary: RUN TERMINAL value: code: RUN_TERMINAL message: 'Exemplo sintético: RUN_TERMINAL' '410': description: RUN_EXPIRED — sessão ainda válida, partida não finalizada vencida. content: application/json: schema: $ref: '#/components/schemas/Error' examples: RUN_EXPIRED: summary: RUN EXPIRED value: code: RUN_EXPIRED message: 'Exemplo sintético: RUN_EXPIRED' '422': description: UNSUPPORTED_VERSION, INVALID_EVENT, CLOCK_AHEAD, EVENT_LIMIT_EXCEEDED ou INVALID_FINISH. content: application/json: schema: $ref: '#/components/schemas/Error' examples: INVALID_EVENT: summary: INVALID EVENT value: code: INVALID_EVENT message: 'Exemplo sintético: INVALID_EVENT' CLOCK_AHEAD: summary: CLOCK AHEAD value: code: CLOCK_AHEAD message: 'Exemplo sintético: CLOCK_AHEAD' EVENT_LIMIT_EXCEEDED: summary: EVENT LIMIT EXCEEDED value: code: EVENT_LIMIT_EXCEEDED message: 'Exemplo sintético: EVENT_LIMIT_EXCEEDED' '429': description: RATE_LIMITED — aguardar Retry-After antes de repetir. headers: Retry-After: schema: type: integer const: 60 content: application/json: schema: $ref: '#/components/schemas/Error' examples: RATE_LIMITED: summary: RATE LIMITED value: code: RATE_LIMITED message: 'Exemplo sintético: RATE_LIMITED' /api/v1/runs/{run_id}/finish: parameters: - name: run_id in: path required: true schema: $ref: '#/components/schemas/ID' example: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb post: tags: - Partidas summary: Finalizar e publicar uma vez operationId: finishRun description: Recalcula exclusivamente pelo diário/snapshot. Exige last_seq igual à confirmação e terminal. Score local só diagnóstico. Reenvio com mesmos last_seq/client_score recupera exatamente resultado salvo, inclusive após expirar partida; alteração gera FINISH_CONFLICT. Sessão vencida gera 401 antes de divulgar partida. Saída manual também elegível. Snapshot do nome é do servidor. parameters: - $ref: '#/components/parameters/Origin' requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: - last_seq - client_score properties: last_seq: type: integer minimum: 0 client_score: type: integer minimum: 0 maximum: 1000000000 examples: manual_confirmed: summary: manual confirmed value: last_seq: 2 client_score: 550 responses: '400': description: INVALID_JSON para corpo inválido; INVALID_QUERY para consulta inválida. content: application/json: schema: $ref: '#/components/schemas/Error' examples: INVALID_JSON: summary: INVALID JSON value: code: INVALID_JSON message: 'Exemplo sintético: INVALID_JSON' '401': description: UNAUTHENTICATED — credencial ausente, inválida, expirada ou cookie de sessão duplicado. content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNAUTHENTICATED: summary: UNAUTHENTICATED value: code: UNAUTHENTICATED message: 'Exemplo sintético: UNAUTHENTICATED' '403': description: ORIGIN_NOT_ALLOWED — origem ausente, null, múltipla ou diferente. content: application/json: schema: $ref: '#/components/schemas/Error' examples: ORIGIN_NOT_ALLOWED: summary: ORIGIN NOT ALLOWED value: code: ORIGIN_NOT_ALLOWED message: 'Exemplo sintético: ORIGIN_NOT_ALLOWED' '413': description: BODY_TOO_LARGE — corpo excede limite da rota (4096 ou 131072 bytes). content: application/json: schema: $ref: '#/components/schemas/Error' examples: BODY_TOO_LARGE: summary: BODY TOO LARGE value: code: BODY_TOO_LARGE message: 'Exemplo sintético: BODY_TOO_LARGE' '415': description: UNSUPPORTED_MEDIA_TYPE — exatamente um Content-Type application/json e ausência de Content-Encoding, inclusive vazio. Headers duplicados são rejeitados. content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNSUPPORTED_MEDIA_TYPE: summary: UNSUPPORTED MEDIA TYPE value: code: UNSUPPORTED_MEDIA_TYPE message: 'Exemplo sintético: UNSUPPORTED_MEDIA_TYPE' '503': description: DEPENDENCY_UNAVAILABLE ou UNAVAILABLE; sem SQL/credenciais. Resultado de commit pode ser desconhecido; consultar identidade antes de nova escolha. content: application/json: schema: $ref: '#/components/schemas/Error' examples: DEPENDENCY_UNAVAILABLE: summary: DEPENDENCY UNAVAILABLE value: code: DEPENDENCY_UNAVAILABLE message: 'Exemplo sintético: DEPENDENCY_UNAVAILABLE' UNAVAILABLE: summary: UNAVAILABLE value: code: UNAVAILABLE message: 'Exemplo sintético: UNAVAILABLE' '200': description: Resultado oficial persistido, incluindo divergência do score local. content: application/json: schema: $ref: '#/components/schemas/Result' examples: published: summary: published value: run_id: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb stage_id: missao-merenda-transparente rules_version: mission-4860209-v1 display_name: Arara Cívica Azul score: combat: 50 integrity_bonus: 500 total: 550 reason: MANUAL_EXIT client_score: 550 diverged: false last_seq: 2 published: true finalized_at: '2026-10-16T12:00:00Z' '404': description: RUN_NOT_FOUND — inexistente ou de outro jogador, sem revelar proprietário. content: application/json: schema: $ref: '#/components/schemas/Error' examples: RUN_NOT_FOUND: summary: RUN NOT FOUND value: code: RUN_NOT_FOUND message: 'Exemplo sintético: RUN_NOT_FOUND' '409': description: RUN_ALREADY_OPEN, MISSING_EVENTS, EVENT_CONFLICT, RUN_TERMINAL, RUN_NOT_TERMINAL ou FINISH_CONFLICT. content: application/json: schema: $ref: '#/components/schemas/Error' examples: MISSING_EVENTS: summary: MISSING EVENTS value: code: MISSING_EVENTS message: 'Exemplo sintético: MISSING_EVENTS' details: expected_seq: 1 last_accepted_seq: 0 RUN_NOT_TERMINAL: summary: RUN NOT TERMINAL value: code: RUN_NOT_TERMINAL message: 'Exemplo sintético: RUN_NOT_TERMINAL' FINISH_CONFLICT: summary: FINISH CONFLICT value: code: FINISH_CONFLICT message: 'Exemplo sintético: FINISH_CONFLICT' '410': description: RUN_EXPIRED — sessão ainda válida, partida não finalizada vencida. content: application/json: schema: $ref: '#/components/schemas/Error' examples: RUN_EXPIRED: summary: RUN EXPIRED value: code: RUN_EXPIRED message: 'Exemplo sintético: RUN_EXPIRED' '422': description: UNSUPPORTED_VERSION, INVALID_EVENT, CLOCK_AHEAD, EVENT_LIMIT_EXCEEDED ou INVALID_FINISH. content: application/json: schema: $ref: '#/components/schemas/Error' examples: INVALID_FINISH: summary: INVALID FINISH value: code: INVALID_FINISH message: 'Exemplo sintético: INVALID_FINISH' /api/v1/leaderboard: get: tags: - Partidas summary: Ranking público por fase e regras operationId: getLeaderboard security: [] description: Uma entrada por partida publicada. Ordenação score desc, finalized_at asc, run_id asc. Sem sessão, credencial ou diário. Versão omitida usa ponteiro corrente compartilhado. Consulta malformada, parâmetro desconhecido, repetido ou versão explicitamente vazia retorna INVALID_QUERY; nenhum par malformado é ignorado. Cookie de sessão duplicado retorna 401. parameters: - name: stage_id in: query required: true schema: type: string const: missao-merenda-transparente example: missao-merenda-transparente - name: rules_version in: query schema: type: string enum: - mission-4860209-v1 example: mission-4860209-v1 - name: limit in: query schema: type: integer minimum: 1 maximum: 100 default: 20 example: 20 responses: '200': description: Resultados publicados. content: application/json: schema: type: object additionalProperties: false required: - entries properties: entries: type: array items: type: object additionalProperties: false required: - run_id - display_name - score - finalized_at properties: run_id: $ref: '#/components/schemas/ID' display_name: type: string score: type: integer minimum: 0 finalized_at: type: string format: date-time examples: empty: summary: empty value: entries: [] published: summary: published value: entries: - run_id: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb display_name: Arara Cívica Azul score: 550 finalized_at: '2026-10-16T12:00:00Z' '400': description: INVALID_JSON para corpo inválido; INVALID_QUERY para consulta inválida. content: application/json: schema: $ref: '#/components/schemas/Error' examples: INVALID_QUERY: summary: INVALID QUERY value: code: INVALID_QUERY message: 'Exemplo sintético: INVALID_QUERY' '401': description: UNAUTHENTICATED — credencial ausente, inválida, expirada ou cookie de sessão duplicado. content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNAUTHENTICATED: summary: UNAUTHENTICATED value: code: UNAUTHENTICATED message: 'Exemplo sintético: UNAUTHENTICATED' '422': description: UNSUPPORTED_VERSION, INVALID_EVENT, CLOCK_AHEAD, EVENT_LIMIT_EXCEEDED ou INVALID_FINISH. content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNSUPPORTED_VERSION: summary: UNSUPPORTED VERSION value: code: UNSUPPORTED_VERSION message: 'Exemplo sintético: UNSUPPORTED_VERSION' '503': description: DEPENDENCY_UNAVAILABLE ou UNAVAILABLE; sem SQL/credenciais. Resultado de commit pode ser desconhecido; consultar identidade antes de nova escolha. content: application/json: schema: $ref: '#/components/schemas/Error' examples: DEPENDENCY_UNAVAILABLE: summary: DEPENDENCY UNAVAILABLE value: code: DEPENDENCY_UNAVAILABLE message: 'Exemplo sintético: DEPENDENCY_UNAVAILABLE' UNAVAILABLE: summary: UNAVAILABLE value: code: UNAVAILABLE message: 'Exemplo sintético: UNAVAILABLE' /api/v1/engagement/top: get: tags: - Engajamento summary: Consultar os mais escolhidos da semana operationId: getEngagementTop security: [] description: Seleção por quantidade de cliques em Jogar nos últimos sete dias. Desempate pelo slug. Até quatro jogos da lista permitida, com cache de um minuto por processo. Sem parâmetros, cookies novos ou identificação; visitas não participam. Requer ENGAGEMENT_ORIGIN configurada. Contagens podem conter automação. responses: '200': description: Até quatro jogos disponíveis com cliques nos últimos sete dias; vazio sem cliques. Sem identificação e sem contagens públicas. content: application/json: schema: type: object additionalProperties: false required: - games - window_days properties: games: type: array maxItems: 4 uniqueItems: true items: type: string enum: - genius-6x1 - vire-a-carta - corrida-presidencial window_days: type: integer const: 7 examples: empty: value: games: [] window_days: 7 selected: value: games: - genius-6x1 window_days: 7 '400': description: INVALID_QUERY content: application/json: schema: $ref: '#/components/schemas/Error' examples: INVALID_QUERY: summary: INVALID QUERY value: code: INVALID_QUERY message: 'Exemplo sintético: INVALID_QUERY' '401': description: UNAUTHENTICATED content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNAUTHENTICATED: summary: UNAUTHENTICATED value: code: UNAUTHENTICATED message: 'Exemplo sintético: UNAUTHENTICATED' '503': description: DEPENDENCY_UNAVAILABLE, UNAVAILABLE content: application/json: schema: $ref: '#/components/schemas/Error' examples: DEPENDENCY_UNAVAILABLE: summary: DEPENDENCY UNAVAILABLE value: code: DEPENDENCY_UNAVAILABLE message: 'Exemplo sintético: DEPENDENCY_UNAVAILABLE' UNAVAILABLE: summary: UNAVAILABLE value: code: UNAVAILABLE message: 'Exemplo sintético: UNAVAILABLE' /api/v1/trophies/check: post: tags: - Troféus summary: Testar chave e slot sem gravar conquistas operationId: checkTrophy description: Diagnóstico somente de leitura. Aceita origem HTTP(S) exata em localhost ou IP loopback, com qualquer porta válida, sem cadastro local; fora de loopback exige origem ativa cadastrada para a chave. Origin null, file://, LAN e previews não cadastrados são recusados. Valida X-Game-Key ativo e trophy_id; origin_registered informa se a origem também está cadastrada para emissão real. Não cria token, link, claim, sessão, troféu ou contador SQL; não altera limites de emissão. As defesas HTTP gerais continuam aplicáveis. Sem cookies. OPTIONS permite POST e Content-Type/X-Game-Key. O sucesso não comprova vitória nem autoriza emitir/resgatar de localhost. security: - gameKey: [] parameters: - name: Origin in: header required: true description: Origem exata local ou origem cadastrada do jogo. schema: type: string example: http://localhost:5173 requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: - trophy_id properties: trophy_id: $ref: '#/components/schemas/TrophyID' examples: sample: value: trophy_id: bronze-1 responses: '200': description: Diagnóstico somente de leitura; nenhuma claim, identidade, cota SQL ou conquista criada/alterada. content: application/json: schema: $ref: '#/components/schemas/TrophyCheck' examples: local: value: mode: test game_id: meu-jogo trophy_id: bronze-1 origin_registered: false persisted: false '400': description: INVALID_JSON, INVALID_QUERY, INVALID_TROPHY content: application/json: schema: $ref: '#/components/schemas/Error' examples: INVALID_JSON: value: code: INVALID_JSON message: 'Exemplo sintético: INVALID_JSON' INVALID_QUERY: value: code: INVALID_QUERY message: 'Exemplo sintético: INVALID_QUERY' INVALID_TROPHY: value: code: INVALID_TROPHY message: 'Exemplo sintético: INVALID_TROPHY' '403': description: ORIGIN_NOT_ALLOWED, INVALID_GAME_KEY content: application/json: schema: $ref: '#/components/schemas/Error' examples: ORIGIN_NOT_ALLOWED: value: code: ORIGIN_NOT_ALLOWED message: 'Exemplo sintético: ORIGIN_NOT_ALLOWED' INVALID_GAME_KEY: value: code: INVALID_GAME_KEY message: 'Exemplo sintético: INVALID_GAME_KEY' '405': description: METHOD_NOT_ALLOWED content: application/json: schema: $ref: '#/components/schemas/Error' examples: METHOD_NOT_ALLOWED: value: code: METHOD_NOT_ALLOWED message: 'Exemplo sintético: METHOD_NOT_ALLOWED' '413': description: BODY_TOO_LARGE content: application/json: schema: $ref: '#/components/schemas/Error' examples: BODY_TOO_LARGE: value: code: BODY_TOO_LARGE message: 'Exemplo sintético: BODY_TOO_LARGE' '415': description: UNSUPPORTED_MEDIA_TYPE content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNSUPPORTED_MEDIA_TYPE: value: code: UNSUPPORTED_MEDIA_TYPE message: 'Exemplo sintético: UNSUPPORTED_MEDIA_TYPE' '429': description: RATE_LIMITED content: application/json: schema: $ref: '#/components/schemas/Error' examples: RATE_LIMITED: value: code: RATE_LIMITED message: 'Exemplo sintético: RATE_LIMITED' '503': description: DEPENDENCY_UNAVAILABLE, UNAVAILABLE content: application/json: schema: $ref: '#/components/schemas/Error' examples: DEPENDENCY_UNAVAILABLE: value: code: DEPENDENCY_UNAVAILABLE message: 'Exemplo sintético: DEPENDENCY_UNAVAILABLE' UNAVAILABLE: value: code: UNAVAILABLE message: 'Exemplo sintético: UNAVAILABLE' /api/v1/trophies/claims: post: tags: - Troféus summary: Emitir link de resgate sem identificar jogador operationId: issueTrophy description: 'CORS apenas para origem ativa cadastrada; OPTIONS aceita POST e Content-Type/X-Game-Key. Sem cookies. SHA-256 hexadecimal dos 64 caracteres hexadecimais de um token aleatório de 256 bits criado pelo SDK. UUID v4 e conteúdo idênticos permitem reenvio, inclusive após perda da resposta. Conflito retorna 409; prazo fixo de 15 minutos, sem renovação. Por minuto no PostgreSQL: 10 provas novas por jogo/solicitante e teto agregado de 60 por jogo; reenvios idempotentes não debitam provas novas. Admissão separada de 120 pedidos válidos por jogo/solicitante inclui retries e conflitos. Solicitante é o IP da conexão (IPv6 /64); X-Forwarded-For só é usado através de TRUSTED_PROXY_CIDRS explícitos. NAT compartilha cota; erro de admissão retorna 429 com Retry-After 60. Chave/origem públicas não impedem conquista fabricada. claim_url é sempre PUBLIC_ORIGIN/trofeus/ por compatibilidade com SDKs distribuídos; esse endereço redireciona sem corpo para HUB_ORIGIN/trofeus/. SDK acrescenta #token=, nunca query ou URL de retorno arbitrária.' responses: '200': description: Estado persistido ou reenvio idempotente. content: application/json: schema: $ref: '#/components/schemas/TrophyReceipt' examples: sample: value: game_id: piloto trophy_id: bronze-1 expires_at: '2026-10-17T15:15:00Z' claim_url: https://api.gamerspelobrasil.tech/trofeus/ '400': description: INVALID_JSON, INVALID_QUERY, INVALID_TROPHY content: application/json: schema: $ref: '#/components/schemas/Error' examples: INVALID_JSON: value: code: INVALID_JSON message: 'Exemplo sintético: INVALID_JSON' INVALID_QUERY: value: code: INVALID_QUERY message: 'Exemplo sintético: INVALID_QUERY' INVALID_TROPHY: value: code: INVALID_TROPHY message: 'Exemplo sintético: INVALID_TROPHY' '403': description: ORIGIN_NOT_ALLOWED, INVALID_GAME_KEY content: application/json: schema: $ref: '#/components/schemas/Error' examples: ORIGIN_NOT_ALLOWED: value: code: ORIGIN_NOT_ALLOWED message: 'Exemplo sintético: ORIGIN_NOT_ALLOWED' INVALID_GAME_KEY: value: code: INVALID_GAME_KEY message: 'Exemplo sintético: INVALID_GAME_KEY' '409': description: TROPHY_CONFLICT content: application/json: schema: $ref: '#/components/schemas/Error' examples: TROPHY_CONFLICT: value: code: TROPHY_CONFLICT message: 'Exemplo sintético: TROPHY_CONFLICT' '410': description: CLAIM_EXPIRED content: application/json: schema: $ref: '#/components/schemas/Error' examples: CLAIM_EXPIRED: value: code: CLAIM_EXPIRED message: 'Exemplo sintético: CLAIM_EXPIRED' '413': description: BODY_TOO_LARGE content: application/json: schema: $ref: '#/components/schemas/Error' examples: BODY_TOO_LARGE: value: code: BODY_TOO_LARGE message: 'Exemplo sintético: BODY_TOO_LARGE' '415': description: UNSUPPORTED_MEDIA_TYPE content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNSUPPORTED_MEDIA_TYPE: value: code: UNSUPPORTED_MEDIA_TYPE message: 'Exemplo sintético: UNSUPPORTED_MEDIA_TYPE' '429': description: RATE_LIMITED content: application/json: schema: $ref: '#/components/schemas/Error' examples: RATE_LIMITED: value: code: RATE_LIMITED message: 'Exemplo sintético: RATE_LIMITED' '503': description: DEPENDENCY_UNAVAILABLE, UNAVAILABLE content: application/json: schema: $ref: '#/components/schemas/Error' examples: DEPENDENCY_UNAVAILABLE: value: code: DEPENDENCY_UNAVAILABLE message: 'Exemplo sintético: DEPENDENCY_UNAVAILABLE' UNAVAILABLE: value: code: UNAVAILABLE message: 'Exemplo sintético: UNAVAILABLE' security: - gameKey: [] parameters: - name: Origin in: header required: true description: Origem exata cadastrada para o jogo. schema: type: string example: https://jogos.gamerspelobrasil.tech requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/TrophyIssue' examples: sample: value: event_id: 12345678-1234-4123-8123-123456789abc trophy_id: bronze-1 claim_hash: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa /api/v1/trophies/redeem: post: tags: - Troféus summary: Resgatar troféu na identidade da sessão central operationId: redeemTrophy description: A página /trofeus/ no hub (HUB_ORIGIN) lê o fragmento, remove-o do endereço e preserva a intenção em sessionStorage. GET não resgata nem cria sessão. O POST após confirmação valida token, prazo, jogo ativo, chave vigente e cookie; grava em transação. Uma identidade por link; um troféu de cada slot por jogador/jogo. Reenvio pelo mesmo dono é idempotente, inclusive após vencer o prazo. Outra identidade recebe 409. Limite de 60 tentativas autenticadas/minuto. Sessão ausente recebe 401 sem consumir o link. A página ainda oferece apenas criar identidade de teste; quando habilitadas, contas (tag Contas) usam o mesmo cookie. responses: '200': description: Estado persistido ou reenvio idempotente. content: application/json: schema: $ref: '#/components/schemas/Trophy' examples: sample: value: &id002 game_id: piloto game_name: Jogo piloto trophy_id: bronze-1 awarded_at: '2026-10-17T15:00:00Z' '400': description: INVALID_JSON, INVALID_QUERY, INVALID_CLAIM content: application/json: schema: $ref: '#/components/schemas/Error' examples: INVALID_JSON: value: code: INVALID_JSON message: 'Exemplo sintético: INVALID_JSON' INVALID_QUERY: value: code: INVALID_QUERY message: 'Exemplo sintético: INVALID_QUERY' INVALID_CLAIM: value: code: INVALID_CLAIM message: 'Exemplo sintético: INVALID_CLAIM' '403': description: ORIGIN_NOT_ALLOWED content: application/json: schema: $ref: '#/components/schemas/Error' examples: ORIGIN_NOT_ALLOWED: value: code: ORIGIN_NOT_ALLOWED message: 'Exemplo sintético: ORIGIN_NOT_ALLOWED' '409': description: CLAIM_USED content: application/json: schema: $ref: '#/components/schemas/Error' examples: CLAIM_USED: value: code: CLAIM_USED message: 'Exemplo sintético: CLAIM_USED' '410': description: CLAIM_EXPIRED content: application/json: schema: $ref: '#/components/schemas/Error' examples: CLAIM_EXPIRED: value: code: CLAIM_EXPIRED message: 'Exemplo sintético: CLAIM_EXPIRED' '413': description: BODY_TOO_LARGE content: application/json: schema: $ref: '#/components/schemas/Error' examples: BODY_TOO_LARGE: value: code: BODY_TOO_LARGE message: 'Exemplo sintético: BODY_TOO_LARGE' '415': description: UNSUPPORTED_MEDIA_TYPE content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNSUPPORTED_MEDIA_TYPE: value: code: UNSUPPORTED_MEDIA_TYPE message: 'Exemplo sintético: UNSUPPORTED_MEDIA_TYPE' '429': description: RATE_LIMITED content: application/json: schema: $ref: '#/components/schemas/Error' examples: RATE_LIMITED: value: code: RATE_LIMITED message: 'Exemplo sintético: RATE_LIMITED' '503': description: DEPENDENCY_UNAVAILABLE, UNAVAILABLE content: application/json: schema: $ref: '#/components/schemas/Error' examples: DEPENDENCY_UNAVAILABLE: value: code: DEPENDENCY_UNAVAILABLE message: 'Exemplo sintético: DEPENDENCY_UNAVAILABLE' UNAVAILABLE: value: code: UNAVAILABLE message: 'Exemplo sintético: UNAVAILABLE' '401': description: UNAUTHENTICATED content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNAUTHENTICATED: value: code: UNAUTHENTICATED message: 'Exemplo sintético: UNAUTHENTICATED' '404': description: CLAIM_NOT_FOUND content: application/json: schema: $ref: '#/components/schemas/Error' examples: CLAIM_NOT_FOUND: value: code: CLAIM_NOT_FOUND message: 'Exemplo sintético: CLAIM_NOT_FOUND' parameters: - name: Origin in: header required: true description: Origem central PUBLIC_ORIGIN ou HUB_ORIGIN exata, com CORS de credenciais restrito ao hub. schema: type: string example: https://api.gamerspelobrasil.tech requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: - token properties: token: $ref: '#/components/schemas/ID' examples: sample: value: token: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb /api/v1/players/me/trophies: get: tags: - Troféus summary: Consultar a coleção da própria identidade operationId: getTrophies description: Até 1000 troféus, mais recentes primeiro. Sem parâmetros. Sessão central obrigatória; jogos não recebem acesso CORS a esta coleção. responses: '200': description: Estado persistido ou reenvio idempotente. content: application/json: schema: type: object additionalProperties: false required: - trophies properties: trophies: type: array items: $ref: '#/components/schemas/Trophy' maxItems: 1000 examples: sample: value: trophies: - *id002 '400': description: INVALID_QUERY content: application/json: schema: $ref: '#/components/schemas/Error' examples: INVALID_QUERY: value: code: INVALID_QUERY message: 'Exemplo sintético: INVALID_QUERY' '401': description: UNAUTHENTICATED content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNAUTHENTICATED: value: code: UNAUTHENTICATED message: 'Exemplo sintético: UNAUTHENTICATED' '503': description: DEPENDENCY_UNAVAILABLE, UNAVAILABLE content: application/json: schema: $ref: '#/components/schemas/Error' examples: DEPENDENCY_UNAVAILABLE: value: code: DEPENDENCY_UNAVAILABLE message: 'Exemplo sintético: DEPENDENCY_UNAVAILABLE' UNAVAILABLE: value: code: UNAVAILABLE message: 'Exemplo sintético: UNAVAILABLE' /api/v1/submissions/upload: post: tags: - Comunidade operationId: submitCommunityUpload summary: Receber HTML/ZIP privado para revisão security: [] description: 'Desativado sem SUBMISSIONS_ORIGIN e SUBMISSIONS_UPLOAD_DIR. Público exige HTTPS e ponte autenticada do hub. Mesma origem, protocolo, segredo, estados e fila do envio por link. Multipart em ordem: metadata (texto JSON estrito, até 16384 bytes), file (HTML ou ZIP); nenhuma parte extra. Sem execução ou download público. Até 5 MiB HTML autocontido ou 25 MiB ZIP com index.html na raiz e recursos relativos, 100 MiB expandidos, 1000 entradas, 5 MiB por arquivo, razão até 100:1. Allowlist estática; paths ASCII até 240 bytes/16 níveis; recusa caminhos perigosos, colisões, links, especiais, criptografia e compactados internos. O SHA-256 declarado deve conferir com o arquivo real. Hashes individuais e do pacote ficam na fila administrativa. Validação estática não comprova ausência de rede dinâmica ou segurança do jogo; revisão separada obrigatória. Chave idempotente é comum a link/upload: conteúdo ou segredo diferentes retornam 409. Retry idêntico exige reenvio dos mesmos bytes e verifica objeto salvo. Até 10 tentativas/minuto por solicitante, 120 tentativas/minuto compartilhadas, 20 novos/minuto e 500 submissões totais; até 100 MiB por solicitante e 512 MiB no armazenamento, com reserva de 25 MiB para nova entrega. Uma operação de upload ativa por banco, entre processos; UPLOAD_BUSY é recuperável. Até 90 segundos no servidor. No piloto local os solicitantes compartilham loopback. No modo público, o hub identifica o solicitante pelos proxies explicitamente confiáveis e a API autentica a ponte; cabeçalhos do navegador não são retransmitidos. Arquivo privado finalizado e sincronizado antes do commit; sem recibo de sucesso antes de ambos. Falha ambígua é recuperada pela mesma chave; órfãos/temporários são reconciliados sob trava no próximo envio, reinício ou CLI. Recusadas e suas versões são removidas após 90 dias da recusa da última versão, com limpeza periódica. Não aprova, publica, executa builds ou cadastra chave SDK.' parameters: - name: Origin in: header required: true description: Uma única origem exata configurada em SUBMISSIONS_ORIGIN, loopback no piloto ou HTTPS no modo público por ponte do hub. schema: type: string example: http://localhost:8091 requestBody: required: true content: multipart/form-data: schema: type: object additionalProperties: false required: - metadata - file properties: metadata: type: string maxLength: 16384 contentMediaType: application/json contentSchema: $ref: '#/components/schemas/SubmissionUploadInput' file: type: string format: binary description: Bytes originais; nome do arquivo não é usado como caminho. example: metadata: '{"idempotency_key": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "tracking_secret": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", "title": "Fixture de upload", "description": "HTML sintético para revisão", "public_credit": "Equipe fixture", "private_contact": "fixture@example.invalid", "relationship": "author", "authorization": true, "licenses": "Materiais próprios, CC0", "format": "html", "file_sha256": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"}' file: Fixture responses: '200': description: Recebimento confirmado ou retry idêntico; consulta retorna somente estado e mensagem ao remetente. content: application/json: schema: $ref: '#/components/schemas/SubmissionReceipt' examples: received: value: protocol: dddddddddddddddddddddddddddddddd status: received public_message: '' created_at: '2026-10-18T15:00:00Z' updated_at: '2026-10-18T15:00:00Z' '400': description: INVALID_JSON, INVALID_QUERY, INVALID_SUBMISSION. content: application/json: schema: $ref: '#/components/schemas/Error' examples: INVALID_JSON: value: code: INVALID_JSON message: 'Exemplo sintético: INVALID_JSON' INVALID_QUERY: value: code: INVALID_QUERY message: 'Exemplo sintético: INVALID_QUERY' INVALID_SUBMISSION: value: code: INVALID_SUBMISSION message: 'Exemplo sintético: INVALID_SUBMISSION' '403': description: ORIGIN_NOT_ALLOWED. content: application/json: schema: $ref: '#/components/schemas/Error' examples: ORIGIN_NOT_ALLOWED: value: code: ORIGIN_NOT_ALLOWED message: 'Exemplo sintético: ORIGIN_NOT_ALLOWED' '413': description: BODY_TOO_LARGE. content: application/json: schema: $ref: '#/components/schemas/Error' examples: BODY_TOO_LARGE: value: code: BODY_TOO_LARGE message: 'Exemplo sintético: BODY_TOO_LARGE' '415': description: UNSUPPORTED_MEDIA_TYPE. content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNSUPPORTED_MEDIA_TYPE: value: code: UNSUPPORTED_MEDIA_TYPE message: 'Exemplo sintético: UNSUPPORTED_MEDIA_TYPE' '429': description: RATE_LIMITED. content: application/json: schema: $ref: '#/components/schemas/Error' examples: RATE_LIMITED: value: code: RATE_LIMITED message: 'Exemplo sintético: RATE_LIMITED' headers: Retry-After: description: Segundos até tentar de novo. schema: type: integer const: 60 '503': description: DEPENDENCY_UNAVAILABLE, UNAVAILABLE, SUBMISSION_CAPACITY, UPLOAD_BUSY, UPLOAD_INTEGRITY. content: application/json: schema: $ref: '#/components/schemas/Error' examples: DEPENDENCY_UNAVAILABLE: value: code: DEPENDENCY_UNAVAILABLE message: 'Exemplo sintético: DEPENDENCY_UNAVAILABLE' UNAVAILABLE: value: code: UNAVAILABLE message: 'Exemplo sintético: UNAVAILABLE' SUBMISSION_CAPACITY: value: code: SUBMISSION_CAPACITY message: 'Exemplo sintético: SUBMISSION_CAPACITY' UPLOAD_BUSY: value: code: UPLOAD_BUSY message: 'Exemplo sintético: UPLOAD_BUSY' UPLOAD_INTEGRITY: value: code: UPLOAD_INTEGRITY message: 'Exemplo sintético: UPLOAD_INTEGRITY' '409': description: SUBMISSION_CONFLICT. Conteúdo divergente para a chave, ou versão anterior indisponível, segredo incorreto, modalidade diferente ou sucessora já existente. content: application/json: schema: $ref: '#/components/schemas/Error' examples: SUBMISSION_CONFLICT: value: code: SUBMISSION_CONFLICT message: 'Exemplo sintético: SUBMISSION_CONFLICT' '404': description: Piloto desativado. content: text/plain: schema: type: string example: '404 page not found ' '422': description: INVALID_PACKAGE, UPLOAD_HASH_MISMATCH. content: application/json: schema: $ref: '#/components/schemas/Error' examples: INVALID_PACKAGE: value: code: INVALID_PACKAGE message: 'Exemplo sintético: INVALID_PACKAGE' UPLOAD_HASH_MISMATCH: value: code: UPLOAD_HASH_MISMATCH message: 'Exemplo sintético: UPLOAD_HASH_MISMATCH' /api/v1/accounts/google/start: get: tags: - Contas summary: Entrar com Google operationId: startGoogleLogin description: 'Navegação do navegador, não chamada fetch. Google é a única forma de entrar; não há cadastro por senha. Rotas desligadas por padrão: sem GOOGLE_CLIENT_ID e GOOGLE_CLIENT_SECRET, todo /api/v1/accounts responde 404. Grava um início de login de uso único (10 minutos), define o cookie defende_brasil_google (HttpOnly, SameSite=Lax, Path=/api/v1/accounts/google/, host-only) com o state e redireciona ao Google com PKCE S256, nonce e escopo openid email. Nome e foto do Google não são pedidos. Cota de 30 inícios por minuto por solicitante; excedida, redireciona a /conta/#erro=RATE_LIMITED.' security: [] parameters: - name: return in: query required: false description: Caminho permitido no HUB_ORIGIN que recebe o navegador depois do login. Padrão /conta/. schema: type: string enum: - /conta/ - /trofeus/ example: '/trofeus/' responses: '303': description: Para o Google (accounts.google.com) ou, se a cota acabou, para /conta/#erro=RATE_LIMITED. headers: Location: schema: type: string '400': description: INVALID_QUERY content: application/json: schema: $ref: '#/components/schemas/Error' examples: INVALID_QUERY: value: code: INVALID_QUERY message: 'Exemplo sintético: INVALID_QUERY' /api/v1/accounts/google/callback: get: tags: - Contas summary: Retorno do Google operationId: googleLoginCallback description: 'Endereço cadastrado no cliente OAuth como URI de redirecionamento (PUBLIC_ORIGIN + este caminho). Só aceita o navegador que iniciou o login (cookie igual ao state), gasta o state antes de trocar o código e valida o ID token: assinatura RS256 pelas chaves do Google, emissor, audiência, validade, nonce e e-mail verificado. A conta é encontrada pelo sujeito (sub) do Google, nunca pelo e-mail; o primeiro login cria jogador e conta. Em sucesso, define defende_brasil_session host-only na API (30 dias por padrão) e redireciona sem corpo HTML à página pedida em HUB_ORIGIN. Falhas redirecionam a /conta/#erro=CÓDIGO com GOOGLE_LOGIN_INVALID (state ausente, alheio, vencido ou já usado; código ou token recusado), GOOGLE_CANCELLED, GOOGLE_EMAIL_UNVERIFIED, GOOGLE_UNAVAILABLE ou UNAVAILABLE. O cookie de state é sempre apagado.' security: [] parameters: - name: state in: query required: true schema: type: string example: 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' - name: code in: query required: false schema: type: string example: '4/0Exemplo-sintetico' - name: error in: query required: false schema: type: string example: 'access_denied' - name: scope in: query required: false schema: type: string example: 'email openid' - name: authuser in: query required: false schema: type: string example: '0' responses: '303': description: Para /conta/ ou /trofeus/ com sessão aberta, ou para /conta/#erro=CÓDIGO. headers: Location: schema: type: string /api/v1/accounts/logout: post: tags: - Contas summary: Sair da sessão atual operationId: logoutAccount description: 'Revoga somente a sessão do cookie enviado e apaga o cookie. Partidas e troféus permanecem. Idempotente.' responses: '204': description: Sessão encerrada. '400': description: INVALID_JSON content: application/json: schema: $ref: '#/components/schemas/Error' examples: INVALID_JSON: value: code: INVALID_JSON message: 'Exemplo sintético: INVALID_JSON' '401': description: UNAUTHENTICATED content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNAUTHENTICATED: value: code: UNAUTHENTICATED message: 'Exemplo sintético: UNAUTHENTICATED' '403': description: ORIGIN_NOT_ALLOWED content: application/json: schema: $ref: '#/components/schemas/Error' examples: ORIGIN_NOT_ALLOWED: value: code: ORIGIN_NOT_ALLOWED message: 'Exemplo sintético: ORIGIN_NOT_ALLOWED' '413': description: BODY_TOO_LARGE content: application/json: schema: $ref: '#/components/schemas/Error' examples: BODY_TOO_LARGE: value: code: BODY_TOO_LARGE message: 'Exemplo sintético: BODY_TOO_LARGE' '415': description: UNSUPPORTED_MEDIA_TYPE content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNSUPPORTED_MEDIA_TYPE: value: code: UNSUPPORTED_MEDIA_TYPE message: 'Exemplo sintético: UNSUPPORTED_MEDIA_TYPE' '503': description: DEPENDENCY_UNAVAILABLE, UNAVAILABLE content: application/json: schema: $ref: '#/components/schemas/Error' examples: DEPENDENCY_UNAVAILABLE: value: code: DEPENDENCY_UNAVAILABLE message: 'Exemplo sintético: DEPENDENCY_UNAVAILABLE' UNAVAILABLE: value: code: UNAVAILABLE message: 'Exemplo sintético: UNAVAILABLE' parameters: - name: Origin in: header required: true description: Origem central PUBLIC_ORIGIN ou HUB_ORIGIN exata, com CORS de credenciais restrito ao hub. schema: type: string example: https://api.gamerspelobrasil.tech requestBody: required: true content: application/json: schema: type: object additionalProperties: false maxProperties: 0 examples: sample: value: {} /api/v1/accounts/me: get: tags: - Contas summary: Consultar a própria conta operationId: getAccount description: 'Nome, identificação nome#número e avatar aparecem depois da escolha de nome em /api/v1/players/me/name. Avatar: animal → avatar, adjetivo → borda, cor → fundo. Identidade de teste sem conta recebe 404.' security: - session: [] responses: '200': description: Conta da sessão. content: application/json: schema: $ref: '#/components/schemas/Account' examples: unnamed: value: email: jogadora@exemplo.org player_id: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa display_name: null handle: null avatar: null expires_at: '2026-11-17T15:00:00Z' named: value: email: jogadora@exemplo.org player_id: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa display_name: CapivaraValenteAzul handle: CapivaraValenteAzul#4821 avatar: animal: a00 border: j00 background: c00 expires_at: '2026-11-17T15:00:00Z' '400': description: INVALID_QUERY content: application/json: schema: $ref: '#/components/schemas/Error' examples: INVALID_QUERY: value: code: INVALID_QUERY message: 'Exemplo sintético: INVALID_QUERY' '401': description: UNAUTHENTICATED content: application/json: schema: $ref: '#/components/schemas/Error' examples: UNAUTHENTICATED: value: code: UNAUTHENTICATED message: 'Exemplo sintético: UNAUTHENTICATED' '404': description: ACCOUNT_NOT_FOUND content: application/json: schema: $ref: '#/components/schemas/Error' examples: ACCOUNT_NOT_FOUND: value: code: ACCOUNT_NOT_FOUND message: 'Exemplo sintético: ACCOUNT_NOT_FOUND' '503': description: DEPENDENCY_UNAVAILABLE, UNAVAILABLE content: application/json: schema: $ref: '#/components/schemas/Error' examples: DEPENDENCY_UNAVAILABLE: value: code: DEPENDENCY_UNAVAILABLE message: 'Exemplo sintético: DEPENDENCY_UNAVAILABLE' UNAVAILABLE: value: code: UNAVAILABLE message: 'Exemplo sintético: UNAVAILABLE' components: securitySchemes: session: type: apiKey in: cookie name: defende_brasil_session description: Credencial opaca de 256 bits; não usar nome como credencial. Mais de uma ocorrência deste cookie é ambígua e retorna UNAUTHENTICATED, inclusive se um valor estiver malformado. gameKey: type: apiKey in: header name: X-Game-Key description: Chave pública instalada no JavaScript. Identifica o cadastro, não comprova legitimidade da conquista. parameters: Origin: name: Origin in: header required: true description: Uma única origem exatamente igual a PUBLIC_ORIGIN, ou a HUB_ORIGIN nas rotas de conta/me, logout, sessão, jogador/me, nome/opções, coleção e resgate. O hub usa credentials include e cookie host-only da API. Nas rotas legadas de identidade/partidas, também permite à GAME_ORIGIN explicitamente configurada. Produção usa https://api.gamerspelobrasil.tech e o jogo em https://jogos.gamerspelobrasil.tech. GAME_ORIGIN ausente mantém acesso somente na origem da API. Ausente/null ou origem do site de documentação gera 403. O navegador fornece o cabeçalho; clientes curl o enviam explicitamente. schema: type: string example: https://api.gamerspelobrasil.tech schemas: ArenaState: type: object required: - t - event - rehearsal - phase - hp - max_hp - defeats - resolved - online - ranking - checked - source_status - source_updated - server_time - stun_until - combo_until - revision properties: t: const: state event: type: string rehearsal: type: boolean phase: enum: - waiting - live - paused - ended hp: type: integer minimum: 0 max_hp: type: integer minimum: 0 defeats: type: integer minimum: 0 resolved: type: integer minimum: 0 online: type: integer minimum: 0 checked: type: integer minimum: 0 source_updated: type: integer minimum: 0 server_time: type: integer minimum: 0 stun_until: type: integer minimum: 0 combo_until: type: integer minimum: 0 revision: type: integer minimum: 0 source_status: type: string ranking: type: array items: type: object required: - uf - total - players - average properties: uf: type: string total: type: integer players: type: integer average: type: integer power: type: object description: Na fase question contém fala, agência, fonte, prazo e escudo; verdict/explanation aparecem somente na fase reveal. checks: type: array items: type: object ArenaPlayer: type: object additionalProperties: false required: - uf - damage - points - answers - last_hit - tokens properties: uf: type: string damage: type: integer minimum: 0 points: type: integer minimum: 0 answers: type: object additionalProperties: type: object required: - value - points - correct properties: value: enum: - falso - enganoso - exagerado - sem_contexto - insustentavel points: type: integer minimum: 0 correct: type: boolean last_hit: type: integer tokens: type: number minimum: 0 maximum: 15 SubmissionInput: type: object additionalProperties: false required: - idempotency_key - tracking_secret - title - url - description - public_credit - private_contact - relationship dependentRequired: parent_protocol: [parent_tracking_secret] parent_tracking_secret: [parent_protocol] properties: parent_protocol: type: string pattern: ^[0-9a-f]{32}$ description: Protocolo anterior em needs_changes, da mesma modalidade e sem sucessora. Exige parent_tracking_secret; omitido no primeiro envio. parent_tracking_secret: type: string pattern: ^[0-9a-f]{64}$ description: Segredo da versão anterior, usado somente para autorizar o vínculo. Exige parent_protocol. Nunca retornado. idempotency_key: &id003 type: string pattern: ^[0-9a-f]{64}$ minLength: 64 maxLength: 64 tracking_secret: *id003 title: type: string minLength: 1 maxLength: 120 url: type: string minLength: 1 maxLength: 2048 format: uri pattern: ^https:// description: HTTPS com host, sem credenciais ou espaços; não é buscada pelo servidor. description: type: string minLength: 1 maxLength: 1000 public_credit: type: string minLength: 1 maxLength: 120 private_contact: type: string minLength: 1 maxLength: 240 relationship: type: string enum: - author - recommendation SubmissionLookup: type: object additionalProperties: false required: - protocol - tracking_secret properties: protocol: &id004 type: string pattern: ^[0-9a-f]{32}$ minLength: 32 maxLength: 32 tracking_secret: *id003 SubmissionReceipt: type: object additionalProperties: false required: - protocol - status - public_message - created_at - updated_at properties: protocol: *id004 parent_protocol: type: string pattern: ^[0-9a-f]{32}$ description: Versão anterior, quando este recibo é uma correção. next_protocol: type: string pattern: ^[0-9a-f]{32}$ description: Sucessora, somente na consulta privada da versão anterior; seu segredo continua independente. status: type: string enum: - received - reviewing - needs_changes - approved - rejected public_message: type: string minLength: 0 maxLength: 1000 created_at: type: string format: date-time updated_at: type: string format: date-time ID: type: string pattern: ^[a-f0-9]{64}$ minLength: 64 maxLength: 64 Player: type: object additionalProperties: false required: - player_id - display_name - repertoire_version - expires_at - open_run_id properties: player_id: $ref: '#/components/schemas/ID' display_name: type: - string - 'null' maxLength: 128 repertoire_version: type: - string - 'null' enum: - br-test-v1 - null expires_at: type: string format: date-time open_run_id: type: - string - 'null' description: Referência da partida aberta ainda válida, recuperável após resposta de abertura perdida. Option: type: object additionalProperties: false required: - option_id - display_name - expires_at - repertoire_version - avatar properties: option_id: $ref: '#/components/schemas/ID' display_name: type: string maxLength: 128 expires_at: type: string format: date-time repertoire_version: type: string const: br-test-v1 avatar: $ref: '#/components/schemas/Avatar' Avatar: type: object additionalProperties: false description: 'Partes do nome: animal → personagem, adjetivo → moldura, cor → fundo. As imagens ficam em /avatar/.' required: - animal - border - background properties: animal: type: string pattern: ^a[0-9]{2}$ border: type: string pattern: ^j[0-9]{2}$ background: type: string pattern: ^c[0-9]{2}$ Options: type: object additionalProperties: false required: - options - editorial_status properties: options: type: array minItems: 3 maxItems: 3 items: $ref: '#/components/schemas/Option' editorial_status: type: string const: unreviewed Error: type: object additionalProperties: false required: - code - message properties: code: type: string enum: - ARENA_INVALID - ARENA_CLOSED - ARENA_UNAVAILABLE - ARENA_ALREADY_ANSWERED - ARENA_CHARACTER_REQUIRED - FORBIDDEN - INVALID_SUBMISSION - SUBMISSION_CONFLICT - SUBMISSION_NOT_FOUND - SUBMISSION_CAPACITY - INVALID_JSON - BODY_TOO_LARGE - ORIGIN_NOT_ALLOWED - UNSUPPORTED_MEDIA_TYPE - METHOD_NOT_ALLOWED - UNAUTHENTICATED - NAME_ALREADY_SET - NAME_OPTIONS_EXPIRED - INVALID_NAME_OPTION - RATE_LIMITED - DEPENDENCY_UNAVAILABLE - UNAVAILABLE - NAME_REQUIRED - UNSUPPORTED_VERSION - RUN_ALREADY_OPEN - RUN_NOT_FOUND - RUN_EXPIRED - MISSING_EVENTS - EVENT_CONFLICT - RUN_TERMINAL - EVENT_LIMIT_EXCEEDED - CLOCK_AHEAD - INVALID_EVENT - INVALID_FINISH - FINISH_CONFLICT - RUN_NOT_TERMINAL - INVALID_QUERY - INVALID_ENGAGEMENT - ENGAGEMENT_CONFLICT - INVALID_TROPHY - INVALID_GAME_KEY - TROPHY_CONFLICT - CLAIM_EXPIRED - INVALID_CLAIM - CLAIM_USED - CLAIM_NOT_FOUND - INVALID_PACKAGE - UPLOAD_HASH_MISMATCH - UPLOAD_BUSY - UPLOAD_INTEGRITY - INVALID_ACCOUNT - INVALID_CREDENTIALS - ACCOUNT_TOKEN_INVALID - ACCOUNT_EXISTS - ACCOUNT_NOT_FOUND - ACCOUNT_BUSY - MAIL_UNAVAILABLE - HANDLE_UNAVAILABLE message: type: string details: type: object additionalProperties: true description: run_id em RUN_ALREADY_OPEN; expected_seq e last_accepted_seq em MISSING_EVENTS. Health: type: object additionalProperties: false required: - status properties: status: type: string enum: - alive - ready - unavailable Breakdown: type: object additionalProperties: false required: - combat - integrity_bonus - total properties: combat: type: integer minimum: 0 integrity_bonus: type: integer minimum: 0 total: type: integer minimum: 0 Result: type: object additionalProperties: false required: - run_id - stage_id - rules_version - display_name - score - reason - client_score - diverged - last_seq - published - finalized_at properties: run_id: $ref: '#/components/schemas/ID' stage_id: type: string rules_version: type: string display_name: type: string maxLength: 128 score: $ref: '#/components/schemas/Breakdown' reason: type: string enum: - VICTORY - DEFENSE_DEPLETED - MANUAL_EXIT client_score: type: integer minimum: 0 maximum: 1000000000 diverged: type: boolean last_seq: type: integer minimum: 0 published: type: boolean const: true finalized_at: type: string format: date-time Spawn: type: object additionalProperties: false required: - id - archetype - wave properties: id: type: string archetype: type: string wave: type: integer minimum: 1 maximum: 3 Snapshot: type: object additionalProperties: false required: - stage_id - rules_version - reference_commit - mode - archetypes - targets - entities - fire_intervals_ms properties: stage_id: type: string rules_version: type: string reference_commit: type: string mode: type: string enum: - KEEP_ALL_ADD_INTEGRITY_BONUS archetypes: type: array items: type: object additionalProperties: false required: - id - hp - points properties: id: type: string hp: type: integer points: type: integer targets: type: object additionalProperties: type: integer minimum: 1 entities: type: array items: $ref: '#/components/schemas/Spawn' fire_intervals_ms: type: array items: type: integer minimum: 1 minItems: 3 maxItems: 3 Progress: type: object additionalProperties: false required: - wave - combat - integrity - last_accepted_seq - last_elapsed_ms - active_entities properties: wave: type: integer minimum: 1 maximum: 3 combat: type: integer minimum: 0 integrity: type: object additionalProperties: type: integer minimum: 0 reason: type: string enum: - VICTORY - DEFENSE_DEPLETED - MANUAL_EXIT last_accepted_seq: type: integer minimum: 0 last_elapsed_ms: type: integer minimum: 0 active_entities: type: object additionalProperties: type: string Run: type: object additionalProperties: false required: - run_id - stage_id - rules_version - contract_version - snapshot - created_at - expires_at - state - last_accepted_seq - preview_combat - progress - result - limits properties: run_id: $ref: '#/components/schemas/ID' stage_id: type: string rules_version: type: string contract_version: type: string const: journal-v1 snapshot: $ref: '#/components/schemas/Snapshot' created_at: type: string format: date-time expires_at: type: string format: date-time state: type: string enum: - active - ready_to_finalize - finalized - expired last_accepted_seq: type: integer minimum: 0 preview_combat: type: integer minimum: 0 progress: $ref: '#/components/schemas/Progress' result: oneOf: - $ref: '#/components/schemas/Result' - type: 'null' limits: type: object additionalProperties: false required: - batch_events - journal_events - body_bytes - clock_lead_ms properties: batch_events: type: integer const: 256 journal_events: type: integer const: 16384 body_bytes: type: integer const: 131072 clock_lead_ms: type: integer const: 2000 Event_enemy_eliminated: type: object additionalProperties: false required: - seq - elapsed_ms - type - payload properties: seq: type: integer minimum: 1 elapsed_ms: type: integer minimum: 0 type: type: string const: enemy_eliminated payload: type: object additionalProperties: false required: - entity_id - cause properties: entity_id: type: string minLength: 1 maxLength: 128 cause: type: string enum: - laser - player_contact target_id: type: string minLength: 1 maxLength: 128 examples: - seq: 1 elapsed_ms: 0 type: enemy_eliminated payload: entity_id: w1/swarm/0 cause: laser Event_enemy_breached: type: object additionalProperties: false required: - seq - elapsed_ms - type - payload properties: seq: type: integer minimum: 1 elapsed_ms: type: integer minimum: 0 type: type: string const: enemy_breached payload: type: object additionalProperties: false required: - entity_id - target_id properties: entity_id: type: string minLength: 1 maxLength: 128 target_id: type: string minLength: 1 maxLength: 128 examples: - seq: 1 elapsed_ms: 0 type: enemy_breached payload: entity_id: w1/swarm/0 target_id: target-merenda Event_projectile_fired: type: object additionalProperties: false required: - seq - elapsed_ms - type - payload properties: seq: type: integer minimum: 1 elapsed_ms: type: integer minimum: 0 type: type: string const: projectile_fired payload: type: object additionalProperties: false required: - entity_id - projectile_id properties: entity_id: type: string minLength: 1 maxLength: 128 projectile_id: type: string pattern: ^p/[1-9][0-9]*$ maxLength: 128 examples: - seq: 1 elapsed_ms: 2500 type: projectile_fired payload: entity_id: w1/swarm/0 projectile_id: p/1 Event_projectile_hit: type: object additionalProperties: false required: - seq - elapsed_ms - type - payload properties: seq: type: integer minimum: 1 elapsed_ms: type: integer minimum: 0 type: type: string const: projectile_hit payload: type: object additionalProperties: false required: - projectile_id - cause properties: projectile_id: type: string minLength: 1 maxLength: 128 cause: type: string enum: - target - player target_id: type: string minLength: 1 maxLength: 128 examples: - seq: 2 elapsed_ms: 2500 type: projectile_hit payload: projectile_id: p/1 cause: target target_id: target-merenda Event_wave_advanced: type: object additionalProperties: false required: - seq - elapsed_ms - type - payload properties: seq: type: integer minimum: 1 elapsed_ms: type: integer minimum: 0 type: type: string const: wave_advanced payload: type: object additionalProperties: false required: [] properties: {} examples: - seq: 22 elapsed_ms: 1000 type: wave_advanced payload: {} Event_manual_exit: type: object additionalProperties: false required: - seq - elapsed_ms - type - payload properties: seq: type: integer minimum: 1 elapsed_ms: type: integer minimum: 0 type: type: string const: manual_exit payload: type: object additionalProperties: false required: [] properties: {} examples: - seq: 2 elapsed_ms: 0 type: manual_exit payload: {} Event: oneOf: - $ref: '#/components/schemas/Event_enemy_eliminated' - $ref: '#/components/schemas/Event_enemy_breached' - $ref: '#/components/schemas/Event_projectile_fired' - $ref: '#/components/schemas/Event_projectile_hit' - $ref: '#/components/schemas/Event_wave_advanced' - $ref: '#/components/schemas/Event_manual_exit' discriminator: propertyName: type mapping: enemy_eliminated: '#/components/schemas/Event_enemy_eliminated' enemy_breached: '#/components/schemas/Event_enemy_breached' projectile_fired: '#/components/schemas/Event_projectile_fired' projectile_hit: '#/components/schemas/Event_projectile_hit' wave_advanced: '#/components/schemas/Event_wave_advanced' manual_exit: '#/components/schemas/Event_manual_exit' TrophyID: type: string enum: - bronze-1 - bronze-2 - bronze-3 - prata-1 - prata-2 - ouro-1 - diamante-1 TrophyCheck: type: object additionalProperties: false required: - mode - game_id - trophy_id - origin_registered - persisted properties: mode: type: string const: test game_id: type: string trophy_id: $ref: '#/components/schemas/TrophyID' origin_registered: type: boolean description: Origem também cadastrada para emissão real; false é esperado no diagnóstico local sem cadastro. persisted: type: boolean const: false TrophyIssue: type: object additionalProperties: false required: - event_id - trophy_id - claim_hash properties: event_id: type: string pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ trophy_id: $ref: '#/components/schemas/TrophyID' claim_hash: $ref: '#/components/schemas/ID' TrophyReceipt: type: object additionalProperties: false required: - game_id - trophy_id - expires_at - claim_url properties: game_id: type: string trophy_id: $ref: '#/components/schemas/TrophyID' expires_at: type: string format: date-time claim_url: type: string format: uri Account: type: object additionalProperties: false required: - email - player_id - display_name - handle - avatar - expires_at properties: email: type: string player_id: $ref: '#/components/schemas/ID' display_name: type: - string - 'null' handle: type: - string - 'null' pattern: ^.+#[1-9][0-9]{3}$ description: Nome escolhido seguido de número único de 1000 a 9999. avatar: oneOf: - type: 'null' - $ref: '#/components/schemas/Avatar' expires_at: type: string format: date-time Trophy: type: object additionalProperties: false required: - game_id - game_name - trophy_id - awarded_at properties: game_id: type: string game_name: type: string trophy_id: $ref: '#/components/schemas/TrophyID' awarded_at: type: string format: date-time SubmissionUploadInput: type: object additionalProperties: false required: - idempotency_key - tracking_secret - title - description - public_credit - private_contact - relationship - authorization - licenses - format - file_sha256 dependentRequired: parent_protocol: [parent_tracking_secret] parent_tracking_secret: [parent_protocol] properties: parent_protocol: type: string pattern: ^[0-9a-f]{32}$ description: Protocolo anterior em needs_changes, da mesma modalidade e sem sucessora. Exige parent_tracking_secret; omitido no primeiro envio. parent_tracking_secret: type: string pattern: ^[0-9a-f]{64}$ description: Segredo da versão anterior, usado somente para autorizar o vínculo. Exige parent_protocol. Nunca retornado. idempotency_key: &id005 type: string pattern: ^[0-9a-f]{64}$ minLength: 64 maxLength: 64 tracking_secret: *id005 title: type: string minLength: 1 maxLength: 120 description: type: string minLength: 1 maxLength: 1000 public_credit: type: string minLength: 1 maxLength: 120 private_contact: type: string minLength: 1 maxLength: 240 relationship: type: string const: author authorization: type: boolean const: true licenses: type: string minLength: 1 maxLength: 2000 format: type: string enum: - html - zip file_sha256: type: string pattern: ^[0-9a-f]{64}$ minLength: 64 maxLength: 64 examples: createNameOptions_issued: summary: issued value: editorial_status: unreviewed options: - option_id: cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc display_name: Arara Cívica Azul repertoire_version: br-test-v1 expires_at: '2026-10-16T12:10:00Z' avatar: animal: a02 border: j04 background: c00 - option_id: dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd display_name: Tatu Livre Verde repertoire_version: br-test-v1 expires_at: '2026-10-16T12:10:00Z' avatar: animal: a01 border: j10 background: c01 - option_id: eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee display_name: Onça Plural Rosa repertoire_version: br-test-v1 expires_at: '2026-10-16T12:10:00Z' avatar: animal: a03 border: j11 background: c07 openRun_active: summary: active value: run_id: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb stage_id: missao-merenda-transparente rules_version: mission-4860209-v1 contract_version: journal-v1 snapshot: stage_id: missao-merenda-transparente rules_version: mission-4860209-v1 reference_commit: 486020951cdca54a073bb2ecfada7389ad181c9c mode: KEEP_ALL_ADD_INTEGRITY_BONUS archetypes: - id: swarm hp: 1 points: 50 - id: tank hp: 4 points: 200 - id: splitter hp: 2 points: 120 - id: stealer hp: 2 points: 150 - id: splitter_child hp: 1 points: 40 targets: target-merenda: 100 target-saude: 100 target-urna: 100 entities: - id: w1/swarm/0 archetype: swarm wave: 1 - id: w1/swarm/1 archetype: swarm wave: 1 - id: w1/swarm/2 archetype: swarm wave: 1 - id: w1/swarm/3 archetype: swarm wave: 1 - id: w1/swarm/4 archetype: swarm wave: 1 - id: w1/swarm/5 archetype: swarm wave: 1 - id: w1/swarm/6 archetype: swarm wave: 1 - id: w1/swarm/7 archetype: swarm wave: 1 - id: w1/swarm/8 archetype: swarm wave: 1 - id: w1/swarm/9 archetype: swarm wave: 1 - id: w1/swarm/10 archetype: swarm wave: 1 - id: w1/swarm/11 archetype: swarm wave: 1 - id: w1/splitter/0 archetype: splitter wave: 1 - id: w1/splitter/1 archetype: splitter wave: 1 - id: w1/splitter/2 archetype: splitter wave: 1 - id: w2/swarm/0 archetype: swarm wave: 2 - id: w2/swarm/1 archetype: swarm wave: 2 - id: w2/swarm/2 archetype: swarm wave: 2 - id: w2/swarm/3 archetype: swarm wave: 2 - id: w2/swarm/4 archetype: swarm wave: 2 - id: w2/swarm/5 archetype: swarm wave: 2 - id: w2/swarm/6 archetype: swarm wave: 2 - id: w2/swarm/7 archetype: swarm wave: 2 - id: w2/splitter/0 archetype: splitter wave: 2 - id: w2/splitter/1 archetype: splitter wave: 2 - id: w2/splitter/2 archetype: splitter wave: 2 - id: w2/splitter/3 archetype: splitter wave: 2 - id: w2/splitter/4 archetype: splitter wave: 2 - id: w2/splitter/5 archetype: splitter wave: 2 - id: w2/stealer/0 archetype: stealer wave: 2 - id: w2/stealer/1 archetype: stealer wave: 2 - id: w2/stealer/2 archetype: stealer wave: 2 - id: w2/stealer/3 archetype: stealer wave: 2 - id: w3/swarm/0 archetype: swarm wave: 3 - id: w3/swarm/1 archetype: swarm wave: 3 - id: w3/swarm/2 archetype: swarm wave: 3 - id: w3/swarm/3 archetype: swarm wave: 3 - id: w3/swarm/4 archetype: swarm wave: 3 - id: w3/swarm/5 archetype: swarm wave: 3 - id: w3/swarm/6 archetype: swarm wave: 3 - id: w3/swarm/7 archetype: swarm wave: 3 - id: w3/tank/0 archetype: tank wave: 3 - id: w3/tank/1 archetype: tank wave: 3 - id: w3/tank/2 archetype: tank wave: 3 - id: w3/tank/3 archetype: tank wave: 3 - id: w3/splitter/0 archetype: splitter wave: 3 - id: w3/splitter/1 archetype: splitter wave: 3 - id: w3/splitter/2 archetype: splitter wave: 3 - id: w3/splitter/3 archetype: splitter wave: 3 - id: w3/splitter/4 archetype: splitter wave: 3 - id: w3/splitter/5 archetype: splitter wave: 3 - id: w3/stealer/0 archetype: stealer wave: 3 - id: w3/stealer/1 archetype: stealer wave: 3 - id: w3/stealer/2 archetype: stealer wave: 3 - id: w3/stealer/3 archetype: stealer wave: 3 - id: w3/stealer/4 archetype: stealer wave: 3 - id: w3/stealer/5 archetype: stealer wave: 3 fire_intervals_ms: - 2500 - 2000 - 1600 created_at: '2026-10-16T12:00:00Z' expires_at: '2026-10-16T12:30:00Z' state: active last_accepted_seq: 0 preview_combat: 0 progress: wave: 1 combat: 0 integrity: target-merenda: 100 target-saude: 100 target-urna: 100 last_accepted_seq: 0 last_elapsed_ms: 0 active_entities: w1/swarm/0: swarm w1/swarm/1: swarm w1/swarm/2: swarm w1/swarm/3: swarm w1/swarm/4: swarm w1/swarm/5: swarm w1/swarm/6: swarm w1/swarm/7: swarm w1/swarm/8: swarm w1/swarm/9: swarm w1/swarm/10: swarm w1/swarm/11: swarm w1/splitter/0: splitter w1/splitter/1: splitter w1/splitter/2: splitter result: null limits: batch_events: 256 journal_events: 16384 body_bytes: 131072 clock_lead_ms: 2000 getRun_terminal: summary: terminal value: run_id: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb stage_id: missao-merenda-transparente rules_version: mission-4860209-v1 contract_version: journal-v1 snapshot: stage_id: missao-merenda-transparente rules_version: mission-4860209-v1 reference_commit: 486020951cdca54a073bb2ecfada7389ad181c9c mode: KEEP_ALL_ADD_INTEGRITY_BONUS archetypes: - id: swarm hp: 1 points: 50 - id: tank hp: 4 points: 200 - id: splitter hp: 2 points: 120 - id: stealer hp: 2 points: 150 - id: splitter_child hp: 1 points: 40 targets: target-merenda: 100 target-saude: 100 target-urna: 100 entities: - id: w1/swarm/0 archetype: swarm wave: 1 - id: w1/swarm/1 archetype: swarm wave: 1 - id: w1/swarm/2 archetype: swarm wave: 1 - id: w1/swarm/3 archetype: swarm wave: 1 - id: w1/swarm/4 archetype: swarm wave: 1 - id: w1/swarm/5 archetype: swarm wave: 1 - id: w1/swarm/6 archetype: swarm wave: 1 - id: w1/swarm/7 archetype: swarm wave: 1 - id: w1/swarm/8 archetype: swarm wave: 1 - id: w1/swarm/9 archetype: swarm wave: 1 - id: w1/swarm/10 archetype: swarm wave: 1 - id: w1/swarm/11 archetype: swarm wave: 1 - id: w1/splitter/0 archetype: splitter wave: 1 - id: w1/splitter/1 archetype: splitter wave: 1 - id: w1/splitter/2 archetype: splitter wave: 1 - id: w2/swarm/0 archetype: swarm wave: 2 - id: w2/swarm/1 archetype: swarm wave: 2 - id: w2/swarm/2 archetype: swarm wave: 2 - id: w2/swarm/3 archetype: swarm wave: 2 - id: w2/swarm/4 archetype: swarm wave: 2 - id: w2/swarm/5 archetype: swarm wave: 2 - id: w2/swarm/6 archetype: swarm wave: 2 - id: w2/swarm/7 archetype: swarm wave: 2 - id: w2/splitter/0 archetype: splitter wave: 2 - id: w2/splitter/1 archetype: splitter wave: 2 - id: w2/splitter/2 archetype: splitter wave: 2 - id: w2/splitter/3 archetype: splitter wave: 2 - id: w2/splitter/4 archetype: splitter wave: 2 - id: w2/splitter/5 archetype: splitter wave: 2 - id: w2/stealer/0 archetype: stealer wave: 2 - id: w2/stealer/1 archetype: stealer wave: 2 - id: w2/stealer/2 archetype: stealer wave: 2 - id: w2/stealer/3 archetype: stealer wave: 2 - id: w3/swarm/0 archetype: swarm wave: 3 - id: w3/swarm/1 archetype: swarm wave: 3 - id: w3/swarm/2 archetype: swarm wave: 3 - id: w3/swarm/3 archetype: swarm wave: 3 - id: w3/swarm/4 archetype: swarm wave: 3 - id: w3/swarm/5 archetype: swarm wave: 3 - id: w3/swarm/6 archetype: swarm wave: 3 - id: w3/swarm/7 archetype: swarm wave: 3 - id: w3/tank/0 archetype: tank wave: 3 - id: w3/tank/1 archetype: tank wave: 3 - id: w3/tank/2 archetype: tank wave: 3 - id: w3/tank/3 archetype: tank wave: 3 - id: w3/splitter/0 archetype: splitter wave: 3 - id: w3/splitter/1 archetype: splitter wave: 3 - id: w3/splitter/2 archetype: splitter wave: 3 - id: w3/splitter/3 archetype: splitter wave: 3 - id: w3/splitter/4 archetype: splitter wave: 3 - id: w3/splitter/5 archetype: splitter wave: 3 - id: w3/stealer/0 archetype: stealer wave: 3 - id: w3/stealer/1 archetype: stealer wave: 3 - id: w3/stealer/2 archetype: stealer wave: 3 - id: w3/stealer/3 archetype: stealer wave: 3 - id: w3/stealer/4 archetype: stealer wave: 3 - id: w3/stealer/5 archetype: stealer wave: 3 fire_intervals_ms: - 2500 - 2000 - 1600 created_at: '2026-10-16T12:00:00Z' expires_at: '2026-10-16T12:30:00Z' state: ready_to_finalize last_accepted_seq: 2 preview_combat: 50 progress: wave: 1 combat: 50 integrity: target-merenda: 100 target-saude: 100 target-urna: 100 last_accepted_seq: 2 last_elapsed_ms: 0 active_entities: w1/swarm/1: swarm w1/swarm/2: swarm w1/swarm/3: swarm w1/swarm/4: swarm w1/swarm/5: swarm w1/swarm/6: swarm w1/swarm/7: swarm w1/swarm/8: swarm w1/swarm/9: swarm w1/swarm/10: swarm w1/swarm/11: swarm w1/splitter/0: splitter w1/splitter/1: splitter w1/splitter/2: splitter reason: MANUAL_EXIT result: null limits: batch_events: 256 journal_events: 16384 body_bytes: 131072 clock_lead_ms: 2000 getRun_finalized: summary: finalized value: run_id: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb stage_id: missao-merenda-transparente rules_version: mission-4860209-v1 contract_version: journal-v1 snapshot: stage_id: missao-merenda-transparente rules_version: mission-4860209-v1 reference_commit: 486020951cdca54a073bb2ecfada7389ad181c9c mode: KEEP_ALL_ADD_INTEGRITY_BONUS archetypes: - id: swarm hp: 1 points: 50 - id: tank hp: 4 points: 200 - id: splitter hp: 2 points: 120 - id: stealer hp: 2 points: 150 - id: splitter_child hp: 1 points: 40 targets: target-merenda: 100 target-saude: 100 target-urna: 100 entities: - id: w1/swarm/0 archetype: swarm wave: 1 - id: w1/swarm/1 archetype: swarm wave: 1 - id: w1/swarm/2 archetype: swarm wave: 1 - id: w1/swarm/3 archetype: swarm wave: 1 - id: w1/swarm/4 archetype: swarm wave: 1 - id: w1/swarm/5 archetype: swarm wave: 1 - id: w1/swarm/6 archetype: swarm wave: 1 - id: w1/swarm/7 archetype: swarm wave: 1 - id: w1/swarm/8 archetype: swarm wave: 1 - id: w1/swarm/9 archetype: swarm wave: 1 - id: w1/swarm/10 archetype: swarm wave: 1 - id: w1/swarm/11 archetype: swarm wave: 1 - id: w1/splitter/0 archetype: splitter wave: 1 - id: w1/splitter/1 archetype: splitter wave: 1 - id: w1/splitter/2 archetype: splitter wave: 1 - id: w2/swarm/0 archetype: swarm wave: 2 - id: w2/swarm/1 archetype: swarm wave: 2 - id: w2/swarm/2 archetype: swarm wave: 2 - id: w2/swarm/3 archetype: swarm wave: 2 - id: w2/swarm/4 archetype: swarm wave: 2 - id: w2/swarm/5 archetype: swarm wave: 2 - id: w2/swarm/6 archetype: swarm wave: 2 - id: w2/swarm/7 archetype: swarm wave: 2 - id: w2/splitter/0 archetype: splitter wave: 2 - id: w2/splitter/1 archetype: splitter wave: 2 - id: w2/splitter/2 archetype: splitter wave: 2 - id: w2/splitter/3 archetype: splitter wave: 2 - id: w2/splitter/4 archetype: splitter wave: 2 - id: w2/splitter/5 archetype: splitter wave: 2 - id: w2/stealer/0 archetype: stealer wave: 2 - id: w2/stealer/1 archetype: stealer wave: 2 - id: w2/stealer/2 archetype: stealer wave: 2 - id: w2/stealer/3 archetype: stealer wave: 2 - id: w3/swarm/0 archetype: swarm wave: 3 - id: w3/swarm/1 archetype: swarm wave: 3 - id: w3/swarm/2 archetype: swarm wave: 3 - id: w3/swarm/3 archetype: swarm wave: 3 - id: w3/swarm/4 archetype: swarm wave: 3 - id: w3/swarm/5 archetype: swarm wave: 3 - id: w3/swarm/6 archetype: swarm wave: 3 - id: w3/swarm/7 archetype: swarm wave: 3 - id: w3/tank/0 archetype: tank wave: 3 - id: w3/tank/1 archetype: tank wave: 3 - id: w3/tank/2 archetype: tank wave: 3 - id: w3/tank/3 archetype: tank wave: 3 - id: w3/splitter/0 archetype: splitter wave: 3 - id: w3/splitter/1 archetype: splitter wave: 3 - id: w3/splitter/2 archetype: splitter wave: 3 - id: w3/splitter/3 archetype: splitter wave: 3 - id: w3/splitter/4 archetype: splitter wave: 3 - id: w3/splitter/5 archetype: splitter wave: 3 - id: w3/stealer/0 archetype: stealer wave: 3 - id: w3/stealer/1 archetype: stealer wave: 3 - id: w3/stealer/2 archetype: stealer wave: 3 - id: w3/stealer/3 archetype: stealer wave: 3 - id: w3/stealer/4 archetype: stealer wave: 3 - id: w3/stealer/5 archetype: stealer wave: 3 fire_intervals_ms: - 2500 - 2000 - 1600 created_at: '2026-10-16T12:00:00Z' expires_at: '2026-10-16T12:30:00Z' state: finalized last_accepted_seq: 2 preview_combat: 50 progress: wave: 1 combat: 50 integrity: target-merenda: 100 target-saude: 100 target-urna: 100 last_accepted_seq: 2 last_elapsed_ms: 0 active_entities: w1/swarm/1: swarm w1/swarm/2: swarm w1/swarm/3: swarm w1/swarm/4: swarm w1/swarm/5: swarm w1/swarm/6: swarm w1/swarm/7: swarm w1/swarm/8: swarm w1/swarm/9: swarm w1/swarm/10: swarm w1/swarm/11: swarm w1/splitter/0: splitter w1/splitter/1: splitter w1/splitter/2: splitter reason: MANUAL_EXIT result: run_id: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb stage_id: missao-merenda-transparente rules_version: mission-4860209-v1 display_name: Arara Cívica Azul score: combat: 50 integrity_bonus: 500 total: 550 reason: MANUAL_EXIT client_score: 550 diverged: false last_seq: 2 published: true finalized_at: '2026-10-16T12:00:00Z' limits: batch_events: 256 journal_events: 16384 body_bytes: 131072 clock_lead_ms: 2000 appendEvents_active: summary: Um swarm confirmado; partida ainda ativa value: run_id: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb stage_id: missao-merenda-transparente rules_version: mission-4860209-v1 contract_version: journal-v1 snapshot: stage_id: missao-merenda-transparente rules_version: mission-4860209-v1 reference_commit: 486020951cdca54a073bb2ecfada7389ad181c9c mode: KEEP_ALL_ADD_INTEGRITY_BONUS archetypes: - id: swarm hp: 1 points: 50 - id: tank hp: 4 points: 200 - id: splitter hp: 2 points: 120 - id: stealer hp: 2 points: 150 - id: splitter_child hp: 1 points: 40 targets: target-merenda: 100 target-saude: 100 target-urna: 100 entities: - id: w1/swarm/0 archetype: swarm wave: 1 - id: w1/swarm/1 archetype: swarm wave: 1 - id: w1/swarm/2 archetype: swarm wave: 1 - id: w1/swarm/3 archetype: swarm wave: 1 - id: w1/swarm/4 archetype: swarm wave: 1 - id: w1/swarm/5 archetype: swarm wave: 1 - id: w1/swarm/6 archetype: swarm wave: 1 - id: w1/swarm/7 archetype: swarm wave: 1 - id: w1/swarm/8 archetype: swarm wave: 1 - id: w1/swarm/9 archetype: swarm wave: 1 - id: w1/swarm/10 archetype: swarm wave: 1 - id: w1/swarm/11 archetype: swarm wave: 1 - id: w1/splitter/0 archetype: splitter wave: 1 - id: w1/splitter/1 archetype: splitter wave: 1 - id: w1/splitter/2 archetype: splitter wave: 1 - id: w2/swarm/0 archetype: swarm wave: 2 - id: w2/swarm/1 archetype: swarm wave: 2 - id: w2/swarm/2 archetype: swarm wave: 2 - id: w2/swarm/3 archetype: swarm wave: 2 - id: w2/swarm/4 archetype: swarm wave: 2 - id: w2/swarm/5 archetype: swarm wave: 2 - id: w2/swarm/6 archetype: swarm wave: 2 - id: w2/swarm/7 archetype: swarm wave: 2 - id: w2/splitter/0 archetype: splitter wave: 2 - id: w2/splitter/1 archetype: splitter wave: 2 - id: w2/splitter/2 archetype: splitter wave: 2 - id: w2/splitter/3 archetype: splitter wave: 2 - id: w2/splitter/4 archetype: splitter wave: 2 - id: w2/splitter/5 archetype: splitter wave: 2 - id: w2/stealer/0 archetype: stealer wave: 2 - id: w2/stealer/1 archetype: stealer wave: 2 - id: w2/stealer/2 archetype: stealer wave: 2 - id: w2/stealer/3 archetype: stealer wave: 2 - id: w3/swarm/0 archetype: swarm wave: 3 - id: w3/swarm/1 archetype: swarm wave: 3 - id: w3/swarm/2 archetype: swarm wave: 3 - id: w3/swarm/3 archetype: swarm wave: 3 - id: w3/swarm/4 archetype: swarm wave: 3 - id: w3/swarm/5 archetype: swarm wave: 3 - id: w3/swarm/6 archetype: swarm wave: 3 - id: w3/swarm/7 archetype: swarm wave: 3 - id: w3/tank/0 archetype: tank wave: 3 - id: w3/tank/1 archetype: tank wave: 3 - id: w3/tank/2 archetype: tank wave: 3 - id: w3/tank/3 archetype: tank wave: 3 - id: w3/splitter/0 archetype: splitter wave: 3 - id: w3/splitter/1 archetype: splitter wave: 3 - id: w3/splitter/2 archetype: splitter wave: 3 - id: w3/splitter/3 archetype: splitter wave: 3 - id: w3/splitter/4 archetype: splitter wave: 3 - id: w3/splitter/5 archetype: splitter wave: 3 - id: w3/stealer/0 archetype: stealer wave: 3 - id: w3/stealer/1 archetype: stealer wave: 3 - id: w3/stealer/2 archetype: stealer wave: 3 - id: w3/stealer/3 archetype: stealer wave: 3 - id: w3/stealer/4 archetype: stealer wave: 3 - id: w3/stealer/5 archetype: stealer wave: 3 fire_intervals_ms: - 2500 - 2000 - 1600 created_at: '2026-10-16T12:00:00Z' expires_at: '2026-10-16T12:30:00Z' state: active last_accepted_seq: 1 preview_combat: 50 progress: wave: 1 combat: 50 integrity: target-merenda: 100 target-saude: 100 target-urna: 100 last_accepted_seq: 1 last_elapsed_ms: 0 active_entities: w1/swarm/1: swarm w1/swarm/2: swarm w1/swarm/3: swarm w1/swarm/4: swarm w1/swarm/5: swarm w1/swarm/6: swarm w1/swarm/7: swarm w1/swarm/8: swarm w1/swarm/9: swarm w1/swarm/10: swarm w1/swarm/11: swarm w1/splitter/0: splitter w1/splitter/1: splitter w1/splitter/2: splitter result: null limits: batch_events: 256 journal_events: 16384 body_bytes: 131072 clock_lead_ms: 2000