openapi: 3.0.3
info:
  title: Spot Buyer API
  description: 'OpenAI-compatible inference and delivery tickets for buyers. The URL version is /v1. Breaking changes increment the path. Deprecated operations receive RFC 9745 Deprecation and RFC 8594 Sunset headers at least 90 days apart. Policy: https://aispotmarket.com/docs/api/versioning/.'
  license:
    name: MIT
  version: 1.0.0
servers:
- url: https://aispotmarket.com/buy/api
  description: Production Buyer API
paths:
  /v1/account:
    get:
      tags:
      - crate
      summary: Read the authenticated buyer's current ledger balance and reservations.
      description: Read the authenticated buyer's current ledger balance and reservations.
      operationId: get_account
      responses:
        '200':
          description: Current buyer account summary
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BuyerAccountSummary'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
      security:
      - bearer_buyer: []
  /v1/chat/completions:
    post:
      tags:
      - crate
      summary: OpenAI-compatible chat completion with an optional Spot `max_price` bid.
      description: |-
        OpenAI-compatible chat completion with an optional Spot `max_price` bid.

        Authenticate with `Authorization: Bearer <buyer_key>`. Set `max_price` to the
        most you will pay per 1,000 tokens. Base URL:
        `https://aispotmarket.com/buy/api`.
      operationId: chat_completions
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ChatCompletionsRequest'
        required: true
      responses:
        '200':
          description: OpenAI-compatible JSON completion, or an SSE token stream when `stream=true`
          headers:
            RateLimit:
              schema:
                type: string
              description: Remaining quota for the current window
            RateLimit-Policy:
              schema:
                type: string
              description: Advertised per-key quota policy
            X-RateLimit-Limit:
              schema:
                type: string
              description: Burst size
            X-RateLimit-Remaining:
              schema:
                type: string
              description: Requests left in the burst
            X-RateLimit-Reset:
              schema:
                type: string
              description: Seconds until the window refreshes
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ChatCompletionResponse'
            text/event-stream:
              schema:
                type: string
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '402':
          description: Nothing crossed at or under `max_price`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '429':
          description: Per-key rate limit exceeded
          headers:
            RateLimit:
              schema:
                type: string
              description: Remaining quota (0 when exhausted)
            RateLimit-Policy:
              schema:
                type: string
              description: Advertised per-key quota policy
            Retry-After:
              schema:
                type: string
              description: Seconds to wait before retrying
            X-RateLimit-Limit:
              schema:
                type: string
              description: Burst size
            X-RateLimit-Remaining:
              schema:
                type: string
              description: Requests left in the burst
            X-RateLimit-Reset:
              schema:
                type: string
              description: Seconds until the window refreshes
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
      security:
      - bearer_buyer: []
  /v1/orders:
    get:
      tags:
      - crate
      summary: Search all capacity orders owned by the authenticated buyer.
      description: Search all capacity orders owned by the authenticated buyer.
      operationId: list_orders
      parameters:
      - name: cursor
        in: query
        description: Cursor from the previous page
        required: false
        schema:
          type: string
          nullable: true
      - name: limit
        in: query
        description: Page size, 1-100
        required: false
        schema:
          type: integer
          nullable: true
          minimum: 0
      - name: q
        in: query
        description: Order id, ticket id, or instrument search
        required: false
        schema:
          type: string
          nullable: true
      - name: model_id
        in: query
        description: Exact instrument
        required: false
        schema:
          type: string
          nullable: true
      - name: region
        in: query
        description: Exact region
        required: false
        schema:
          type: string
          nullable: true
      - name: status
        in: query
        description: Order status
        required: false
        schema:
          type: string
          nullable: true
      responses:
        '200':
          description: Paginated order history
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ListOrdersResponse'
      security:
      - bearer_buyer: []
  /v1/orders/{id}:
    get:
      tags:
      - crate
      summary: Poll a capacity order. GTC rests return `open` plus this id.
      description: Poll a capacity order. GTC rests return `open` plus this id.
      operationId: get_order
      parameters:
      - name: id
        in: path
        description: Capacity order id
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Capacity order
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CapacityOrderResponse'
        '403':
          description: Not the owner
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '404':
          description: Unknown order
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
      security:
      - bearer_buyer: []
    delete:
      tags:
      - crate
      summary: Cancel the unfilled remainder of a resting buyer order.
      description: Cancel the unfilled remainder of a resting buyer order.
      operationId: cancel_order
      parameters:
      - name: id
        in: path
        description: Capacity order id
        required: true
        schema:
          type: string
      responses:
        '204':
          description: Order remainder cancelled
        '404':
          description: Open order not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
      security:
      - bearer_buyer: []
  /v1/tickets:
    get:
      tags:
      - crate
      summary: Search all delivery tickets owned by the authenticated buyer.
      description: Search all delivery tickets owned by the authenticated buyer.
      operationId: list_contracts
      parameters:
      - name: cursor
        in: query
        description: Cursor from the previous page
        required: false
        schema:
          type: string
          nullable: true
      - name: limit
        in: query
        description: Page size, 1-100
        required: false
        schema:
          type: integer
          nullable: true
          minimum: 0
      - name: q
        in: query
        description: Ticket, order, or instrument search
        required: false
        schema:
          type: string
          nullable: true
      - name: model_id
        in: query
        description: Exact instrument
        required: false
        schema:
          type: string
          nullable: true
      - name: region
        in: query
        description: Exact region
        required: false
        schema:
          type: string
          nullable: true
      - name: status
        in: query
        description: Ticket status
        required: false
        schema:
          type: string
          nullable: true
      responses:
        '200':
          description: Paginated ticket history; credentials are not returned
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ListTicketsResponse'
      security:
      - bearer_buyer: []
    post:
      tags:
      - crate
      summary: Buy a capacity ticket (take / set / market).
      description: |-
        Buy a capacity ticket (take / set / market).

        Authenticate with `Authorization: Bearer <buyer_key>`. Fill mints a
        delivery ticket (`sk-contract-…`). GTC returns immediately; poll
        `GET /v1/orders/{id}`.
      operationId: create_contract
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateContractRequest'
        required: true
      responses:
        '200':
          description: Capacity order (open, partial, or filled)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CapacityOrderResponse'
        '402':
          description: IOC/FOK did not fill
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
      security:
      - bearer_buyer: []
  /v1/tickets/{id}:
    get:
      tags:
      - crate
      summary: Read a delivery ticket the caller owns.
      description: Read a delivery ticket the caller owns.
      operationId: get_contract
      parameters:
      - name: id
        in: path
        description: Contract id
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Delivery ticket
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contract'
        '403':
          description: Not the owner
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '404':
          description: Unknown contract
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
      security:
      - bearer_buyer: []
  /v1/trades:
    get:
      tags:
      - crate
      summary: Search all executions owned by the authenticated buyer.
      description: Search all executions owned by the authenticated buyer.
      operationId: list_trades
      parameters:
      - name: cursor
        in: query
        description: Cursor from the previous page
        required: false
        schema:
          type: string
          nullable: true
      - name: limit
        in: query
        description: Page size, 1-100
        required: false
        schema:
          type: integer
          nullable: true
          minimum: 0
      - name: q
        in: query
        description: Trade, leg, or instrument search
        required: false
        schema:
          type: string
          nullable: true
      - name: model_id
        in: query
        description: Exact instrument
        required: false
        schema:
          type: string
          nullable: true
      - name: region
        in: query
        description: Exact region
        required: false
        schema:
          type: string
          nullable: true
      responses:
        '200':
          description: Paginated buyer execution history
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ListTradesResponse'
      security:
      - bearer_buyer: []
