> ## Documentation Index
> Fetch the complete documentation index at: https://docs.musterbox.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Submit a mutually-confirmed result

> Records a player's claim about how a match ended. The browser is never
the sole authority: the claim is stored only after its session token
validates to a real, unconsumed, non-expired match session bound to
this user and match.

A match finalizes only when **both** participants independently claim
the same outcome through their own sessions. The response reports the
reconciliation decision:

- `pending` — awaiting the opponent's claim,
- `replay` — mutual `DRAW` in a single-elimination match (match stays running),
- `game_advance` — claims agree and the bracket advanced,
- `game_advance_pending_finalization` — agree but bracket finalization deferred,
- `dispute` — claims conflict or outcome could not be mapped to a slot.

The `x-session-token` is consumed on a successful (status 2xx)
submission.




## OpenAPI

````yaml /openapi/musterbox-public.yaml post /api/v1/game-sdk/{gameId}/results
openapi: 3.0.3
info:
  title: MusterBox Public API
  version: 1.0.0
  description: |
    The public surface of the MusterBox developer platform. Every path in this
    document is verified against the MusterBox backend source code. The client
    SDK (`MusterBoxSdk`) is the recommended way to drive these endpoints; these
    pages document the wire contract for teams that need to understand or
    debug the traffic the SDK produces.

    Environments are selected by the base URL. The control-plane base URL per
    environment is:

    | Environment  | Base URL                          |
    | ------------ | --------------------------------- |
    | `local`      | `http://localhost:8081`           |
    | `sandbox`    | `https://sandbox-api.musterbox.dev` |
    | `staging`    | `https://staging-api.musterbox.dev` |
    | `production` | `https://api.musterbox.com`       |

    All paths below include the `api/v1` backend prefix explicitly, matching
    how the SDK resolves URLs (`control plane base` + `/api/v1`).

    > **Security boundary**: authentication, game sessions and results are the
    > only integration surfaces. Purchase endpoints exist for the on-device
    > PIN-verification flow and should always be driven through the SDK's
    > `PurchaseClient` — never hand-rolled. Do not roll your own purchase flow.
servers:
  - url: https://api.musterbox.com
    description: Production (default)
  - url: https://sandbox-api.musterbox.dev
    description: Sandbox — pre-production validation
  - url: https://staging-api.musterbox.dev
    description: Staging — pre-release integration environment
  - url: http://localhost:8081
    description: Local development backend
security:
  - bearerAuth: []
tags:
  - name: Authentication
    description: Player login, session refresh, and logout via the backend auth API
  - name: Game SDK
    description: >-
      Match sessions and mutually-confirmed results for games hosted by
      MusterBox
  - name: Secure purchases
    description: SDK-mediated PIN sessions for secure player purchases
paths:
  /api/v1/game-sdk/{gameId}/results:
    post:
      tags:
        - Game SDK
      summary: Submit a mutually-confirmed result
      description: >
        Records a player's claim about how a match ended. The browser is never

        the sole authority: the claim is stored only after its session token

        validates to a real, unconsumed, non-expired match session bound to

        this user and match.


        A match finalizes only when **both** participants independently claim

        the same outcome through their own sessions. The response reports the

        reconciliation decision:


        - `pending` — awaiting the opponent's claim,

        - `replay` — mutual `DRAW` in a single-elimination match (match stays
        running),

        - `game_advance` — claims agree and the bracket advanced,

        - `game_advance_pending_finalization` — agree but bracket finalization
        deferred,

        - `dispute` — claims conflict or outcome could not be mapped to a slot.


        The `x-session-token` is consumed on a successful (status 2xx)

        submission.
      operationId: submitGameResult
      parameters:
        - name: gameId
          in: path
          required: true
          description: The identifier of the game.
          schema:
            type: string
          example: my-arena-game
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - environment
                - matchId
                - outcome
              properties:
                environment:
                  $ref: '#/components/schemas/Environment'
                matchId:
                  type: integer
                  example: 1042
                outcome:
                  $ref: '#/components/schemas/GameOutcome'
                boardHash:
                  type: string
                  description: Optional integrity digest of the final board state.
                  example: b4c8f6d1ea3f0c26597da05a21c66f7e
      responses:
        '200':
          description: Claim recorded and reconciliation decision returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ResultDecision'
        '400':
          description: Invalid outcome value.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
        '401':
          description: >-
            Missing `x-game-key` or `x-session-token` header, or invalid player
            token.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
        '403':
          description: >-
            Credential rejected, session token consumed/expired/not matching
            this user and match.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
      security:
        - bearerAuth: []
        - gameKey: []
        - gameSessionToken: []
components:
  schemas:
    Environment:
      type: string
      enum:
        - local
        - sandbox
        - staging
        - production
      description: Backend environment the credential was created for.
    GameOutcome:
      type: string
      enum:
        - PLAYER_A_WIN
        - PLAYER_B_WIN
        - DRAW
    ResultDecision:
      type: object
      properties:
        decision:
          type: string
          enum:
            - pending
            - replay
            - game_advance
            - game_advance_pending_finalization
            - dispute
        matchId:
          type: integer
        tournamentId:
          type: string
          nullable: true
        winnerOpponent:
          type: integer
          enum:
            - 1
            - 2
          description: The opponent slot whose claimed outcome won, when determinable.
        reason:
          type: string
          description: Human-readable reconciliation reason.
        claimCount:
          type: integer
          description: Number of claims recorded so far for this match.
      required:
        - decision
        - matchId
        - reason
        - claimCount
    ApiError:
      type: object
      description: >-
        NestJS error envelope ({ error: { code, message, status } }) or the
        plain { message } shape.
      properties:
        error:
          type: object
          properties:
            code:
              type: string
            message:
              type: string
            status:
              type: integer
        message:
          type: string
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: Player access token returned by login/refresh.
    gameKey:
      type: apiKey
      in: header
      name: x-game-key
      description: >-
        Long-lived game credential bound to allowed origins. Public by design in
        the browser; keep it out of mobile app code.
    gameSessionToken:
      type: apiKey
      in: header
      name: x-session-token
      description: >-
        Single-use match session token returned by
        `/game-sdk/{gameId}/sessions`.

````