openapi: 3.1.0
info:
  title: trade API ver. 1.0
  version: 1.0.0
  description: |
    trade API ver. 1.0. Separate from partner API ver. 1.0.

    A bot key places and cancels stake orders on the existing book. It does not
    amend an order in place, open subaccounts, or split a stake into outcome tokens.
    Cancel the unmatched remainder and place again.

    Base path `/trading/v1`. Call it from the bot, not from a browser page.
    Responses do not send CORS headers. A breaking change ships as `/trading/v2`.

    The key prefix is `tp_bot_`. A partner key `tp_live_` is rejected.
    `trade` places, takes, and cancels. `withdraw` sends only to addresses saved
    in the player cabinet. One right does not include the other.

    Money is a decimal string in USDC or USDT. Errors are `{ "code", "message" }`.
    Lists use a cursor and return at most 100 rows.

    After a dropped stream, read `GET /orders?open=true` and `GET /fills`, then quote again.
    `POST /heartbeat` arms a dead-man switch. If the bot stays silent longer than
    `timeout_seconds` (10–120, default 20), the platform cancels that key's unmatched
    orders while the bet window is still open. After `closes_at` the platform
    cancels unmatched stake itself and charges the cutoff fee. A matched fill cannot
    be cancelled. Replaying the same `idempotency_key` does not place a second order.
servers:
  - url: https://api.takepair.com
