openapi: 3.1.0
info:
  title: Raise → Operator Wallet Callbacks (Proposed)
  version: 0.1.0
  description: >-
    Proposed operator-hosted wire contract implemented by the outbound client in PR #7. These are not
    Raise-hosted public routes. Payload fields, error codes, balance buckets, and signing headers are
    explicitly pending external confirmation. Gameplay wiring and ledger reconciliation are deferred. This
    document does not promise production readiness or exactly-once delivery.


    Raise retains requestId and transactionId across redelivery and signs each attempt with the current
    timestamp and configuration. A later caller invocation may retry an ambiguous delivery; there is no
    automatic retry loop, retry schedule, or Retry-After handling. Default timeout is 5000 ms including
    reading the body. Network failures and malformed success bodies are unknown/retryable; 401/403 can retry
    after fixing configuration; 408/429/5xx can retry. Other 4xx are stored as rejected. Malformed error
    bodies use an internal http_<status> fallback. Operators return only the wire schemas below, never
    internal WalletCallResult or local rollback wrappers.


    Signing: lowercase hex HMAC-SHA256(secret, UTF-8(timestampMs + "." + exactRawJsonBody)). The timestamp is
    epoch milliseconds. Do not reserialize JSON before verification. The method and path are not signed. No
    timestamp-age tolerance is defined by this implementation; agree replay protection before production
    integration.
    The production Raise factory serializes calls per operator using a database session lock. Competing
    calls return a retryable internal in-progress result without another HTTP request. Recovery reuses
    the original request ID after a lost owner; operators must deduplicate retries. Rollback identity
    must match the stored debit, and changed game IDs conflict with an existing idempotency identity.
  x-source-revision: 5ddb71d7c4d25ce823b47ff620a1a52dbbc25973
servers:
  - url: https://wallet.operator.example
    description: Illustrative operator-owned host; replace with the registered callback base URL.
security:
  - raiseSignature: []
    raiseTimestamp: []
tags:
  - name: Operator callbacks
    description: Proposed callbacks hosted by the operator and invoked by Raise.