components:
  schemas:
    BuyerAccountSummary:
      type: object
      description: Current buyer ledger balance.
      required:
      - buyer_id
      - balance_units
      - pending_units
      - available_units
      properties:
        available_units:
          type: integer
          format: int64
          description: Integer settlement units not reserved by held or active paygo allocations.
        balance_units:
          type: integer
          format: int64
          description: Total integer settlement units on the account.
        buyer_id:
          type: string
          description: Stable buyer id.
        pending_units:
          type: integer
          format: int64
          description: Integer settlement units reserved by held or active paygo allocations.
    CapacityOrderResponse:
      type: object
      description: Response for `POST /v1/contracts` and `GET /v1/orders/{id}`.
      required:
      - order_id
      - status
      - instrument
      - quantity
      - filled
      properties:
        contract:
          allOf:
          - $ref: '#/components/schemas/Contract'
          nullable: true
        filled:
          type: integer
          format: int64
          description: Filled so far.
          minimum: 0
        instrument:
          type: string
          description: Instrument.
        order_id:
          type: string
          description: Buyer order id (the resting or completed bid).
        quantity:
          type: integer
          format: int64
          description: Requested size.
          minimum: 0
        status:
          $ref: '#/components/schemas/CapacityOrderStatus'
    CapacityOrderStatus:
      type: string
      description: Lifecycle of a capacity order.
      enum:
      - open
      - partial
      - filled
      - rejected
      - cancelled
    ChatChoice:
      type: object
      description: One choice in a chat completion response.
      required:
      - index
      - message
      properties:
        finish_reason:
          type: string
          description: Why generation stopped (for example `"stop"`).
          nullable: true
        index:
          type: integer
          format: int32
          description: Zero-based index among choices.
          minimum: 0
        message:
          $ref: '#/components/schemas/ChatCompletionResponseMessage'
    ChatCompletionResponse:
      type: object
      description: OpenAI-compatible chat completion response body.
      required:
      - id
      - object
      - created
      - model
      - choices
      properties:
        choices:
          type: array
          items:
            $ref: '#/components/schemas/ChatChoice'
          description: Generated choices.
        created:
          type: integer
          format: int64
          description: Unix timestamp (seconds).
        id:
          type: string
          description: Completion identifier.
        model:
          type: string
          description: Model that produced the completion.
        object:
          type: string
          description: Object type, typically `"chat.completion"`.
        usage:
          allOf:
          - $ref: '#/components/schemas/Usage'
          nullable: true
    ChatCompletionResponseMessage:
      type: object
      description: A single message in a chat completion.
      required:
      - role
      - content
      properties:
        content:
          type: string
          description: Message text.
        role:
          $ref: '#/components/schemas/ChatRole'
    ChatCompletionsRequest:
      type: object
      description: |-
        Buyer-facing chat completions request, including an optional `max_price` bid.

        When `max_price` is omitted it deserializes as `None` (no price cap at the
        type layer). Handler default behavior is defined by the gateway.
      required:
      - model
      - messages
      properties:
        max_price:
          type: string
          description: Maximum price per 1,000 tokens the buyer will pay, if any.
          nullable: true
        max_tokens:
          type: integer
          format: int32
          description: Maximum tokens to generate, if the client sent one.
          nullable: true
          minimum: 0
        messages:
          type: array
          items:
            $ref: '#/components/schemas/ChatMessage'
          description: Conversation so far.
        model:
          type: string
          description: Model identifier the buyer wants to run.
        stream:
          type: boolean
          description: When `true`, the seller should stream tokens (SSE).
          default: false
        temperature:
          type: number
          format: double
          description: Sampling temperature, if the client sent one.
          nullable: true
    ChatMessage:
      type: object
      description: |-
        A single chat message as sent by OpenAI-compatible clients.

        `role` is a free-form string so unknown roles do not fail deserialization.
      required:
      - role
      - content
      properties:
        content:
          type: string
          description: Message text.
        role:
          type: string
          description: Speaker role (`system`, `user`, `assistant`, …).
    ChatRole:
      type: string
      description: Role of a chat message, matching OpenAI's public names.
      enum:
      - system
      - user
      - assistant
    Contract:
      type: object
      description: Delivery ticket minted when a capacity order fills.
      required:
      - id
      - remaining
      - quantity
      - expires_at
      - delivery_start
      - lots
      properties:
        credential:
          type: string
          description: |-
            Bearer credential (`sk-contract-…`). Present when the caller is
            allowed to see it (create / owner GET).
          nullable: true
        delivery_start:
          type: string
          description: When delivery may begin (forwards may be in the future).
        expires_at:
          type: string
          description: When the ticket stops being deliverable.
        id:
          type: string
          description: Contract identifier.
        lots:
          type: array
          items:
            $ref: '#/components/schemas/ContractLot'
          description: Reserved lots (seller ids only — no endpoints).
        quantity:
          type: integer
          format: int64
          description: Original filled size.
          minimum: 0
        remaining:
          type: integer
          format: int64
          description: Tokens still deliverable.
          minimum: 0
    ContractLot:
      type: object
      description: One reserved seller lot on a delivery ticket.
      required:
      - id
      - seller_id
      - quantity
      - remaining
      - price
      properties:
        id:
          type: string
          description: Lot identifier.
        price:
          type: string
          description: Fill price per 1,000 tokens.
        quantity:
          type: integer
          format: int64
          description: Original lot size.
          minimum: 0
        remaining:
          type: integer
          format: int64
          description: Unburned tokens.
          minimum: 0
        seller_id:
          type: string
          description: Bound seller.
    ContractOrderType:
      type: string
      description: How the bid lifts or joins the book.
      enum:
      - take
      - set
      - limit
      - market
    ContractSplit:
      type: object
      description: Optional routing filters on a capacity buy.
      properties:
        privacy:
          type: string
          description: Privacy / data-handling class.
          nullable: true
        region:
          type: string
          description: Jurisdiction / region (`any` or omitted = sweep).
          nullable: true
        venue_pin:
          type: string
          description: Pin a specific venue / model id.
          nullable: true
    CreateContractRequest:
      type: object
      description: Body of `POST /v1/contracts`.
      required:
      - instrument
      - quantity
      properties:
        delivery:
          allOf:
          - $ref: '#/components/schemas/DeliverySpec'
          nullable: true
        instrument:
          type: string
          description: Instrument / model id.
        max_price:
          type: string
          description: Limit price per 1,000 tokens. Required except for `market` orders.
          nullable: true
        order:
          $ref: '#/components/schemas/ContractOrderType'
        quantity:
          type: integer
          format: int64
          description: Token quantity to buy.
          minimum: 0
        split:
          allOf:
          - $ref: '#/components/schemas/ContractSplit'
          nullable: true
        time_in_force:
          $ref: '#/components/schemas/TimeInForce'
        ttl_seconds:
          type: integer
          format: int64
          description: Delivery window after fill (spot tickets). Defaults to 86400.
          nullable: true
          minimum: 0
    DeliverySpec:
      oneOf:
      - type: string
        description: Named tenor (`spot`, `1d`, `1m`).
      - type: object
        description: Explicit delivery window.
        required:
        - start
        - end
        properties:
          end:
            $ref: '#/components/schemas/DateTime'
          start:
            $ref: '#/components/schemas/DateTime'
      description: |-
        Forward delivery specification.

        Untagged so the wire accepts `"1d"`, `"1m"`, `"spot"`, or
        `{"start":"…","end":"…"}`.
    ErrorBody:
      type: object
      required:
      - code
      - error
      properties:
        code:
          type: string
          description: Wire error code.
        error:
          type: string
          description: Human-readable detail.
    ListOrdersResponse:
      type: object
      description: Paginated buyer order history.
      required:
      - orders
      - page
      properties:
        orders:
          type: array
          items:
            $ref: '#/components/schemas/OrderSummary'
          description: Orders in reverse chronological order.
        page:
          $ref: '#/components/schemas/PageInfo'
    ListTicketsResponse:
      type: object
      description: Paginated buyer ticket history.
      required:
      - tickets
      - page
      properties:
        page:
          $ref: '#/components/schemas/PageInfo'
        tickets:
          type: array
          items:
            $ref: '#/components/schemas/TicketSummary'
          description: Tickets in reverse chronological order.
    ListTradesResponse:
      type: object
      description: Paginated execution history.
      required:
      - trades
      - page
      properties:
        page:
          $ref: '#/components/schemas/PageInfo'
        trades:
          type: array
          items:
            $ref: '#/components/schemas/TradeRecord'
          description: Executions in reverse chronological order.
    OrderSummary:
      type: object
      description: Buyer order row suitable for activity tables.
      required:
      - order_id
      - instrument
      - quantity
      - filled
      - max_price
      - status
      - created_at
      properties:
        contract_id:
          type: string
          description: Ticket id, when any quantity filled.
          nullable: true
        created_at:
          type: string
          description: Order acceptance time.
        filled:
          type: integer
          format: int64
          description: Filled tokens.
          minimum: 0
        instrument:
          type: string
          description: Instrument.
        max_price:
          type: string
          description: Limit price per 1,000 tokens.
        order_id:
          type: string
          description: Capacity order id.
        quantity:
          type: integer
          format: int64
          description: Requested tokens.
          minimum: 0
        region:
          type: string
          description: Requested region.
          nullable: true
        status:
          $ref: '#/components/schemas/CapacityOrderStatus'
    PageInfo:
      type: object
      description: Cursor metadata returned by list endpoints.
      properties:
        next_cursor:
          type: string
          description: Cursor for the next page, absent at the end.
          nullable: true
    TicketSummary:
      type: object
      description: Delivery ticket row without the bearer credential.
      required:
      - id
      - order_id
      - instrument
      - quantity
      - remaining
      - status
      - credential_fingerprint
      - delivery_start
      - expires_at
      - created_at
      properties:
        created_at:
          type: string
          description: Mint time.
        credential_fingerprint:
          type: string
          description: Non-secret display fingerprint.
        delivery_start:
          type: string
          description: Delivery start.
        expires_at:
          type: string
          description: Expiry.
        id:
          type: string
          description: Ticket id.
        instrument:
          type: string
          description: Instrument.
        order_id:
          type: string
          description: Originating capacity order.
        quantity:
          type: integer
          format: int64
          description: Filled token quantity.
          minimum: 0
        region:
          type: string
          description: Requested region.
          nullable: true
        remaining:
          type: integer
          format: int64
          description: Unspent tokens.
          minimum: 0
        status:
          type: string
          description: '`open`, `filled`, or `expired`.'
    TimeInForce:
      type: string
      description: Time-in-force for a capacity order.
      enum:
      - ioc
      - fok
      - gtc
    TradeRecord:
      type: object
      description: |-
        A single trade in a seller's history, projected for the dashboard.

        This is the read shape the seller dashboard consumes; the matching-engine
        brief is responsible for populating trade history from executed fills.
      required:
      - ask_id
      - bid_id
      - seller_id
      - buyer_id
      - model_id
      - price
      - tokens
      - executed_at
      properties:
        ask_id:
          type: string
          description: Ask side of the fill.
        bid_id:
          type: string
          description: Bid side of the fill.
        buyer_id:
          type: string
          description: Buyer participant.
        executed_at:
          type: string
          description: When the trade executed.
        model_id:
          type: string
          description: Model / instrument that traded.
        price:
          type: string
          description: Execution price (maker price) per 1000 tokens.
        region:
          type: string
          description: Seller capacity region.
        seller_id:
          type: string
          description: Seller participant.
        taker_side:
          type: string
          description: Incoming side that caused the execution.
        tokens:
          type: integer
          format: int64
          description: Filled token quantity.
          minimum: 0
        trade_id:
          type: string
          description: Stable execution identifier.
    Usage:
      type: object
      description: Token accounting on an OpenAI-compatible completion.
      required:
      - prompt_tokens
      - completion_tokens
      - total_tokens
      properties:
        completion_tokens:
          type: integer
          format: int32
          description: Tokens generated in the completion.
          minimum: 0
        prompt_tokens:
          type: integer
          format: int32
          description: Tokens consumed from the prompt.
          minimum: 0
        total_tokens:
          type: integer
          format: int32
          description: '`prompt_tokens + completion_tokens`.'
          minimum: 0
  securitySchemes:
    bearer_buyer:
      type: http
      scheme: bearer
    x_seller_api_key:
      type: apiKey
      in: header
      name: X-Seller-API-Key