paths:
  /trading/v1/time:
    get:
      operationId: tradingTime
      summary: Server clock
      security: [{ botKey: [] }]
      responses:
        '200':
          description: Current server time
          content:
            application/json:
              schema:
                type: object
                required: [server_time]
                properties:
                  server_time: { type: string, format: date-time }
  /trading/v1/markets:
    get:
      operationId: tradingMarkets
      summary: Markets the bot may quote
      description: Default is markets open for bets and markets whose bet window just closed. Targeted markets are omitted.
      security: [{ tradeKey: [] }]
      parameters:
        - $ref: '#/components/parameters/locale'
        - $ref: '#/components/parameters/cursor'
        - $ref: '#/components/parameters/limit'
        - name: status
          in: query
          schema:
            type: string
            enum: [live, OPEN, BET_CUTOFF, LOCKED, RESOLVING, RESOLVED, CLOSED]
      responses:
        '200':
          description: Market cards, including the winning-side fee and cutoff rules
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MarketPage'
  /trading/v1/markets/{event_id}:
    get:
      operationId: tradingMarket
      security: [{ tradeKey: [] }]
      parameters:
        - $ref: '#/components/parameters/eventId'
        - $ref: '#/components/parameters/locale'
      responses:
        '200':
          description: One market
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Market'
  /trading/v1/markets/{event_id}/book:
    get:
      operationId: tradingBook
      summary: Resting orders, including other players, without their account ids
      security: [{ tradeKey: [] }]
      parameters:
        - $ref: '#/components/parameters/eventId'
        - $ref: '#/components/parameters/cursor'
        - $ref: '#/components/parameters/limit'
      responses:
        '200':
          description: Resting size on an open market. Own rows are marked is_own.
  /trading/v1/markets/{event_id}/position:
    get:
      operationId: tradingPosition
      summary: Matched and resting size per outcome, and the worst matched loss
      security: [{ tradeKey: [] }]
      parameters:
        - $ref: '#/components/parameters/eventId'
      responses:
        '200':
          description: Position for this account
  /trading/v1/markets/{event_id}/resolution:
    get:
      operationId: tradingResolution
      summary: Oracle prices, winner, winning-side fee, and payout on this account's matches
      security: [{ tradeKey: [] }]
      parameters:
        - $ref: '#/components/parameters/eventId'
      responses:
        '200':
          description: |
            `p_start` and `p_end` are the oracle prices. On a win, `payout` is
            `2 × matched stake − fee`. A loss pays `0`. The fee is the winning side's fee.
  /trading/v1/orders:
    get:
      operationId: tradingListOrders
      security: [{ tradeKey: [] }]
      parameters:
        - name: open
          in: query
          description: "true returns this account's still-resting bot orders. Use this after a stream drop."
          schema: { type: string, enum: ['true'] }
        - name: event_id
          in: query
          schema: { type: string, format: uuid }
        - $ref: '#/components/parameters/cursor'
        - $ref: '#/components/parameters/limit'
      responses:
        '200':
          description: Own orders
    post:
      operationId: tradingPlaceOrder
      summary: Rest a maker order on the book
      security: [{ tradeKey: [] }]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OrderRequest'
      responses:
        '201':
          description: The order. Replaying the same idempotency_key returns this order and does not place another.
    delete:
      operationId: tradingCancelAll
      summary: Cancel unmatched bot orders on one market, or on every market
      security: [{ tradeKey: [] }]
      parameters:
        - name: event_id
          in: query
          required: false
          schema: { type: string, format: uuid }
      responses:
        '200':
          description: How many unmatched orders were cancelled. Matched size is left in place.
  /trading/v1/orders/batch:
    post:
      operationId: tradingPlaceBatch
      summary: Place both outcomes in one call
      description: One or two orders. Each needs its own idempotency_key. The same key does not place a second stake.
      security: [{ tradeKey: [] }]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [orders]
              properties:
                orders:
                  type: array
                  minItems: 1
                  maxItems: 2
                  items:
                    $ref: '#/components/schemas/OrderRequest'
      responses:
        '201':
          description: Both orders accepted
        '400':
          description: The first order may already be on the book when `code` is `batch_partial`
  /trading/v1/orders/{order_id}:
    get:
      operationId: tradingGetOrder
      security: [{ tradeKey: [] }]
      parameters:
        - $ref: '#/components/parameters/orderId'
      responses:
        '200':
          description: Own order and its status
    delete:
      operationId: tradingCancelOrder
      summary: Cancel the unmatched remainder before closes_at
      description: The unmatched stake returns in full. The matched part stays. After closes_at this call is rejected.
      security: [{ tradeKey: [] }]
      parameters:
        - $ref: '#/components/parameters/orderId'
      responses:
        '204':
          description: Unmatched remainder cancelled
  /trading/v1/orders/{order_id}/take:
    post:
      operationId: tradingTakeOrder
      summary: Take another player's resting order, in full or in part
      security: [{ tradeKey: [] }]
      parameters:
        - $ref: '#/components/parameters/orderId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [idempotency_key]
              properties:
                stake_amount: { type: string, description: Omit to take the remaining size }
                idempotency_key: { type: string, minLength: 8 }
                counter_outcome_id: { type: string, format: uuid }
      responses:
        '201':
          description: Taker order, match id, and matched amount
  /trading/v1/fills:
    get:
      operationId: tradingFills
      summary: Recent matches for this account
      security: [{ tradeKey: [] }]
      parameters:
        - $ref: '#/components/parameters/cursor'
        - $ref: '#/components/parameters/limit'
      responses:
        '200':
          description: Own fills
  /trading/v1/heartbeat:
    post:
      operationId: tradingHeartbeat
      summary: Arm or refresh the dead-man switch
      security: [{ tradeKey: [] }]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                timeout_seconds: { type: integer, minimum: 10, maximum: 120 }
      responses:
        '201':
          description: Heartbeat armed
  /trading/v1/stream/book:
    get:
      operationId: tradingBookStream
      summary: Public book and market life
      description: |
        Server-sent events. `market.opened`, `market.bets_closed`, `market.resolved`,
        `order.created`, `order.matched`, `order.cancelled`, plus `ping`.
        Account ids are not included. Reconnect with `GET /markets` and `GET /markets/{id}/book`.
      security: [{ tradeKey: [] }]
      responses:
        '200':
          description: text/event-stream
  /trading/v1/stream/account:
    get:
      operationId: tradingAccountStream
      summary: Own fills, cancels, and market life
      description: |
        Server-sent events for this account only, plus market life.
        Reconnect with `GET /orders?open=true` and `GET /fills`.
      security: [{ tradeKey: [] }]
      responses:
        '200':
          description: text/event-stream
  /trading/v1/balances:
    get:
      operationId: tradingBalances
      summary: Available and locked USDC and USDT
      security: [{ botKey: [] }]
      responses:
        '200':
          description: Balances
  /trading/v1/deposit-addresses:
    get:
      operationId: tradingDepositAddresses
      summary: Deposit address and network. The bot sends coins itself.
      security: [{ botKey: [] }]
      responses:
        '200':
          description: Addresses
  /trading/v1/deposits:
    get:
      operationId: tradingDeposits
      summary: Credit status of deposits
      security: [{ botKey: [] }]
      parameters:
        - name: page
          in: query
          schema: { type: integer, minimum: 1 }
        - $ref: '#/components/parameters/limit'
      responses:
        '200':
          description: Deposits, including tx_hash and status
  /trading/v1/withdrawals/fee-estimate:
    get:
      operationId: tradingWithdrawalFee
      security: [{ withdrawKey: [] }]
      parameters:
        - name: currency
          in: query
          required: true
          schema: { type: string, enum: [USDC, USDT] }
        - name: network
          in: query
          required: true
          schema: { type: string }
        - name: amount
          in: query
          required: true
          schema: { type: string }
      responses:
        '200':
          description: Fee and total debit
  /trading/v1/withdrawals:
    get:
      operationId: tradingListWithdrawals
      security: [{ withdrawKey: [] }]
      responses:
        '200':
          description: Status and tx_hash
    post:
      operationId: tradingCreateWithdrawal
      summary: Withdraw to an address already saved in the cabinet
      security: [{ withdrawKey: [] }]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [currency, amount, network, destination, idempotency_key]
              properties:
                currency: { type: string, enum: [USDC, USDT] }
                amount: { type: string }
                network: { type: string }
                destination: { type: string }
                idempotency_key: { type: string, minLength: 8 }
      responses:
        '201':
          description: Withdrawal created, or the original one if the key is replayed