paths:
  /wallet/authenticate:
    post:
      operationId: authenticateOperatorWallet
      summary: Authenticate a player
      tags:
        - Operator callbacks
      description: Proposed operator-hosted callback. Raise sends this request to the registered operator callback
        base URL; TheDash does not expose this HTTP route.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AuthenticateRequest"
      responses:
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Unauthorized"
        "408":
          $ref: "#/components/responses/Retryable"
        "429":
          $ref: "#/components/responses/Retryable"
        2XX:
          description: JSON matching this schema is required for every successful HTTP status. An empty 204 is not
            accepted by the client.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AuthenticateResponse"
        5XX:
          $ref: "#/components/responses/Retryable"
        4XX:
          $ref: "#/components/responses/Rejected"
  /wallet/balance:
    post:
      operationId: getOperatorWalletBalance
      summary: Read a player balance
      tags:
        - Operator callbacks
      description: Proposed operator-hosted callback. Raise sends this request to the registered operator callback
        base URL; TheDash does not expose this HTTP route.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BalanceRequest"
      responses:
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Unauthorized"
        "408":
          $ref: "#/components/responses/Retryable"
        "429":
          $ref: "#/components/responses/Retryable"
        2XX:
          description: JSON matching this schema is required for every successful HTTP status. An empty 204 is not
            accepted by the client.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BalanceResponse"
        5XX:
          $ref: "#/components/responses/Retryable"
        4XX:
          $ref: "#/components/responses/Rejected"
  /wallet/debit:
    post:
      operationId: debitOperatorWallet
      summary: Debit a player wallet
      tags:
        - Operator callbacks
      description: Proposed operator-hosted callback. Raise sends this request to the registered operator callback
        base URL; TheDash does not expose this HTTP route.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TransactionRequest"
      responses:
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Unauthorized"
        "408":
          $ref: "#/components/responses/Retryable"
        "429":
          $ref: "#/components/responses/Retryable"
        2XX:
          description: JSON matching this schema is required for every successful HTTP status. An empty 204 is not
            accepted by the client.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TransactionResponse"
        5XX:
          $ref: "#/components/responses/Retryable"
        4XX:
          $ref: "#/components/responses/Rejected"
  /wallet/credit:
    post:
      operationId: creditOperatorWallet
      summary: Credit a player wallet
      tags:
        - Operator callbacks
      description: Proposed operator-hosted callback. Raise sends this request to the registered operator callback
        base URL; TheDash does not expose this HTTP route.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TransactionRequest"
      responses:
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Unauthorized"
        "408":
          $ref: "#/components/responses/Retryable"
        "429":
          $ref: "#/components/responses/Retryable"
        2XX:
          description: JSON matching this schema is required for every successful HTTP status. An empty 204 is not
            accepted by the client.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TransactionResponse"
        5XX:
          $ref: "#/components/responses/Retryable"
        4XX:
          $ref: "#/components/responses/Rejected"
  /wallet/rollback:
    post:
      operationId: rollbackOperatorWalletDebit
      summary: Reverse a referenced debit
      tags:
        - Operator callbacks
      description: Proposed operator-hosted callback. Raise sends this request to the registered operator callback
        base URL; TheDash does not expose this HTTP route.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RollbackRequest"
      responses:
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Unauthorized"
        "408":
          $ref: "#/components/responses/Retryable"
        "429":
          $ref: "#/components/responses/Retryable"
        2XX:
          description: JSON matching this schema is required for every successful HTTP status. An empty 204 is not
            accepted by the client.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TransactionResponse"
        5XX:
          $ref: "#/components/responses/Retryable"
        4XX:
          $ref: "#/components/responses/Rejected"
  /wallet/round-end:
    post:
      operationId: endOperatorWalletRound
      summary: Notify the operator that a round ended
      tags:
        - Operator callbacks
      description: Proposed operator-hosted callback. Raise sends this request to the registered operator callback
        base URL; TheDash does not expose this HTTP route.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RoundEndRequest"
      responses:
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Unauthorized"
        "408":
          $ref: "#/components/responses/Retryable"
        "429":
          $ref: "#/components/responses/Retryable"
        2XX:
          description: JSON matching this schema is required for every successful HTTP status. An empty 204 is not
            accepted by the client.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RoundEndResponse"
        5XX:
          $ref: "#/components/responses/Retryable"
        4XX:
          $ref: "#/components/responses/Rejected"