components:
  securitySchemes:
    botKey:
      type: http
      scheme: bearer
      description: Any active tp_bot_ key
    tradeKey:
      type: http
      scheme: bearer
      description: tp_bot_ key with the trade right
    withdrawKey:
      type: http
      scheme: bearer
      description: tp_bot_ key with the withdraw right
  parameters:
    eventId:
      name: event_id
      in: path
      required: true
      schema: { type: string, format: uuid }
    orderId:
      name: order_id
      in: path
      required: true
      schema: { type: string, format: uuid }
    cursor:
      name: cursor
      in: query
      schema: { type: string, format: date-time }
    limit:
      name: limit
      in: query
      schema: { type: integer, minimum: 1, maximum: 100 }
    locale:
      name: locale
      in: query
      schema: { type: string, enum: [en, ru, es, zh] }
  schemas:
    OrderRequest:
      type: object
      required: [event_id, outcome_id, side, currency, stake_amount, odds, idempotency_key]
      properties:
        event_id: { type: string, format: uuid }
        outcome_id: { type: string, format: uuid }
        side: { type: string, enum: [YES, NO] }
        currency: { type: string, enum: [USDC, USDT] }
        stake_amount: { type: string }
        odds: { type: string }
        idempotency_key: { type: string, minLength: 8 }
    Market:
      type: object
      required: [id, status, closes_at, outcomes, rules]
      properties:
        id: { type: string, format: uuid }
        slug: { type: string }
        title: { type: string }
        status: { type: string }
        symbol: { type: [string, 'null'] }
        opens_at: { type: [string, 'null'], format: date-time }
        closes_at: { type: [string, 'null'], format: date-time }
        resolves_at: { type: [string, 'null'], format: date-time }
        outcomes:
          type: array
          items:
            type: object
            properties:
              id: { type: string, format: uuid }
              code: { type: string }
        rules:
          type: object
          properties:
            win_fee_percent: { type: string }
            cancel_before_closes_at: { type: string }
            after_closes_at: { type: string }
            cutoff_cancel_fee_percent: { type: string }
            cutoff_gas_usd: { type: string }
            matched_cannot_cancel: { type: boolean }
    MarketPage:
      type: object
      required: [items, next_cursor]
      properties:
        items:
          type: array
          items:
            $ref: '#/components/schemas/Market'
        next_cursor: { type: [string, 'null'], format: date-time }