components:
  securitySchemes:
    raiseSignature:
      type: apiKey
      in: header
      name: x-raise-signature
      description: Required lowercase hexadecimal HMAC-SHA256 signature over timestampMs + "." + exact raw JSON.
        Recomputed by Raise, never forwarded from an upstream provider.
    raiseTimestamp:
      type: apiKey
      in: header
      name: x-raise-timestamp
      description: Required positive safe-integer epoch-milliseconds timestamp encoded as a decimal string. Used
        with x-raise-signature, not as independent authentication.
  responses:
    Unauthorized:
      description: Signing/configuration failure; Raise records an unknown result that may retry after configuration
        is repaired.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    Retryable:
      description: Ambiguous or unavailable operator; Raise leaves the result unknown and allows a later invocation
        to retry.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    Rejected:
      description: Other 4xx responses are stored as rejected and replayed locally. Error codes are not an
        exhaustive enum.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
  schemas:
    Identifier:
      type: string
      minLength: 1
      maxLength: 255
    RequestId:
      type: string
      minLength: 1
      description: Raise operation ID. Retained when the same operation is redelivered.
    Currency:
      type: string
      pattern: ^[A-Z0-9]{3,10}$
    MinorAmount:
      type: integer
      minimum: 0
      maximum: 9007199254740991
      description: Nonnegative integer minor units within the JavaScript safe-integer range.
    Balance:
      type: object
      additionalProperties: false
      required:
        - totalMinor
      properties:
        totalMinor:
          $ref: "#/components/schemas/MinorAmount"
        cashMinor:
          $ref: "#/components/schemas/MinorAmount"
        bonusMinor:
          $ref: "#/components/schemas/MinorAmount"
      description: If cashMinor and bonusMinor are both supplied, their sum must equal totalMinor. Bucket semantics
        remain proposed.
    AuthenticateRequest:
      type: object
      additionalProperties: false
      required:
        - requestId
        - playerId
        - currency
      properties:
        requestId: &a1
          $ref: "#/components/schemas/RequestId"
        playerId: &a2
          $ref: "#/components/schemas/Identifier"
        currency: &a3
          $ref: "#/components/schemas/Currency"
        gameId:
          $ref: "#/components/schemas/Identifier"
    BalanceRequest:
      type: object
      additionalProperties: false
      required:
        - requestId
        - playerId
        - currency
      properties:
        requestId: *a1
        playerId: *a2
        currency: *a3
    TransactionRequest:
      type: object
      additionalProperties: false
      required:
        - requestId
        - transactionId
        - roundId
        - playerId
        - currency
        - amountMinor
      properties:
        requestId: *a1
        playerId: *a2
        currency: *a3
        transactionId:
          $ref: "#/components/schemas/Identifier"
        roundId:
          $ref: "#/components/schemas/Identifier"
        amountMinor:
          $ref: "#/components/schemas/MinorAmount"
        gameId:
          $ref: "#/components/schemas/Identifier"
    RollbackRequest:
      type: object
      additionalProperties: false
      required:
        - requestId
        - transactionId
        - playerId
        - currency
        - amountMinor
      properties:
        requestId: *a1
        playerId: *a2
        currency: *a3
        transactionId:
          $ref: "#/components/schemas/Identifier"
        roundId:
          $ref: "#/components/schemas/Identifier"
        amountMinor:
          $ref: "#/components/schemas/MinorAmount"
      description: transactionId identifies the original provider debit. amountMinor is required in the callback
        payload even when Raise derives it from a stored debit.
    RoundEndRequest:
      type: object
      additionalProperties: false
      required:
        - requestId
        - roundId
        - playerId
        - currency
      properties:
        requestId: *a1
        playerId: *a2
        currency: *a3
        roundId:
          $ref: "#/components/schemas/Identifier"
    AuthenticateResponse:
      type: object
      additionalProperties: false
      required:
        - playerId
        - currency
        - balance
      properties:
        playerId:
          $ref: "#/components/schemas/Identifier"
        currency:
          $ref: "#/components/schemas/Currency"
        balance:
          $ref: "#/components/schemas/Balance"
        sessionToken:
          type: string
          minLength: 1
    BalanceResponse:
      type: object
      additionalProperties: false
      required:
        - balance
      properties:
        balance:
          $ref: "#/components/schemas/Balance"
    TransactionResponse:
      type: object
      additionalProperties: false
      required:
        - transactionId
        - balance
      properties:
        transactionId:
          $ref: "#/components/schemas/Identifier"
        operatorTransactionId:
          $ref: "#/components/schemas/Identifier"
        balance:
          $ref: "#/components/schemas/Balance"
    RoundEndResponse:
      type: object
      additionalProperties: false
      required:
        - roundId
      properties:
        roundId:
          $ref: "#/components/schemas/Identifier"
        balance:
          $ref: "#/components/schemas/Balance"
    ErrorResponse:
      type: object
      additionalProperties: false
      required:
        - error
      properties:
        error:
          type: object
          additionalProperties: false
          required:
            - code
          properties:
            code:
              type: string
              pattern: ^[a-z0-9_]{1,64}$
            message:
              type: string
              maxLength: 2000
