openapi: 3.1.0
info:
  title: Liquid Compute RFQ Customer and Guest API
  version: 0.0.1
  summary: Company-scoped customer history and single-use guest collaboration.
  description: |
    Contract for the manual Liquid Compute brokerage workspace. Authenticated
    customer authority comes only from the original Auth0 bearer resolved by
    lico-rfq-service through the canonical customer-session authority. Request
    bodies never select a company, seat, actor type, source party, or
    counterparty.

    Physical-settlement RFQs, quote groups, presentation responses and signing
    are available to every active company seat regardless of subscription plan.
    Eligibility documents and physical matching approval still apply.
    Cash-settled RFQ creation, quote-group mutations and presentation responses
    require the organization's effective Standard or Enterprise plan, including
    on idempotent replays. Otherwise they return 403 with code paid_plan_required.
    Historical reads and withdrawals remain available after a downgrade.

    Invitation routes are capability-bound and deliberately separate. Invalid,
    unknown, expired, revoked, consumed, and raced tokens return the same
    closed-link representation and never reveal seeded data. Technician sessions
    use the same redacted header with distinct request- and role-scoped authority.
    Standalone GPU tests create a technician session directly, without a deal,
    allocation, invitation, or customer account.

    Platform states, presentations, responses, and counterterms are
    non-binding. Liquid Compute remains between counterparties throughout.

    A Draft is read as it was saved. An intake saved as a Draft in a quote
    group may state a null service_window_start and, for a fixed term, a
    null tenor until it is submitted for review, and home lists it with
    status DRAFT; clients read both as not yet stated, never as invalid.
    These reads are part of version 0.0.1: the API is pre-release, so they
    arrive with the quote-group routes rather than as a new version.
servers:
  - url: https://api.liquidcompute.co
    description: Production base URL.
  - url: https://api.staging.liquidcompute.co
    description: Staging base URL.
security:
  - CustomerBearer: []
tags:
  - name: Machine access
    description: Rental access, registered SSH keys, connection files, and buyer confirmation.
  - name: Market data
    description: >-
      Customer-safe projections of desk quote flow and completed desk deals. No
      party, contact, location text, note, evidence or record id is ever
      included. Prices are USD per GPU-hour and appear only as $0.50 bands and
      bucket medians, only with the live desk-quotes grant, and only for cells
      that meet the privacy minimums.
  - name: Legal
    description: Onboarding documents and signing sessions.
  - name: Home
  - name: Intakes
  - name: Presentations
  - name: Deals
  - name: Positions
  - name: Finance
  - name: Documents
  - name: Contract review
    description: >-
      A company hands in a compute contract, Liquid Compute's lawyer annotates
      it, and the company reads the notes and replies. Mounted only where
      contract review is enabled.
  - name: Guest
  - name: Qualification
  - name: Machines
    description: >-
      A supplier company's machines: one record per machine, created by the
      company's own GPU tests and used by deal deliveries. Mounted where
      qualification routes are enabled; the customer app shows them as My
      machines.
  - name: Inference supply
    description: >-
      A company puts its machines to work for Liquid Compute with one switch
      per machine, under Liquid Compute's supply terms. Liquid Compute decides
      how a switched-on machine is used (today AI model serving: it picks the
      model, installs a pinned model server over the GCAT runner and registers
      the machine with its inference router as vetted supply) and pays a rate
      per GPU-hour of its own for every hour the machine serves and passes
      health checks, less a 2% fee, wired by staff. The machine view's work
      shows what the switch has it do. Mounted only where the inference
      router is configured.
  - name: Short-term compute
    description: >-
      Week-long GPU bookings between companies: a seller lists a tested block
      as an ask, a buyer books whole nodes of it for up to twelve weeks or
      leaves a bid that notifies or books itself when an ask fits, and each
      booking becomes a deal that names neither company to the other. These
      routes exist in every deployment; the customer app shows them as On
      demand. Taking money requires the
      deployment's payments configuration; without it, a route that takes
      money answers 503 payments_unavailable.
paths:
  '/v1/rfq/customer/deals/{dealId}/supplier-settlement':
    {
      'get':
        {
          'operationId': 'customerSupplierSettlement',
          'summary': 'Read supplier invoices and manual wire records',
          'description': 'Seller-only customer access. Customer responses omit private bank evidence and staff identities. Buyer billing is never included.',
          'parameters':
            [
              { '$ref': '#/components/parameters/DealId' },
              {
                'name': 'page',
                'in': 'query',
                'schema': { 'type': 'integer', 'minimum': 0, 'maximum': 1000000, 'default': 0 },
                'description': 'Zero-based page of ten invoices, newest first.',
              },
              {
                'name': 'invoice_id',
                'in': 'query',
                'schema': { 'type': 'string', 'format': 'uuid' },
                'description': 'Select one invoice for payment advice; requires page zero.',
              },
            ],
          'responses':
            {
              '200':
                {
                  'description': 'Supplier invoice records. Cache-Control: private, no-store.',
                  'content':
                    {
                      'application/json':
                        { 'schema': { '$ref': '#/components/schemas/SupplierState' } },
                    },
                },
              '400': { 'description': 'Invalid invoice, wire, date or evidence.' },
              '403': { 'description': 'Not authorized.' },
              '404': { 'description': 'Deal or supplier record not visible.' },
              '409': { 'description': 'Contract execution or correct payment state required.' },
              '412': { 'description': 'Stale ledger version; reload.' },
            },
        },
    }
  '/v1/rfq/customer/deals/{dealId}/settlement':
    {
      'get':
        {
          'operationId': 'customerRecurringSettlement',
          'summary': 'Read independent pages of monthly rental invoices, node credits and refunds',
          'parameters':
            [
              { '$ref': '#/components/parameters/DealId' },
              { '$ref': '#/components/parameters/InvoicePage' },
              {
                'name': 'invoice_id',
                'in': 'query',
                'schema': { 'type': 'string', 'format': 'uuid' },
                'description': 'Select one invoice by ID for PDF download; requires invoice_page zero. An invoice outside this deal returns an empty invoices array.',
              },
              { '$ref': '#/components/parameters/ClaimPage' },
              { '$ref': '#/components/parameters/RefundPage' },
            ],
          'responses':
            {
              '200':
                {
                  'description': 'Buyer-scoped history; bank receipt and private monitoring evidence are staff-only. Each history array is limited to ten records. Balances, unpaid totals, earliest due date and next month cover the entire ledger. Credit application details are omitted from these list projections. Cache-Control: private, no-store.',
                  'content':
                    {
                      'application/json':
                        { 'schema': { '$ref': '#/components/schemas/RecurringState' } },
                    },
                },
              '400':
                { 'description': 'Invalid month, fee, evidence, deadline or ledger operation.' },
              '403': { 'description': 'Human administrator required for staff operations.' },
              '404': { 'description': 'Deal not visible to this buyer.' },
              '409': { 'description': 'Executed terms or accepted machines required.' },
              '412': { 'description': 'Stale ledger version; reload before retry.' },
            },
        },
    }
  /v1/rfq/customer/deals/{dealId}/prepayments:
    parameters:
      - $ref: '#/components/parameters/DealId'
    get:
      parameters:
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Cursor'
      operationId: listCustomerPrepayments
      summary: List private buyer prepayment invoices
      tags:
        - Deals
      responses:
        '200':
          description: 'Private invoice records; Cache-Control: private, no-store.'
          content:
            application/json:
              schema:
                type: object
                required:
                  - items
                  - current
                  - awaiting_funds
                  - next_cursor
                properties:
                  next_cursor: { type: [string, 'null'] }
                  awaiting_funds: { type: boolean }
                  current:
                    anyOf:
                      - $ref: '#/components/schemas/PrepaymentInvoice'
                      - { type: 'null' }
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/PrepaymentInvoice'
        '400':
          description: BadRequest
        '401':
          description: Unauthorized
        '404':
          description: NotFound
        '409':
          description: Conflict
        '412':
          description: PreconditionFailed
  /v1/rfq/customer/deals/{dealId}/prepayments/{invoiceId}:
    parameters:
      - $ref: '#/components/parameters/DealId'
      - name: invoiceId
        in: path
        required: true
        schema: { type: string, format: uuid }
    get:
      operationId: getCustomerPrepayment
      summary: Read one private prepayment invoice
      responses:
        '200':
          description: 'Private invoice; Cache-Control: private, no-store.'
          content:
            application/json:
              schema: { $ref: '#/components/schemas/PrepaymentInvoice' }
        '400': { description: Invalid identifier }
        '401': { description: Unauthorized }
        '403': { description: Forbidden }
        '404': { description: Invoice not found }

  /v1/rfq/customer/hedge-requests:
    get:
      operationId: listCustomerHedgeRequests
      summary: List original inquiries for the authenticated seat and organization
      parameters:
        - name: limit
          in: query
          schema:
            type: integer
            default: 25
            minimum: 1
            maximum: 100
        - $ref: '#/components/parameters/Cursor'
      responses:
        '200':
          description: Customer-safe original snapshots; includes closed records.
          content:
            application/json:
              schema:
                $ref: hedge.openapi.yaml#/components/schemas/CustomerHedgeRequestPage
        '401': { $ref: '#/components/responses/Unauthorized' }
        '503': { description: Intake disabled or dependency unavailable. }
    post:
      operationId: createCustomerHedgeRequest
      summary: Send an illustrative hedge inquiry
      description: Maximum body 32 KiB. Returns only a receipt. Matching
        scope/key/body replays for seven days before creation gates or quotas;
        expired keys remain permanent tombstones. No account creation, email
        verification or trade execution.
      parameters:
        - name: Idempotency-Key
          in: header
          required: true
          schema:
            type: string
            pattern: ^[a-f0-9]{64}$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: hedge.openapi.yaml#/components/schemas/CreateHedgeRequest
      responses:
        '200':
          description: Previously accepted receipt; no duplicate writes or notifications.
          content:
            application/json:
              schema:
                $ref: hedge.openapi.yaml#/components/schemas/HedgeReceipt
        '201':
          description: Inquiry durably accepted.
          content:
            application/json:
              schema:
                $ref: hedge.openapi.yaml#/components/schemas/HedgeReceipt
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '409': { $ref: '#/components/responses/Conflict' }
        '429':
          description: Successful-create quota exceeded.
          headers:
            Retry-After:
              schema:
                type: integer
                minimum: 1
        '503': { description: Intake disabled or dependency unavailable. }
  /v1/rfq/customer/hedge-requests/{hedgeRequestId}:
    parameters:
      - name: hedgeRequestId
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      operationId: getCustomerHedgeRequest
      summary: Read the original inquiry for this exact seat and organization
      responses:
        '200':
          description: No staff notes, assignments or working terms.
          content:
            application/json:
              schema:
                $ref: hedge.openapi.yaml#/components/schemas/CustomerHedgeRequest
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404':
          $ref: '#/components/responses/NotFound'
  /v1/rfq/customer/intake-readiness:
    get:
      operationId: getCustomerIntakeReadiness
      tags: [Intakes]
      summary: Check the signed-in customer's company and matching contact setup
      description: >-
        Advisory, read-only check using the authenticated company and email.
        Missing and inactive records share a setup-required state. Intake creation
        independently rechecks eligibility; a READY response never authorizes a write.
      responses:
        '200':
          description: Current intake setup readiness; private and never cached
          content:
            application/json:
              schema:
                type: object
                additionalProperties: false
                required: [status]
                properties:
                  status:
                    type: string
                    enum: [READY, COMPANY_SETUP_REQUIRED, CONTACT_SETUP_REQUIRED]
                  onboarding_ready:
                    type: boolean
                    description: True only when the company, matching contact and account access are ready. This observation does not authorize a write.
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '500':
          description: Readiness could not be checked
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ErrorEnvelope' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /v1/rfq/customer/forward-pricing:
    get:
      operationId: getPhysicalForwardPricing
      summary: Read physical delivery quotes and a separate research-based forward estimate
      description: >-
        Requires an active customer seat and live grants for the exact index and
        desk-quotes. Only explicitly approved, revision-current, unexpired physical
        RFQ publications participate. Products preserve region, hardware, size,
        interconnect, service, payment and price-inclusion terms. Each selected
        product has separate monthly bid/offer observations and exact UTC delivery
        strips. Gaps remain null; no midpoint or interpolated prices are supplied.
        Source dates are preserved. Quantities count quoted GPUs, not executable depth.
        The independent estimate field contains a dated strip-residual model when
        research coverage is available. It never supplies bid/offer observations,
        inventory or RFQ publication approval. The model vintage is preserved;
        months outside its original coverage remain null.
      parameters:
        - name: index_id
          in: query
          required: true
          schema: { type: string, enum: [a100-us, h100-us, h200-us, b200-us, b300-us] }
        - name: product_id
          in: query
          schema: { type: string, pattern: '^[a-f0-9]{64}$' }
          description: Full product-definition SHA256. Omit to select an available approved product.
      responses:
        '200':
          description: Approved product inventory, selected quotes and a separate nullable model estimate. Empty inventory yields null pricing even when an estimate exists.
          headers:
            Cache-Control: { schema: { const: 'private, no-store' } }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ForwardPricing' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /v1/rfq/customer/market/quote-flow:
    get:
      operationId: getDeskQuoteFlow
      tags: [Market data]
      summary: Read desk quote flow for one chip
      description: >-
        Available to every active customer seat. Counts every physical desk
        quote past Draft whose quoted date falls in the window, by side, term
        bucket and region; alternative offers of one capacity pool count once
        in each count. Quote flow is history, so a quote counts on its quoted
        date even once its commercial expiry has passed. Month-to-month terms
        count as under three months.
        Regions come only from a structured delivery country; free-text
        locations are UNKNOWN. Sizes are rounded to whole nodes (8 GPUs per node
        unless the quote recorded another node size) and dates are to the day.
        Only quotes priced per GPU-hour or node-hour enter price statistics.
        A term bucket shows a median, and its quotes show bands, only when it
        holds at least 3 priced quotes from at least 2 companies, alternative
        offers of one capacity pool being one quote at the best of their
        prices; medians and bands also need the live desk-quotes grant, reread
        on every request.
        The curve's relative shape is shown to everyone once both the bucket
        and the first bucket with quotes meet the minimum.
      parameters:
        - $ref: '#/components/parameters/DeskChip'
        - name: window
          in: query
          description: Whole UTC days ending today, written like 30d. Defaults to 30d.
          schema: { $ref: '#/components/schemas/DeskWindow' }
      responses:
        '200':
          description: Quote flow for the chip and window; private and never cached.
          headers:
            Cache-Control: { schema: { const: 'private, no-store' } }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/DeskQuoteFlow' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /v1/rfq/customer/market/order-book:
    get:
      operationId: getDeskOrderBook
      tags: [Market data]
      summary: Read the desk's live book for one chip as a grid
      description: >-
        Available to every active customer seat. Places every Active physical
        desk quote on the chip that has a start date, a commercial expiry, if
        it states one, still to come and GPUs still wanted or left to offer,
        under the staff order book's capacity rule, in a row per
        start month from the current UTC month and a cell per term bucket. A
        quote rests in its start month; a month-to-month offer that has already
        started rests in the current month. Any other quote whose start month
        has passed is left off until the desk reconfirms it, and one that starts
        after the last row is only counted in later. Rows run from 6 to 12
        months. Sizes are rounded to whole nodes, and alternative offers of one
        capacity pool count once. A cell shows a median only when it holds at
        least 3 priced quotes from at least 2 companies, alternative offers of
        one capacity pool being one quote at the best of their prices, and only
        with the live desk-quotes grant, reread on every request.
      parameters:
        - $ref: '#/components/parameters/DeskChip'
      responses:
        '200':
          description: The chip's live book as a grid; private and never cached.
          headers:
            Cache-Control: { schema: { const: 'private, no-store' } }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/DeskOrderBook' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /v1/rfq/customer/market/order-book/orders:
    get:
      operationId: listDeskBookOrders
      tags: [Market data]
      summary: List one side of the desk's live book for one chip
      description: >-
        Available to every active customer seat. Lists the quotes
        getDeskOrderBook places on one side, ordered by the price the caller
        can see, best first (the lowest offer, the highest bid): the exact price
        with the desk-quote-prices grant, otherwise the band's floor. Ties, and
        quotes without a shown price after them, follow by start month, term,
        size and quote date; a caller without the desk-quotes grant gets no
        price ordering at all. A quote shows a band only with the live
        desk-quotes grant and a cell that meets the privacy minimum, and its
        exact price per GPU-hour only with desk-quote-prices as well. Prices
        are decided over the whole book before the page is cut.
      parameters:
        - $ref: '#/components/parameters/DeskChip'
        - name: side
          in: query
          required: true
          description: SELL for supply offers, BUY for buy requests.
          schema: { type: string, enum: [SELL, BUY] }
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/DeskLimit'
      responses:
        '200':
          description: One page of one side of the chip's live book; private and never cached.
          headers:
            Cache-Control: { schema: { const: 'private, no-store' } }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/DeskBookOrders' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /v1/rfq/customer/market/deals:
    get:
      operationId: getDeskDeals
      tags: [Market data]
      summary: Read our completed desk deals for one chip
      description: >-
        Available to every active customer seat. Lists physical desk deals in
        CONTRACTED or a later stage whose latest agreed-terms round was
        recorded in the window, newest first, a page at a time, from that
        round's terms. Sizes are rounded to whole nodes of 8 GPUs and dates to
        the month. A deal shows a band only with the live desk-quotes grant, a
        price per GPU-hour or node-hour, and at least 2 priced deals in its term
        bucket over the whole window with no one company a party to all of
        them; its exact price per GPU-hour also needs the live
        desk-quote-prices grant.
      parameters:
        - $ref: '#/components/parameters/DeskChip'
        - name: window
          in: query
          description: Whole UTC days ending today, written like 90d. Defaults to 90d.
          schema: { $ref: '#/components/schemas/DeskWindow' }
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/DeskLimit'
      responses:
        '200':
          description: Completed deals for the chip and window; private and never cached.
          headers:
            Cache-Control: { schema: { const: 'private, no-store' } }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/DeskDeals' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /v1/rfq/customer/market/deal-candles:
    get:
      operationId: getDeskDealCandles
      tags: [Market data]
      summary: Read our completed desk deals for one chip as monthly candles
      description: >-
        Available to every active customer seat. Groups the deals getDeskDeals
        lists by the UTC month their latest agreed-terms round was recorded, for
        the 12 calendar months ending with the current one, every month
        included. Every caller sees each month's deal count and GPUs at node
        precision. A month shows prices only when it holds at least 3 deals
        priced per GPU-hour or node-hour with no one company a party to all of
        them: its median and its range of whole $0.50 bands need the live
        desk-quotes grant, and its exact open, high, low and close need
        desk-quote-prices as well.
      parameters:
        - $ref: '#/components/parameters/DeskChip'
      responses:
        '200':
          description: Twelve monthly candles for the chip; private and never cached.
          headers:
            Cache-Control: { schema: { const: 'private, no-store' } }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/DeskDealCandles' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /v1/rfq/customer/access/keys:
    get:
      tags: [Machine access]
      operationId: listMachineSSHKeys
      summary: List the current seat's registered SSH public keys
      description: Keys belong to the authenticated seat and organization; other seats cannot list or use them.
      responses:
        '200':
          description: Registered keys that have not been revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MachineSSHKeyList'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
    post:
      tags: [Machine access]
      operationId: registerMachineSSHKey
      summary: Register an SSH public key once for future rentals
      description: Duplicate registration returns the same active key. Up to ten keys may be registered per seat and organization. Private keys are never accepted.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              required: [public_key]
              properties:
                public_key:
                  type: string
                  minLength: 1
                  maxLength: 16384
      responses:
        '201':
          description: Registered SSH key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MachineSSHKey'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/access/keys/{keyId}/revocation:
    parameters:
      - name: keyId
        in: path
        required: true
        schema:
          $ref: '#/components/schemas/Uuid'
    post:
      tags: [Machine access]
      operationId: revokeMachineSSHKey
      summary: Revoke one of the current seat's SSH keys
      description: Prevents new connections and closes this seat's affected relay connections within sixty seconds. Other seats retain access. Re-registering a public key creates a new key identity and never revives revoked credentials.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
      responses:
        '200':
          description: Key revoked.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: false
                required: [revoked]
                properties:
                  revoked:
                    type: boolean
                    const: true
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/deals/{dealId}/access:
    parameters:
      - $ref: '#/components/parameters/DealId'
    get:
      tags:
        - Machine access
      operationId: listBuyerMachineAccess
      summary: List machine access for a physical deal
      description:
        Available only when machine access is enabled. Access follows the allocation delivery
        window, not commercial completion. Unknown and unauthorized buyer deals return the same not-found
        response.
      responses:
        '200':
          description: Current machine access state.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MachineAccessList'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '403':
          $ref: '#/components/responses/Forbidden'
  /v1/rfq/customer/allocations/{allocationId}/access/credentials:
    parameters:
      - $ref: '#/components/parameters/AllocationId'
    get:
      tags: [Machine access]
      operationId: listMachineAccessCredentials
      summary: Retrieve this seat's existing SSH connection files
      description: Requires current buyer membership and visibility of the deal. Unavailable access returns no connection files. Credentials are scoped to the current inventory revision and registered key.
      responses:
        '200':
          description: Current connection files for this seat.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: false
                required: [items]
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/AccessCredential'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
    post:
      tags:
        - Machine access
      operationId: issueMachineAccessCredential
      summary: Prepare reusable SSH connection files for a registered key
      description:
        Requires an active buyer organization seat and available allocation access. Certificates
        remain valid through delivery end. Existing authorized sessions survive routine credential expiry;
        rental expiry, suspension, key revocation, and membership removal close connections. The original bearer is never retained.
        Returned connection files are installed once for the rental. Reusing the same key returns the existing
        setup for the current inventory revision. Supplying a public key registers it for this seat.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/IssueAccessCredentialRequest'
      responses:
        '201':
          description: Issued credentials and connection files.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AccessCredential'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '403':
          $ref: '#/components/responses/Forbidden'
  /v1/rfq/customer/allocations/{allocationId}/access/acceptances:
    parameters:
      - $ref: '#/components/parameters/AllocationId'
    post:
      tags:
        - Machine access
      operationId: acceptMachines
      summary: Accept delivery of specific ready machines
      description: Records the buyer's scoped commercial acceptance of connection-checked,
        currently reachable machines, distinct from connection confirmation. Commencement is
        the acceptance time or the delivery start if later. Idempotent; accepting an already
        accepted machine is a no-op. The order quantity is never changed.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AcceptMachinesRequest'
      responses:
        '200':
          description: Current machine access state.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MachineAccess'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '412':
          description: The allocation record changed; re-read before accepting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '403':
          $ref: '#/components/responses/Forbidden'
  /v1/rfq/customer/allocations/{allocationId}/access/confirmation:
    parameters:
      - $ref: '#/components/parameters/AllocationId'
    post:
      tags:
        - Machine access
      operationId: confirmMachineAccess
      summary: Confirm buyer access to the current machine revision
      description:
        Records the authenticated seat and confirmation time. A stale inventory revision is rejected;
        credential issuance is not confirmation.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ConfirmMachineAccessRequest'
      responses:
        '200':
          description: Current machine access state.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MachineAccess'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '403':
          $ref: '#/components/responses/Forbidden'
  /v1/rfq/customer/machines:
    get:
      tags: [Machines]
      operationId: listMachines
      summary: List the company's machines
      description: |
        Every machine record the company owns, newest first: machines its own
        GPU tests registered once their brief check passed, and machines deal
        deliveries recorded, which carry no observed facts until a test runs
        on them. Mounted where qualification routes are enabled. status is
        derived on the server from the machine's current job, its open GPU
        test and the GPU test result that decides its qualification.
      responses:
        '200':
          description: The company's machines.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/MachineList' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /v1/rfq/customer/machines/{id}:
    parameters:
      - { name: id, in: path, required: true, schema: { $ref: '#/components/schemas/Uuid' } }
    get:
      tags: [Machines]
      operationId: getMachine
      summary: Read one of the company's machines
      responses:
        '200':
          description: The machine.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Machine' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
    patch:
      tags: [Machines]
      operationId: renameMachine
      summary: Rename one of the company's machines
      description: The label is the company's own; it changes nothing else about the machine and later GPU tests keep it.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              required: [name]
              properties:
                name:
                  type: string
                  description: Trimmed; 1 to 64 characters without control characters.
      responses:
        '200':
          description: The renamed machine.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Machine' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /v1/rfq/customer/qualification/profiles:
    get:
      tags: [Qualification]
      operationId: listOwnedQualificationProfiles
      summary: List the GPU test configurations a company can run on its own machines
      description: The same approved standalone profiles as GET /v1/rfq/guest/qualification/profiles.
      responses:
        '200':
          description: Available configurations. An empty list means none are enabled.
          content:
            application/json:
              schema:
                type: object
                required: [items]
                properties:
                  items:
                    type: array
                    items: { $ref: '#/components/schemas/QualificationProfileOption' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /v1/rfq/customer/qualification/tests:
    get:
      tags: [Qualification]
      operationId: listOwnedQualificationTests
      summary: List the company's own GPU tests, newest first
      description: |
        A company's own tests are the same standalone tests the hosted GPU
        test MCP server drives: the company's ownership, not a technician
        session token, authorizes every step. At most the 50 most recent.
      responses:
        '200':
          description: The company's tests.
          content:
            application/json:
              schema:
                type: object
                required: [items]
                properties:
                  items:
                    type: array
                    items: { $ref: '#/components/schemas/OwnedQualificationTestSummary' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
    post:
      tags: [Qualification]
      operationId: createOwnedQualificationTest
      summary: Start a GPU test of the company's own machines
      description: |
        Requires one approved profile: machines join only through the
        authorized_keys line and the host list, so installer pairing is not
        offered. A company holds at most 3 tests open and starts at most 10 in
        24 hours; past either limit the answer is 409 quota_exceeded. Cancelling
        a test that has not started frees its open slot. When a
        listed machine passes its brief check, the company's machine record
        at that address and port is created or refreshed (GET
        /v1/rfq/customer/machines); another company's machine at the same address
        is left untouched.
        No replay receipt; these are the technician routes, and the test's state guards a repeated call.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              required: [profile_id, profile_version]
              properties:
                profile_id: { type: string }
                profile_version: { type: string }
      responses:
        '201':
          description: The new test. No session token is issued.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/OwnedQualificationTest' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '409': { $ref: '#/components/responses/Conflict' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /v1/rfq/customer/qualification/tests/{testId}:
    parameters:
      - $ref: '#/components/parameters/OwnedTestId'
    get:
      tags: [Qualification]
      operationId: getOwnedQualificationTest
      summary: Read the company's test and its machines' progress
      description: |
        The technician view of GET /v1/rfq/guest/qualification/request. Each
        machine that has passed a brief check also carries machine, the
        company's machine record at its endpoint_ip and ssh_port. When
        another company registered that address and port first, the machine
        instead carries attention with failure_class
        REGISTERED_TO_ANOTHER_COMPANY and a message that names no company,
        unless it already needs attention for another reason. Another
        company's test and an unknown one are 404. Attempt reports stay
        readable for as long as the company owns the test.
      responses:
        '200':
          description: The test.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/QualificationTechnicianRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /v1/rfq/customer/qualification/tests/{testId}/setup-instructions:
    parameters:
      - $ref: '#/components/parameters/OwnedTestId'
    get:
      tags: [Qualification]
      operationId: getOwnedQualificationSetupInstructions
      summary: Read the authorized_keys line and setup steps for the test's machines
      responses:
        '200':
          description: Setup instructions, as the technician view returns them.
          content:
            application/json:
              schema: { type: object }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /v1/rfq/customer/qualification/tests/{testId}/host-lists:
    parameters:
      - $ref: '#/components/parameters/OwnedTestId'
    post:
      tags: [Qualification]
      operationId: enrollOwnedQualificationHostList
      summary: Add machines to the test as user@address lines
      description: |
        Enrolls every listed machine at once, or none, and dispatches each
        machine's brief connection check. access_confirmed must be true: it is
        the company's confirmation that the authorized_keys line is in place
        and that it may give Liquid Compute SSH access to these machines.
        A refused list returns 400 invalid_host_list with every line marked.
        No replay receipt; these are the technician routes, and the test's state guards a repeated call.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              required: [hosts, access_confirmed]
              properties:
                hosts:
                  {
                    type: string,
                    description: 'One user@address[:port] per line; # starts a comment.',
                  }
                access_confirmed: { type: boolean }
                start_testing_when_ready: { type: boolean }
      responses:
        '200':
          description: The enrolled lines.
          content:
            application/json:
              schema:
                type: object
                required: [results]
                properties:
                  results:
                    type: array
                    items:
                      type: object
                      properties:
                        line_number: { type: integer }
                        text: { type: string }
                        username: { type: string }
                        endpoint_ip: { type: string }
                        ssh_port: { type: integer }
                        enrollment_id: { $ref: '#/components/schemas/Uuid' }
                        error: { type: string }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /v1/rfq/customer/qualification/tests/{testId}/enrollments/{id}/connection-checks:
    parameters:
      - $ref: '#/components/parameters/OwnedTestId'
      - { name: id, in: path, required: true, schema: { $ref: '#/components/schemas/Uuid' } }
    post:
      tags: [Qualification]
      operationId: retryOwnedQualificationConnectionCheck
      summary: Run a machine's brief connection check again
      description: No replay receipt; these are the technician routes, and the test's state guards a repeated call.
      responses:
        '201':
          description: The dispatched check.
          content:
            application/json:
              schema: { type: object }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '429': { $ref: '#/components/responses/RateLimited' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /v1/rfq/customer/qualification/tests/{testId}/enrollments/{id}/brief-check-consent:
    parameters:
      - $ref: '#/components/parameters/OwnedTestId'
      - { name: id, in: path, required: true, schema: { $ref: '#/components/schemas/Uuid' } }
    post:
      tags: [Qualification]
      operationId: renewOwnedQualificationBriefCheckConsent
      summary: Renew the consent that lets Liquid Compute run a machine's brief check
      description: No replay receipt; these are the technician routes, and the test's state guards a repeated call.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              required: [consent]
              properties:
                consent: { type: boolean }
      responses:
        '200':
          description: The machine's progress.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/QualificationMachineProgress' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /v1/rfq/customer/qualification/tests/{testId}/start:
    parameters:
      - $ref: '#/components/parameters/OwnedTestId'
    post:
      tags: [Qualification]
      operationId: startOwnedQualificationTest
      summary: Start the test as soon as possible over its ready machines
      description: |
        Arms the earliest window over every machine that has passed its brief
        check. Both attestations must be true: the machines are reserved for
        this test with their GPUs idle until it finishes, and the company is
        authorized to put them under full load. 409 not_ready when no machine
        is ready yet.
        No replay receipt; these are the technician routes, and the test's state guards a repeated call.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              required: [exclusive_window_attested, capacity_authority_confirmed]
              properties:
                exclusive_window_attested: { type: boolean }
                capacity_authority_confirmed: { type: boolean }
      responses:
        '201':
          description: The armed attempt.
          content:
            application/json:
              schema: { type: object }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /v1/rfq/customer/qualification/tests/{testId}/cancel:
    parameters:
      - $ref: '#/components/parameters/OwnedTestId'
    post:
      tags: [Qualification]
      operationId: cancelOwnedQualificationTest
      summary: Cancel the test while nothing runs on it
      description: |
        Cancels the company's test so it no longer counts toward its 3 open
        tests. Its machines can no longer be added, checked or started, and
        the test's own view closes; machine records its checks created, their
        qualifications and the reports of attempts it already ran stay. 409
        while an attempt is armed, running or stopping (stop it first), and
        for a completed test. Cancelling a cancelled test answers it again.
        No replay receipt; these are the technician routes, and the test's state guards a repeated call.
      responses:
        '200':
          description: The cancelled test.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/OwnedQualificationTestSummary' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /v1/rfq/customer/qualification/tests/{testId}/attempts/{id}/stop:
    parameters:
      - $ref: '#/components/parameters/OwnedTestId'
      - { name: id, in: path, required: true, schema: { $ref: '#/components/schemas/Uuid' } }
    post:
      tags: [Qualification]
      operationId: stopOwnedQualificationAttempt
      summary: Stop the test's attempt
      description: As POST /v1/rfq/guest/qualification/attempts/{id}/stop, for the company's own test. No replay receipt; these are the technician routes, and the test's state guards a repeated call.
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/QualificationRecoveryRequest' }
      responses:
        '200':
          description: Current closure facts.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/QualificationAttemptClosureStatus' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /v1/rfq/customer/qualification/tests/{testId}/attempts/{id}/report:
    parameters:
      - $ref: '#/components/parameters/OwnedTestId'
      - { name: id, in: path, required: true, schema: { $ref: '#/components/schemas/Uuid' } }
    get:
      tags: [Qualification]
      operationId: readOwnedQualificationAttemptReport
      summary: Read a finished attempt's report
      description: As GET /v1/rfq/guest/qualification/attempts/{id}/report, for the company's own test. Readable for as long as the company owns the test, so the result a machine's qualification names can always be opened.
      responses:
        '200':
          description: Recipient-safe report.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/QualificationAttemptReport' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /v1/rfq/customer/qualification/tests/{testId}/attempts/{id}/test-share:
    parameters:
      - $ref: '#/components/parameters/OwnedTestId'
      - { name: id, in: path, required: true, schema: { $ref: '#/components/schemas/Uuid' } }
    get:
      tags: [Qualification]
      operationId: getOwnedQualificationReportShare
      summary: Read the active report link's metadata
      responses:
        '200':
          description: Active share metadata, null when there is none.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/StandaloneReportShareResponse' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
    post:
      tags: [Qualification]
      operationId: createOwnedQualificationReportShare
      summary: Publish a recipient-safe snapshot of a finished attempt
      description: As POST /v1/rfq/guest/qualification/attempts/{id}/test-share, for the company's own test. No replay receipt; these are the technician routes, and the test's state guards a repeated call.
      requestBody:
        required: true
        content:
          application/json:
            schema: { type: object, additionalProperties: false }
      responses:
        '201':
          description: Saved share metadata and one-time capability URL.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/StandaloneReportShareResponse' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /v1/rfq/customer/qualification/tests/{testId}/attempts/{id}/test-share/revoke:
    parameters:
      - $ref: '#/components/parameters/OwnedTestId'
      - { name: id, in: path, required: true, schema: { $ref: '#/components/schemas/Uuid' } }
    post:
      tags: [Qualification]
      operationId: revokeOwnedQualificationReportShare
      summary: Revoke the attempt's active report link
      description: No replay receipt; these are the technician routes, and the test's state guards a repeated call.
      requestBody:
        required: true
        content:
          application/json:
            schema: { type: object, additionalProperties: false }
      responses:
        '200':
          description: Share is null.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/StandaloneReportShareResponse' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /v1/rfq/guest/qualification/profiles:
    get:
      tags: [Qualification]
      operationId: listStandaloneQualificationProfiles
      summary: List supported one-off GPU test configurations
      description: |
        Public, read-only discovery of the profiles approved in this environment
        and eligible for a standalone test. Each configuration targets one machine
        with no inter-node testing. GPU model and count must match exactly;
        the runner verifies the remaining hardware and software prerequisites.
        Uses the same catalogue and eligibility rules as pairing confirmation.
        Evaluation-only CPU profiles appear only in local development.
        Does not create a session, authorize a machine, or expose test results.
      security: []
      responses:
        '200':
          description: Available standalone configurations. An empty list means none are enabled.
          headers:
            Cache-Control: { $ref: '#/components/headers/NoStore' }
            Referrer-Policy: { $ref: '#/components/headers/NoReferrer' }
            X-Robots-Tag: { $ref: '#/components/headers/NoIndex' }
          content:
            application/json:
              schema:
                type: object
                required: [items]
                properties:
                  items:
                    type: array
                    items: { $ref: '#/components/schemas/QualificationProfileOption' }
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/guest/qualification/tests:
    post:
      tags: [Qualification]
      operationId: createStandaloneQualificationTest
      summary: Start a standalone GPU test as its technician
      description: |
        Creates a standalone qualification request and a request-scoped TECHNICIAN
        session. No deal, allocation, invitation, customer account, or guest token
        is required. The request has eligibility_rule STANDALONE and defaults to
        one machine. A request naming one of the approved single-node profiles
        from GET /v1/rfq/guest/qualification/profiles pins it, so the machine can
        join through the authorized_keys line and POST /qualification/host-lists.
        Otherwise pairing confirmation selects the profile from the detected
        hardware. Existing machine setup, pairing,
        maintenance-window approval, execution, and cleanup operations use the
        returned session; creating the session does not authorize GPU workloads.
        The session expires 24 hours after creation, which is also the request's
        results deadline.

        Browser clients use POST /api/qualification/tests. That BFF stores
        session_token in an HttpOnly session cookie and excludes the token from
        its response. Keep the session token out of URLs, logs, and audit metadata.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              description: Empty, or both profile fields of one approved single-node profile.
              properties:
                profile_id: { type: string }
                profile_version: { type: string }
              dependentRequired:
                profile_id: [profile_version]
                profile_version: [profile_id]
            examples:
              installer: { value: {} }
              authorized_keys:
                { value: { profile_id: gpu-a10-24gb-pcie-1gpu, profile_version: v0.1.0 } }
      responses:
        '201':
          description: Standalone request and technician session created.
          content:
            application/json:
              schema:
                allOf:
                  - { $ref: '#/components/schemas/QualificationTechnicianSession' }
                  - type: object
                    properties:
                      role: { type: string, const: TECHNICIAN }
          headers:
            Cache-Control: { $ref: '#/components/headers/NoStore' }
            Referrer-Policy: { $ref: '#/components/headers/NoReferrer' }
            X-Robots-Tag: { $ref: '#/components/headers/NoIndex' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '429': { $ref: '#/components/responses/RateLimited' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /v1/rfq/guest/qualification/sessions:
    post:
      tags: [Qualification]
      operationId: createQualificationTechnicianSession
      summary: Exchange an invitation capability for a request- and role-scoped session
      description:
        Invoked only after the recipient explicitly continues from the review page. Opening or previewing
        a link never creates a session. All delivery channels use the same capability without requiring email verification.
        An empty body or empty JSON object exchanges the capability directly; challenge_id and code remain supported
        together for older clients. Sessions last up to twelve hours, bounded by invitation expiry. Protected operations
        recheck revocation, replacement, expiry, request state and role. The browser BFF stores session_token in a
        secure HttpOnly cookie and excludes it from the browser response. The invitation fragment is forwarded only
        in X-RFQ-Guest-Token, never in server request URLs, logs or audit metadata.
      requestBody:
        required: false
        content:
          application/json:
            schema: { $ref: '#/components/schemas/CreateQualificationTechnicianSessionRequest' }
      responses:
        '201':
          description: Scoped session created; no verified email identity is asserted.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/QualificationTechnicianSession' }
          headers:
            Cache-Control: { $ref: '#/components/headers/NoStore' }
            Referrer-Policy: { $ref: '#/components/headers/NoReferrer' }
            X-Robots-Tag: { $ref: '#/components/headers/NoIndex' }
        '200':
          description: Invalid, expired, revoked or replaced capability returns the neutral closed representation.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/GuestInvitationClosed' }
          headers:
            Cache-Control: { $ref: '#/components/headers/NoStore' }
            Referrer-Policy: { $ref: '#/components/headers/NoReferrer' }
            X-Robots-Tag: { $ref: '#/components/headers/NoIndex' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '409': { $ref: '#/components/responses/Conflict' }
        '429': { $ref: '#/components/responses/RateLimited' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
      security:
        - GuestCapability: []
  /v1/rfq/guest/qualification/request:
    get:
      tags: [Qualification]
      operationId: getQualificationTechnicianRequest
      summary: Read the current technician's request and machine progress
      description: Requires the technician session in X-RFQ-Guest-Token, created by
        an invitation exchange or standalone test. Machine attention, attempt
        closure and window approval are derived from current facts.
      security: [{ TechnicianSession: [] }]
      responses:
        '200':
          description: Request view, or the neutral CLOSED body for a closed session.
          content:
            application/json:
              schema:
                oneOf:
                  - { $ref: '#/components/schemas/QualificationTechnicianRequest' }
                  - { $ref: '#/components/schemas/GuestInvitationClosed' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /v1/rfq/guest/qualification/test-report:
    get:
      tags: [Qualification]
      operationId: downloadStandaloneQualificationTestReport
      summary: Download the current results of a standalone GPU test
      description: |
        Requires the standalone test's TECHNICIAN session in X-RFQ-Guest-Token.
        Returns the same technician-scoped request view as GET
        /v1/rfq/guest/qualification/request as a JSON attachment, including current
        machine, attempt, execution, and cleanup status. A report downloaded before the
        test finishes contains the outcomes observed so far; hardware outcome,
        execution completeness, and cleanup are independent facts.
      security: [{ TechnicianSession: [] }]
      responses:
        '200':
          description: Current standalone test report, or the neutral CLOSED body for a closed session.
          content:
            application/json:
              schema:
                oneOf:
                  - { $ref: '#/components/schemas/QualificationTechnicianRequest' }
                  - { $ref: '#/components/schemas/GuestInvitationClosed' }
          headers:
            Content-Disposition:
              description: Present for a report response; prompts the browser to download the JSON file.
              schema: { type: string, const: 'attachment; filename="gpu-test-report.json"' }
            Cache-Control: { $ref: '#/components/headers/NoStore' }
            Referrer-Policy: { $ref: '#/components/headers/NoReferrer' }
            X-Robots-Tag: { $ref: '#/components/headers/NoIndex' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /v1/rfq/guest/qualification/attempts/{id}/report:
    parameters:
      - { name: id, in: path, required: true, schema: { $ref: '#/components/schemas/Uuid' } }
    get:
      tags: [Qualification]
      operationId: readQualificationAttemptReport
      summary: Read a finished attempt's recipient-safe report through its test session
      description: |
        Available to both invited and standalone sessions in X-RFQ-Guest-Token.
        The attempt must belong to that session's request and be COMPLETED,
        INCOMPLETE or CANCELLED. Completed requests remain readable until the
        session or invitation expires or is revoked. Returns the same sanitized
        report fields as a shared report, without creating a link or sending a
        message. Machine addresses, credentials and setup commands are excluded.
      security: [{ TechnicianSession: [] }]
      responses:
        '200':
          description: Recipient-safe report, or the neutral CLOSED body for a closed session.
          content:
            application/json:
              schema:
                oneOf:
                  - { $ref: '#/components/schemas/QualificationAttemptReport' }
                  - { $ref: '#/components/schemas/GuestInvitationClosed' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /v1/rfq/guest/qualification/attempts/{id}/test-share:
    parameters:
      - { name: id, in: path, required: true, schema: { $ref: '#/components/schemas/Uuid' } }
    get:
      tags: [Qualification]
      operationId: getStandaloneQualificationReportShare
      summary: Read the active share's metadata for an owned standalone test
      description: Requires the standalone technician session. Completed requests remain
        readable until session expiry. The raw URL is never returned by this read.
      security: [{ TechnicianSession: [] }]
      responses:
        '200':
          description: Active share metadata, null when no active share exists, or the neutral CLOSED body.
          content:
            application/json:
              schema:
                oneOf:
                  - { $ref: '#/components/schemas/StandaloneReportShareResponse' }
                  - { $ref: '#/components/schemas/GuestInvitationClosed' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
    post:
      tags: [Qualification]
      operationId: createStandaloneQualificationReportShare
      summary: Publish a recipient-safe snapshot of a finished standalone test
      description: |
        Requires the owning standalone TECHNICIAN session in X-RFQ-Guest-Token,
        including after request completion, until session expiry. Only COMPLETED,
        INCOMPLETE and CANCELLED attempts can be shared. Replaces any previous link
        for this attempt. The returned URL contains a fragment credential and is
        returned once; only its digest is stored. Anyone with it may read the saved
        projection through GET /v1/rfq/guest/qualification/report until revocation or
        expiry (30 days by default), independently of owner-session expiry. The
        snapshot excludes machine addresses, SSH credentials and setup commands;
        later attempt or closure changes do not modify it. No email is sent.
      security: [{ TechnicianSession: [] }]
      requestBody:
        required: true
        content:
          application/json:
            schema: { type: object, additionalProperties: false }
      responses:
        '201':
          description: Saved share metadata and one-time capability URL.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/StandaloneReportShareResponse' }
        '200':
          description: Neutral response for a closed session.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/GuestInvitationClosed' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /v1/rfq/guest/qualification/attempts/{id}/test-share/revoke:
    parameters:
      - { name: id, in: path, required: true, schema: { $ref: '#/components/schemas/Uuid' } }
    post:
      tags: [Qualification]
      operationId: revokeStandaloneQualificationReportShare
      summary: Revoke the active report link for an owned standalone test
      description: Requires the owning standalone technician session. Idempotently
        closes the current link; copies already saved by recipients cannot be revoked.
      security: [{ TechnicianSession: [] }]
      requestBody:
        required: true
        content:
          application/json:
            schema: { type: object, additionalProperties: false }
      responses:
        '200':
          description: Share is null, or the neutral CLOSED body for a closed session.
          content:
            application/json:
              schema:
                oneOf:
                  - { $ref: '#/components/schemas/StandaloneReportShareResponse' }
                  - { $ref: '#/components/schemas/GuestInvitationClosed' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /v1/rfq/guest/qualification/attempts/{id}/stop:
    parameters:
      - { name: id, in: path, required: true, schema: { $ref: '#/components/schemas/Uuid' } }
    post:
      tags: [Qualification]
      operationId: stopQualificationAttemptAsTechnician
      summary: Stop the current technician's scoped attempt
      description: Uses the technician session in X-RFQ-Guest-Token. Requests closure;
        reservations remain blocked until workload, cleanup, sessions and grants are verified.
      security: [{ TechnicianSession: [] }]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/QualificationRecoveryRequest' }
      responses:
        '200':
          description: Current closure facts, or the neutral CLOSED body for a closed session.
          content:
            application/json:
              schema:
                oneOf:
                  - { $ref: '#/components/schemas/QualificationAttemptClosureStatus' }
                  - { $ref: '#/components/schemas/GuestInvitationClosed' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /v1/rfq/guest/qualification/enrollments/{id}/replacement:
    parameters:
      - { name: id, in: path, required: true, schema: { $ref: '#/components/schemas/Uuid' } }
    post:
      tags: [Qualification]
      operationId: replaceQualificationEnrollment
      summary: Record a lost machine before pairing its replacement
      description: Uses the technician session in X-RFQ-Guest-Token. Marks only an
        enrollment belonging to this request LOST and releases its slot and endpoint.
        Active reservations must first close; the replacement needs a new pairing.
      security: [{ TechnicianSession: [] }]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/QualificationRecoveryRequest' }
      responses:
        '200':
          description: Updated machine progress, or the neutral CLOSED body for a closed session.
          content:
            application/json:
              schema:
                oneOf:
                  - { $ref: '#/components/schemas/QualificationMachineProgress' }
                  - { $ref: '#/components/schemas/GuestInvitationClosed' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
  /v1/rfq/customer/legal-status:
    get:
      tags: [Legal]
      operationId: getCustomerLegalStatus
      summary: Report the company's onboarding documents and issue the next one that is due
      description: >-
        Every read issues an MNDA when none is effective or open. ECP attestation is not required
        for physical compute and is not issued by this endpoint. Existing attestation records
        remain visible. An effective MNDA establishes physical standing for RFQ submission
        and order-form signing. No staff meeting or call approval is required.
      responses:
        '200':
          description: The company's standing on the attestation and the MNDA.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LegalStatus'
        '409':
          $ref: '#/components/responses/Conflict'
  /v1/rfq/customer/signature-requests:
    get:
      tags: [Legal]
      operationId: listCustomerSignatureRequests
      summary: List one page of the company signature requests, newest issued first
      description: All rows remain scoped to the authenticated company. History includes current requests. Use the same deal_id and current filters when following next_cursor.
      parameters:
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Cursor'
        - name: deal_id
          in: query
          description: Restrict results to order forms addressed to this company for one deal.
          schema: { $ref: '#/components/schemas/Uuid' }
        - name: current
          in: query
          description: Return only the latest revision per company, document kind, subject and deal side, including a terminal latest revision. The result is still paginated.
          schema: { type: boolean, default: false }
      responses:
        '200':
          description: One company-scoped page. next_cursor is null when no later page exists.
          content:
            application/json:
              schema:
                type: object
                required: [items, next_cursor]
                properties:
                  items:
                    type: array
                    maxItems: 200
                    items: { $ref: '#/components/schemas/SignatureRequest' }
                  next_cursor: { $ref: '#/components/schemas/PageCursor' }
                additionalProperties: false
        '400':
          description: Invalid deal identifier, current filter, limit or cursor.
  /v1/rfq/customer/signature-requests/{requestId}:
    parameters:
      - name: requestId
        in: path
        required: true
        schema:
          $ref: '#/components/schemas/Uuid'
    get:
      tags: [Legal]
      operationId: getCustomerSignatureRequest
      summary: Read one of the company's signature requests
      responses:
        '200':
          description: The request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SignatureRequest'
        '404':
          $ref: '#/components/responses/NotFound'
  /v1/rfq/customer/signature-requests/{requestId}/signing-session:
    parameters:
      - name: requestId
        in: path
        required: true
        schema:
          $ref: '#/components/schemas/Uuid'
    post:
      tags: [Legal]
      operationId: mintCustomerSigningSession
      summary: Mint the signing URL for the current user on a request
      description: >-
        Any user of the company may sign. For an envelope kind the first click creates the envelope
        with this user as the recipient; later clicks by the same user mint a fresh view on it. A
        click by another user while a session is open answers `signing_in_progress` naming the
        signer. For the attestation the body carries one or more unique ECP qualifications and any required
        details. The Click agreement page is keyed on the request revision and the user's seat; an agreement Click reports already agreed
        completes the request and returns no URL. URLs are short-lived and never stored, so the call
        takes no Idempotency-Key. No replay receipt is stored.
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                qualifications:
                  type: array
                  minItems: 1
                  maxItems: 18
                  description: Required for an attestation; not accepted for envelope signing. Categories must be unique.
                  items: { $ref: '#/components/schemas/ECPQualification' }
              additionalProperties: false
      responses:
        '200':
          description: The signing URL to open in the signing panel, or none when the attestation was already agreed.
          content:
            application/json:
              schema:
                type: object
                required: [url, request_id]
                properties:
                  url: { type: [string, 'null'], format: uri }
                  request_id: { $ref: '#/components/schemas/Uuid' }
                  status: { $ref: '#/components/schemas/SignatureRequestStatus' }
                additionalProperties: false
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: >-
            `signing_in_progress`, `awaiting_liquidcompute`, `not_an_envelope`, `legal_prerequisites_missing`
            (an order form while the MNDA is not effective, with `blocking` naming MNDA), `signer_identity_unavailable`, or `conflict`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '503':
          description: >-
            DocuSign did not answer; the request is back to READY and the call may be retried.
            If qualifications changed, error.signature_request_id identifies the committed
            replacement. Retry against that ID; the original request is now VOIDED.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
  /v1/rfq/customer/signature-requests/{requestId}/confirm:
    parameters:
      - name: requestId
        in: path
        required: true
        schema:
          $ref: '#/components/schemas/Uuid'
    post:
      tags: [Legal]
      operationId: confirmCustomerAttestation
      summary: Read the attestation's Click agreement from the return page and apply it
      description: >-
        No replay receipt is stored. Each call reads the current Click agreement and applies its
        state under the signature request lock; repeated confirmation preserves the resolved state.
      responses:
        '200':
          description: The request after Click's status was applied; unchanged while the agreement is pending.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SignatureRequest'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
  /v1/rfq/customer/signature-requests/{requestId}/decline:
    parameters:
      - name: requestId
        in: path
        required: true
        schema:
          $ref: '#/components/schemas/Uuid'
    post:
      tags: [Legal]
      operationId: declineCustomerSignatureRequest
      summary: Decline an envelope request with a reason
      description: >-
        DocuSign's embedded view offers no decline, so this is the platform's. Any envelope is voided
        with DocuSign first, and refused with `envelope_completed` if DocuSign reports it already
        completed; then the request is DECLINED with the reason for staff to see.
        No replay receipt is stored. The request is locked and must still be open; a repeat decline
        returns conflict after the first transition. Reload the request after an uncertain response.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [reason]
              properties:
                reason: { type: string, minLength: 1, maxLength: 1000 }
              additionalProperties: false
      responses:
        '200':
          description: The declined request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SignatureRequest'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: >-
            `legal_prerequisites_missing` with `blocking` naming the onboarding documents still due, when
            RFQ_SIGNATURE_GATES_ENABLED is on and the company's MNDA is
            not effective for an order form; otherwise `conflict`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
  /v1/rfq/customer/signature-requests/{requestId}/restart:
    parameters:
      - name: requestId
        in: path
        required: true
        schema:
          $ref: '#/components/schemas/Uuid'
    post:
      tags: [Legal]
      operationId: restartCustomerSignatureRequest
      summary: Abandon the current envelope and get a fresh request for the same document
      description: >-
        Allowed while the company's own signature is outstanding; refused with `already_signed` once
        it is on the envelope. The current request is VOIDED and a new READY request supersedes it.
        Every existing envelope is durably reserved and its void confirmed with DocuSign first.
        A remotely completed envelope is preserved. No replay receipt is stored: the request state
        guards repeats, so retries of a reserved restart return the same replacement once available.
        Uncertain provider outcomes retain the reservation for recovery.
      responses:
        '201':
          description: The fresh request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SignatureRequest'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: >-
            `legal_prerequisites_missing` with `blocking` naming the onboarding documents still due, when
            RFQ_SIGNATURE_GATES_ENABLED is on and the company's MNDA is
            not effective for an order form; otherwise `conflict`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
  /v1/rfq/customer/home:
    get:
      tags: [Home]
      operationId: getCustomerHome
      summary: Get company-wide pending and recent work
      responses:
        '200':
          description: Action-first summary for the caller's company.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Home'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/activity:
    get:
      tags: [Home]
      operationId: listCustomerActivity
      summary: List published company activity
      parameters:
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Company-scoped activity page.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ActivityPage'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/activity/{activityId}/resolve:
    parameters:
      - { $ref: '#/components/parameters/ActivityId' }
    post:
      tags: [Home]
      operationId: resolveCustomerActivity
      summary: Resolve an open request for the caller's company
      description: Appends a company-wide resolution without changing the RFQ lifecycle, deal phase, or published request text. Only intake and deal requests may be resolved; another company's activity returns 404. Repeated resolution retains the original actor, time, and reason.
      parameters:
        - { $ref: '#/components/parameters/IdempotencyKey' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ResolveActivityRequest' }
      responses:
        '200':
          description: Resolved request, including on replay.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Activity' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
  /v1/rfq/customer/activity/{activityId}/read:
    parameters:
      - $ref: '#/components/parameters/ActivityId'
    patch:
      tags: [Home]
      operationId: markCustomerActivityRead
      summary: Mark one activity item read for the active seat
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      responses:
        '200':
          description: Read marker created or replayed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Activity'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/intakes:
    get:
      tags: [Intakes]
      operationId: listCustomerIntakes
      summary: List all company physical and cash submissions
      parameters:
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Limit'
        - name: settlement_type
          in: query
          schema:
            $ref: '#/components/schemas/SettlementType'
        - name: lifecycle
          in: query
          schema: { type: string, enum: [DRAFT, ACTIVE, ARCHIVED] }
      responses:
        '200':
          description: Company-scoped intake history.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IntakePage'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
    post:
      tags: [Intakes]
      operationId: createCustomerIntake
      summary: Submit a physical delivery or cash trade intake
      description: |
        The represented company and submitting seat are derived from the bearer.
        The terms are complete, as activation needs them, and the intake is a
        Draft submitted for review as it is created (submitted_at). A partial
        Draft, or a quote offering several terms, is saved as a quote group with
        createCustomerIntakeGroup instead.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateCustomerIntakeRequest'
      responses:
        '201':
          description: Submission recorded.
          headers:
            ETag:
              $ref: '#/components/headers/VersionETag'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Intake'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          description: >-
            `legal_prerequisites_missing` with `blocking` naming the onboarding documents still due, when
            RFQ_SIGNATURE_GATES_ENABLED is on and the company's MNDA is
            not effective (physical intakes only); otherwise `conflict`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/intakes/{intakeId}:
    parameters:
      - $ref: '#/components/parameters/IntakeId'
    get:
      tags: [Intakes]
      operationId: getCustomerIntake
      summary: Get one company submission
      description: Unknown and cross-company identifiers return the same not-found response.
      responses:
        '200':
          description: Customer-safe intake detail.
          headers:
            ETag:
              $ref: '#/components/headers/VersionETag'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Intake'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/intakes/{intakeId}/withdraw:
    parameters:
      - $ref: '#/components/parameters/IntakeId'
    post:
      tags: [Intakes]
      operationId: withdrawCustomerIntake
      summary: Withdraw one of the company's own intakes
      description: |
        Withdraws a single intake, or one term of a quote group, that the
        company submitted. A withdrawn intake is archived, since nothing an
        intake records is deleted. An intake with a deal, a sent quote or an
        allocation is withdrawn through the desk (409 desk_change_required),
        and an intake already withdrawn is refused with 409
        already_withdrawn, whether or not it is in a quote group. Intakes
        the desk entered for the company, and other companies' intakes,
        return 404. The intake's withdrawable field states the same rule.
        The desk is told once of the withdrawal of a submitted or Active
        intake, and not of a Draft never submitted.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/IfMatch'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WithdrawRequest'
      responses:
        '200':
          description: Intake archived.
          headers:
            ETag:
              $ref: '#/components/headers/VersionETag'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Intake'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '412':
          $ref: '#/components/responses/PreconditionFailed'
  /v1/rfq/customer/intake-groups:
    post:
      tags: [Intakes]
      operationId: createCustomerIntakeGroup
      summary: Save a quote as a group of Draft intakes, one per term
      description: |
        Each delivery term the quote lists becomes a sibling intake with the
        quote's shared terms and that term's own price and prepayment; a quote
        without a term yet is a group of one. Sell siblings draw down one pool of GPUs, and
        buy siblings are alternatives for one request. The quantity is stated
        in GPUs or in nodes of 8 GPUs and stored on each sibling in GPUs.

        Saving needs a GPU and a quantity. Every sibling is saved as a Draft
        that staff do not review until it is submitted with
        submitCustomerIntakeGroup. Cash quotes one fixed term with no
        interconnect or country. The company, its contact and the submitting
        seat are derived from the bearer. The create is all-or-nothing.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateIntakeGroupRequest'
      responses:
        '201':
          description: Quote group and its Draft intakes saved.
          headers:
            ETag:
              $ref: '#/components/headers/VersionETag'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IntakeGroup'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          $ref: '#/components/responses/Conflict'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/intake-groups/{groupId}:
    parameters:
      - $ref: '#/components/parameters/GroupId'
    get:
      tags: [Intakes]
      operationId: getCustomerIntakeGroup
      summary: Get one of the company's quote groups
      description: Quote groups the company did not save, including those the desk
        entered for it, return the same not-found response as unknown ones.
        The ETag carries the version that guards the group's commands, and an
        editable quote's form is what an edit starts from, read with the same
        version.
      responses:
        '200':
          description: Quote group with every term, open terms first.
          headers:
            ETag:
              $ref: '#/components/headers/VersionETag'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IntakeGroup'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
    patch:
      tags: [Intakes]
      operationId: updateCustomerIntakeGroup
      summary: Edit a Draft quote group
      description: |
        Applies the shared terms to every open term and replaces the list of
        terms. A term keeps its intake while it is listed; removing a term
        archives its intake and adding one saves a Draft intake. Only a quote
        whose open terms are all Drafts, none with a deal, a sent quote or an
        allocation, is edited here (editable); changing an Active intake goes
        through the desk (409 desk_change_required), and a quote with no open
        term is refused with 409 quote_group_conflict, as is an edit that
        would grow the quote past 48 intakes, archived ones included; save
        the changed terms as a new quote instead. The company and the seat's
        contact must still be active (409 customer_profile_incomplete).
        An edit that changes the quote takes it out of staff review until it
        is submitted again; one that changes nothing keeps its submission.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/IfMatch'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateIntakeGroupRequest'
      responses:
        '200':
          description: Quote group updated.
          headers:
            ETag:
              $ref: '#/components/headers/VersionETag'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IntakeGroup'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '412':
          $ref: '#/components/responses/PreconditionFailed'
  /v1/rfq/customer/intake-groups/{groupId}/submit:
    parameters:
      - $ref: '#/components/parameters/GroupId'
    post:
      tags: [Intakes]
      operationId: submitCustomerIntakeGroup
      summary: Submit a Draft quote group for review
      description: |
        Records submitted_at on every open Draft not yet submitted. Each must
        be complete enough for staff to activate it: a start date, a term and,
        for physical delivery, a country or Flexible. One incomplete term
        submits none. Staff review only submitted Drafts and activate them;
        submitting activates nothing. A quote with no Draft awaiting
        submission is refused with 409 quote_group_conflict, and one whose
        company or seat contact is no longer active with 409
        customer_profile_incomplete. The request has no body.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/IfMatch'
      responses:
        '200':
          description: Quote group submitted for review.
          headers:
            ETag:
              $ref: '#/components/headers/VersionETag'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IntakeGroup'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '412':
          $ref: '#/components/responses/PreconditionFailed'
  /v1/rfq/customer/intake-groups/{groupId}/withdraw:
    parameters:
      - $ref: '#/components/parameters/GroupId'
    post:
      tags: [Intakes]
      operationId: withdrawCustomerIntakeGroup
      summary: Withdraw every open term of a quote group
      description: Archives every open term together. A quote with a deal, a sent quote
        or an allocation on any open term is withdrawn through the desk (409
        desk_change_required), and nothing is archived. A quote with no open
        term is refused with 409 quote_group_conflict. The desk is told once
        of a withdrawal that takes back a submitted or Active term, and not
        of one that takes back only Drafts never submitted.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/IfMatch'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WithdrawRequest'
      responses:
        '200':
          description: Open terms archived.
          headers:
            ETag:
              $ref: '#/components/headers/VersionETag'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IntakeGroup'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '412':
          $ref: '#/components/responses/PreconditionFailed'
  /v1/rfq/customer/presentations:
    get:
      tags: [Presentations]
      operationId: listCustomerPresentations
      summary: List every presentation distributed to the company
      description: Superseded, withdrawn, and unavailable revisions remain in history.
      parameters:
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Recipient-scoped presentation history.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PresentationPage'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/presentations/{presentationId}:
    parameters:
      - $ref: '#/components/parameters/PresentationId'
    get:
      tags: [Presentations]
      operationId: getCustomerPresentation
      summary: Get one immutable recipient snapshot
      description: Unknown, another recipient's, and cross-company identifiers return not found.
      responses:
        '200':
          description: Exact presentation revision sent to this company.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Presentation'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/presentations/{presentationId}/responses:
    parameters:
      - $ref: '#/components/parameters/PresentationId'
    get:
      tags: [Presentations]
      operationId: getCustomerPresentationResponse
      summary: Get the company's immutable response to a presentation
      description: Missing responses and presentation identifiers outside the caller's company return not found.
      responses:
        '200':
          description: The response recorded by this company for the presentation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Response'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
    post:
      tags: [Presentations]
      operationId: createCustomerResponse
      summary: Record interest, a question, pass, or counterproposal for staff
      description: The response goes only to Liquid Compute staff and is never forwarded automatically.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateResponseRequest'
      responses:
        '201':
          description: Immutable response recorded for the caller's company.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Response'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: >-
            `legal_prerequisites_missing` with `blocking` naming the onboarding documents still due, when
            RFQ_SIGNATURE_GATES_ENABLED is on and the company's MNDA is
            not effective (physical presentations only); otherwise `conflict`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/responses:
    get:
      tags: [Presentations]
      operationId: listCustomerResponses
      summary: List responses submitted by the company
      parameters:
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Immutable company response history.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ResponsePage'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/deals:
    get:
      tags: [Deals]
      operationId: listCustomerDeals
      summary: List active and terminal company deals
      parameters:
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Company-scoped deal history.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DealPage'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/deals/{dealId}:
    parameters:
      - $ref: '#/components/parameters/DealId'
    get:
      tags: [Deals]
      operationId: getCustomerDeal
      summary: Get a customer-safe deal workspace
      description: Counterparty identity is absent until the locked bilateral disclosure succeeds.
      responses:
        '200':
          description: Customer-safe deal detail.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Deal'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/trading-pnl:
    get:
      tags: [Positions]
      operationId: getCustomerTradingPnL
      summary: Recorded trading performance and indicative open valuation
      description: |
        Company-scoped UTC daily closing balances. Period PnL equals changes in
        cumulative realized cash PnL less cumulative incurred costs plus the change
        in indicative unrealized PnL. Use the preceding UTC day's close as the
        period opening. Null means incomplete, never zero. Financial corrections
        post when recorded; economic dates do not backdate reports. Price versions
        are selected as known at each cutoff, using only completed daily prints.
        The valuation uses full frozen hours; no forward curve, elapsed-exposure
        settlement or executable exit quote is inferred. Older-than-yesterday
        prints are stale. Existing history starts at an explicitly flagged legacy
        baseline; no settlements or historical prices are fabricated. No partial
        cash settlement or hedge-effectiveness calculation is provided. Prices
        require current index entitlement and costs respect side-specific visibility.
        Deal financial_revisions expose cumulative signed gross from this company's
        side, its authorized incurred costs, and net (gross minus costs). Net stays
        null if either input is unknown or undisclosed. Revisions are ordered oldest
        first; only the final revision is current. Never sum revision amounts.
        Staff-only notes and the counterparty's costs are excluded. phase
        follows the current commercial facts, separate from financial status.
        Histories exceeding 20000 revisions, 100000 prices or ten years fail closed.
      responses:
        '200':
          description: Daily balances and current financial states, including closed deals.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/TradingPnL' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /v1/rfq/customer/positions:
    get:
      tags: [Positions]
      operationId: listCustomerPositions
      summary: List the company's cash-settled positions
      description: |
        One position per explicitly shared cash deal, seen from the
        company's own side, with the economics frozen from the agreed-terms
        round. Position status and lifecycle timestamps follow recorded economic
        events. Sharing a deal does not alter its economic position.
        VOID and SETTLED rows remain available as historical records. Unexecuted
        superseded positions are not listed. Existing settlement document and
        communication visibility rules are unchanged. Index marks are separate.
        Cursors bind the page walk to one position-book revision. If the book
        changes between pages, the endpoint returns 409 positions_changed
        (retryable); discard all collected pages and restart without a cursor.
        Cursors from an earlier deployment may require restarting the walk.
      parameters:
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Company-scoped positions, newest agreement first.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PositionPage'
        '409':
          description: The position book changed; restart pagination from the first page.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ErrorEnvelope' }
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/financing-requests:
    get:
      tags: [Finance]
      operationId: listCustomerFinancingRequests
      summary: List pending and historical company financing requests
      parameters:
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Company financing history.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FinancingRequestPage'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
    post:
      tags: [Finance]
      operationId: createCustomerFinancingRequest
      summary: Submit a progressively completed financing request
      description: The represented company is derived from the bearer. Only amount and GPU specifications are required, and no underwriting decision is made.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateFinancingRequest'
      responses:
        '201':
          description: Financing request recorded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FinancingRequest'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '409':
          $ref: '#/components/responses/Conflict'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/financing-requests/{financingRequestId}:
    parameters:
      - $ref: '#/components/parameters/FinancingRequestId'
    get:
      tags: [Finance]
      operationId: getCustomerFinancingRequest
      summary: Get one company financing request
      responses:
        '200':
          description: Company financing request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FinancingRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
    patch:
      tags: [Finance]
      operationId: updateCustomerFinancingRequest
      summary: Progress an open company financing request
      description: Staff-only status and closure fields are not accepted. When supplied, questionnaire replaces the current questionnaire snapshot.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/IfMatch'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateCustomerFinancingRequest'
      responses:
        '200':
          description: Open financing request updated without changing its staff-controlled status.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FinancingRequest'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '412':
          $ref: '#/components/responses/PreconditionFailed'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/financing-requests/{financingRequestId}/responses:
    parameters:
      - $ref: '#/components/parameters/FinancingRequestId'
    get:
      tags: [Finance]
      operationId: listCustomerFinancingResponses
      summary: The company's answers to Liquid Compute's asks, newest first
      parameters:
        - { $ref: '#/components/parameters/Limit' }
        - { $ref: '#/components/parameters/Cursor' }
      responses:
        '200':
          description: Answers with the ask each answered and the files each carried.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FinancingResponsePage'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
    post:
      tags: [Finance]
      operationId: createCustomerFinancingResponse
      summary: Answer what Liquid Compute asked for on a financing request
      description: Accepted only while the request is ACTION_REQUIRED. Records the answer, moves the request to IN_REVIEW, clears the ask, and alerts the lending desk once.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/IfMatch'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateFinancingResponse'
      responses:
        '200':
          description: Answer recorded; the request is back in review.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FinancingRequest'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '412':
          $ref: '#/components/responses/PreconditionFailed'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/documents/capabilities:
    get:
      tags: [Documents]
      operationId: getCustomerDocumentCapabilities
      summary: Whether this deployment accepts company uploads
      responses:
        '200':
          description: Document capabilities for the signed-in company.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CustomerDocumentCapabilities'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/documents:
    get:
      tags: [Documents]
      operationId: listCustomerDocuments
      summary: List company uploads and documents explicitly shared with the company
      parameters:
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Limit'
        - name: context_type
          in: query
          schema:
            type: string
            enum: [INTAKE, DEAL, COMPANY_AGREEMENT, FINANCING]
        - name: context_id
          in: query
          schema:
            $ref: '#/components/schemas/Uuid'
      responses:
        '200':
          description: Authorized document history without durable object URLs.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentPage'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/documents/uploads:
    post:
      tags: [Documents]
      operationId: createCustomerDocumentUpload
      summary: Create a five-minute upload grant
      description: Disabled until the document safety and retention policy is approved.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateCustomerDocumentUpload'
      responses:
        '201':
          description: Pending immutable version and purpose-bound upload grant.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UploadGrant'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          $ref: '#/components/responses/Conflict'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/documents/{documentId}/versions/uploads:
    parameters:
      - $ref: '#/components/parameters/DocumentId'
    post:
      tags: [Documents]
      operationId: createCustomerDocumentVersionUpload
      summary: Create a pending immutable company-owned version
      description: Disabled until the document safety and retention policy is approved. New versions inherit no audience.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateDocumentVersionUpload'
      responses:
        '201':
          description: Pending immutable version and purpose-bound upload grant.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UploadGrant'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/documents/{documentId}/versions/{versionId}/finalize:
    parameters:
      - $ref: '#/components/parameters/DocumentId'
      - $ref: '#/components/parameters/DocumentVersionId'
    post:
      tags: [Documents]
      operationId: finalizeCustomerDocumentUpload
      summary: Finalize one company-owned upload
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FinalizeUploadRequest'
      responses:
        '200':
          description: Finalization recorded; availability still depends on safety policy.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Document'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/documents/{documentId}/versions/{versionId}/download:
    parameters:
      - $ref: '#/components/parameters/DocumentId'
      - $ref: '#/components/parameters/DocumentVersionId'
    post:
      tags: [Documents]
      operationId: createCustomerDocumentDownload
      summary: Create a five-minute forced-download grant
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      responses:
        '200':
          description: Short-lived grant for exactly one authorized object version.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DownloadGrant'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/contract-reviews/terms:
    get:
      tags: [Contract review]
      operationId: getCustomerContractReviewTerms
      summary: Whether the company has agreed to the contract review terms
      responses:
        '200':
          description: The current terms version and the company's agreement.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContractReviewTerms'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
    post:
      tags: [Contract review]
      operationId: acceptCustomerContractReviewTerms
      summary: Agree to the contract review terms for the company
      description: Agreeing again answers with the company's first agreement to the same terms version.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AcceptContractReviewTerms'
      responses:
        '200':
          description: The company's agreement.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContractReviewTerms'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          $ref: '#/components/responses/Conflict'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/contract-reviews/billing:
    get:
      tags: [Contract review]
      operationId: getCustomerContractReviewBilling
      summary: What the company's next contract review costs
      description: >-
        A review costs price_cents, in US cents, charged on Stripe's page when the company
        sends its first version; every later version is included. While
        free_reviews_left is above zero, the next review is free instead.
      responses:
        '200':
          description: The price and the company's free reviews.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContractReviewBilling'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/contract-reviews:
    get:
      tags: [Contract review]
      operationId: listCustomerContractReviews
      summary: List the company's contract reviews, newest first
      description: At most 500 reviews, the newest first.
      responses:
        '200':
          description: The company's reviews.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContractReviewList'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/contract-reviews/uploads:
    post:
      tags: [Contract review]
      operationId: createCustomerContractReviewUpload
      summary: Hand in a contract for review and get a five-minute upload grant
      description: >-
        Opens a review whose first version waits for its file. PUT the file to
        upload_url with every required header, read x-amz-version-id from the
        answer, then finalize the version with it. Refused with 409
        terms_required until the company agrees to the terms, and with 400
        unsupported_file_type for anything but a PDF.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateContractReviewUpload'
      responses:
        '201':
          description: The new review, its first version and the upload grant. A replay presigns a fresh grant.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContractReviewUpload'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          $ref: '#/components/responses/Conflict'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/contract-reviews/turned-away:
    post:
      tags: [Contract review]
      operationId: recordCustomerContractReviewTurnedAway
      summary: Record a PDF the browser turned away as an unreadable scan
      description: The file itself is never sent; only these facts are kept for staff.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RecordContractReviewTurnedAway'
      responses:
        '201':
          description: Recorded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContractReviewTurnedAwayRecorded'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          $ref: '#/components/responses/Conflict'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/contract-reviews/{reviewId}:
    parameters:
      - $ref: '#/components/parameters/ContractReviewId'
    get:
      tags: [Contract review]
      operationId: getCustomerContractReview
      summary: Read one of the company's reviews with its sent notes
      description: Notes on a version not yet sent are not shown. Another company's review is not found.
      responses:
        '200':
          $ref: '#/components/responses/ContractReviewCommand'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/contract-reviews/{reviewId}/versions/uploads:
    parameters:
      - $ref: '#/components/parameters/ContractReviewId'
    post:
      tags: [Contract review]
      operationId: createCustomerContractReviewVersionUpload
      summary: Upload a new version of a reviewed contract
      description: >-
        The new version starts with no notes; earlier versions keep theirs.
        Refused with 409 payment_needed while the review waits for its
        payment, with 409 upload_in_progress while another version is
        uploading, and with 409
        notes_unanswered while the newest ready version has been sent and one
        of its open notes waits on the company: it has no reply, or our
        lawyer replied last. A version still with our lawyer holds nothing back.
        A version left uploading for 30 minutes is marked failed and replaced.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateContractReviewVersionUpload'
      responses:
        '201':
          description: The review, the new version and its upload grant. A replay presigns a fresh grant.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContractReviewUpload'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/contract-reviews/{reviewId}/versions/{versionId}/finalize:
    parameters:
      - $ref: '#/components/parameters/ContractReviewId'
      - $ref: '#/components/parameters/ContractReviewVersionId'
    post:
      tags: [Contract review]
      operationId: finalizeCustomerContractReviewUpload
      summary: Finish an upload once storage holds the file
      description: >-
        Storage must hold exactly the size, checksum and content type the grant
        was for. A PDF version is readable at once; a Word version is
        converting until the PDF made from it is stored. A review's first
        readable version then waits in awaiting_payment until it is paid for
        (see pay); every later version is ready at once. Its due date starts
        when it is ready. 409 upload_incomplete when storage does not hold the
        named version yet.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FinalizeContractReviewUpload'
      responses:
        '200':
          $ref: '#/components/responses/ContractReviewCommand'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/contract-reviews/{reviewId}/pay:
    parameters:
      - $ref: '#/components/parameters/ContractReviewId'
    post:
      tags: [Contract review]
      operationId: payCustomerContractReview
      summary: Pay for a review waiting for payment, and send it to our lawyer
      description: >-
        For a review in awaiting_payment. A free review the company has left
        pays at once and sends the review to our lawyer. Otherwise the answer
        names checkout_url, Stripe's page, where the customer pays the price in
        full; no card is saved. A page still open is answered again; one Stripe
        closed unpaid is replaced by a new one. Any member of the company may
        pay. A review not waiting for payment is answered as it is, with no
        checkout_url. No body. 503 payments_unavailable when the payment
        provider can't be reached; nothing is charged and a retry with the same
        Idempotency-Key is safe.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      responses:
        '200':
          description: The review after paying, and the page to pay on while it waits for a card payment.
          headers:
            ETag:
              $ref: '#/components/headers/VersionETag'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContractReviewPayResult'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/contract-reviews/{reviewId}/pay/sync:
    parameters:
      - $ref: '#/components/parameters/ContractReviewId'
    post:
      tags: [Contract review]
      operationId: syncCustomerContractReviewPayment
      summary: Record the review's Stripe page as Stripe reports it now
      description: >-
        For a customer back from Stripe's page. A paid page sends the review to
        our lawyer; a page Stripe closed unpaid leaves it waiting, with
        payment.failure_code saying why. A background job does the same within
        a minute or so for a tab nobody came back from. No body.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      responses:
        '200':
          $ref: '#/components/responses/ContractReviewCommand'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/contract-reviews/{reviewId}/versions/{versionId}/file:
    parameters:
      - $ref: '#/components/parameters/ContractReviewId'
      - $ref: '#/components/parameters/ContractReviewVersionId'
    post:
      tags: [Contract review]
      operationId: createCustomerContractReviewFileGrant
      summary: Create a five-minute grant to read one version's file
      description: >-
        Takes no body. file=view (the default) grants the PDF the viewers show:
        the upload itself, or the PDF made from a Word upload. file=original
        grants the uploaded file, which for a Word version is the .docx.
        409 version_not_ready until that file exists.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
        - name: file
          in: query
          required: false
          schema: { type: string, enum: [view, original], default: view }
      responses:
        '200':
          description: A version-pinned read grant the browser can fetch.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DownloadGrant'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/contract-reviews/{reviewId}/notes/{noteId}/replies:
    parameters:
      - $ref: '#/components/parameters/ContractReviewId'
      - $ref: '#/components/parameters/ContractReviewNoteId'
    post:
      tags: [Contract review]
      operationId: createCustomerContractReviewReply
      summary: Reply under a sent note
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/IfMatch'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContractReviewReplyRequest'
      responses:
        '201':
          $ref: '#/components/responses/ContractReviewCommand'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '412':
          $ref: '#/components/responses/PreconditionFailed'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/contract-reviews/{reviewId}/notes/{noteId}:
    parameters:
      - $ref: '#/components/parameters/ContractReviewId'
      - $ref: '#/components/parameters/ContractReviewNoteId'
    patch:
      tags: [Contract review]
      operationId: resolveCustomerContractReviewNote
      summary: Resolve or reopen a sent note
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/IfMatch'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ResolveContractReviewNote'
      responses:
        '200':
          $ref: '#/components/responses/ContractReviewCommand'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '412':
          $ref: '#/components/responses/PreconditionFailed'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/guest/invitations:
    get:
      tags: [Guest]
      operationId: getGuestInvitation
      summary: View one open form or a neutral closed-link page
      security:
        - GuestCapability: []
      responses:
        '200':
          description: Open seeded context or the indistinguishable closed representation.
          headers:
            Cache-Control:
              $ref: '#/components/headers/NoStore'
            Referrer-Policy:
              $ref: '#/components/headers/NoReferrer'
            X-Robots-Tag:
              $ref: '#/components/headers/NoIndex'
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/GuestInvitationOpen'
                  - $ref: '#/components/schemas/GuestInvitationClosed'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/guest/invitations/submit:
    post:
      tags: [Guest]
      operationId: submitGuestInvitation
      summary: Atomically submit once or return the neutral closed result
      security:
        - GuestCapability: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GuestSubmissionRequest'
      responses:
        '200':
          description: First successful submission or indistinguishable closed result.
          headers:
            Cache-Control:
              $ref: '#/components/headers/NoStore'
            Referrer-Policy:
              $ref: '#/components/headers/NoReferrer'
            X-Robots-Tag:
              $ref: '#/components/headers/NoIndex'
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/GuestSubmissionAccepted'
                  - $ref: '#/components/schemas/GuestInvitationClosed'
        '400':
          $ref: '#/components/responses/BadRequest'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/guest/qualification/installer/enrollments/{id}/buyer-access:
    parameters:
      - { name: id, in: path, required: true, schema: { $ref: '#/components/schemas/Uuid' } }
    get:
      tags: [Qualification]
      operationId: getInstallerBuyerAccess
      summary: Fetch the buyer SSH configuration for this enrolled machine
      description: Requires the installer's receipt token in X-RFQ-Guest-Token.
        A specification is available only while a current delivery covers this identity.
      security: [{ InstallerReceipt: [] }]
      responses:
        '200':
          description: Current revision's specification, or CLOSED when no delivery applies.
          content:
            application/json:
              schema:
                oneOf:
                  - { $ref: '#/components/schemas/QualificationBuyerAccessSpec' }
                  - { $ref: '#/components/schemas/GuestInvitationClosed' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /v1/rfq/guest/qualification/installer/enrollments/{id}/buyer-access-report:
    parameters:
      - { name: id, in: path, required: true, schema: { $ref: '#/components/schemas/Uuid' } }
    post:
      tags: [Qualification]
      operationId: recordInstallerBuyerAccessReport
      summary: Report application or removal of one buyer SSH configuration revision
      description: Requires the installer's receipt token in X-RFQ-Guest-Token.
        Stale revisions cannot provision a replacement identity. FAILED requires a
        failed step; APPLIED and REMOVED must not contain failed steps.
      security: [{ InstallerReceipt: [] }]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/QualificationBuyerAccessReport' }
      responses:
        '200':
          description: Current buyer-access task state.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/QualificationBuyerAccessStatus' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /v1/rfq/customer/allocations/{allocationId}/trial:
    parameters:
      - $ref: '#/components/parameters/AllocationId'
    post:
      tags:
        - Machine access
      operationId: updateWorkloadTrial
      summary: Submit workload results or request more trial time
      description:
        Buyer-operated workload trials are separate from hardware qualification and commercial
        acceptance. Trial access is restricted to the selected machine identities and approved window. Extension
        requests do not extend access until the trial owner or an administrator approves them within allocated
        capacity. Completion closes trial access; explicit staff release restores delivery access without
        accepting machines.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BuyerWorkloadTrialRequest'
      responses:
        '200':
          description: Current workload trial.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkloadTrial'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '412':
          description: The allocation record changed; re-read before accepting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '403':
          $ref: '#/components/responses/Forbidden'
  /v1/rfq/customer/short-term/market:
    get:
      tags: [Short-term compute]
      operationId: getCustomerShortTermMarket
      summary: Read the short-term market for the coming twelve weeks
      description: |
        Every live ask another company listed that still has nodes free for
        its smallest booking in at least one of the twelve weeks from this
        week's Monday, cheapest first, with the nodes free in each week. gpus
        summarizes every GPU chip, including chips with no offers. No ask
        names its seller. The company's own asks are read with
        listCustomerShortTermAsks instead.
      responses:
        '200':
          description: The market as it stands.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShortTermMarket'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/short-term/evidence:
    get:
      tags: [Short-term compute]
      operationId: listCustomerShortTermEvidence
      summary: List the GPU tests the company may list an ask from
      description: |
        The passing GCAT runs this seat claimed and completed in the last 14
        days, newest first, at most 50. Each names the chip and the nodes it
        proves; an ask cites one as its evidence. With test_waiver=1, staging
        also lists each chip's TEST_WAIVER, which covers a block of any size
        so a seller can list without running a test; any other deployment
        refuses the parameter with 409 test_waiver_unavailable.
      parameters:
        - name: test_waiver
          in: query
          description: 1 to include each chip's test waiver, on staging only.
          schema:
            type: string
            enum: ['1']
      responses:
        '200':
          description: Listable GPU tests.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShortTermEvidenceList'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          $ref: '#/components/responses/Conflict'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/short-term/asks:
    get:
      tags: [Short-term compute]
      operationId: listCustomerShortTermAsks
      summary: List the company's short-term asks
      description: Every ask the company listed, newest first, at most 500,
        withdrawn asks included as CLOSED.
      responses:
        '200':
          description: The company's asks.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShortTermAskList'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
    post:
      tags: [Short-term compute]
      operationId: createCustomerShortTermAsk
      summary: List a tested GPU block for weekly booking
      description: |
        The ask goes straight to the market as an active SELL intake of the
        company. Its evidence must be a passing GCAT run of the same chip the
        seat claimed and completed in the last 14 days, as
        listCustomerShortTermEvidence lists them; any other evidence,
        including a STANDALONE_TEST, is refused with 409
        short_term_conflict. On staging the evidence may instead be the
        chip's TEST_WAIVER; any other deployment refuses a waiver with 409
        test_waiver_unavailable. A company or seat contact that is not set up is
        refused with 409 customer_profile_incomplete. Taking no money, the
        route needs no payments configuration.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateShortTermAskRequest'
      responses:
        '201':
          description: Ask listed.
          headers:
            ETag:
              $ref: '#/components/headers/VersionETag'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShortTermAsk'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          $ref: '#/components/responses/ShortTermConflict'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/short-term/asks/{askId}:
    parameters:
      - $ref: '#/components/parameters/AskId'
    patch:
      tags: [Short-term compute]
      operationId: updateCustomerShortTermAsk
      summary: Change what a live ask offers
      description: |
        Changes the ask's weeks, price, booking limits or payment plan; an
        omitted field keeps its value. A payment plan and its cadence are
        one unit: installment_weeks alone applies to a stored INSTALLMENTS
        plan, and a plan other than INSTALLMENTS clears it. Bookings already
        made keep their terms, so dropping a week a booking uses is refused
        with 409 short_term_conflict, as is any change to a withdrawn ask.
        Another company's ask returns the same not-found response as an
        unknown one.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/IfMatch'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateShortTermAskRequest'
      responses:
        '200':
          description: Ask updated.
          headers:
            ETag:
              $ref: '#/components/headers/VersionETag'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShortTermAsk'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/ShortTermConflict'
        '412':
          $ref: '#/components/responses/PreconditionFailed'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/short-term/asks/{askId}/withdraw:
    parameters:
      - $ref: '#/components/parameters/AskId'
    post:
      tags: [Short-term compute]
      operationId: withdrawCustomerShortTermAsk
      summary: Take an ask off the market
      description: The ask closes and leaves the market; bookings already made
        stand. An ask already withdrawn is refused with 409
        short_term_conflict. The request has no body.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/IfMatch'
      responses:
        '200':
          description: Ask withdrawn.
          headers:
            ETag:
              $ref: '#/components/headers/VersionETag'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShortTermAsk'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/ShortTermConflict'
        '412':
          $ref: '#/components/responses/PreconditionFailed'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/short-term/bookings:
    get:
      tags: [Short-term compute]
      operationId: listCustomerShortTermBookings
      summary: List the bookings the company buys or sells
      description: Every short-term booking the company is the buyer or the
        seller of, newest first, at most 200, each as that side sees it.
      responses:
        '200':
          description: The company's bookings.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShortTermBookingList'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
    post:
      tags: [Short-term compute]
      operationId: createCustomerShortTermBooking
      summary: Book nodes of an ask by the week
      description: |
        Books whole nodes of a live ask from start_date for whole weeks and
        makes the booking a deal between the two companies, neither named to
        the other. A total under $10,000 is paid by card; at or above it, by
        bank transfer behind a card hold of 2% of the total, at least $500
        and at most $5,000. A card booking whose company has a saved card goes
        to the seller at once as REQUESTED; any other waits in
        AWAITING_PAYMENT for the checkout that payment_url opens, which
        returns the buyer to return_path with a booking query parameter.
        Booking a withdrawn ask, the company's own ask, or nodes and weeks the
        ask cannot give is refused with 409 short_term_conflict.

        The route answers 503 payments_unavailable before reading the body
        when payments are not configured. A retry with the same
        Idempotency-Key after a 503 replays the recorded booking and resumes
        its checkout.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateShortTermBookingRequest'
      responses:
        '201':
          description: Booking recorded, with its checkout when it waits for one.
          headers:
            ETag:
              $ref: '#/components/headers/VersionETag'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShortTermBookingCreated'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/ShortTermPaymentConflict'
        '503':
          $ref: '#/components/responses/PaymentsUnavailable'
  /v1/rfq/customer/short-term/bookings/{bookingId}:
    parameters:
      - $ref: '#/components/parameters/BookingId'
    get:
      tags: [Short-term compute]
      operationId: getCustomerShortTermBooking
      summary: Get one of the company's bookings
      description: The booking as the company's side sees it. A booking the company
        neither buys nor sells returns the same not-found response as an
        unknown one. The ETag carries the version that guards the booking's
        confirm, decline and cancel commands.
      responses:
        '200':
          description: The booking.
          headers:
            ETag:
              $ref: '#/components/headers/VersionETag'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShortTermBooking'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/short-term/bookings/{bookingId}/confirm:
    parameters:
      - $ref: '#/components/parameters/BookingId'
    post:
      tags: [Short-term compute]
      operationId: confirmCustomerShortTermBooking
      summary: Confirm a booking request as its seller
      description: The seller's yes to a REQUESTED booking before respond_by. The
        booking becomes CONFIRMED, its deal reaches Terms agreed with the
        booking's terms recorded for both companies, and its payments are
        scheduled by its plan. A booking not waiting for the seller, or one
        whose request lapsed, is refused with 409 short_term_conflict. Only
        the seller's company may confirm; the buyer's gets 404. The request
        has no body.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/IfMatch'
      responses:
        '200':
          $ref: '#/components/responses/ShortTermBookingCommand'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/ShortTermConflict'
        '412':
          $ref: '#/components/responses/PreconditionFailed'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/short-term/bookings/{bookingId}/decline:
    parameters:
      - $ref: '#/components/parameters/BookingId'
    post:
      tags: [Short-term compute]
      operationId: declineCustomerShortTermBooking
      summary: Decline a booking request as its seller
      description: The seller's no to a REQUESTED booking. The booking becomes
        DECLINED and its deal is lost. A booking not waiting for the seller is
        refused with 409 short_term_conflict. Only the seller's company may
        decline; the buyer's gets 404. The request has no body.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/IfMatch'
      responses:
        '200':
          $ref: '#/components/responses/ShortTermBookingCommand'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/ShortTermConflict'
        '412':
          $ref: '#/components/responses/PreconditionFailed'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/short-term/bookings/{bookingId}/cancel:
    parameters:
      - $ref: '#/components/parameters/BookingId'
    post:
      tags: [Short-term compute]
      operationId: cancelCustomerShortTermBooking
      summary: Cancel a booking request as its buyer
      description: The buyer takes back a booking still AWAITING_PAYMENT or
        REQUESTED. The booking becomes CANCELLED, its deal is cancelled, and a
        card hold not yet captured is released. A confirmed booking is refused
        with 409 short_term_conflict; it is ended through Liquid Compute.
        Only the buyer's company may cancel; the seller's gets 404. The
        request has no body.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/IfMatch'
      responses:
        '200':
          $ref: '#/components/responses/ShortTermBookingCommand'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/ShortTermConflict'
        '412':
          $ref: '#/components/responses/PreconditionFailed'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/short-term/bookings/{bookingId}/stop:
    parameters:
      - $ref: '#/components/parameters/BookingId'
    post:
      tags: [Short-term compute]
      operationId: stopCustomerShortTermBooking
      summary: Stop a booking's VMs as its buyer
      description: The buyer stops the VMs of a CONFIRMED booking that has not ended, on a
        listing whose servers Kliq runs. The servers stay the buyer's and are
        still billed; a VM's disk resets when it starts again. Stopping VMs
        already stopped changes nothing. A booking that is not running, or
        whose machines Liquid Compute delivers, is refused with 409
        short_term_conflict. Only the buyer's company may stop; the seller's
        gets 404. The request has no body.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/IfMatch'
      responses:
        '200':
          $ref: '#/components/responses/ShortTermBookingCommand'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/ShortTermConflict'
        '412':
          $ref: '#/components/responses/PreconditionFailed'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/short-term/bookings/{bookingId}/start:
    parameters:
      - $ref: '#/components/parameters/BookingId'
    post:
      tags: [Short-term compute]
      operationId: startCustomerShortTermBooking
      summary: Start a booking's stopped VMs as its buyer
      description: The buyer starts the VMs they stopped. A VM stopped for an overdue
        payment starts once it is paid. Starting VMs already running changes
        nothing. The same refusals as stop apply. The request has no body.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/IfMatch'
      responses:
        '200':
          $ref: '#/components/responses/ShortTermBookingCommand'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/ShortTermConflict'
        '412':
          $ref: '#/components/responses/PreconditionFailed'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/short-term/bookings/{bookingId}/terminate:
    parameters:
      - $ref: '#/components/parameters/BookingId'
    post:
      tags: [Short-term compute]
      operationId: terminateCustomerShortTermBooking
      summary: End a booking on Kliq servers early as its buyer
      description: The buyer ends a CONFIRMED booking that has not ended, on a listing
        whose servers Kliq runs. The booking becomes TERMINATED, nothing paid
        is refunded and nothing more is charged; its VMs are released and
        their servers wiped and freed for the next buyer. A booking that is
        not running, or whose machines Liquid Compute delivers, is refused
        with 409 short_term_conflict. Only the buyer's company may end it;
        the seller's gets 404. The request has no body.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/IfMatch'
      responses:
        '200':
          $ref: '#/components/responses/ShortTermBookingCommand'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/ShortTermConflict'
        '412':
          $ref: '#/components/responses/PreconditionFailed'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/short-term/bookings/{bookingId}/payment-sync:
    parameters:
      - $ref: '#/components/parameters/BookingId'
    post:
      tags: [Short-term compute]
      operationId: syncCustomerShortTermBookingPayments
      summary: Read a booking's payments from the payment provider now
      description: For a buyer back from a checkout or an invoice. Applies the
        provider's current state of the booking's checkout and issued amounts,
        then answers the booking as it stands; the payment jobs apply the same
        state on their own schedule. The request has no body and no If-Match.
        The route answers 503 payments_unavailable when payments are not
        configured.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      responses:
        '200':
          $ref: '#/components/responses/ShortTermBookingCommand'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/ShortTermPaymentConflict'
        '503':
          $ref: '#/components/responses/PaymentsUnavailable'
  /v1/rfq/customer/short-term/bookings/{bookingId}/payments/{paymentId}/pay:
    parameters:
      - $ref: '#/components/parameters/BookingId'
      - $ref: '#/components/parameters/PaymentId'
    post:
      tags: [Short-term compute]
      operationId: payCustomerShortTermPayment
      summary: Get where to pay one of a booking's amounts now
      description: Answers the open invoice of an ISSUED amount, or issues a new
        invoice, payable by bank transfer or card, for a SCHEDULED or FAILED
        amount, due in two days. An amount not open for payment, such as one
        already PAID, is refused with 409 short_term_conflict. Only the
        buyer's company may pay; the seller's gets 404. The request has no
        body and no If-Match. The route answers 503 payments_unavailable when
        payments are not configured.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      responses:
        '200':
          description: Where to pay the amount.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShortTermPaymentLink'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/ShortTermPaymentConflict'
        '503':
          $ref: '#/components/responses/PaymentsUnavailable'
  /v1/rfq/customer/short-term/bookings/{bookingId}/extensions:
    parameters:
      - $ref: '#/components/parameters/BookingId'
    post:
      tags: [Short-term compute]
      operationId: requestCustomerShortTermExtension
      summary: Ask the seller for more weeks as the buyer
      description: Asks for 1, 2 or 4 more weeks of a CONFIRMED booking that has
        not ended, at the booking's price. The ask must have the booking's
        nodes free in every added week, and a booking runs at most 52 weeks.
        One extension waits for the seller at a time. Each broken rule is
        refused with 409 short_term_conflict. Only the buyer's company may
        ask; the seller's gets 404. The response is the booking with the
        request among its extensions.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateShortTermExtensionRequest'
      responses:
        '201':
          description: Extension requested.
          headers:
            ETag:
              $ref: '#/components/headers/VersionETag'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShortTermBooking'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/ShortTermConflict'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/short-term/bookings/{bookingId}/extensions/{extensionId}/approve:
    parameters:
      - $ref: '#/components/parameters/BookingId'
      - $ref: '#/components/parameters/ExtensionId'
    post:
      tags: [Short-term compute]
      operationId: approveCustomerShortTermExtension
      summary: Approve an extension as the seller
      description: |
        The seller's yes. The booking runs the extra weeks, its deal gains an
        agreed-terms round, and the weeks are billed as its plan bills. A
        booking that has ended or is no longer CONFIRMED, or an ask without
        the nodes free, is refused with 409 short_term_conflict, as is an
        extension already answered. Only the seller's company may approve; the
        buyer's gets 404. The request has no body.

        If-Match carries the extension's version from the booking's
        extensions, not the booking's; the response ETag is the booking's.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/IfMatch'
      responses:
        '200':
          $ref: '#/components/responses/ShortTermBookingCommand'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/ShortTermConflict'
        '412':
          $ref: '#/components/responses/PreconditionFailed'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/short-term/bookings/{bookingId}/extensions/{extensionId}/decline:
    parameters:
      - $ref: '#/components/parameters/BookingId'
      - $ref: '#/components/parameters/ExtensionId'
    post:
      tags: [Short-term compute]
      operationId: declineCustomerShortTermExtension
      summary: Decline an extension as the seller
      description: |
        The seller's no; the booking keeps its weeks. An extension already
        answered is refused with 409 short_term_conflict. Only the seller's
        company may decline; the buyer's gets 404. The request has no body.

        If-Match carries the extension's version from the booking's
        extensions, not the booking's; the response ETag is the booking's.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/IfMatch'
      responses:
        '200':
          $ref: '#/components/responses/ShortTermBookingCommand'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/ShortTermConflict'
        '412':
          $ref: '#/components/responses/PreconditionFailed'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/short-term/bookings/{bookingId}/extensions/{extensionId}/withdraw:
    parameters:
      - $ref: '#/components/parameters/BookingId'
      - $ref: '#/components/parameters/ExtensionId'
    post:
      tags: [Short-term compute]
      operationId: withdrawCustomerShortTermExtension
      summary: Withdraw an extension request as the buyer
      description: |
        The buyer takes back a request the seller has not answered. An
        extension already answered is refused with 409 short_term_conflict.
        Only the buyer's company may withdraw; the seller's gets 404. The
        request has no body.

        If-Match carries the extension's version from the booking's
        extensions, not the booking's; the response ETag is the booking's.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/IfMatch'
      responses:
        '200':
          $ref: '#/components/responses/ShortTermBookingCommand'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/ShortTermConflict'
        '412':
          $ref: '#/components/responses/PreconditionFailed'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/short-term/bids:
    get:
      tags: [Short-term compute]
      operationId: listCustomerShortTermBids
      summary: List the company's short-term bids
      description: Every AUTO_BOOK and NOTIFY bid the company left, newest first, at
        most 200, stopped ones included. The bid behind a booking request is
        not listed.
      responses:
        '200':
          description: The company's bids.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShortTermBidList'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
    post:
      tags: [Short-term compute]
      operationId: createCustomerShortTermBid
      summary: Leave a bid that watches for a fitting ask
      description: |
        A NOTIFY bid tells the company when an ask fits and starts ACTIVE. An
        AUTO_BOOK bid books the first fitting ask itself and first holds a
        card for 2% of its largest possible spend, at least $500 and at most
        $5,000: on the company's saved card at once, or through the checkout
        that payment_url opens, which returns the buyer to return_path with a
        bid query parameter. It stays PENDING_HOLD until the hold is
        authorized. A bid with no ask yet stays ACTIVE until it matches, is
        cancelled or its start window passes. A company or seat contact that
        is not set up is refused with 409 customer_profile_incomplete.

        An AUTO_BOOK bid answers 503 payments_unavailable when payments are
        not configured; a NOTIFY bid takes no money. A retry with the same
        Idempotency-Key after a 503 replays the recorded bid and resumes its
        hold.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateShortTermBidRequest'
      responses:
        '201':
          description: Bid recorded, with its checkout when its hold waits for one.
          headers:
            ETag:
              $ref: '#/components/headers/VersionETag'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShortTermBidCreated'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          $ref: '#/components/responses/ShortTermPaymentConflict'
        '503':
          $ref: '#/components/responses/PaymentsUnavailable'
  /v1/rfq/customer/short-term/bids/{bidId}/cancel:
    parameters:
      - $ref: '#/components/parameters/BidId'
    post:
      tags: [Short-term compute]
      operationId: cancelCustomerShortTermBid
      summary: Stop a bid
      description: Stops an ACTIVE or PENDING_HOLD bid; its card hold, if any, is
        released after. A bid already stopped is refused with 409
        short_term_conflict. The request has no body.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/IfMatch'
      responses:
        '200':
          $ref: '#/components/responses/ShortTermBidCommand'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/ShortTermConflict'
        '412':
          $ref: '#/components/responses/PreconditionFailed'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /v1/rfq/customer/short-term/bids/{bidId}/payment-sync:
    parameters:
      - $ref: '#/components/parameters/BidId'
    post:
      tags: [Short-term compute]
      operationId: syncCustomerShortTermBidHold
      summary: Read an AUTO_BOOK bid's card hold from the payment provider now
      description: For a buyer back from the hold's checkout. A PENDING_HOLD bid
        whose hold is authorized becomes ACTIVE; any other bid is answered as
        it stands. The request has no body and no If-Match. Reading a pending
        hold answers 503 payments_unavailable when payments are not
        configured.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      responses:
        '200':
          $ref: '#/components/responses/ShortTermBidCommand'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/ShortTermPaymentConflict'
        '503':
          $ref: '#/components/responses/PaymentsUnavailable'
  /v1/rfq/customer/machines/{id}/inference:
    parameters:
      - name: id
        in: path
        required: true
        description: The machine.
        schema:
          $ref: '#/components/schemas/Uuid'
    get:
      tags: [Inference supply]
      operationId: getCustomerMachineInference
      summary: Read the machine's newest model-serving setup
      description: Not found when the machine was never set up to serve a model.
      responses:
        '200':
          description: The setup.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InferenceDeployment'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /v1/rfq/customer/supply-terms:
    get:
      tags: [Inference supply]
      operationId: getCustomerSupplyTerms
      summary: Read the supply terms a switch accepts
      description: >-
        The current version of Liquid Compute's supply terms and their text, which
        a company accepts once when it first switches a machine on.
      responses:
        '200':
          description: The supply terms.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SupplyTerms'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
  /v1/rfq/customer/machines/{id}/offer:
    parameters:
      - name: id
        in: path
        required: true
        description: The machine.
        schema:
          $ref: '#/components/schemas/Uuid'
    post:
      tags: [Inference supply]
      operationId: switchCustomerMachineOn
      summary: Switch the machine on for AI model serving
      description: >-
        Turns the machine's switch on, or changes what it allows. Turning it on
        needs a passing GPU test that is still valid and hardware Liquid
        Compute can serve on (the machine's work shows a rate). A machine never
        switched on, or on older terms, must accept the current supply terms;
        changing an existing switch names the version read from its work.
        Liquid Compute starts serving on it within a minute when nothing else
        holds it. Version-guarded rather than replayed: a repeated switch-on
        changes nothing. No replay receipt is stored.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SwitchMachineOnRequest'
      responses:
        '200':
          description: The machine's work after the change.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MachineWork'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: version_conflict — the switch changed since it was read; reload the machine.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '422':
          description: >-
            not_qualified — turning on needs a valid GPU test; terms_required — accept
            the current supply terms; no_rate — Liquid Compute can't serve AI models on
            this hardware yet; invalid_uses — uses must be ["INFERENCE"] for now.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '503':
          $ref: '#/components/responses/InferenceUnavailable'
  /v1/rfq/customer/machines/{id}/offer/withdraw:
    parameters:
      - name: id
        in: path
        required: true
        description: The machine.
        schema:
          $ref: '#/components/schemas/Uuid'
    post:
      tags: [Inference supply]
      operationId: switchCustomerMachineOff
      summary: Switch the machine off and take it back
      description: >-
        Turns the switch off and stops serving at once: the machine leaves the
        router and its model server is removed within minutes. Idempotent; a
        machine never switched on stops whatever it still serves. The request
        has no body. No replay receipt is stored; switching off is state-guarded.
      responses:
        '200':
          description: The machine's work after the change.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MachineWork'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /v1/rfq/customer/machines/{id}/earnings:
    parameters:
      - name: id
        in: path
        required: true
        description: The machine.
        schema:
          $ref: '#/components/schemas/Uuid'
    get:
      tags: [Inference supply]
      operationId: listCustomerMachineEarnings
      summary: List what the machine earned, by day
      description: >-
        One row per setup, UTC day and rate, newest day first, at most 500.
        Today's row grows with every passing health check; a day closes for
        payout 15 minutes after it ends (DUE) and is PAID once Liquid Compute
        wires it.
      responses:
        '200':
          description: The machine's earnings.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MachineEarningsList'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
components:
  securitySchemes:
    CustomerBearer:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Original customer Auth0 bearer; never a staff or API-key credential.
    GuestCapability:
      type: apiKey
      in: header
      name: X-RFQ-Guest-Token
      description: |
        A 43-character, 256-bit base64url capability supplied only on fixed
        guest paths. Malformed values receive the same neutral CLOSED
        representation, and infrastructure must redact this header.
    TechnicianSession:
      type: apiKey
      in: header
      name: X-RFQ-Guest-Token
      description: Request- and role-scoped technician session token created by the
        qualification session exchange or standalone test. An invitation capability
        is not a session token.
    InstallerReceipt:
      type: apiKey
      in: header
      name: X-RFQ-Guest-Token
      description: Receipt token bound to one enrolled machine. Unknown enrollment/token
        pairs return 404; invitation and technician-session tokens are not accepted.
  parameters:
    DeskChip:
      name: chip
      in: query
      required: true
      description: The chip's lowercase id.
      schema: { type: string, enum: [a100, h100, h200, b200, b300] }
    AllocationId:
      name: allocationId
      in: path
      required: true
      schema:
        $ref: '#/components/schemas/Uuid'
    OwnedTestId:
      name: testId
      in: path
      required: true
      schema: { $ref: '#/components/schemas/Uuid' }
      description: One of the company's own GPU tests; another company's test is 404.
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      schema:
        type: string
        minLength: 16
        maxLength: 255
      description: Scoped to actor, method, canonical path, and key. Conflicting reuse returns 409.
    IfMatch:
      name: If-Match
      in: header
      required: true
      schema:
        type: string
        pattern: '^"v[1-9][0-9]*"$'
      description: Strong aggregate version returned as the ETag on the current representation.
    InvoicePage:
      name: invoice_page
      in: query
      description: Zero-based invoice page. Each page contains at most ten records; other history sections have independent page selections.
      schema:
        type: integer
        minimum: 0
        maximum: 1000000
        default: 0
    ClaimPage:
      name: claim_page
      in: query
      description: Zero-based service-credit request page. Each page contains at most ten records; other history sections have independent page selections.
      schema:
        type: integer
        minimum: 0
        maximum: 1000000
        default: 0
    RefundPage:
      name: refund_page
      in: query
      description: Zero-based refund page. Each page contains at most ten records; other history sections have independent page selections.
      schema:
        type: integer
        minimum: 0
        maximum: 1000000
        default: 0
    Cursor:
      name: cursor
      in: query
      schema:
        type: string
        minLength: 1
        maxLength: 1024
    Limit:
      name: limit
      in: query
      schema:
        type: integer
        minimum: 1
        maximum: 200
        default: 50
    DeskLimit:
      name: limit
      in: query
      description: Rows on one page of a desk market list.
      schema:
        type: integer
        minimum: 1
        maximum: 50
        default: 50
    ActivityId:
      name: activityId
      in: path
      required: true
      schema:
        $ref: '#/components/schemas/Uuid'
    IntakeId:
      name: intakeId
      in: path
      required: true
      schema:
        $ref: '#/components/schemas/Uuid'
    GroupId:
      name: groupId
      in: path
      required: true
      schema:
        $ref: '#/components/schemas/Uuid'
    PresentationId:
      name: presentationId
      in: path
      required: true
      schema:
        $ref: '#/components/schemas/Uuid'
    DealId:
      name: dealId
      in: path
      required: true
      schema:
        $ref: '#/components/schemas/Uuid'
    FinancingRequestId:
      name: financingRequestId
      in: path
      required: true
      schema:
        $ref: '#/components/schemas/Uuid'
    DocumentId:
      name: documentId
      in: path
      required: true
      schema:
        $ref: '#/components/schemas/Uuid'
    DocumentVersionId:
      name: versionId
      in: path
      required: true
      schema:
        $ref: '#/components/schemas/Uuid'
    ContractReviewId:
      name: reviewId
      in: path
      required: true
      schema:
        $ref: '#/components/schemas/Uuid'
    ContractReviewVersionId:
      name: versionId
      in: path
      required: true
      schema:
        $ref: '#/components/schemas/Uuid'
    ContractReviewNoteId:
      name: noteId
      in: path
      required: true
      schema:
        $ref: '#/components/schemas/Uuid'
    AskId:
      name: askId
      in: path
      required: true
      description: One of the company's short-term asks.
      schema:
        $ref: '#/components/schemas/Uuid'
    BookingId:
      name: bookingId
      in: path
      required: true
      description: A short-term booking's deal id.
      schema:
        $ref: '#/components/schemas/Uuid'
    PaymentId:
      name: paymentId
      in: path
      required: true
      description: One of the booking's payments.
      schema:
        $ref: '#/components/schemas/Uuid'
    ExtensionId:
      name: extensionId
      in: path
      required: true
      description: One of the booking's extensions.
      schema:
        $ref: '#/components/schemas/Uuid'
    BidId:
      name: bidId
      in: path
      required: true
      description: One of the company's short-term bids.
      schema:
        $ref: '#/components/schemas/Uuid'
  headers:
    VersionETag:
      description: Strong aggregate version, sent back as If-Match to guard a command.
      schema:
        type: string
        pattern: '^"v[1-9][0-9]*"$'
    NoStore:
      description: Guest representations are never cached.
      schema:
        type: string
        const: no-store
    NoReferrer:
      description: Guest pages and fragment-bearing share URLs are never sent as a referrer.
      schema:
        type: string
        const: no-referrer
    NoIndex:
      description: Guest pages are not indexed.
      schema:
        type: string
        const: noindex, nofollow
  responses:
    BadRequest:
      description: Malformed or conditionally invalid input.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    Unauthorized:
      description: Missing or invalid customer bearer.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    Forbidden:
      description: Valid identity without an active company seat, or paid_plan_required for a cash-settled RFQ mutation without an effective paid plan.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    NotFound:
      description: Unknown or not visible to the caller's company.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    Conflict:
      description: Idempotency conflict or current business state rejects the command.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    PreconditionFailed:
      description: Expected financing version is stale; current_version is returned without applying the mutation.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    RateLimited:
      description: Coarse transport or definitive downstream limit reached.
      headers:
        Retry-After:
          schema:
            type: integer
            minimum: 1
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    ServiceUnavailable:
      description: Identity authority, database, or required dependency is unavailable; no mutation occurred.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    InferenceUnavailable:
      description: The inference router did not answer; inference_unavailable is retryable.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    ShortTermBookingCommand:
      description: The booking after the command, as the company's side sees it.
      headers:
        ETag:
          $ref: '#/components/headers/VersionETag'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ShortTermBooking'
    ShortTermBidCommand:
      description: The bid after the command.
      headers:
        ETag:
          $ref: '#/components/headers/VersionETag'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ShortTermBid'
    ShortTermConflict:
      description: short_term_conflict when the market's current state breaks a
        short-term rule, such as a week already taken, a withdrawn ask or a
        booking that already moved on, with a message worded for the
        customer; customer_profile_incomplete when the company or seat
        contact is not set up; or an Idempotency-Key conflict.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    ShortTermPaymentConflict:
      description: A short-term conflict as ShortTermConflict describes, or
        payment_refused when the payment provider refused to set up the
        payment; the customer contacts Liquid Compute.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    PaymentsUnavailable:
      description: payments_unavailable, retryable, when this deployment has no
        payments configuration or the payment provider did not answer;
        service_unavailable when the database or identity authority is
        unavailable. A retry with the same Idempotency-Key replays what was
        recorded and resumes its payment step.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    ContractReviewCommand:
      description: >-
        The review as the company sees it, read after the command; a replay
        reads it afresh. 409 codes name the refusal (terms_required,
        payment_needed, upload_in_progress, notes_unanswered,
        version_not_ready, upload_incomplete).
      headers:
        ETag:
          $ref: '#/components/headers/VersionETag'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ContractReview'
  schemas:
    'SupplierWire':
      {
        'type': 'object',
        'additionalProperties': false,
        'properties':
          {
            'amount_usd': { 'type': 'string', 'pattern': "^(0|[1-9][0-9]{0,9})(\\.[0-9]{1,2})?$" },
            'sent_on': { 'type': 'string', 'format': 'date' },
            'bank_reference': { 'type': 'string', 'maxLength': 200 },
            'payment_advice': { 'type': 'string', 'maxLength': 2000 },
            'document_version_id': { 'type': 'string', 'format': 'uuid' },
            'recorded_at': { 'type': 'string', 'format': 'date-time' },
          },
        'required': ['amount_usd', 'sent_on', 'bank_reference', 'payment_advice', 'recorded_at'],
      }
    'SupplierInvoice':
      {
        'type': 'object',
        'additionalProperties': false,
        'properties':
          {
            'reference': { 'type': 'string', 'maxLength': 200 },
            'period_start': { 'type': 'string', 'format': 'date' },
            'period_end': { 'type': 'string', 'format': 'date' },
            'due_date': { 'type': 'string', 'format': 'date' },
            'gross_usd': { 'type': 'string', 'pattern': "^(0|[1-9][0-9]{0,9})(\\.[0-9]{1,2})?$" },
            'deduction_usd':
              { 'type': 'string', 'pattern': "^(0|[1-9][0-9]{0,9})(\\.[0-9]{1,2})?$" },
            'deduction_reason': { 'type': 'string', 'maxLength': 2000 },
            'description': { 'type': 'string', 'maxLength': 2000 },
            'document_version_id': { 'type': 'string', 'format': 'uuid' },
            'id': { 'type': 'string', 'format': 'uuid' },
            'net_usd': { 'type': 'string', 'pattern': "^(0|[1-9][0-9]{0,9})(\\.[0-9]{1,2})?$" },
            'status':
              {
                'type': 'string',
                'enum': ['UNPAID', 'WIRE_SENT', 'RECEIPT_CONFIRMED', 'VOIDED', 'WIRE_REVERSED'],
              },
            'recorded_at': { 'type': 'string', 'format': 'date-time' },
            'wire': { '$ref': '#/components/schemas/SupplierWire' },
            'confirmed_on': { 'type': 'string', 'format': 'date' },
            'void_reason': { 'type': 'string', 'maxLength': 2000 },
          },
        'required':
          [
            'reference',
            'period_start',
            'period_end',
            'due_date',
            'gross_usd',
            'deduction_usd',
            'deduction_reason',
            'description',
            'id',
            'net_usd',
            'status',
            'recorded_at',
          ],
      }
    'SupplierState':
      {
        'type': 'object',
        'additionalProperties': false,
        'properties':
          {
            'page':
              {
                'type': 'object',
                'required': ['page', 'total', 'page_size'],
                'additionalProperties': false,
                'properties':
                  {
                    'page': { 'type': 'integer', 'minimum': 0 },
                    'total': { 'type': 'integer', 'minimum': 0 },
                    'page_size': { 'type': 'integer', 'enum': [10] },
                  },
              },
            'version': { 'type': 'integer', 'minimum': 0 },
            'invoices':
              { 'type': 'array', 'items': { '$ref': '#/components/schemas/SupplierInvoice' } },
          },
        'required': ['version', 'invoices'],
      }
    RecurringTerms:
      {
        'type': 'object',
        'required':
          [
            'terms_snapshot_id',
            'first_month',
            'term_ends_on',
            'issuer_name',
            'recipient_name',
            'wiring_instructions',
            'billing_schedule',
            'credit_policy',
            'credit_request_days',
            'refund_days',
            'nodes',
          ],
        'properties':
          {
            'version': { 'type': 'integer', 'minimum': 1 },
            'terms_snapshot_id': { 'type': 'string', 'format': 'uuid' },
            'first_month': { 'type': 'string' },
            'term_ends_on': { 'type': 'string' },
            'issuer_name': { 'type': 'string' },
            'recipient_name': { 'type': 'string' },
            'wiring_instructions': { 'type': 'string' },
            'billing_schedule': { 'type': 'string' },
            'credit_policy': { 'type': 'string' },
            'credit_request_days': { 'anyOf': [{ 'type': 'integer' }, { 'type': 'null' }] },
            'refund_days': { 'anyOf': [{ 'type': 'integer' }, { 'type': 'null' }] },
            'nodes': { 'type': 'array', 'items': { '$ref': '#/components/schemas/RecurringNode' } },
          },
      }
    RecurringNode:
      {
        'type': 'object',
        'required': ['machine_id', 'label', 'monthly_fee_usd'],
        'properties':
          {
            'machine_id': { 'type': 'string', 'format': 'uuid' },
            'label': { 'type': 'string' },
            'monthly_fee_usd': { 'type': 'string' },
          },
      }
    RecurringLine:
      {
        'type': 'object',
        'required': ['machine_id', 'label', 'fee_usd'],
        'properties':
          {
            'machine_id': { 'type': 'string', 'format': 'uuid' },
            'label': { 'type': 'string' },
            'fee_usd': { 'type': 'string' },
          },
      }
    RecurringUse:
      {
        'type': 'object',
        'required': ['claim_id', 'amount_usd'],
        'properties':
          {
            'claim_id': { 'type': 'string', 'format': 'uuid' },
            'amount_usd': { 'type': 'string' },
          },
      }
    RecurringReceipt:
      {
        'type': 'object',
        'required': ['amount_usd', 'received_on', 'bank_reference', 'evidence', 'recorded_by'],
        'properties':
          {
            'amount_usd': { 'type': 'string' },
            'received_on': { 'type': 'string' },
            'bank_reference': { 'type': 'string' },
            'evidence': { 'type': 'string' },
            'recorded_by': { 'type': 'string' },
          },
      }
    RecurringInvoice:
      {
        'type': 'object',
        'required':
          [
            'id',
            'reference',
            'month',
            'due_date',
            'description',
            'lines',
            'gross_usd',
            'credit_usd',
            'prepayment_usd',
            'due_usd',
            'credit_applications',
            'status',
            'issued_at',
          ],
        'properties':
          {
            'terms_version': { 'type': 'integer', 'minimum': 1 },
            'billing_terms': { '$ref': '#/components/schemas/RecurringTerms' },
            'id': { 'type': 'string', 'format': 'uuid' },
            'reference': { 'type': 'string' },
            'month': { 'type': 'string' },
            'due_date': { 'type': 'string' },
            'description': { 'type': 'string' },
            'lines': { 'type': 'array', 'items': { '$ref': '#/components/schemas/RecurringLine' } },
            'gross_usd': { 'type': 'string' },
            'credit_usd': { 'type': 'string' },
            'prepayment_usd': { 'type': 'string' },
            'due_usd': { 'type': 'string' },
            'credit_applications':
              {
                'type': 'array',
                'items': { '$ref': '#/components/schemas/RecurringUse' },
                'description': 'Empty on settlement history pages; complete allocation detail may be returned by invoice preview.',
              },
            'status':
              { 'type': 'string', 'enum': ['ISSUED', 'SETTLED', 'PAID', 'VOIDED', 'REVERSED'] },
            'issued_at': { 'type': 'string', 'format': 'date-time' },
            'paid_on': { 'type': 'string' },
            'void_reason': { 'type': 'string' },
          },
      }
    RecurringClaim:
      {
        'type': 'object',
        'required':
          [
            'source',
            'id',
            'invoice_id',
            'machine_id',
            'month',
            'written_request',
            'submitted_at',
            'status',
            'credit_usd',
          ],
        'properties':
          {
            'source': { 'type': 'string' },
            'id': { 'type': 'string', 'format': 'uuid' },
            'invoice_id': { 'type': 'string', 'format': 'uuid' },
            'machine_id': { 'type': 'string', 'format': 'uuid' },
            'month': { 'type': 'string' },
            'written_request': { 'type': 'string' },
            'submitted_at': { 'type': 'string', 'format': 'date-time' },
            'status': { 'type': 'string' },
            'credit_usd': { 'type': 'string' },
            'decision_reason': { 'type': 'string' },
            'reviewed_at':
              { 'anyOf': [{ 'type': 'string', 'format': 'date-time' }, { 'type': 'null' }] },
          },
      }
    RecurringRefund:
      {
        'type': 'object',
        'required': ['id', 'amount_usd', 'credit_applications', 'paid_on', 'status'],
        'properties':
          {
            'terms_version': { 'type': 'integer', 'minimum': 1 },
            'id': { 'type': 'string', 'format': 'uuid' },
            'amount_usd': { 'type': 'string' },
            'credit_applications':
              { 'type': 'array', 'items': { '$ref': '#/components/schemas/RecurringUse' } },
            'paid_on': { 'type': 'string' },
            'status': { 'type': 'string', 'enum': ['PAID', 'REVERSED'] },
          },
      }
    RecurringHistoryPage:
      type: object
      required: [page, total, page_size]
      properties:
        page: { type: integer, minimum: 0, maximum: 1000000 }
        total: { type: integer, minimum: 0 }
        page_size: { type: integer, enum: [10] }
      additionalProperties: false
    RecurringHistoryPages:
      type: object
      required: [invoices, claims, refunds]
      properties:
        invoices: { $ref: '#/components/schemas/RecurringHistoryPage' }
        claims: { $ref: '#/components/schemas/RecurringHistoryPage' }
        refunds: { $ref: '#/components/schemas/RecurringHistoryPage' }
      additionalProperties: false
    RecurringInvoicePageItem:
      allOf:
        - $ref: '#/components/schemas/RecurringInvoice'
        - type: object
          required: [requested_machine_ids]
          properties:
            requested_machine_ids:
              type: array
              description: Every machine with a customer-originated request for this invoice, across all claim pages. Used to suppress duplicate customer requests.
              items: { type: string, format: uuid }
    RecurringState:
      {
        'type': 'object',
        'required':
          [
            'version',
            'terms',
            'invoices',
            'claims',
            'refunds',
            'available_credit_usd',
            'pages',
            'next_month',
            'unpaid_invoice_count',
            'unpaid_amount_usd',
            'earliest_unpaid_due_date',
          ],
        'properties':
          {
            'version': { 'type': 'integer' },
            'pages': { '$ref': '#/components/schemas/RecurringHistoryPages' },
            'next_month':
              {
                'type': 'string',
                'description': 'Authoritative next billing month in YYYY-MM format; empty before billing terms are configured.',
              },
            'unpaid_invoice_count':
              {
                'type': 'integer',
                'minimum': 0,
                'description': 'Count of every ISSUED invoice, independent of the selected page.',
              },
            'unpaid_amount_usd':
              {
                'type': 'string',
                'pattern': '^[0-9]+\.[0-9]{2}$',
                'description': 'Total amount due across every ISSUED invoice.',
              },
            'earliest_unpaid_due_date':
              {
                'type': 'string',
                'description': 'Earliest due date across every ISSUED invoice; empty when none exist.',
              },
            'terms':
              { 'anyOf': [{ '$ref': '#/components/schemas/RecurringTerms' }, { 'type': 'null' }] },
            'invoices':
              {
                'type': 'array',
                'maxItems': 10,
                'items': { '$ref': '#/components/schemas/RecurringInvoicePageItem' },
              },
            'claims':
              {
                'type': 'array',
                'maxItems': 10,
                'items': { '$ref': '#/components/schemas/RecurringClaim' },
              },
            'refunds':
              {
                'type': 'array',
                'maxItems': 10,
                'items': { '$ref': '#/components/schemas/RecurringRefund' },
              },
            'available_credit_usd': { 'type': 'string' },
            'refund_due_on': { 'type': 'string' },
          },
      }
    RecurringIssue:
      {
        'type': 'object',
        'required': ['month', 'due_date', 'description', 'lines'],
        'properties':
          {
            'month': { 'type': 'string' },
            'due_date': { 'type': 'string' },
            'description': { 'type': 'string' },
            'lines': { 'type': 'array', 'items': { '$ref': '#/components/schemas/RecurringLine' } },
          },
      }
    RecurringReview:
      {
        'type': 'object',
        'required': ['approve', 'amount_usd', 'reason', 'evidence'],
        'properties':
          {
            'approve': { 'type': 'boolean' },
            'amount_usd': { 'type': 'string' },
            'reason': { 'type': 'string' },
            'evidence': { 'type': 'string' },
          },
      }
    RecurringGrant:
      {
        'type': 'object',
        'required': ['invoice_id', 'machine_id', 'amount_usd', 'reason', 'evidence'],
        'properties':
          {
            'invoice_id': { 'type': 'string', 'format': 'uuid' },
            'machine_id': { 'type': 'string', 'format': 'uuid' },
            'amount_usd': { 'type': 'string' },
            'reason': { 'type': 'string' },
            'evidence': { 'type': 'string' },
          },
      }
    PrepaymentInvoice:
      type: object
      required:
        - reference
        - issuer_name
        - recipient_name
        - amount_usd
        - wiring_instructions
        - description
        - id
        - deal_id
        - terms_snapshot_id
        - due_date
        - status
        - issued_at
        - received_on
        - confirmed_at
        - void_reason
        - version
      properties:
        reference:
          type: string
        issuer_name:
          type: string
        recipient_name:
          type: string
        amount_usd:
          type: string
        wiring_instructions:
          type: string
        description:
          type: string
        id:
          type: string
          format: uuid
        deal_id:
          type: string
          format: uuid
        terms_snapshot_id:
          type: string
          format: uuid
        due_date:
          type: string
          format: date
        status:
          type: string
          enum:
            - ISSUED
            - CONFIRMED
            - VOIDED
            - REVERSED
        issued_at:
          type: string
          format: date-time
        received_on:
          type:
            - string
            - 'null'
          format: date
        confirmed_at:
          type:
            - string
            - 'null'
          format: date-time
        void_reason:
          type:
            - string
            - 'null'
        version:
          type: integer
          minimum: 1
      description: Immutable manual USD prepayment invoice for executed physical terms.
        Buyer-only wiring instructions; no bank transfer is initiated by this record.
    MachineRequirements:
      type: object
      additionalProperties: false
      description: Each populated fact is required per machine. Model names match the full observed name, case-insensitively. Omitted fields are not requirements; freeform specifications never become automatic delivery gates. Capacities distinguish installed RAM, OS-visible RAM, proven local raw disk bytes, and scratch filesystem bytes.
      properties:
        cpu_model: { type: string, minLength: 1, maxLength: 256 }
        minimum_physical_cores: { type: integer, minimum: 1, maximum: 65536 }
        minimum_logical_cpus: { type: integer, minimum: 1, maximum: 65536 }
        minimum_installed_memory_bytes: { type: integer, minimum: 1, maximum: 9007199254740992 }
        minimum_os_memory_bytes: { type: integer, minimum: 1, maximum: 9007199254740992 }
        minimum_local_raw_storage_bytes: { type: integer, minimum: 1, maximum: 9007199254740992 }
        minimum_scratch_filesystem_bytes: { type: integer, minimum: 1, maximum: 9007199254740992 }
        gpu_model: { type: string, minLength: 1, maxLength: 256 }
        gpus_per_machine: { type: integer, minimum: 1, maximum: 16 }
        minimum_gpu_memory_mib: { type: integer, minimum: 1, maximum: 9007199254740992 }
        network:
          type: object
          additionalProperties: false
          description: Port line rates do not prove throughput or which RDMA path was used. A minimum rate requires a minimum port count; ALL_RAILS requires RDMA and stays unverified until dedicated evidence exists.
          properties:
            inter_node: { type: string, enum: [RDMA] }
            minimum_active_ports_per_machine: { type: integer, minimum: 1, maximum: 128 }
            minimum_port_rate_gbps: { type: number, exclusiveMinimum: 0, maximum: 3200 }
            verification_scope: { type: string, enum: [ALL_RAILS] }
    MachineSpecificationReport:
      type: object
      additionalProperties: false
      required: [source, status, required_satisfied, comparisons]
      properties:
        agreed_revision: { type: string }
        observed_at: { type: string, format: date-time }
        source: { type: string }
        status: { type: string, enum: [MATCH, MISMATCH, UNVERIFIED] }
        required_satisfied: { type: boolean }
        comparisons:
          type: array
          items:
            type: object
            additionalProperties: false
            required: [key, label, status]
            properties:
              key: { type: string }
              label: { type: string }
              required: { type: string }
              observed: { type: string }
              status: { type: string, enum: [MATCH, MISMATCH, UNVERIFIED, OBSERVED, NOT_TESTED] }
    ResolveActivityRequest:
      type: object
      required: [reason]
      properties:
        reason: { type: string, minLength: 1, maxLength: 1000 }
      additionalProperties: false
    QualificationAttemptReport:
      type: object
      required:
        [
          window,
          closure_verified,
          attempt_id,
          report_revision,
          generated_at,
          profile,
          outcome,
          execution_completeness,
          node_count,
          assisted_setup,
          nodes,
          notes,
        ]
      properties:
        attempt_id: { $ref: '#/components/schemas/Uuid' }
        report_revision: { type: string }
        generated_at: { type: string, format: date-time }
        closure_verified: { type: boolean }
        outcome: { type: string, enum: [PASS, FAIL, INCOMPLETE] }
        execution_completeness: { type: string }
        node_count: { type: integer, minimum: 0 }
        assisted_setup: { type: boolean }
        share_expires_at: { type: string, format: date-time }
        recipient_email: { type: string }
        window:
          type: object
          required: [starts_at, ends_at]
          properties:
            starts_at: { type: string, format: date-time }
            ends_at: { type: string, format: date-time }
            completed_at: { type: string, format: date-time }
        profile:
          type: object
          required: [id, version, label, qualifying]
          properties:
            id: { type: string }
            version: { type: string }
            label: { type: string }
            qualifying: { type: string }
            evaluation_only: { type: boolean }
        cluster:
          $ref: '#/components/schemas/QualificationClusterReport'
        notes:
          type: [array, 'null']
          items: { type: string }
        nodes:
          type: array
          items:
            type: object
            required: [label, hardware_verdict, execution_completeness, stages]
            properties:
              label: { type: string }
              hardware_verdict: { type: string }
              execution_completeness: { type: string }
              specifications: { $ref: '#/components/schemas/MachineSpecificationReport' }
              stages:
                type: [array, 'null']
                items:
                  type: object
                  required: [stage_code, outcome, metrics]
                  properties:
                    stage_code: { type: string }
                    outcome: { type: string }
                    metrics:
                      type: [array, 'null']
                      items:
                        type: object
                        additionalProperties: false
                        required: [name, value]
                        properties:
                          name: { type: string }
                          value: { type: [number, 'null'] }
                          unit: { type: string }
                    telemetry:
                      type: array
                      description: Display summaries of recorded measurements; excludes collection cadence and private diagnostic flags.
                      items:
                        type: object
                        additionalProperties: false
                        required:
                          [observed_at, gpu_index, temperature_c, power_w, utilization_percent]
                        properties:
                          observed_at: { type: string, format: date-time }
                          gpu_index: { type: integer, minimum: 0 }
                          temperature_c: { type: [number, 'null'] }
                          power_w: { type: [number, 'null'] }
                          utilization_percent: { type: [number, 'null'] }
                    findings:
                      type: array
                      items:
                        type: object
                        additionalProperties: false
                        required: [category, symptom, action]
                        properties:
                          category: { type: string }
                          gpu_index: { type: integer, minimum: 0 }
                          symptom: { type: string }
                          action: { type: string }
    QualificationClusterReport:
      type: object
      required: [scope, machines, collective_status, fabric_status, limitations]
      properties:
        scope: { type: string, enum: [PER_MACHINE, CONNECTED_CLUSTER] }
        machines:
          type: array
          items: { type: string }
          description: Exact machine labels covered by this connected attempt; empty for independent machine checks.
        collective_status: { type: string, enum: [UNVERIFIED, PASS, FAIL, INCOMPLETE] }
        bus_bandwidth_gbytes_per_second:
          type: number
          minimum: 0
          description: Measured collective bus bandwidth in GB/s; not a certification of advertised link speed or all rails.
        fabric_status:
          type: string
          enum: [UNVERIFIED]
          description: Full-rail throughput, GPUDirect use and congestion behavior are not verified by a connectivity result.
        limitations:
          type: array
          items: { type: string }
    StandaloneReportShareResponse:
      type: object
      required: [share]
      properties:
        share:
          oneOf:
            - type: 'null'
            - type: object
              required: [id, created_at, expires_at]
              properties:
                id: { $ref: '#/components/schemas/Uuid' }
                created_at: { type: string, format: date-time }
                expires_at: { type: string, format: date-time }
        url:
          type: string
          format: uri
          description: Returned only by creation. Contains the report capability in the fragment.
    QualificationProfileOption:
      type: object
      required:
        [
          profile_id,
          profile_version,
          profile_digest,
          label,
          gpu_vendor,
          gpu_model,
          gpu_form_factor,
          gpus_per_target,
          target_count,
          inter_node_kind,
          host_operating_system,
          host_operating_system_versions,
          evaluation_only,
          maximum_minutes,
        ]
      description: One environment-approved release resolved from the committed
        profile catalogue. profile_id and profile_version are what a request names.
      properties:
        profile_id: { type: string }
        profile_version: { type: string }
        profile_digest: { type: string, pattern: '^sha256:[0-9a-f]{64}$' }
        label: { type: string, description: Staff-facing name of the test. }
        gpu_vendor: { type: string }
        gpu_model: { type: string }
        gpu_form_factor: { type: string }
        gpus_per_target:
          type: integer
          minimum: 0
          description: Zero is supported only by evaluation-only software profiles.
        target_count: { type: integer, minimum: 1 }
        inter_node_kind:
          type: string
          description: none tests each machine separately; any other kind certifies a
            connected cluster and needs every machine at once.
        host_operating_system: { type: string }
        host_operating_system_versions:
          type: array
          items: { type: string }
        evaluation_only:
          type: boolean
          description: A pipeline-proof release that never qualifies hardware for a buyer.
        maximum_minutes:
          type: integer
          minimum: 1
          description: The profile's overall test timeout in whole minutes, rounded up.
            Setup and cleanup run on top of it.
      oneOf:
        - properties:
            evaluation_only: { const: true }
        - properties:
            evaluation_only: { const: false }
            gpus_per_target: { minimum: 1 }
    ForwardSpecification:
      type: object
      additionalProperties: false
      required: [id, version]
      properties:
        id: { type: string, pattern: '^[a-f0-9]{64}$' }
        version: { const: '1' }
    ForwardProductDefinition:
      type: object
      additionalProperties: false
      required:
        [
          index_id,
          label,
          region,
          hardware,
          gpus,
          gpus_per_node,
          interconnect,
          service_terms,
          payment_terms,
          price_inclusions,
          prepayment_percentage,
        ]
      description: >-
        Explicit comparable physical reserved-compute terms. Unknown fields are not
        defaulted. Different complete definitions are separate products; quoted
        cluster size cannot be normalized into a different trade size.
      properties:
        index_id: { type: string, enum: [a100-us, h100-us, h200-us, b200-us, b300-us] }
        label: { type: string, minLength: 1, maxLength: 128 }
        region: { type: string, minLength: 1, maxLength: 256 }
        hardware: { type: string, minLength: 1, maxLength: 512 }
        gpus: { type: integer, minimum: 1, maximum: 9007199254740991 }
        gpus_per_node: { type: integer, minimum: 1, maximum: 9007199254740991 }
        interconnect: { type: string, minLength: 1, maxLength: 512 }
        service_terms: { type: string, minLength: 1, maxLength: 2048 }
        payment_terms: { type: string, minLength: 1, maxLength: 2048 }
        price_inclusions: { type: string, minLength: 1, maxLength: 2048 }
        prepayment_percentage:
          { type: string, pattern: '^(100(\.0{1,4})?|[0-9]{1,2}(\.[0-9]{1,4})?)$' }
    ForwardProduct:
      type: object
      additionalProperties: false
      required: [specification, definition, method]
      properties:
        specification: { $ref: '#/components/schemas/ForwardSpecification' }
        definition: { $ref: '#/components/schemas/ForwardProductDefinition' }
        method:
          const: PHYSICAL_RESERVED_V1
          description: US physical delivery of dedicated, nonpreemptible full GPUs every hour of the UTC calendar delivery window, priced in USD per GPU-hour. Start is inclusive and end is exclusive.
    ForwardQuoteLevel:
      type: object
      additionalProperties: false
      required:
        [usd_per_gpu_hour, quoted_gpu_count, source_count, oldest_quote_date, newest_quote_date]
      properties:
        usd_per_gpu_hour: { type: string, pattern: '^[0-9]+(\.[0-9]+)?$' }
        quoted_gpu_count: { type: integer, minimum: 1, maximum: 9007199254740991 }
        source_count: { type: integer, minimum: 1 }
        oldest_quote_date: { type: string, format: date }
        newest_quote_date: { type: string, format: date }
      description: >-
        Best observed bid or offer and quoted quantity at exactly that price.
        Counts are distinct source quotes, not independent firms or executable
        supply. Date bounds include only quotes at the selected price.
    ForwardDeliveryWindow:
      type: object
      additionalProperties: false
      required: [delivery_start, delivery_end, bid, ask, source_count, crossed]
      properties:
        delivery_start: { type: string, format: date }
        delivery_end: { type: string, format: date }
        bid:
          oneOf: [{ $ref: '#/components/schemas/ForwardQuoteLevel' }, { type: 'null' }]
        ask:
          oneOf: [{ $ref: '#/components/schemas/ForwardQuoteLevel' }, { type: 'null' }]
        source_count: { type: integer, minimum: 0 }
        crossed: { type: boolean }
      description: UTC calendar delivery starts inclusively and ends exclusively. Crossed means best bid exceeds best offer; no midpoint is produced.
    ForwardPricingResult:
      type: object
      additionalProperties: false
      required: [specification, as_of, as_of_date, price_type, monthly, strips]
      properties:
        specification: { $ref: '#/components/schemas/ForwardSpecification' }
        as_of: { type: string, format: date-time }
        as_of_date: { type: string, format: date }
        price_type: { const: INDICATIVE }
        monthly:
          {
            type: array,
            minItems: 1,
            maxItems: 120,
            items: { $ref: '#/components/schemas/ForwardDeliveryWindow' },
          }
        strips: { type: array, items: { $ref: '#/components/schemas/ForwardDeliveryWindow' } }
      description: >-
        Snapshot timestamp is UTC, separately from original quote dates. Exact
        UTC calendar months form monthly observations. Longer or midmonth strips
        never populate constituent monthly prices. Quotes require explicit expiry
        after the snapshot; there is no implicit age cutoff or stale-price fallback.
    ForwardEstimatedMonth:
      type: object
      additionalProperties: false
      required: [delivery_start, delivery_end, modeled_usd_gpu_hour]
      properties:
        delivery_start: { type: string, format: date }
        delivery_end: { type: string, format: date }
        modeled_usd_gpu_hour: { type: [string, 'null'], pattern: '^[0-9]+(\.[0-9]+)?$' }
    ForwardEstimateSourceTerm:
      type: object
      additionalProperties: false
      required: [months, usd_per_gpu_hour]
      properties:
        months: { type: integer, minimum: 1, maximum: 120 }
        usd_per_gpu_hour: { type: string, pattern: '^[0-9]+(\.[0-9]+)?$' }
      description: Source cumulative commitment rate from the model start; not an independent monthly quote. Private source identifiers are omitted.
    ForwardEstimate:
      type: object
      additionalProperties: false
      required:
        [
          index_id,
          as_of_date,
          label,
          basis,
          methodology_version,
          delivery_start,
          delivery_end,
          monthly,
          source_terms,
        ]
      properties:
        index_id: { type: string, enum: [a100-us, h100-us, h200-us, b200-us, b300-us] }
        as_of_date: { type: string, format: date }
        label: { type: string, minLength: 1, maxLength: 128 }
        basis: { type: string, minLength: 1, maxLength: 256 }
        methodology_version: { const: strip-residual-v1 }
        delivery_start: { type: string, format: date }
        delivery_end: { type: string, format: date }
        monthly:
          type: array
          minItems: 24
          maxItems: 24
          items: { $ref: '#/components/schemas/ForwardEstimatedMonth' }
        source_terms:
          type: array
          minItems: 2
          maxItems: 120
          items: { $ref: '#/components/schemas/ForwardEstimateSourceTerm' }
      description: >-
        Calendar-hour residual decomposition of nested commitment prices, with
        flat residual blocks, assumed comparable terms and no financing adjustment.
        as_of_date is the research vintage, never the request time. Monthly estimates
        are not executable quotes or forecasts of spot prices; no extrapolation is
        supplied. Raw source evidence and research assumptions are internal.
    ForwardPricing:
      type: object
      additionalProperties: false
      required: [as_of, products, selected_product_id, pricing, estimate]
      properties:
        as_of: { type: string, format: date-time }
        products:
          { type: array, maxItems: 128, items: { $ref: '#/components/schemas/ForwardProduct' } }
        selected_product_id: { type: [string, 'null'], pattern: '^[a-f0-9]{64}$' }
        pricing:
          oneOf: [{ $ref: '#/components/schemas/ForwardPricingResult' }, { type: 'null' }]
        estimate:
          oneOf: [{ $ref: '#/components/schemas/ForwardEstimate' }, { type: 'null' }]
    AccessMachine:
      type: object
      additionalProperties: false
      required:
        - id
        - address
        - port
        - host_key
      properties:
        id:
          $ref: '#/components/schemas/Uuid'
        address:
          type: string
          description: Pinned public IPv4 or IPv6 literal; private, reserved, and platform
            destinations are rejected.
        port:
          type: integer
          minimum: 1
          maximum: 65535
        host_key:
          type: string
          description: Verified OpenSSH host public key; certificates and authorized_keys
            options are not accepted.
        source:
          type: string
          enum: [MANUAL, QUALIFICATION]
          description: Assigned by the service; MANUAL machines are delivered under manual
            evidence, QUALIFICATION machines come from a qualified enrollment.
        enrollment_id:
          type: [string, 'null']
          format: uuid
        enrollment_generation:
          type: [integer, 'null']
        identity_revision:
          type: integer
          minimum: 1
          description: Changes only when the host key or enrollment changes; fences this
            machine's login, credentials, check and acceptance.
    AccessEvidence:
      type: object
      additionalProperties: false
      required: [revision, source, attempt_id, evidence_document_version_id, accepted_at]
      properties:
        revision:
          type: integer
          minimum: 1
        source:
          type: string
          enum: [QUALIFICATION, MANUAL_DOCUMENT]
        attempt_id:
          type: [string, 'null']
          format: uuid
        evidence_document_version_id:
          type: [string, 'null']
          format: uuid
        accepted_at:
          $ref: '#/components/schemas/Timestamp'
    MachineDelivery:
      type: object
      additionalProperties: false
      required:
        [
          machine_id,
          identity_revision,
          user,
          state,
          evidence,
          provisioned,
          connection_checked_at,
          accepted_at,
          commencement_at,
        ]
      properties:
        machine_id:
          $ref: '#/components/schemas/Uuid'
        identity_revision:
          type: integer
          minimum: 1
        user:
          type: string
          description: Login provisioned on this machine for this allocation and identity.
        state:
          type: string
          enum: [PENDING_PROVISIONING, AWAITING_CHECK, READY, ACCEPTED, REPLACED]
          description: PENDING_PROVISIONING until an APPLIED supplier report exists for the
            machine's current identity; READY once its connection check passed; ACCEPTED
            once the buyer commercially accepted it at this identity.
        evidence:
          oneOf:
            - $ref: '#/components/schemas/AccessEvidence'
            - type: 'null'
        provisioned:
          type: boolean
        connection_checked_at:
          type: [string, 'null']
          format: date-time
        accepted_at:
          type: [string, 'null']
          format: date-time
        commencement_at:
          type: [string, 'null']
          format: date-time
    MachineAcceptance:
      type: object
      additionalProperties: false
      required:
        [
          machine_id,
          revision,
          identity_revision,
          accepted_at,
          accepted_by_seat_id,
          commencement_at,
          state,
        ]
      properties:
        machine_id:
          $ref: '#/components/schemas/Uuid'
        revision:
          type: integer
          minimum: 1
        identity_revision:
          type: integer
          minimum: 1
        accepted_at:
          $ref: '#/components/schemas/Timestamp'
        accepted_by_seat_id:
          $ref: '#/components/schemas/Uuid'
        commencement_at:
          $ref: '#/components/schemas/Timestamp'
        state:
          type: string
          enum: [ACCEPTED, REPLACED]
    WorkloadTrial:
      type: object
      additionalProperties: false
      properties:
        reviewed_at:
          type: string
          format: date-time
        review_note:
          type: string
          maxLength: 2000
        status:
          type: string
        id:
          type: string
          format: uuid
        allocation_id:
          type: string
          format: uuid
        version:
          type: integer
          minimum: 1
        state:
          type: string
          enum:
            - OPEN
            - COMPLETED
            - CANCELLED
            - RELEASED
            - SUPERSEDED
        starts_at:
          type: string
          format: date-time
        ends_at:
          type: string
          format: date-time
        machines:
          type: array
          minItems: 1
          maxItems: 100
          items:
            type: object
            additionalProperties: false
            properties:
              address:
                type: string
              port:
                type: integer
                minimum: 1
                maximum: 65535
              id:
                type: string
                format: uuid
              identity_revision:
                type: integer
                minimum: 1
            required:
              - address
              - port
              - id
              - identity_revision
        workload:
          type: string
          maxLength: 4000
        baseline:
          type: string
          maxLength: 4000
        outcome:
          type: string
          enum:
            - ''
            - PROCEED
            - ISSUES
            - NOT_PROCEEDING
        results:
          type: string
          maxLength: 8000
        feedback:
          type: string
          maxLength: 4000
        extension_until:
          type: string
          format: date-time
          nullable: true
        extension_reason:
          type: string
          maxLength: 2000
        extension_decision:
          type: string
          enum:
            - ''
            - PENDING
            - APPROVED
            - REJECTED
            - WITHDRAWN
        extension_response:
          type: string
          maxLength: 2000
        created_at:
          type: string
          format: date-time
      required:
        - status
        - id
        - allocation_id
        - version
        - state
        - starts_at
        - ends_at
        - machines
        - workload
        - baseline
        - outcome
        - results
        - feedback
        - extension_until
        - extension_reason
        - extension_decision
        - extension_response
        - created_at
    BuyerWorkloadTrialRequest:
      type: object
      additionalProperties: false
      properties:
        version:
          type: integer
          minimum: 1
        operation:
          type: string
          enum:
            - RESULTS
            - EXTENSION
        outcome:
          type: string
          enum:
            - PROCEED
            - ISSUES
            - NOT_PROCEEDING
        results:
          type: string
          maxLength: 8000
        feedback:
          type: string
          maxLength: 4000
        extension_until:
          type: string
          format: date-time
        reason:
          type: string
          maxLength: 2000
      required:
        - version
        - operation
    MachineAccess:
      type: object
      additionalProperties: false
      required:
        - allocation_id
        - deal_id
        - revision
        - version
        - starts_at
        - ends_at
        - quantity
        - expected_machine_count
        - machines
        - machine_delivery
        - acceptances
        - accepted_machine_ids
        - accepted_count
        - outstanding_count
        - verified_count
        - ready_count
        - commencement_at
        - suspended
        - capacity_verified
        - connection_checked_at
        - buyer_confirmed_at
        - buyer_confirmed_by
        - status
      properties:
        trial:
          $ref: '#/components/schemas/WorkloadTrial'
        trial_history:
          type: array
          description: Previous buyer-reported evaluations, newest first.
          items:
            $ref: '#/components/schemas/WorkloadTrial'
        allocation_id:
          $ref: '#/components/schemas/Uuid'
        deal_id:
          $ref: '#/components/schemas/Uuid'
        revision:
          type: integer
          minimum: 1
        version:
          type: integer
          minimum: 1
        starts_at:
          $ref: '#/components/schemas/Timestamp'
        ends_at:
          $ref: '#/components/schemas/Timestamp'
        quantity:
          type: integer
          description: The allocation's ordered quantity; never changed by delivery.
        expected_machine_count:
          type: [integer, 'null']
          minimum: 1
          description: Ordered NODE quantity, or the consistent expected_node_count of
            non-cancelled qualification requests for a GPU allocation. Null when unknown
            or conflicting; never inferred from the currently delivered subset.
        machines:
          type: array
          minItems: 1
          maxItems: 100
          items:
            $ref: '#/components/schemas/AccessMachine'
        machine_delivery:
          type: array
          items:
            $ref: '#/components/schemas/MachineDelivery'
        acceptances:
          type: array
          items:
            $ref: '#/components/schemas/MachineAcceptance'
        accepted_machine_ids:
          type: array
          items:
            $ref: '#/components/schemas/Uuid'
        accepted_count:
          type: integer
        outstanding_count:
          type: [integer, 'null']
          minimum: 0
          description: Expected machines minus accepted current identities, bounded at zero.
            Null when expected_machine_count is unknown.
        verified_count:
          type: integer
        ready_count:
          type: integer
        commencement_at:
          type: [string, 'null']
          format: date-time
          description: Earliest commencement among the buyer's acceptances.
        suspended:
          type: boolean
        capacity_verified:
          type: boolean
          description: True only when every current machine is covered by evidence.
        connection_checked_at:
          type: [string, 'null']
          format: date-time
          description: Set only when every current machine passed its check.
        buyer_confirmed_at:
          type:
            - string
            - 'null'
          format: date-time
        buyer_confirmed_by:
          type:
            - string
            - 'null'
          format: uuid
        status:
          type: string
          enum:
            - UNAVAILABLE
            - EXPIRED
            - SUSPENDED
            - AWAITING_ACCEPTANCE
            - PREPARING
            - SCHEDULED
            - AVAILABLE
    MachineAccessList:
      type: object
      additionalProperties: false
      required:
        - items
      properties:
        items:
          type: array
          items:
            $ref: '#/components/schemas/MachineAccess'
    MachineSSHKey:
      type: object
      additionalProperties: false
      required: [id, public_key, fingerprint, created_at]
      properties:
        id:
          $ref: '#/components/schemas/Uuid'
        public_key:
          type: string
        fingerprint:
          type: string
        created_at:
          $ref: '#/components/schemas/Timestamp'
    MachineSSHKeyList:
      type: object
      additionalProperties: false
      required: [items]
      properties:
        items:
          type: array
          items:
            $ref: '#/components/schemas/MachineSSHKey'
    AccessCredential:
      type: object
      additionalProperties: false
      required:
        - id
        - allocation_id
        - expires_at
        - relay_certificate
        - machine_certificate
        - ssh_config
        - known_hosts
      properties:
        ssh_key_id:
          $ref: '#/components/schemas/Uuid'
        id:
          $ref: '#/components/schemas/Uuid'
        allocation_id:
          $ref: '#/components/schemas/Uuid'
        expires_at:
          $ref: '#/components/schemas/Timestamp'
        relay_certificate:
          type: string
        machine_certificate:
          type: string
        ssh_config:
          type: string
        known_hosts:
          type: string
      description:
        Rental-scoped public certificates and connection files. Private keys remain with the buyer.
        Save the returned files locally; issuance does not confirm a successful buyer connection.
    IssueAccessCredentialRequest:
      type: object
      additionalProperties: false
      oneOf:
        - required: [public_key]
        - required: [ssh_key_id]
      properties:
        ssh_key_id:
          $ref: '#/components/schemas/Uuid'
        public_key:
          type: string
          description:
            One plain Ed25519 OpenSSH public key whose private key remains on the buyer device;
            options and certificates are rejected.
          minLength: 1
          maxLength: 16384
    ConfirmMachineAccessRequest:
      type: object
      additionalProperties: false
      required:
        - revision
      properties:
        revision:
          type: integer
          minimum: 1
    AcceptMachinesRequest:
      type: object
      additionalProperties: false
      required: [machine_ids, version]
      properties:
        machine_ids:
          type: array
          minItems: 1
          maxItems: 100
          items:
            $ref: '#/components/schemas/Uuid'
        version:
          type: integer
          minimum: 1
          description: The allocation record version being accepted against.
    CreateQualificationTechnicianSessionRequest:
      type: object
      additionalProperties: false
      properties:
        challenge_id:
          type: string
          format: uuid
          description: Legacy verification challenge; provide together with code.
        code:
          type: string
          pattern: ^[0-9]{6}$
          description: Legacy email code; omitted for direct capability exchange.
      dependentRequired:
        challenge_id: [code]
        code: [challenge_id]
    QualificationTechnicianSession:
      type: object
      required: [session_token, expires_at, role, request_id]
      properties:
        session_token:
          type: string
          description: Sensitive request-scoped session capability for the BFF cookie exchange.
        expires_at: { type: string, format: date-time }
        role:
          type: string
          enum: [TECHNICIAN, DECISION_MAKER]
        request_id: { $ref: '#/components/schemas/Uuid' }
    Uuid:
      type: string
      format: uuid
    Timestamp:
      type: string
      format: date-time
    Date:
      type: string
      format: date
    TenorMonths:
      type: integer
      format: int32
      minimum: 1
      maximum: 120
      description: Contract tenor expressed as a whole number of months.
    TermType:
      type: string
      enum: [FIXED, M2M]
      description: FIXED is a tenor from the start month; M2M runs month to month
        from the start with no tenor and is physical-only.
    IntakeTermType:
      type: string
      enum: [FIXED, M2M, WEEKS, HOURS]
      description: FIXED is a tenor from the start month; M2M runs month to month
        from the start with no tenor and is physical-only; WEEKS and HOURS,
        short-term compute's terms, run term_weeks whole weeks or term_hours
        whole hours from the start with no tenor and are physical-only.
        Snapshots may state WEEKS or HOURS; customer intakes and their
        requests state only a TermType.
    TermWeeks:
      type: integer
      format: int32
      minimum: 1
      maximum: 52
      description: A WEEKS term's length in whole weeks from its start day.
    TermHours:
      type: integer
      format: int32
      minimum: 1
      maximum: 744
      description: An HOURS term's length in whole hours from its start, 24 per
        day of a booking of days.
    Interconnect:
      type: string
      enum: [INFINIBAND, ROCE, ETHERNET, NONE]
      description: The GPU-to-GPU network between nodes, offered on a sell and
        required on a buy. NONE is a single node. Physical-only.
    InterconnectSpeed:
      type: integer
      format: int32
      minimum: 1
      maximum: 3200
      description: Interconnect bandwidth per GPU in Gb/s; requires a network.
    DeliveryCountry:
      type: string
      pattern: '^[A-Z]{2}$'
      description: The structured ISO 3166-1 alpha-2 delivery country of a physical
        location, chosen from a country list beside the location text. It needs
        a stated location, not a flexible one, and is never derived from the
        location text. The desk market views show its region.
    PositiveUsdPrice:
      type: string
      pattern: '^(0|[1-9][0-9]{0,11})(\.[0-9]{1,8})?$'
      not:
        pattern: '^0(\.0{1,8})?$'
      description: Positive exact USD price with at most 12 integer and 8 fractional digits.
    PositiveUsdAmount:
      type: string
      pattern: '^(0|[1-9][0-9]{0,17})(\.[0-9]{1,2})?$'
      not:
        pattern: '^0(\.0{1,2})?$'
      description: Positive exact USD amount with at most 18 integer and 2 fractional digits.
    PositiveGpuHours:
      type: string
      pattern: '^(0|[1-9][0-9]{0,19})(\.[0-9]{1,4})?$'
      not:
        pattern: '^0(\.0{1,4})?$'
      description: Positive exact GPU-hour quantity with at most 20 integer and 4 fractional digits.
    PositionStatus:
      type: string
      enum: [PENDING_SIGNATURE, OPEN, SETTLED, VOID]
      description: PENDING_SIGNATURE until contracting; OPEN until economically closed; SETTLED after full settlement/completion; VOID on cancellation/loss. Publication does not change this state.
    UnmarkableReason:
      type: string
      enum: [NON_INDEX_CHIP, TOTAL_PRICE, INCOMPLETE_TERMS]
    NonNegativeUsdAmount:
      type: string
      pattern: '^(0|[1-9][0-9]{0,17})(\.[0-9]{1,2})?$'
      description: Non-negative exact USD amount with at most 18 integer and 2 fractional digits.
    SettlementObservedValue:
      type: string
      pattern: '^-?(0|[1-9][0-9]{0,19})(\.[0-9]{1,10})?$'
      description: Signed exact settlement observation with at most 20 integer and 10 fractional digits.
    Percentage:
      type: string
      pattern: '^(100(\.0{1,4})?|([1-9][0-9]?|0)(\.[0-9]{1,4})?)$'
    PositivePercentage:
      type: string
      pattern: '^(100(\.0{1,4})?|([1-9][0-9]?|0)(\.[0-9]{1,4})?)$'
      not:
        pattern: '^0(\.0{1,4})?$'
    ProductArea:
      type: string
      enum: [COMPUTE, TRADE, FINANCE]
    SettlementType:
      type: string
      enum: [PHYSICAL, CASH]
    Side:
      type: string
      enum: [BUY, SELL]
    QuantityUnit:
      type: string
      enum: [GPU, NODE]
    PriceUnit:
      type: string
      enum: [GPU_HOUR, NODE_HOUR, TOTAL]
    GpuChip:
      type: string
      enum: [A100, H100, H200, B200, B300]
    HomeStatus:
      type: string
      enum:
        [
          DRAFT,
          ACTIVE,
          ARCHIVED,
          STRUCTURING,
          SIGNING,
          EXECUTION,
          COMPLETED,
          CANCELLED,
          SUBMITTED,
          IN_PROGRESS,
          ACTION_REQUIRED,
          CLOSED,
        ]
    DealPhase:
      type: string
      enum: [STRUCTURING, SIGNING, EXECUTION, COMPLETED, CANCELLED]
    PageCursor:
      type: [string, 'null']
      maxLength: 1024
    FlexibleSpecifications:
      type: object
      properties:
        machine_requirements: { $ref: '#/components/schemas/MachineRequirements' }
      maxProperties: 64
      additionalProperties: true
      description: Technical GPU and infrastructure facts only; customer-visible values must not contain counterparty identity, contact data, source provenance, or internal notes.
    CommercialTerms:
      type: object
      required: [gpu_chip, quantity, quantity_unit]
      description: A month-to-month term has no tenor. A Draft saved in a quote group
        may not have its start date or a fixed term's tenor yet; both are
        present once the intake is submitted for review or Active.
      oneOf:
        - properties:
            term_type: { enum: [FIXED] }
            tenor:
              oneOf:
                - $ref: '#/components/schemas/TenorMonths'
                - type: 'null'
        - required: [term_type]
          properties:
            term_type: { enum: [M2M] }
            tenor: { type: 'null' }
      properties:
        gpu_chip:
          type: string
          minLength: 1
          maxLength: 100
          description: Intakes recorded before the chip vocabulary keep their free-text chip until their terms are re-submitted; supplied terms must name a GpuChip.
        quantity:
          type: integer
          format: int64
          minimum: 1
        quantity_unit:
          $ref: '#/components/schemas/QuantityUnit'
        location:
          type: [string, 'null']
          maxLength: 500
        location_flexible:
          type: boolean
          default: false
        country:
          oneOf:
            - $ref: '#/components/schemas/DeliveryCountry'
            - type: 'null'
        service_window_start:
          oneOf:
            - $ref: '#/components/schemas/Date'
            - type: 'null'
        service_window_start_precision:
          $ref: '#/components/schemas/StartPrecision'
          description: DAY unless the start is stated as a month, when service_window_start is its 1st.
        tenor:
          oneOf:
            - $ref: '#/components/schemas/TenorMonths'
            - type: 'null'
        term_type:
          $ref: '#/components/schemas/TermType'
        interconnect:
          oneOf:
            - $ref: '#/components/schemas/Interconnect'
            - type: 'null'
        interconnect_gbps_per_gpu:
          oneOf:
            - $ref: '#/components/schemas/InterconnectSpeed'
            - type: 'null'
        price_usd:
          oneOf:
            - $ref: '#/components/schemas/PositiveUsdPrice'
            - type: 'null'
        price_unit:
          oneOf:
            - $ref: '#/components/schemas/PriceUnit'
            - type: 'null'
        prepayment_percentage:
          oneOf:
            - $ref: '#/components/schemas/Percentage'
            - type: 'null'
        commercial_expires_at:
          oneOf:
            - $ref: '#/components/schemas/Timestamp'
            - type: 'null'
        technical_specifications:
          $ref: '#/components/schemas/FlexibleSpecifications'
        notes:
          type: [string, 'null']
          maxLength: 40000
          description: Free-form intake context, including specifications, settlement index, payment terms, or other commercial details.
      additionalProperties: false
    IntakeGpuChip:
      description: An indexed-market chip, or a chip supported for PHYSICAL delivery only, stored exactly as listed. A physical-only chip has no index, so it is never priced on one.
      oneOf:
        - $ref: '#/components/schemas/GpuChip'
        - { type: string, enum: [A10, GB300, 'RTX PRO 6000', 'RTX 5090'] }
    StartPrecision:
      type: string
      enum: [DAY, MONTH]
      description: How exactly service_window_start is stated. DAY is the date itself; MONTH is any day of that calendar month, stated as its 1st.
    CommercialTermsInput:
      type: object
      description: Terms as supplied by a request; gpu_chip must be a supported intake
        chip. A physical-only chip requires PHYSICAL delivery. A fixed term, the default when
        term_type is omitted, states a tenor; month-to-month states none and is
        physical-only. The interconnect and its speed are physical-only, and a
        speed needs a network other than NONE. A country is physical-only and
        needs a stated location. Weeks and hours terms are listed and booked
        through short-term compute, so term_type WEEKS or HOURS, term_weeks
        and term_hours are refused with 400.
      required: [gpu_chip, quantity, quantity_unit, service_window_start]
      oneOf:
        - required: [tenor]
          properties:
            term_type: { enum: [FIXED] }
            tenor: { $ref: '#/components/schemas/TenorMonths' }
        - required: [term_type]
          properties:
            term_type: { enum: [M2M] }
            tenor: { type: 'null' }
      properties:
        gpu_chip:
          $ref: '#/components/schemas/IntakeGpuChip'
        quantity:
          type: integer
          format: int64
          minimum: 1
        quantity_unit:
          $ref: '#/components/schemas/QuantityUnit'
        location:
          type: [string, 'null']
          maxLength: 500
        location_flexible:
          type: boolean
          default: false
        country:
          oneOf:
            - $ref: '#/components/schemas/DeliveryCountry'
            - type: 'null'
        service_window_start:
          $ref: '#/components/schemas/Date'
        service_window_start_precision:
          oneOf:
            - $ref: '#/components/schemas/StartPrecision'
            - type: 'null'
          description: Omitted or null is DAY. MONTH needs service_window_start on the 1st of its month.
        tenor:
          oneOf:
            - $ref: '#/components/schemas/TenorMonths'
            - type: 'null'
        term_type:
          $ref: '#/components/schemas/TermType'
        interconnect:
          oneOf:
            - $ref: '#/components/schemas/Interconnect'
            - type: 'null'
        interconnect_gbps_per_gpu:
          oneOf:
            - $ref: '#/components/schemas/InterconnectSpeed'
            - type: 'null'
        price_usd:
          oneOf:
            - $ref: '#/components/schemas/PositiveUsdPrice'
            - type: 'null'
        price_unit:
          oneOf:
            - $ref: '#/components/schemas/PriceUnit'
            - type: 'null'
        prepayment_percentage:
          oneOf:
            - $ref: '#/components/schemas/Percentage'
            - type: 'null'
        commercial_expires_at:
          oneOf:
            - $ref: '#/components/schemas/Timestamp'
            - type: 'null'
        technical_specifications:
          $ref: '#/components/schemas/FlexibleSpecifications'
        notes:
          type: [string, 'null']
          maxLength: 40000
          description: Free-form intake context, including specifications, settlement index, payment terms, or other commercial details.
      additionalProperties: false
    CustomerSafeSnapshot:
      type: object
      minProperties: 1
      maxProperties: 39
      description: Staff-curated commercial snapshot. Only these fields may cross a customer boundary; identity and source evidence are separate gated records. A month-to-month term (term_type M2M) has no tenor, so its tenor is absent or null; a fixed snapshot's tenor is a whole number of months when present. A short-term weeks term (term_type WEEKS) states term_weeks and an hours term (term_type HOURS) states term_hours, neither with a tenor; only a WEEKS snapshot states term_weeks and only an HOURS snapshot term_hours.
      oneOf:
        - required: [term_type]
          not: { anyOf: [{ required: [term_weeks] }, { required: [term_hours] }] }
          properties:
            term_type: { const: M2M }
            tenor: { type: 'null' }
        - not: { anyOf: [{ required: [term_weeks] }, { required: [term_hours] }] }
          properties:
            term_type: { const: FIXED }
            tenor: { $ref: '#/components/schemas/TenorMonths' }
        - required: [term_type, term_weeks]
          not: { required: [term_hours] }
          properties:
            term_type: { const: WEEKS }
            tenor: { type: 'null' }
        - required: [term_type, term_hours]
          not: { required: [term_weeks] }
          properties:
            term_type: { const: HOURS }
            tenor: { type: 'null' }
      properties:
        gpu_hours:
          $ref: '#/components/schemas/PositiveGpuHours'
          description: Positive contractual GPU-hours, independent of physical GPU count. Requires a CASH GPU_HOUR price, H100, trade_date, pricing_date and reference_index; must not also specify quantity, quantity_unit or notional_usd.
        trade_date: { type: string, format: date }
        pricing_date:
          type: string
          format: date
          description: Final index observation date, on or after trade_date. Distinct from calculation and payment dates.
        reference_index: { type: string, enum: [LCI-H100-US] }

        settlement_type:
          $ref: '#/components/schemas/SettlementType'
        side:
          $ref: '#/components/schemas/Side'
        gpu_chip:
          type: string
          minLength: 1
          maxLength: 100
        quantity:
          type: integer
          format: int64
          minimum: 1
        quantity_unit:
          $ref: '#/components/schemas/QuantityUnit'
        gpus_per_node:
          type: integer
          format: int64
          minimum: 1
          maximum: 1000
          description: The GPUs in one node of a NODE quantity or a NODE_HOUR price; a snapshot without it has 8.
        location:
          type: string
          maxLength: 500
        location_flexible:
          type: boolean
        service_window_start:
          $ref: '#/components/schemas/Date'
        tenor:
          oneOf:
            - $ref: '#/components/schemas/TenorMonths'
            - type: 'null'
        term_type:
          $ref: '#/components/schemas/IntakeTermType'
        term_weeks:
          $ref: '#/components/schemas/TermWeeks'
        term_hours:
          $ref: '#/components/schemas/TermHours'
        interconnect:
          $ref: '#/components/schemas/Interconnect'
        interconnect_gbps_per_gpu:
          $ref: '#/components/schemas/InterconnectSpeed'
        price_usd:
          $ref: '#/components/schemas/PositiveUsdPrice'
        price_unit:
          $ref: '#/components/schemas/PriceUnit'
        prepayment_percentage:
          $ref: '#/components/schemas/Percentage'
        payment_terms:
          type: string
          maxLength: 2000
        commercial_expires_at:
          $ref: '#/components/schemas/Timestamp'
        settlement_reference:
          type: string
          maxLength: 1000
        technical_specifications:
          $ref: '#/components/schemas/FlexibleSpecifications'
        specification_narrative:
          type: string
          maxLength: 16000
        additional_terms:
          type: string
          maxLength: 16000
        notes:
          type: string
          maxLength: 40000
          description: Customer-shareable notes explicitly curated for this snapshot.
        round:
          type: integer
          minimum: 1
        state:
          type: string
          minLength: 1
          maxLength: 200
        reference:
          type: string
          minLength: 1
          maxLength: 1000
        notional_usd:
          $ref: '#/components/schemas/PositiveUsdAmount'
        observation_convention:
          type: string
          maxLength: 4000
        settlement_method:
          type: string
          maxLength: 4000
        settlement_date:
          $ref: '#/components/schemas/Date'
        observed_value:
          $ref: '#/components/schemas/SettlementObservedValue'
        final_amount_usd:
          $ref: '#/components/schemas/NonNegativeUsdAmount'
        payment_status:
          type: string
          enum: [NOT_RECORDED, PENDING, PAID, DISPUTED, WAIVED]
      additionalProperties: false
    PresentationSnapshot:
      description: A customer-safe presentation snapshot that explicitly selects the physical or cash product stream.
      allOf:
        - $ref: '#/components/schemas/CustomerSafeSnapshot'
        - type: object
          required: [settlement_type]
    CreateCustomerIntakeRequest:
      type: object
      required: [settlement_type, side, terms]
      properties:
        settlement_type:
          $ref: '#/components/schemas/SettlementType'
        side:
          $ref: '#/components/schemas/Side'
        terms:
          $ref: '#/components/schemas/CommercialTermsInput'
      additionalProperties: false
    Intake:
      type: object
      required:
        [
          id,
          reference,
          settlement_type,
          side,
          lifecycle,
          review_required,
          needs_refresh,
          terms,
          submitted_at,
          quote_group_id,
          withdrawable,
          version,
          created_at,
          updated_at,
        ]
      properties:
        lifecycle:
          type: string
          enum: [DRAFT, ACTIVE, ARCHIVED]
          description: Authoritative RFQ lifecycle. Draft RFQs remain outside the active book until explicitly activated.
        review_required:
          {
            type: boolean,
            description: Draft RFQ requires guarded activation before sharing or allocation.,
          }
        needs_refresh:
          { type: boolean, description: 'Existing staleness warning, independent of lifecycle.' }
        id:
          $ref: '#/components/schemas/Uuid'
        reference:
          type: string
        settlement_type:
          $ref: '#/components/schemas/SettlementType'
        side:
          $ref: '#/components/schemas/Side'
        terms:
          $ref: '#/components/schemas/CommercialTerms'
        submitted_at:
          oneOf:
            - $ref: '#/components/schemas/Timestamp'
            - type: 'null'
          description: When the company submitted the intake for review. A single intake is
            submitted when it is created; one created before this field existed reads its
            creation time. A Draft saved in a quote group has none until it is submitted,
            and an intake the desk shares with the company has none.
        quote_group_id:
          oneOf:
            - $ref: '#/components/schemas/Uuid'
            - type: 'null'
          description: The company's quote group this intake is one term of.
        withdrawable:
          type: boolean
          description: Whether the company may withdraw this intake with
            withdrawCustomerIntake. True for the company's own open intake, a
            single intake or a term of its quote, with no deal, sent quote or
            allocation; false for an archived intake, one the desk must change,
            and an intake the desk shares with the company.
        version:
          type: integer
          format: int64
          minimum: 1
          description: Guards withdrawCustomerIntake; also returned as the ETag.
        created_at:
          $ref: '#/components/schemas/Timestamp'
        updated_at:
          $ref: '#/components/schemas/Timestamp'
      additionalProperties: false
    IntakePage:
      type: object
      required: [items, next_cursor]
      properties:
        items:
          type: array
          maxItems: 200
          items:
            $ref: '#/components/schemas/Intake'
        next_cursor:
          $ref: '#/components/schemas/PageCursor'
      additionalProperties: false
    IntakeGroupTerms:
      type: object
      description: Terms every term of a quote shares. The quantity is stated in GPUs or
        in nodes, of 8 GPUs on a create and of the quote's gpus_per_node on an
        edit. A price unit applies to every priced term. Cash states no
        interconnect, location or country. Saving needs only the GPU and the
        quantity; submitting needs a start date and, for physical delivery, a
        country or Flexible. An edit whose terms keep the location and state
        no country keeps the quote's country.
      required: [gpu_chip, quantity, quantity_unit]
      properties:
        gpu_chip:
          $ref: '#/components/schemas/IntakeGpuChip'
        quantity:
          type: integer
          format: int64
          minimum: 1
        quantity_unit:
          $ref: '#/components/schemas/QuantityUnit'
        location:
          type: [string, 'null']
          maxLength: 500
        location_flexible:
          type: boolean
          default: false
        country:
          oneOf:
            - $ref: '#/components/schemas/DeliveryCountry'
            - type: 'null'
        service_window_start:
          oneOf:
            - $ref: '#/components/schemas/Date'
            - type: 'null'
        service_window_start_precision:
          oneOf:
            - $ref: '#/components/schemas/StartPrecision'
            - type: 'null'
          description: MONTH needs service_window_start on the 1st of its month. Omitted or null is DAY on a create; an edit that omits it, or states null, keeps the quote's precision while it keeps the start date, and a changed start date is DAY.
        interconnect:
          oneOf:
            - $ref: '#/components/schemas/Interconnect'
            - type: 'null'
        interconnect_gbps_per_gpu:
          oneOf:
            - $ref: '#/components/schemas/InterconnectSpeed'
            - type: 'null'
        price_unit:
          oneOf:
            - $ref: '#/components/schemas/PriceUnit'
            - type: 'null'
        prepayment_percentage:
          oneOf:
            - $ref: '#/components/schemas/Percentage'
            - type: 'null'
          description: The prepayment of a term that omits its own. A read states the open terms' prepayment when they all agree, and null when they differ. Null states no prepayment here, which is not 0.
        commercial_expires_at:
          oneOf:
            - $ref: '#/components/schemas/Timestamp'
            - type: 'null'
        technical_specifications:
          $ref: '#/components/schemas/FlexibleSpecifications'
        notes:
          type: [string, 'null']
          maxLength: 40000
      additionalProperties: false
    IntakeGroupDeliveryTerm:
      type: object
      description: One term a quote lists with its own optional price and prepayment. A
        fixed term has a tenor; month-to-month has none. A term (term_type and
        tenor) may be listed once per prepayment, and a null prepayment and 0
        are different prepayments.
      required: [term_type]
      properties:
        term_type:
          $ref: '#/components/schemas/TermType'
        tenor:
          oneOf:
            - $ref: '#/components/schemas/TenorMonths'
            - type: 'null'
        price_usd:
          oneOf:
            - $ref: '#/components/schemas/PositiveUsdPrice'
            - type: 'null'
        prepayment_percentage:
          oneOf:
            - $ref: '#/components/schemas/Percentage'
            - type: 'null'
          description: This term's prepayment; null states none, which is not 0. Omitted, the term takes terms.prepayment_percentage when that is stated; otherwise a term an edit keeps keeps its stored prepayment and a new term has none.
      additionalProperties: false
    CreateIntakeGroupRequest:
      type: object
      required: [settlement_type, side, terms]
      properties:
        settlement_type:
          $ref: '#/components/schemas/SettlementType'
        side:
          $ref: '#/components/schemas/Side'
        terms:
          $ref: '#/components/schemas/IntakeGroupTerms'
        delivery_terms:
          type: array
          maxItems: 12
          items:
            $ref: '#/components/schemas/IntakeGroupDeliveryTerm'
          description: Month-to-month is listed first, then tenors. Cash lists at most one
            fixed term. Empty or omitted saves a quote without a term yet.
      additionalProperties: false
    UpdateIntakeGroupRequest:
      type: object
      minProperties: 1
      description: Omitted fields keep the quote's current values. A supplied terms
        object replaces every shared term, and a supplied delivery_terms
        replaces the list of terms. An edit without terms keeps the quote's
        quantity, its pool's.
      properties:
        side:
          $ref: '#/components/schemas/Side'
        terms:
          $ref: '#/components/schemas/IntakeGroupTerms'
        delivery_terms:
          type: array
          maxItems: 12
          items:
            $ref: '#/components/schemas/IntakeGroupDeliveryTerm'
      additionalProperties: false
    WithdrawRequest:
      type: object
      properties:
        reason:
          type: string
          minLength: 1
          maxLength: 500
          description: Omitted, the intake records that the company withdrew it.
      additionalProperties: false
    IntakeGroupMember:
      type: object
      required:
        [
          id,
          reference,
          lifecycle,
          term_type,
          tenor,
          price_usd,
          price_unit,
          prepayment_percentage,
          submitted_at,
          withdrawable,
          version,
        ]
      properties:
        id:
          $ref: '#/components/schemas/Uuid'
        reference:
          type: string
        lifecycle:
          type: string
          enum: [DRAFT, ACTIVE, ARCHIVED]
        term_type:
          $ref: '#/components/schemas/TermType'
        tenor:
          oneOf:
            - $ref: '#/components/schemas/TenorMonths'
            - type: 'null'
        price_usd:
          oneOf:
            - $ref: '#/components/schemas/PositiveUsdPrice'
            - type: 'null'
        price_unit:
          oneOf:
            - $ref: '#/components/schemas/PriceUnit'
            - type: 'null'
        prepayment_percentage:
          oneOf:
            - $ref: '#/components/schemas/Percentage'
            - type: 'null'
          description: The prepayment the price is quoted at; null when none is stated, which is not 0.
        submitted_at:
          oneOf:
            - $ref: '#/components/schemas/Timestamp'
            - type: 'null'
        withdrawable:
          type: boolean
          description: Whether the company may withdraw this term on its own with
            withdrawCustomerIntake. True for an open term with no deal, sent
            quote or allocation.
        version:
          type: integer
          format: int64
          minimum: 1
      additionalProperties: false
    IntakeGroup:
      type: object
      description: A company's quote, its pool of GPUs and its terms, open terms first.
        version rises with every write to the quote or any term, and not
        with the desk's own records behind them, such as a deal; it guards
        the quote's commands.
      required:
        [
          id,
          settlement_type,
          side,
          total_gpus,
          nodes,
          gpus_per_node,
          editable,
          form,
          intakes,
          version,
        ]
      properties:
        id:
          $ref: '#/components/schemas/Uuid'
        settlement_type:
          $ref: '#/components/schemas/SettlementType'
        side:
          $ref: '#/components/schemas/Side'
        total_gpus:
          type: [integer, 'null']
          minimum: 1
        nodes:
          type: [integer, 'null']
          minimum: 1
        gpus_per_node:
          type: [integer, 'null']
          minimum: 1
        editable:
          type: boolean
          description: Every open term is a Draft sharing the quote's terms, and none has
            a deal, a sent quote or an allocation.
        form:
          description: The quote an edit starts from when editable; null otherwise.
          oneOf:
            - $ref: '#/components/schemas/IntakeGroupForm'
            - type: 'null'
        intakes:
          type: array
          items:
            $ref: '#/components/schemas/IntakeGroupMember'
        version:
          type: integer
          format: int64
          minimum: 1
      additionalProperties: false
    IntakeGroupForm:
      type: object
      description: The quote an editable group's open terms agree on, in the shape
        updateCustomerIntakeGroup accepts, so a form is prefilled from the same
        read as its If-Match. terms.quantity is the pool's, in nodes when the
        quote stated nodes, and terms.price_unit is the priced terms' unit.
        delivery_terms lists the open terms in the form's order, and is empty
        for a quote without a term yet. The quote keeps its settlement_type.
      required: [side, terms, delivery_terms]
      properties:
        side:
          $ref: '#/components/schemas/Side'
        terms:
          $ref: '#/components/schemas/IntakeGroupTerms'
        delivery_terms:
          type: array
          maxItems: 12
          items:
            $ref: '#/components/schemas/IntakeGroupDeliveryTerm'
      additionalProperties: false
    PresentationStatus:
      type: string
      enum: [PUBLISHED, SUPERSEDED, WITHDRAWN, NO_LONGER_AVAILABLE]
    Presentation:
      type: object
      required:
        [
          id,
          series_id,
          revision,
          status,
          progress,
          snapshot,
          snapshot_hash_sha256,
          response_open,
          published_at,
        ]
      properties:
        id:
          $ref: '#/components/schemas/Uuid'
        series_id:
          $ref: '#/components/schemas/Uuid'
        revision:
          type: integer
          minimum: 1
        status:
          $ref: '#/components/schemas/PresentationStatus'
        progress:
          type: string
          enum: [SENT, REPLIED, UNAVAILABLE]
          description: Derived from sending, any recorded reply, expiry, source deal closure and revision availability. Unavailable revisions retain their replies and history.
        snapshot:
          $ref: '#/components/schemas/PresentationSnapshot'
        snapshot_hash_sha256:
          type: string
          pattern: '^[0-9a-f]{64}$'
        commercial_expires_at:
          oneOf:
            - $ref: '#/components/schemas/Timestamp'
            - type: 'null'
        response_open:
          type: boolean
          description: True only for a sent, available revision without a recorded reply. Availability and one reply per revision are enforced again when a response is submitted.
        published_at:
          $ref: '#/components/schemas/Timestamp'
      additionalProperties: false
    PresentationPage:
      type: object
      required: [items, next_cursor]
      properties:
        items:
          type: array
          maxItems: 200
          items:
            $ref: '#/components/schemas/Presentation'
        next_cursor:
          $ref: '#/components/schemas/PageCursor'
      additionalProperties: false
    ResponseType:
      type: string
      enum: [INTEREST, MORE_INFO, PASS, COUNTERPROPOSAL]
    CreateResponseRequest:
      type: object
      required: [response_type]
      properties:
        response_type:
          $ref: '#/components/schemas/ResponseType'
        preferred_quantity:
          type: [integer, 'null']
          format: int64
          minimum: 1
        preferred_quantity_unit:
          oneOf:
            - $ref: '#/components/schemas/QuantityUnit'
            - type: 'null'
        preferred_tenor:
          oneOf:
            - $ref: '#/components/schemas/TenorMonths'
            - type: 'null'
        target_price_usd:
          oneOf:
            - $ref: '#/components/schemas/PositiveUsdPrice'
            - type: 'null'
        target_price_unit:
          oneOf:
            - $ref: '#/components/schemas/PriceUnit'
            - type: 'null'
        notes:
          type: [string, 'null']
          maxLength: 16000
        counterterms:
          type: object
          maxProperties: 64
          additionalProperties: true
      additionalProperties: false
    Response:
      type: object
      required: [id, presentation_id, response_type, submitted_at]
      properties:
        id:
          $ref: '#/components/schemas/Uuid'
        presentation_id:
          $ref: '#/components/schemas/Uuid'
        response_type:
          $ref: '#/components/schemas/ResponseType'
        preferred_quantity:
          type: [integer, 'null']
          format: int64
          minimum: 1
        preferred_quantity_unit:
          oneOf:
            - $ref: '#/components/schemas/QuantityUnit'
            - type: 'null'
        preferred_tenor:
          oneOf:
            - $ref: '#/components/schemas/TenorMonths'
            - type: 'null'
        target_price_usd:
          oneOf:
            - $ref: '#/components/schemas/PositiveUsdPrice'
            - type: 'null'
        target_price_unit:
          oneOf:
            - $ref: '#/components/schemas/PriceUnit'
            - type: 'null'
        notes:
          type: [string, 'null']
          maxLength: 16000
        counterterms:
          type: object
          maxProperties: 64
          additionalProperties: true
        submitted_at:
          $ref: '#/components/schemas/Timestamp'
      additionalProperties: false
    ResponsePage:
      type: object
      required: [items, next_cursor]
      properties:
        items:
          type: array
          maxItems: 200
          items:
            $ref: '#/components/schemas/Response'
        next_cursor:
          $ref: '#/components/schemas/PageCursor'
      additionalProperties: false
    CounterpartyHidden:
      type: object
      required: [disclosed]
      properties:
        disclosed:
          type: boolean
          const: false
      additionalProperties: false
    CounterpartyDisclosed:
      type: object
      required: [disclosed, legal_name, display_name]
      properties:
        disclosed:
          type: boolean
          const: true
        legal_name:
          type: string
          minLength: 1
          maxLength: 200
        display_name:
          type: string
          minLength: 1
          maxLength: 200
      additionalProperties: false
    Fee:
      type: object
      required: [payer, fee_type, percentage, percentage_basis, flat_amount_usd, visible_to_you]
      properties:
        payer:
          type: string
          enum: [BUYER, SELLER, BOTH]
        fee_type:
          type: string
          enum: [PERCENTAGE, FLAT_USD]
        percentage:
          oneOf:
            - $ref: '#/components/schemas/PositivePercentage'
            - type: 'null'
        percentage_basis:
          type: [string, 'null']
          maxLength: 500
        flat_amount_usd:
          oneOf:
            - $ref: '#/components/schemas/PositiveUsdAmount'
            - type: 'null'
        visible_to_you:
          type: boolean
      oneOf:
        - properties:
            fee_type: { const: PERCENTAGE }
            percentage: { $ref: '#/components/schemas/PositivePercentage' }
            percentage_basis: { type: string, minLength: 1, maxLength: 500 }
            flat_amount_usd: { type: 'null' }
        - properties:
            fee_type: { const: FLAT_USD }
            percentage: { type: 'null' }
            percentage_basis: { type: 'null' }
            flat_amount_usd: { $ref: '#/components/schemas/PositiveUsdAmount' }
      additionalProperties: false
    QualificationTechnicianRequest:
      type: object
      required: [request, role, machines, attempts, window_approval]
      properties:
        request:
          type: object
          required: [standalone]
          properties:
            standalone:
              type: boolean
              description: True for a self-service GPU test without a deal, allocation, or invitation.
        role: { type: string, enum: [DECISION_MAKER, TECHNICIAN] }
        machines:
          type: array
          items: { $ref: '#/components/schemas/QualificationMachineProgress' }
        attempts:
          type: array
          items: { $ref: '#/components/schemas/QualificationTechnicianAttempt' }
        window_approval:
          type: object
          additionalProperties: false
          required: [allowed]
          properties:
            allowed: { type: boolean }
            reason_code: { type: string }
        next_action: { $ref: '#/components/schemas/QualificationNextAction' }
    QualificationTechnicianAttempt:
      type: object
      properties:
        evaluation_only:
          type: boolean
          description: Frozen attempt profile is a software evaluation and produces no hardware qualification.
        id: { $ref: '#/components/schemas/Uuid' }
        request_id: { $ref: '#/components/schemas/Uuid' }
        attempt_no: { type: integer, minimum: 1 }
        profile_id: { type: string }
        profile_version: { type: string }
        state: { type: string }
        scope_kind: { type: string, enum: [PER_MACHINE, CONNECTED_CLUSTER] }
        hardware_verdict:
          type: string
          enum: [PASS, FAIL, INCOMPLETE]
          description: Derived from observed evidence; omitted before a verdict is available.
        members:
          type: array
          items:
            type: object
            properties:
              enrollment_id: { $ref: '#/components/schemas/Uuid' }
              state: { type: string }
              block_code: { type: string }
              current_stage: { type: string }
              starting_stage:
                type: string
                description: The stage about to run while none is running, before the first stage and between stages.
              next_stage:
                type: string
                description: The planned stage after the running or starting one; omitted during the last stage and once testing ends.
              testing_ended:
                type: boolean
                description: The machine's stages are over and cleanup and results remain; omitted before the first stage starts.
              result: { type: object }
              closure: { type: object }
        results:
          type: object
          description: Current result facts, including per-machine stage outcomes and metrics.
          properties:
            outcome: { type: string, enum: [PASS, FAIL, INCOMPLETE] }
            execution_completeness: { type: string, enum: [COMPLETE, INCOMPLETE, NOT_STARTED] }
            operational_closure: { type: string, enum: [VERIFIED, UNKNOWN, PENDING] }
            members:
              type: array
              items: { type: object }
        closure:
          type: object
          description: Workload, cleanup, and access closure facts, independent of the hardware verdict.
    QualificationRecoveryRequest:
      type: object
      additionalProperties: false
      required: [reason]
      properties:
        reason: { type: string, minLength: 1, maxLength: 1000 }
    QualificationAttemptClosureStatus:
      type: object
      additionalProperties: false
      required: [attempt_id, state, operational_closure, members]
      properties:
        attempt_id: { $ref: '#/components/schemas/Uuid' }
        state: { type: string }
        operational_closure: { type: string }
        members:
          type: array
          items:
            type: object
            required:
              [
                enrollment_id,
                workload_stopped,
                cleanup_status,
                sessions_closed,
                grant_tombstoned,
                workload_quiescence,
                cleanup,
                session_closure,
                enrollment_continuing,
                reservation_released,
                observations,
              ]
            properties:
              enrollment_id: { $ref: '#/components/schemas/Uuid' }
              child_run_id: { $ref: '#/components/schemas/Uuid' }
              workload_stopped: { type: boolean }
              cleanup_status: { type: string }
              sessions_closed: { type: boolean }
              grant_tombstoned: { type: boolean }
              workload_quiescence: { type: string }
              cleanup: { type: string }
              session_closure: { type: string }
              enrollment_continuing: { type: boolean }
              reservation_released: { type: boolean }
              observations: { type: array, items: { type: object } }
    MachineList:
      type: object
      additionalProperties: false
      required: [machines]
      properties:
        machines:
          type: array
          items: { $ref: '#/components/schemas/Machine' }
    Machine:
      type: object
      additionalProperties: false
      description: |
        One machine record as its company sees it. Hardware facts are what the
        latest passing brief check of the company's own test observed; a
        machine no test has checked has none. status, in one place:
        SERVING, SETTING_UP or ATTENTION while a job runs, is set up or has
        failed (a failed setup only while the machine is still switched on);
        else TESTING while an own test is open on it; else READY while
        its deciding result is a PASS within 14 days, TEST_EXPIRED once that
        PASS is older, TEST_FAILED for a FAIL or only unfinished tests, and
        ATTENTION when it has no finished test and none open.
      required:
        [
          id,
          name,
          hostname,
          address,
          port,
          ssh_user,
          gpu,
          driver_version,
          cuda_version,
          country_code,
          created_at,
          updated_at,
          qualification,
          testing,
          use,
          status,
          work,
        ]
      properties:
        id: { $ref: '#/components/schemas/Uuid' }
        name:
          {
            type: string,
            minLength: 1,
            maxLength: 64,
            description: "The company's label; the hostname, else the address, until renamed.",
          }
        hostname: { type: [string, 'null'] }
        address:
          {
            type: string,
            description: "IP address the machine is reached at, written as the test view writes endpoint_ip; (address, port) equals the enrollment's (endpoint_ip, ssh_port).",
          }
        port: { type: integer, minimum: 1, maximum: 65535 }
        ssh_user:
          { type: [string, 'null'], description: The account the company's test logs in as. }
        gpu:
          oneOf:
            - { $ref: '#/components/schemas/MachineGPU' }
            - { type: 'null' }
        driver_version: { type: [string, 'null'] }
        cuda_version: { type: [string, 'null'], examples: ['13.0'] }
        country_code: { type: [string, 'null'], pattern: '^[A-Z]{2}$' }
        created_at: { $ref: '#/components/schemas/Timestamp' }
        updated_at: { $ref: '#/components/schemas/Timestamp' }
        qualification:
          oneOf:
            - { $ref: '#/components/schemas/MachineQualification' }
            - { type: 'null' }
        testing:
          oneOf:
            - { $ref: '#/components/schemas/MachineTesting' }
            - { type: 'null' }
        use:
          oneOf:
            - { $ref: '#/components/schemas/MachineUse' }
            - { type: 'null' }
        status:
          {
            type: string,
            enum: [TESTING, READY, TEST_EXPIRED, TEST_FAILED, SERVING, SETTING_UP, ATTENTION],
          }
        work:
          oneOf:
            - { $ref: '#/components/schemas/MachineWork' }
            - { type: 'null' }
          description: What the company's switch has the machine do; null where the inference router is not configured.
    MachineGPU:
      type: object
      additionalProperties: false
      required: [model, count, memory_mib]
      properties:
        model:
          {
            type: string,
            description: "The first GPU's name as nvidia-smi reports it, such as NVIDIA H100 80GB HBM3.",
          }
        count: { type: integer, minimum: 1, maximum: 64 }
        memory_mib:
          {
            type: integer,
            minimum: 0,
            description: The smallest memory of any of the machine's GPUs.,
          }
    MachineQualification:
      type: object
      additionalProperties: false
      description: |
        The result that decides qualification: the latest PASS or FAIL of the
        company's own tests on this machine, else its latest INCOMPLETE, so an
        unfinished re-test never undoes a pass. Only results recorded under
        the machine's current host key count.
      required:
        [
          verdict,
          profile_id,
          profile_version,
          completed_at,
          valid_until,
          qualified,
          test_id,
          attempt_id,
        ]
      properties:
        verdict: { type: string, enum: [PASS, FAIL, INCOMPLETE] }
        profile_id: { type: string }
        profile_version: { type: string }
        completed_at: { $ref: '#/components/schemas/Timestamp' }
        valid_until:
          oneOf:
            - { $ref: '#/components/schemas/Timestamp' }
            - { type: 'null' }
          description: 14 days after a PASS; null otherwise.
        qualified: { type: boolean }
        test_id: { $ref: '#/components/schemas/Uuid' }
        attempt_id: { $ref: '#/components/schemas/Uuid' }
    MachineTesting:
      type: object
      additionalProperties: false
      description: The company's newest open test with this machine on it, while no attempt of it has finished since it last started one.
      required: [test_id, state]
      properties:
        test_id: { $ref: '#/components/schemas/Uuid' }
        state:
          {
            type: string,
            description: "The test's state, such as ONBOARDING, READY, ARMED or RUNNING.",
          }
    MachineUse:
      type: object
      additionalProperties: false
      description: The one job the machine is doing, written by the service that runs it; status is SERVING, SETTING_UP or ATTENTION while it exists.
      required: [kind, deployment]
      properties:
        kind: { type: string, enum: [INFERENCE] }
        deployment: { type: object, description: The job's own view. }
    MachineWork:
      type: object
      additionalProperties: false
      description: |
        What the company's switch has the machine do for Liquid Compute. status:
        EARNING while it serves and passes health checks; SETTING_UP while Liquid
        Compute sets it up; STOPPING while the model server is removed; HELD while
        Liquid Compute paused it; WAITING while it is switched on but not serving,
        with the reason; NOT_LISTED while it is switched off, or its hardware cannot
        serve yet (reason says so and rate_per_gpu_hour is null).
      required: [offer, status, use, since, reason, rate_per_gpu_hour, earnings]
      properties:
        offer:
          oneOf:
            - { $ref: '#/components/schemas/MachineWorkOffer' }
            - { type: 'null' }
          description: The switch; null for a machine never switched on.
        status: { type: string, enum: [NOT_LISTED, WAITING, SETTING_UP, EARNING, STOPPING, HELD] }
        use:
          type: [string, 'null']
          enum: [INFERENCE, ON_DEMAND, CONTRACT, null]
          description: The job the machine is doing or waits on, if any.
        since:
          oneOf:
            - { $ref: '#/components/schemas/Timestamp' }
            - { type: 'null' }
          description: When the machine began doing what status says.
        reason:
          { type: [string, 'null'], description: 'Why it waits, stops or is held, in plain words.' }
        rate_per_gpu_hour:
          type: [string, 'null']
          pattern: '^(0|[1-9][0-9]{0,5})\.[0-9]{2,4}$'
          description: >-
            What Liquid Compute pays, in USD per GPU-hour, while the machine serves,
            written as On demand prices are; null when its hardware cannot serve yet
            or the rate cannot be read now.
          examples: ['6.60']
        earnings: { $ref: '#/components/schemas/MachineWorkEarnings' }
    MachineWorkOffer:
      type: object
      additionalProperties: false
      required: [uses, terms_version, state, version]
      properties:
        uses:
          type: array
          items: { type: string, enum: [INFERENCE, ON_DEMAND] }
          description: What Liquid Compute may use the machine for; INFERENCE is AI model serving.
        terms_version: { type: string, description: The supply terms version the switch accepted. }
        state:
          type: string
          enum: [ACTIVE, WITHDRAWN]
          description: ACTIVE while switched on, WITHDRAWN once switched off.
        version:
          { type: integer, minimum: 1, description: Names the switch read for the next change. }
    MachineWorkEarnings:
      type: object
      additionalProperties: false
      description: What the machine earned after the 2% fee, in cents; today and this month by UTC day, and all time still due and already paid.
      required: [today_cents, month_cents, due_cents, paid_cents]
      properties:
        today_cents: { type: integer, minimum: 0 }
        month_cents: { type: integer, minimum: 0 }
        due_cents: { type: integer, minimum: 0 }
        paid_cents: { type: integer, minimum: 0 }
    SupplyTerms:
      type: object
      additionalProperties: false
      required: [version, text]
      properties:
        version: { type: string, pattern: '^v[0-9]+$', examples: [v1] }
        text:
          type: string
          description: 'The terms in plain language, paragraphs separated by blank lines.'
    SwitchMachineOnRequest:
      type: object
      additionalProperties: false
      required: [uses]
      properties:
        uses:
          type: array
          minItems: 1
          maxItems: 1
          items: { type: string, enum: [INFERENCE] }
        accept_terms_version:
          type: string
          description: The supply terms version the caller accepts; required when the switch has not accepted the current one.
        version:
          type: [integer, 'null']
          minimum: 1
          description: The switch's version from the machine's work; left out for a machine never switched on.
    MachineEarningsList:
      type: object
      additionalProperties: false
      required: [earnings]
      properties:
        earnings:
          type: array
          items: { $ref: '#/components/schemas/MachineEarning' }
    MachineEarning:
      type: object
      additionalProperties: false
      required:
        [
          day,
          use,
          hours,
          gpus,
          price_per_gpu_hour,
          gross_cents,
          fee_cents,
          net_cents,
          payout_status,
          paid_at,
        ]
      properties:
        day: { type: string, format: date }
        use: { type: string, enum: [INFERENCE] }
        hours:
          type: number
          minimum: 0
          maximum: 24
          description: 'Hours served and passing health checks, to two decimals.'
        gpus: { type: integer, minimum: 0, maximum: 64 }
        price_per_gpu_hour:
          type: string
          description: 'The rate the hours were paid at, USD per GPU-hour, as On demand prices are written.'
        gross_cents:
          type: integer
          minimum: 0
          description: 'Hours x GPUs x rate, rounded half up to the cent.'
        fee_cents:
          { type: integer, minimum: 0, description: "Liquid Compute's 2% fee, rounded down." }
        net_cents: { type: integer, minimum: 0, description: What the company is paid. }
        payout_status: { type: string, enum: [NOT_DUE, DUE, PAID] }
        paid_at:
          oneOf:
            - { $ref: '#/components/schemas/Timestamp' }
            - { type: 'null' }
    MachineRecordLink:
      type: object
      additionalProperties: false
      description: The company's machine record behind a machine of its own test, once that machine passed a brief check.
      required: [id]
      properties:
        id: { $ref: '#/components/schemas/Uuid' }
    OwnedQualificationTest:
      type: object
      additionalProperties: false
      required: [id, expires_at, report_expires_at]
      properties:
        id: { $ref: '#/components/schemas/Uuid' }
        expires_at: { $ref: '#/components/schemas/Timestamp' }
        report_expires_at: { $ref: '#/components/schemas/Timestamp' }
    OwnedQualificationTestSummary:
      type: object
      additionalProperties: false
      required: [id, request_reference, expected_node_count, state, created_at, results_deadline_at]
      properties:
        id: { $ref: '#/components/schemas/Uuid' }
        request_reference: { type: string }
        profile_id: { type: string }
        profile_version: { type: string }
        expected_node_count: { type: integer, minimum: 1, maximum: 16 }
        state: { type: string }
        created_at: { $ref: '#/components/schemas/Timestamp' }
        results_deadline_at: { $ref: '#/components/schemas/Timestamp' }
    QualificationMachineProgress:
      type: object
      description: Technician projection for an enrollment belonging to this session's request.
      required:
        [
          enrollment_id,
          request_id,
          generation,
          endpoint_ip,
          ssh_port,
          state,
          steps,
          attention_reasons,
          support_reference,
          consent_expires_at,
          brief_check_consented_until,
        ]
      properties:
        enrollment_id: { $ref: '#/components/schemas/Uuid' }
        request_id: { $ref: '#/components/schemas/Uuid' }
        generation: { type: integer, minimum: 1 }
        endpoint_ip: { type: string }
        ssh_port: { type: integer, minimum: 1, maximum: 65535 }
        hostname: { type: string }
        state:
          type: string
          enum:
            [CONFIGURING, REGISTERED, CHECKING, READY, NEEDS_ATTENTION, LOST, OFFBOARDING, REMOVED]
        steps: { type: array, items: { type: object } }
        attention_reasons: { type: array, items: { type: object } }
        attention:
          type: object
          required: [failure_class]
          properties:
            failure_class: { type: string }
            message: { type: string }
            required: { type: string }
            observed: { type: string }
            detail: { type: string }
        next_action: { type: string }
        support_reference: { type: string }
        consent_expires_at: { $ref: '#/components/schemas/Timestamp' }
        brief_check_consented_until: { $ref: '#/components/schemas/Timestamp' }
        connection_check_retry_at: { $ref: '#/components/schemas/Timestamp' }
        buyer_access: { $ref: '#/components/schemas/QualificationBuyerAccessStatus' }
        machine: { $ref: '#/components/schemas/MachineRecordLink' }
    Deal:
      type: object
      required:
        [
          id,
          reference,
          settlement_type,
          your_side,
          phase,
          counterparty,
          terms,
          fee,
          cash_settlement,
          documents,
          updated_at,
        ]
      properties:
        phase:
          type: string
          enum: [STRUCTURING, SIGNING, EXECUTION, COMPLETED, CANCELLED]
          description: Derived from bilateral terms, recorded contract exposure, and terminal events. Private proposals do not control this field. EXECUTION is displayed as Delivery for physical compute and Settlement for cash deals.
        id:
          $ref: '#/components/schemas/Uuid'
        reference:
          type: string
        settlement_type:
          $ref: '#/components/schemas/SettlementType'
        your_side:
          $ref: '#/components/schemas/Side'
        counterparty:
          oneOf:
            - $ref: '#/components/schemas/CounterpartyHidden'
            - $ref: '#/components/schemas/CounterpartyDisclosed'
        effective_term_round:
          type: [integer, 'null']
          description: The shared round carried by the executed agreement; proposals do not change it.
        terms:
          type: array
          maxItems: 100
          items:
            $ref: '#/components/schemas/CustomerSafeSnapshot'
        fee:
          oneOf:
            - $ref: '#/components/schemas/Fee'
            - type: 'null'
        cash_settlement:
          description: Staff-recorded cash result, visible after recorded execution unless the deal is cancelled. Staff notes and evidence identifiers are never exposed here.
          oneOf:
            - $ref: '#/components/schemas/CashSettlement'
            - type: 'null'
        documents:
          type: array
          maxItems: 200
          items:
            $ref: '#/components/schemas/Document'
        qualification:
          description: Present on the physical deal detail when it has a qualification request.
          $ref: '#/components/schemas/CustomerQualificationSummary'
        updated_at:
          $ref: '#/components/schemas/Timestamp'
      additionalProperties: false
    CashSettlement:
      type: object
      required:
        [
          settlement_reference,
          observation_convention,
          settlement_method,
          settlement_date,
          observed_value,
          final_amount_usd,
          payment_status,
          updated_at,
        ]
      properties:
        settlement_reference: { type: [string, 'null'], maxLength: 2000 }
        observation_convention: { type: [string, 'null'], maxLength: 4000 }
        settlement_method: { type: [string, 'null'], maxLength: 4000 }
        settlement_date:
          oneOf: [{ $ref: '#/components/schemas/Date' }, { type: 'null' }]
        observed_value:
          oneOf: [{ $ref: '#/components/schemas/SettlementObservedValue' }, { type: 'null' }]
        final_amount_usd:
          oneOf: [{ $ref: '#/components/schemas/NonNegativeUsdAmount' }, { type: 'null' }]
        payment_status: { type: string, enum: [NOT_RECORDED, PENDING, PAID, DISPUTED, WAIVED] }
        updated_at: { $ref: '#/components/schemas/Timestamp' }
      additionalProperties: false
    DealPage:
      type: object
      required: [items, next_cursor]
      properties:
        items:
          type: array
          maxItems: 200
          items:
            $ref: '#/components/schemas/Deal'
        next_cursor:
          $ref: '#/components/schemas/PageCursor'
      additionalProperties: false
    Position:
      type: object
      required:
        [
          id,
          deal_id,
          reference,
          your_side,
          settlement_type,
          status,
          gpu_chip,
          index_id,
          unmarkable_reason,
          gpu_count,
          entry_usd_per_gpu_hr,
          gpu_hours,
          notional_usd,
          tenor,
          service_window_start,
          service_window_end,
          agreed_at,
          opened_at,
          closed_at,
          cash_settlement,
          updated_at,
        ]
      properties:
        trade_date:
          {
            type: [string, 'null'],
            format: date,
            description: Original execution date for an explicitly recorded cash contract; not the import timestamp.,
          }
        pricing_date:
          {
            type: [string, 'null'],
            format: date,
            description: Final pricing day. Later spot prices must not substitute for confirmed fixing and settlement economics.,
          }
        reference_index: { type: [string, 'null'], enum: [LCI-H100-US, null] }

        id:
          $ref: '#/components/schemas/Uuid'
        deal_id:
          $ref: '#/components/schemas/Uuid'
        reference:
          type: string
        your_side:
          $ref: '#/components/schemas/Side'
        settlement_type:
          $ref: '#/components/schemas/SettlementType'
          description: Only CASH positions are listed today; the field discriminates once physical positions are served.
        status:
          $ref: '#/components/schemas/PositionStatus'
          description: Financial lifecycle state for an explicitly shared deal.
        gpu_chip:
          type: [string, 'null']
          maxLength: 100
        index_id:
          type: [string, 'null']
          maxLength: 64
          description: The index series this position marks against, for example h100-us; null exactly when unmarkable_reason says why.
        unmarkable_reason:
          oneOf: [{ $ref: '#/components/schemas/UnmarkableReason' }, { type: 'null' }]
        gpu_count:
          type: [integer, 'null']
          format: int64
          minimum: 1
        entry_usd_per_gpu_hr:
          oneOf: [{ $ref: '#/components/schemas/PositiveUsdPrice' }, { type: 'null' }]
        gpu_hours:
          oneOf: [{ $ref: '#/components/schemas/PositiveGpuHours' }, { type: 'null' }]
        notional_usd:
          oneOf: [{ $ref: '#/components/schemas/PositiveUsdAmount' }, { type: 'null' }]
        tenor:
          oneOf: [{ $ref: '#/components/schemas/TenorMonths' }, { type: 'null' }]
        service_window_start:
          oneOf: [{ $ref: '#/components/schemas/Date' }, { type: 'null' }]
        service_window_end:
          oneOf: [{ $ref: '#/components/schemas/Date' }, { type: 'null' }]
        agreed_at:
          $ref: '#/components/schemas/Timestamp'
        opened_at:
          description: Time contract execution was recorded; retained through amendments and terminal outcomes.
          oneOf: [{ $ref: '#/components/schemas/Timestamp' }, { type: 'null' }]
        closed_at:
          description: Time the economic position closed; null while the position remains open.
          oneOf: [{ $ref: '#/components/schemas/Timestamp' }, { type: 'null' }]
        cash_settlement:
          description: Staff-recorded cash result, visible after recorded execution unless the deal is cancelled.
          oneOf:
            - $ref: '#/components/schemas/CashSettlement'
            - type: 'null'
        updated_at:
          description: Stamps the position row only. The embedded cash_settlement carries its own updated_at and can change without moving this one.
          $ref: '#/components/schemas/Timestamp'
      additionalProperties: false
    PositionPage:
      type: object
      required: [items, next_cursor]
      properties:
        items:
          type: array
          maxItems: 200
          items:
            $ref: '#/components/schemas/Position'
        next_cursor:
          $ref: '#/components/schemas/PageCursor'
      additionalProperties: false
    FinancingQuestionnaireVersion:
      type: string
      enum: ['0.0.1', '0.0.2']
    FinancingAskQuestionnaire:
      type: object
      properties:
        use_of_proceeds: { type: [string, 'null'], minLength: 1, maxLength: 500 }
        preferred_structure: { type: [string, 'null'], minLength: 1, maxLength: 500 }
        capital_needed_by:
          oneOf: [{ $ref: '#/components/schemas/Date' }, { type: 'null' }]
      additionalProperties: false
    FinancingProjectQuestionnaire:
      type: object
      properties:
        total_project_cost_usd:
          oneOf: [{ $ref: '#/components/schemas/NonNegativeUsdAmount' }, { type: 'null' }]
        hardware_cost_usd:
          oneOf: [{ $ref: '#/components/schemas/NonNegativeUsdAmount' }, { type: 'null' }]
        chip: { type: [string, 'null'], minLength: 1, maxLength: 100 }
        node_count: { type: [integer, 'null'], format: int64, minimum: 1 }
        location: { type: [string, 'null'], minLength: 1, maxLength: 500 }
        data_center_tier: { type: [string, 'null'], minLength: 1, maxLength: 200 }
        target_ready_for_service_date:
          oneOf: [{ $ref: '#/components/schemas/Date' }, { type: 'null' }]
        hardware_status: { type: [string, 'null'], minLength: 1, maxLength: 500 }
      additionalProperties: false
    FinancingOfftakeQuestionnaire:
      type: object
      properties:
        status: { type: [string, 'null'], minLength: 1, maxLength: 500 }
        contract_count: { type: [string, 'null'], minLength: 1, maxLength: 100 }
        counterparties: { type: [string, 'null'], minLength: 1, maxLength: 4000 }
        tenor:
          oneOf: [{ $ref: '#/components/schemas/TenorMonths' }, { type: 'null' }]
        total_contract_value_usd:
          oneOf: [{ $ref: '#/components/schemas/NonNegativeUsdAmount' }, { type: 'null' }]
        price_per_gpu_hour_usd:
          oneOf: [{ $ref: '#/components/schemas/PositiveUsdPrice' }, { type: 'null' }]
        prepayment_percentage:
          oneOf: [{ $ref: '#/components/schemas/Percentage' }, { type: 'null' }]
        prepayment_treatment: { type: [string, 'null'], minLength: 1, maxLength: 1000 }
        assignable_to_lender: { type: [string, 'null'], minLength: 1, maxLength: 500 }
      additionalProperties: false
    FinancingBorrowerQuestionnaire:
      type: object
      properties:
        legal_entity: { type: [string, 'null'], minLength: 1, maxLength: 500 }
        entity_type: { type: [string, 'null'], minLength: 1, maxLength: 200 }
        years_in_business: { type: [string, 'null'], minLength: 1, maxLength: 100 }
        existing_debt_usd:
          oneOf: [{ $ref: '#/components/schemas/NonNegativeUsdAmount' }, { type: 'null' }]
        hardware_liens: { type: [string, 'null'], minLength: 1, maxLength: 1000 }
        equity_committed_usd:
          oneOf: [{ $ref: '#/components/schemas/NonNegativeUsdAmount' }, { type: 'null' }]
        equity_gap_usd:
          oneOf: [{ $ref: '#/components/schemas/NonNegativeUsdAmount' }, { type: 'null' }]
      additionalProperties: false
    FinancingOperatorAnswers:
      type: object
      additionalProperties: false
      required: [chip, unit, count]
      properties:
        neededBy: { 'type': 'string', 'minLength': 1, 'maxLength': 500, 'format': 'date' }
        proceeds:
          {
            'type': 'string',
            'minLength': 1,
            'maxLength': 500,
            'enum': ['Hardware purchase', 'Refinance', 'Prepayment funding', 'Other'],
          }
        structure: { 'type': 'string', 'minLength': 1, 'maxLength': 500 }
        hardwareCost: { '$ref': '#/components/schemas/NonNegativeUsdAmount' }
        chip: { 'type': 'string', 'minLength': 1, 'maxLength': 500 }
        unit: { 'type': 'string', 'minLength': 1, 'maxLength': 500, 'enum': ['Nodes', 'GPUs'] }
        count:
          {
            'type': 'string',
            'pattern': '^(0|[1-9][0-9]*)$',
            'description': 'Canonical integer, 1 through 9007199254740991.',
          }
        location: { 'type': 'string', 'minLength': 1, 'maxLength': 500 }
        readyBy: { 'type': 'string', 'minLength': 1, 'maxLength': 500, 'format': 'date' }
        hardwareStatus:
          {
            'type': 'string',
            'minLength': 1,
            'maxLength': 500,
            'enum': ['Ordered', 'Reserved', 'Not committed'],
          }
        sponsor: { 'type': 'string', 'minLength': 1, 'maxLength': 500 }
        years:
          {
            'type': 'string',
            'pattern': '^(0|[1-9][0-9]*)$',
            'description': 'Canonical integer, 0 through 9007199254740991.',
          }
        equity: { 'type': 'string', 'minLength': 1, 'maxLength': 500, 'enum': ['Yes', 'No'] }
        notes: { 'type': 'string', 'minLength': 1, 'maxLength': 4000 }
    FinancingBuyerAnswers:
      type: object
      additionalProperties: false
      required: [contractValue, prepayment]
      properties:
        neededBy: { 'type': 'string', 'minLength': 1, 'maxLength': 500, 'format': 'date' }
        provider: { 'type': 'string', 'minLength': 1, 'maxLength': 500 }
        status:
          {
            'type': 'string',
            'minLength': 1,
            'maxLength': 500,
            'enum': ['Signed', 'Term sheet', 'In negotiation', 'Still shopping'],
          }
        chip: { 'type': 'string', 'minLength': 1, 'maxLength': 500 }
        unit: { 'type': 'string', 'minLength': 1, 'maxLength': 500, 'enum': ['Nodes', 'GPUs'] }
        count:
          {
            'type': 'string',
            'pattern': '^(0|[1-9][0-9]*)$',
            'description': 'Canonical integer, 1 through 9007199254740991.',
          }
        tenor:
          {
            'type': 'string',
            'pattern': '^(0|[1-9][0-9]*)$',
            'description': 'Canonical integer, 1 through 120.',
          }
        contractValue: { '$ref': '#/components/schemas/PositiveUsdAmount' }
        prepayment:
          { 'type': 'string', 'pattern': "^(?:100(?:\\.0{1,4})?|[0-9]{1,4}(?:\\.[0-9]{1,4})?)$" }
        protection:
          {
            'type': 'string',
            'minLength': 1,
            'maxLength': 500,
            'enum': ['Escrowed', 'Refundable if not delivered', 'None', 'Unknown'],
          }
        company: { 'type': 'string', 'minLength': 1, 'maxLength': 500 }
        stage:
          {
            'type': 'string',
            'minLength': 1,
            'maxLength': 500,
            'enum': ['Bootstrapped', 'Seed', 'Series A', 'Series B', 'Series C+', 'Public'],
          }
        funding: { '$ref': '#/components/schemas/NonNegativeUsdAmount' }
        investors: { 'type': 'string', 'minLength': 1, 'maxLength': 500 }
        revenue:
          {
            'type': 'string',
            'minLength': 1,
            'maxLength': 500,
            'enum': ['Pre-revenue', '<$1M', '$1–5M', '$5–20M', '$20–100M', '$100M+'],
          }
        notes: { 'type': 'string', 'minLength': 1, 'maxLength': 4000 }
    FinancingContractAnswers:
      type: object
      additionalProperties: false
      minProperties: 1
      properties:
        status:
          {
            'type': 'string',
            'minLength': 1,
            'maxLength': 500,
            'enum': ['Signed', 'LOI', 'In negotiation'],
          }
        counterparty: { 'type': 'string', 'minLength': 1, 'maxLength': 500 }
        credit:
          {
            'type': 'string',
            'minLength': 1,
            'maxLength': 500,
            'enum': ['Investment grade', 'Venture-backed', 'Other'],
          }
        tenor:
          {
            'type': 'string',
            'pattern': '^(0|[1-9][0-9]*)$',
            'description': 'Canonical integer, 1 through 120.',
          }
        contractValue: { '$ref': '#/components/schemas/NonNegativeUsdAmount' }
        prepayment:
          { 'type': 'string', 'pattern': "^(?:100(?:\\.0{1,4})?|[0-9]{1,4}(?:\\.[0-9]{1,4})?)$" }
    FinancingIntake:
      type: object
      description: Selected financing path. Contact details do not grant access; ownership comes from the authenticated company. Inactive answers are rejected. Amount is stored on the enclosing request. Operator hardwareCost is per unit, never total project cost.
      required: [track, contact, contracts, no_offtake]
      additionalProperties: false
      properties:
        track: { type: string, enum: [operator, buyer] }
        contact:
          type: object
          required: [name, email]
          additionalProperties: false
          properties:
            name: { type: string, minLength: 1, maxLength: 500 }
            email: { type: string, format: email, maxLength: 254 }
        operator: { $ref: '#/components/schemas/FinancingOperatorAnswers' }
        buyer: { $ref: '#/components/schemas/FinancingBuyerAnswers' }
        no_offtake: { type: boolean }
        contracts:
          type: array
          maxItems: 100
          items:
            type: object
            required: [id, answers]
            additionalProperties: false
            properties:
              id:
                {
                  type: string,
                  minLength: 1,
                  maxLength: 100,
                  description: Unique within this request.,
                }
              answers: { $ref: '#/components/schemas/FinancingContractAnswers' }
      oneOf:
        - required: [operator]
          properties:
            track: { const: operator }
            buyer: { maxProperties: 0 }
        - required: [buyer]
          properties:
            track: { const: buyer }
            operator: { maxProperties: 0 }
            contracts: { maxItems: 0 }
            no_offtake: { const: false }
    FinancingQuestionnaire:
      type: object
      description: Version 0.0.1 contains legacy progressive facts. Version 0.0.2 contains intake and requires empty legacy sections. Questionnaire snapshots replace prior answers; version and track cannot change.
      properties:
        ask: { $ref: '#/components/schemas/FinancingAskQuestionnaire' }
        project: { $ref: '#/components/schemas/FinancingProjectQuestionnaire' }
        offtake: { $ref: '#/components/schemas/FinancingOfftakeQuestionnaire' }
        borrower: { $ref: '#/components/schemas/FinancingBorrowerQuestionnaire' }
        intake: { $ref: '#/components/schemas/FinancingIntake' }
      additionalProperties: false
    CreateFinancingRequest:
      type: object
      required: [requested_amount_usd]
      properties:
        questionnaire_version: { $ref: '#/components/schemas/FinancingQuestionnaireVersion' }
        requested_amount_usd:
          $ref: '#/components/schemas/PositiveUsdAmount'
        gpu_specifications:
          type: string
          minLength: 1
          maxLength: 16000
        questionnaire:
          $ref: '#/components/schemas/FinancingQuestionnaire'
        physical_intake_id:
          description: Optional physical intake belonging to the caller's company.
          oneOf:
            - $ref: '#/components/schemas/Uuid'
            - type: 'null'
        physical_deal_id:
          description: Optional physical deal in which the caller's company is a party.
          oneOf:
            - $ref: '#/components/schemas/Uuid'
            - type: 'null'
      oneOf:
        - required: [gpu_specifications]
          properties:
            questionnaire_version: { const: '0.0.1' }
        - required: [questionnaire_version, questionnaire]
          properties:
            questionnaire_version: { const: '0.0.2' }
            questionnaire: { required: [intake] }
      additionalProperties: false
    UpdateCustomerFinancingRequest:
      type: object
      minProperties: 1
      properties:
        requested_amount_usd:
          $ref: '#/components/schemas/PositiveUsdAmount'
        gpu_specifications:
          type: string
          minLength: 1
          maxLength: 16000
        questionnaire:
          description: Replaces the current questionnaire snapshot when present.
          $ref: '#/components/schemas/FinancingQuestionnaire'
        physical_intake_id:
          description: Optional physical intake belonging to the caller's company.
          oneOf:
            - $ref: '#/components/schemas/Uuid'
            - type: 'null'
        physical_deal_id:
          description: Optional physical deal in which the caller's company is a party.
          oneOf:
            - $ref: '#/components/schemas/Uuid'
            - type: 'null'
      additionalProperties: false
    FinancingRequest:
      type: object
      required:
        [
          id,
          reference,
          status,
          requested_amount_usd,
          gpu_specifications,
          questionnaire_version,
          questionnaire,
          version,
          created_at,
          updated_at,
        ]
      properties:
        id:
          $ref: '#/components/schemas/Uuid'
        reference:
          type: string
        status:
          type: string
          enum: [SUBMITTED, IN_REVIEW, ACTION_REQUIRED, CLOSED]
        requested_amount_usd:
          $ref: '#/components/schemas/PositiveUsdAmount'
        gpu_specifications:
          type: string
          maxLength: 16000
        questionnaire_version:
          $ref: '#/components/schemas/FinancingQuestionnaireVersion'
        questionnaire:
          $ref: '#/components/schemas/FinancingQuestionnaire'
        physical_intake_id:
          description: Physical intake belonging to this request's company, when linked.
          oneOf:
            - $ref: '#/components/schemas/Uuid'
            - type: 'null'
        physical_deal_id:
          description: Physical deal involving this request's company, when linked.
          oneOf:
            - $ref: '#/components/schemas/Uuid'
            - type: 'null'
        closure_reason:
          type: [string, 'null']
          maxLength: 1000
        action_required_message:
          description: What Liquid Compute needs from the company. Null unless status is ACTION_REQUIRED.
          type: [string, 'null']
          maxLength: 2000
        action_required_due_at:
          description: Optional date Liquid Compute asked the company to respond by. Null unless status is ACTION_REQUIRED.
          oneOf:
            - $ref: '#/components/schemas/Date'
            - type: 'null'
        version:
          type: integer
          format: int64
          minimum: 1
        created_at:
          $ref: '#/components/schemas/Timestamp'
        updated_at:
          $ref: '#/components/schemas/Timestamp'
      additionalProperties: false
    CreateFinancingResponse:
      type: object
      description: A message, files uploaded for this response, or both. At least one is required.
      properties:
        message:
          type: [string, 'null']
          minLength: 1
          maxLength: 2000
        document_ids:
          type: array
          maxItems: 10
          uniqueItems: true
          description: Documents uploaded with for_financing_response on this request and finished uploading.
          items:
            $ref: '#/components/schemas/Uuid'
      additionalProperties: false
    CustomerDocumentCapabilities:
      type: object
      required: [uploads]
      properties:
        uploads:
          type: boolean
          description: Whether this deployment accepts company uploads.
      additionalProperties: false
    FinancingResponse:
      type: object
      required:
        [
          id,
          financing_request_id,
          kind,
          message,
          ask_message,
          ask_due_at,
          responded_by_seat_id,
          documents,
          created_at,
        ]
      properties:
        id: { $ref: '#/components/schemas/Uuid' }
        financing_request_id: { $ref: '#/components/schemas/Uuid' }
        kind:
          type: string
          enum: [REPLY, DETAILS_EDIT]
          description: REPLY is a message and/or files; DETAILS_EDIT is the company editing the request while it was action required.
        message: { type: [string, 'null'], maxLength: 2000 }
        ask_message:
          description: The ask this answered, as it read at the time.
          type: [string, 'null']
          maxLength: 2000
        ask_due_at:
          oneOf: [{ $ref: '#/components/schemas/Date' }, { type: 'null' }]
        responded_by_seat_id: { $ref: '#/components/schemas/Uuid' }
        documents:
          type: array
          maxItems: 10
          items:
            type: object
            required: [id, title]
            properties:
              id: { $ref: '#/components/schemas/Uuid' }
              title: { type: string, minLength: 1, maxLength: 500 }
            additionalProperties: false
        created_at: { $ref: '#/components/schemas/Timestamp' }
      additionalProperties: false
    FinancingResponsePage:
      type: object
      required: [items, next_cursor]
      properties:
        items:
          { type: array, maxItems: 200, items: { $ref: '#/components/schemas/FinancingResponse' } }
        next_cursor: { $ref: '#/components/schemas/PageCursor' }
      additionalProperties: false
    FinancingRequestPage:
      type: object
      required: [items, next_cursor]
      properties:
        items:
          type: array
          maxItems: 200
          items:
            $ref: '#/components/schemas/FinancingRequest'
        next_cursor:
          $ref: '#/components/schemas/PageCursor'
      additionalProperties: false
    Activity:
      type: object
      required:
        [
          resolved_at,
          id,
          activity_type,
          subject_type,
          subject_id,
          title,
          body,
          product_area,
          settlement_type,
          action_required,
          read,
          occurred_at,
        ]
      properties:
        resolved_at:
          type: [string, 'null']
          format: date-time
          description: Explicit company-wide resolution time. Reading a request does not resolve it.
        id:
          $ref: '#/components/schemas/Uuid'
        activity_type:
          type: string
          enum:
            [
              PRESENTATION_PUBLISHED,
              PRESENTATION_REVISED,
              DOCUMENT_SHARED,
              ACTION_REQUESTED,
              STATUS_PUBLISHED,
            ]
        subject_type:
          type: string
          description: >-
            SHORT_TERM_BOOKING names a short-term booking by its deal id,
            SHORT_TERM_ASK a listing and SHORT_TERM_BID a watch by their
            intake ids.
          enum:
            [
              INTAKE,
              PRESENTATION,
              DEAL,
              DOCUMENT,
              FINANCING,
              SHORT_TERM_BOOKING,
              SHORT_TERM_ASK,
              SHORT_TERM_BID,
            ]
        subject_id:
          $ref: '#/components/schemas/Uuid'
        title:
          type: string
          maxLength: 300
        body:
          type: string
          maxLength: 2000
        product_area:
          oneOf:
            - $ref: '#/components/schemas/ProductArea'
            - type: 'null'
        settlement_type:
          oneOf:
            - $ref: '#/components/schemas/SettlementType'
            - type: 'null'
        action_required:
          type: boolean
        read:
          type: boolean
        occurred_at:
          $ref: '#/components/schemas/Timestamp'
      additionalProperties: false
    ActivityPage:
      type: object
      required: [items, next_cursor]
      properties:
        items:
          type: array
          maxItems: 200
          items:
            $ref: '#/components/schemas/Activity'
        next_cursor:
          $ref: '#/components/schemas/PageCursor'
      additionalProperties: false
    Home:
      type: object
      required: [requires_attention, active, recent_updates, completed_or_closed]
      properties:
        requires_attention:
          type: array
          maxItems: 50
          items:
            $ref: '#/components/schemas/Activity'
        active:
          type: array
          maxItems: 50
          items:
            type: object
            required: [subject_type, subject_id, title, status, product_area, settlement_type]
            properties:
              subject_type:
                type: string
                enum: [INTAKE, DEAL, FINANCING]
              subject_id:
                $ref: '#/components/schemas/Uuid'
              title:
                type: string
                maxLength: 300
              status:
                $ref: '#/components/schemas/HomeStatus'
              product_area:
                $ref: '#/components/schemas/ProductArea'
              settlement_type:
                oneOf:
                  - $ref: '#/components/schemas/SettlementType'
                  - type: 'null'
            additionalProperties: false
        recent_updates:
          type: array
          maxItems: 50
          items:
            $ref: '#/components/schemas/Activity'
        completed_or_closed:
          type: array
          maxItems: 50
          items:
            type: object
            required: [subject_type, subject_id, title, status, product_area, settlement_type]
            properties:
              subject_type:
                type: string
                enum: [INTAKE, DEAL, FINANCING]
              subject_id:
                $ref: '#/components/schemas/Uuid'
              title:
                type: string
                maxLength: 300
              status:
                $ref: '#/components/schemas/HomeStatus'
              product_area:
                $ref: '#/components/schemas/ProductArea'
              settlement_type:
                oneOf:
                  - $ref: '#/components/schemas/SettlementType'
                  - type: 'null'
            additionalProperties: false
      additionalProperties: false
    Document:
      type: object
      required: [id, context_type, context_id, purpose, title, versions, created_at]
      properties:
        review_intent:
          type: string
          enum: [REVIEW_ONLY, REVIEW_AND_SIGN]
          description: Requested action for a deal document. Does not grant access, approve the agreement, or start signing.
        id:
          $ref: '#/components/schemas/Uuid'
        context_type:
          type: string
          enum: [INTAKE, DEAL, COMPANY_AGREEMENT, FINANCING]
        context_id:
          $ref: '#/components/schemas/Uuid'
        purpose:
          type: string
          enum:
            [
              NON_CIRCUMVENTION,
              DRAFT_AGREEMENT,
              REDLINE,
              EXECUTED_AGREEMENT,
              FULFILLMENT,
              SETTLEMENT,
              OTHER,
            ]
        title:
          type: string
          maxLength: 500
        versions:
          type: array
          maxItems: 100
          items:
            $ref: '#/components/schemas/DocumentVersion'
        created_at:
          $ref: '#/components/schemas/Timestamp'
      additionalProperties: false
    DocumentVersion:
      type: object
      required: [id, version, filename, content_type, size_bytes, state, created_at]
      properties:
        id:
          $ref: '#/components/schemas/Uuid'
        version:
          type: integer
          minimum: 1
        filename:
          type: string
          maxLength: 255
        content_type:
          type: string
          maxLength: 255
        size_bytes:
          type: integer
          format: int64
          minimum: 1
        state:
          type: string
          enum: [AVAILABLE, MISSING, UNDER_REVIEW]
          description: UNDER_REVIEW is a version this party uploaded that Liquid Compute has not yet released.
        created_at:
          $ref: '#/components/schemas/Timestamp'
      additionalProperties: false
    DocumentPage:
      type: object
      required: [items, next_cursor]
      properties:
        items:
          type: array
          maxItems: 200
          items:
            $ref: '#/components/schemas/Document'
        next_cursor:
          $ref: '#/components/schemas/PageCursor'
      additionalProperties: false
    CreateCustomerDocumentUpload:
      type: object
      required:
        [
          context_type,
          context_id,
          purpose,
          title,
          filename,
          content_type,
          size_bytes,
          checksum_sha256,
        ]
      properties:
        context_type:
          type: string
          enum: [INTAKE, DEAL, FINANCING]
        context_id:
          $ref: '#/components/schemas/Uuid'
        purpose:
          type: string
          enum: [DRAFT_AGREEMENT, REDLINE, EXECUTED_AGREEMENT, FULFILLMENT, SETTLEMENT, OTHER]
        title:
          type: string
          minLength: 1
          maxLength: 500
        filename:
          type: string
          minLength: 1
          maxLength: 255
        content_type:
          type: string
          minLength: 1
          maxLength: 255
        size_bytes:
          type: integer
          format: int64
          minimum: 1
          maximum: 104857600
        checksum_sha256:
          type: string
          pattern: '^[0-9a-f]{64}$'
        for_financing_response:
          type: boolean
          description: Hold the file for an answer to a financing ask. Requires a FINANCING context in ACTION_REQUIRED, purpose OTHER, and a PDF, Word, Excel, CSV, PNG, or JPEG file. The file stays out of document lists and alerts until the answer is sent.
        review_intent:
          type: string
          enum: [REVIEW_ONLY, REVIEW_AND_SIGN]
          description: Requested action for a deal document. Does not grant access, approve the agreement, or start signing.
      additionalProperties: false
    CreateDocumentVersionUpload:
      type: object
      required: [filename, content_type, size_bytes, checksum_sha256]
      properties:
        filename: { type: string, minLength: 1, maxLength: 255 }
        content_type: { type: string, minLength: 1, maxLength: 255 }
        size_bytes: { type: integer, format: int64, minimum: 1, maximum: 104857600 }
        checksum_sha256: { type: string, pattern: '^[0-9a-f]{64}$' }
      additionalProperties: false
    UploadGrant:
      type: object
      required: [document_id, version_id, upload_url, required_headers, expires_at]
      properties:
        document_id:
          $ref: '#/components/schemas/Uuid'
        version_id:
          $ref: '#/components/schemas/Uuid'
        upload_url:
          type: string
          format: uri
        required_headers:
          type: object
          additionalProperties: { type: string }
          description: Exact headers covered by the upload signature; callers must send every entry unchanged.
        expires_at:
          $ref: '#/components/schemas/Timestamp'
      additionalProperties: false
    FinalizeUploadRequest:
      type: object
      required: [storage_version_id]
      properties:
        storage_version_id:
          type: string
          minLength: 1
          maxLength: 1024
      additionalProperties: false
    DownloadGrant:
      type: object
      required: [download_url, filename, expires_at]
      properties:
        download_url:
          type: string
          format: uri
        filename:
          type: string
          maxLength: 255
        expires_at:
          $ref: '#/components/schemas/Timestamp'
      additionalProperties: false
    ContractReviewRect:
      type: object
      required: [x, y, w, h]
      description: A highlighted line as fractions of the page box.
      properties:
        x: { type: number, minimum: 0, maximum: 1 }
        y: { type: number, minimum: 0, maximum: 1 }
        w: { type: number, minimum: 0, maximum: 1 }
        h: { type: number, minimum: 0, maximum: 1 }
      additionalProperties: false
    ContractReviewAnchor:
      type: object
      required: [page, start, end, quote, rects]
      description: >-
        The words a note is pinned to on one page of a version's PDF. start and
        end are offsets into the page's text layer, with 0 <= start <= end; the
        quote counts UTF-16 code units, as the shared isTextAnchor guard does.
      properties:
        page: { type: integer, minimum: 1, maximum: 10000 }
        start: { type: integer, minimum: 0 }
        end: { type: integer, minimum: 0 }
        quote: { type: string, minLength: 1, maxLength: 4000 }
        rects:
          type: array
          minItems: 1
          maxItems: 200
          items: { $ref: '#/components/schemas/ContractReviewRect' }
      additionalProperties: false
    ContractReviewVersionState:
      type: string
      enum: [uploading, converting, awaiting_payment, ready, failed]
      description: >-
        uploading until the file is finalized; converting while a Word file is
        turned into the PDF both apps show; awaiting_payment while a review's
        first version waits to be paid for; ready to read and annotate; failed
        when the upload was abandoned or a Word file couldn't be converted
        (failure_reason says so, and the company uploads again).
    ContractReviewPaymentMethod:
      type: [string, 'null']
      enum: [card, free_review, no_billing, null]
      description: >-
        How the review was paid for: on Stripe's page, with one of the
        company's free reviews, or not at all where the deployment has no
        billing. Null until it is paid.
    ContractReviewPayment:
      type: object
      description: >-
        What a review owes for version_id, the version that reached the pay
        step. Every later version is included.
      required: [method, state, amount_cents, version_id, charged_at, receipt_url, failure_code]
      properties:
        method: { $ref: '#/components/schemas/ContractReviewPaymentMethod' }
        state: { type: string, enum: [needed, charged] }
        amount_cents:
          type: integer
          format: int64
          minimum: 0
          description: The price while needed; what was charged once charged, 0 for a free review.
        version_id: { $ref: '#/components/schemas/Uuid' }
        charged_at: { type: [string, 'null'], format: date-time }
        receipt_url:
          type: [string, 'null']
          format: uri
          description: Stripe's receipt for a card payment, when Stripe gives one.
        failure_code:
          type: [string, 'null']
          maxLength: 100
          description: >-
            Why the last Stripe page didn't take a payment, while the review
            still waits: checkout_expired, or Stripe's decline code.
      additionalProperties: false
    ContractReviewPaymentSummary:
      type: object
      required: [method, state, amount_cents]
      properties:
        method: { $ref: '#/components/schemas/ContractReviewPaymentMethod' }
        state: { type: string, enum: [needed, charged] }
        amount_cents: { type: integer, format: int64, minimum: 0 }
      additionalProperties: false
    ContractReviewBilling:
      type: object
      required: [price_cents, free_reviews_left]
      properties:
        price_cents: { type: integer, format: int64, minimum: 1 }
        free_reviews_left: { type: integer, minimum: 0 }
      additionalProperties: false
    ContractReviewPayResult:
      type: object
      required: [review, checkout_url]
      properties:
        review: { $ref: '#/components/schemas/ContractReview' }
        checkout_url:
          type: [string, 'null']
          format: uri
          description: Stripe's page to pay on, while the review waits for a card payment.
      additionalProperties: false
    ContractReviewStatus:
      type: string
      enum: [uploading, awaiting_payment, in_review, returned]
      description: >-
        uploading until a first version is readable; awaiting_payment until it
        is paid for; in_review while the newest ready version is unsent;
        returned once it is sent.
    ContractReviewVersion:
      type: object
      required:
        [
          id,
          number,
          file_name,
          content_type,
          state,
          failure_reason,
          ready_at,
          due_at,
          sent_at,
          sent_by_name,
          summary,
          created_at,
        ]
      properties:
        id: { $ref: '#/components/schemas/Uuid' }
        number: { type: integer, minimum: 1 }
        file_name: { type: string, minLength: 1, maxLength: 255 }
        content_type:
          $ref: '#/components/schemas/ContractReviewContentType'
          description: What was uploaded. A Word version is shown as the PDF made from it.
        state: { $ref: '#/components/schemas/ContractReviewVersionState' }
        failure_reason: { type: [string, 'null'], maxLength: 500 }
        ready_at: { type: [string, 'null'], format: date-time }
        due_at:
          type: [string, 'null']
          format: date-time
          description: The same time on the next weekday (Monday to Friday, UTC) after ready_at.
        sent_at: { type: [string, 'null'], format: date-time }
        sent_by_name:
          type: [string, 'null']
          maxLength: 200
          description: Liquid Compute once sent. Companies never see which of our staff sent it.
        summary: { type: [string, 'null'], maxLength: 4000 }
        created_at: { $ref: '#/components/schemas/Timestamp' }
      additionalProperties: false
    ContractReviewReply:
      type: object
      required: [id, author_kind, author_name, body, created_at]
      properties:
        id: { $ref: '#/components/schemas/Uuid' }
        author_kind: { type: string, enum: [customer, staff] }
        author_name:
          type: string
          minLength: 1
          maxLength: 320
          description: The company member's name, or Liquid Compute for a staff reply.
        body: { type: string, minLength: 1, maxLength: 4000 }
        created_at: { $ref: '#/components/schemas/Timestamp' }
      additionalProperties: false
    ContractReviewNote:
      type: object
      required:
        [
          id,
          version_id,
          kind,
          body,
          replacement,
          anchor,
          author_name,
          draft,
          resolved_at,
          created_at,
          updated_at,
          replies,
        ]
      properties:
        id: { $ref: '#/components/schemas/Uuid' }
        version_id: { $ref: '#/components/schemas/Uuid' }
        kind: { type: string, enum: [comment, suggestion] }
        body: { type: string, minLength: 1, maxLength: 8000 }
        replacement:
          type: [string, 'null']
          maxLength: 8000
          description: The suggested wording; set on every suggestion and on no comment.
        anchor: { $ref: '#/components/schemas/ContractReviewAnchor' }
        author_name:
          type: string
          minLength: 1
          maxLength: 200
          description: Always Liquid Compute. Companies never see which of our staff wrote a note.
        draft:
          type: boolean
          description: True while the note's version is unsent. Companies never see drafts.
        resolved_at: { type: [string, 'null'], format: date-time }
        created_at: { $ref: '#/components/schemas/Timestamp' }
        updated_at: { $ref: '#/components/schemas/Timestamp' }
        replies:
          type: array
          maxItems: 100
          description: Oldest first.
          items: { $ref: '#/components/schemas/ContractReviewReply' }
      additionalProperties: false
    ContractReviewReplyRequest:
      type: object
      required: [body]
      properties:
        body: { type: string, minLength: 1, maxLength: 4000 }
      additionalProperties: false
    ContractReviewTerms:
      type: object
      required: [terms_version, accepted, accepted_at]
      properties:
        terms_version: { type: string, pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}$' }
        accepted: { type: boolean }
        accepted_at: { type: [string, 'null'], format: date-time }
      additionalProperties: false
    AcceptContractReviewTerms:
      type: object
      required: [terms_version]
      properties:
        terms_version:
          type: string
          enum: ['2026-10-21']
          description: The terms version shown to the company; any other is refused.
      additionalProperties: false
    ContractReviewContentType:
      type: string
      enum:
        [application/pdf, application/vnd.openxmlformats-officedocument.wordprocessingml.document]
    CreateContractReviewUpload:
      type: object
      required: [file_name, content_type, size_bytes, checksum_sha256]
      properties:
        title:
          type: [string, 'null']
          maxLength: 200
          description: Defaults to the file name without its extension.
        customer_focus:
          type: [string, 'null']
          maxLength: 1000
          description: What the company tells the lawyer, such as what the contract is, which side it is on, and what to look at closely.
        file_name: { type: string, minLength: 1, maxLength: 255 }
        content_type: { $ref: '#/components/schemas/ContractReviewContentType' }
        size_bytes: { type: integer, format: int64, minimum: 1, maximum: 52428800 }
        checksum_sha256: { type: string, pattern: '^[0-9a-f]{64}$' }
      additionalProperties: false
    CreateContractReviewVersionUpload:
      type: object
      required: [file_name, content_type, size_bytes, checksum_sha256]
      properties:
        file_name: { type: string, minLength: 1, maxLength: 255 }
        content_type: { $ref: '#/components/schemas/ContractReviewContentType' }
        size_bytes: { type: integer, format: int64, minimum: 1, maximum: 52428800 }
        checksum_sha256: { type: string, pattern: '^[0-9a-f]{64}$' }
      additionalProperties: false
    FinalizeContractReviewUpload:
      type: object
      required: [storage_version_id]
      properties:
        storage_version_id:
          type: string
          minLength: 1
          maxLength: 1024
          description: The x-amz-version-id storage answered the upload with.
      additionalProperties: false
    ResolveContractReviewNote:
      type: object
      required: [resolved]
      properties:
        resolved: { type: boolean }
      additionalProperties: false
    RecordContractReviewTurnedAway:
      type: object
      required: [file_name, content_type, size_bytes, page_count, pages_with_text, reason]
      properties:
        file_name: { type: string, minLength: 1, maxLength: 255 }
        content_type:
          {
            type: string,
            enum:
              [
                application/pdf,
                application/vnd.openxmlformats-officedocument.wordprocessingml.document,
              ],
          }
        size_bytes: { type: integer, format: int64, minimum: 1, maximum: 1073741824 }
        page_count: { type: integer, minimum: 0, maximum: 100000 }
        pages_with_text:
          type: integer
          minimum: 0
          maximum: 100000
          description: At most page_count.
        reason:
          type: string
          enum: [no_text, locked, macros, too_large]
          description: >-
            no_text: a scan, or a Word file with almost no text. locked: a PDF
            locked against changes or a password-protected Word file. macros: a
            macro-enabled Word file. too_large: a Word file that unzips past the
            safe limits.
      additionalProperties: false
    ContractReviewTurnedAwayRecorded:
      type: object
      required: [recorded]
      properties:
        recorded: { type: boolean, const: true }
      additionalProperties: false
    ContractReview:
      type: object
      required:
        [
          id,
          title,
          counterparty_name,
          customer_role,
          customer_focus,
          status,
          decline_reason,
          version,
          created_at,
          updated_at,
          versions,
          notes,
          payment,
        ]
      properties:
        id: { $ref: '#/components/schemas/Uuid' }
        title: { type: string, minLength: 1, maxLength: 200 }
        counterparty_name:
          type: 'null'
          deprecated: true
          description: Always null, and removed in a later release.
        customer_role:
          type: 'null'
          deprecated: true
          description: Always null, and removed in a later release.
        customer_focus:
          type: [string, 'null']
          maxLength: 1000
          description: What the company told the lawyer, if anything.
        status: { $ref: '#/components/schemas/ContractReviewStatus' }
        decline_reason:
          type: 'null'
          deprecated: true
          description: Always null, and removed in a later release.
        version:
          type: integer
          format: int64
          minimum: 1
          description: Goes up on every change to the review; send it back as If-Match.
        created_at: { $ref: '#/components/schemas/Timestamp' }
        updated_at: { $ref: '#/components/schemas/Timestamp' }
        versions:
          type: array
          description: Ascending by number.
          items: { $ref: '#/components/schemas/ContractReviewVersion' }
        notes:
          type: array
          description: Sent notes only, in reading order (version, page, position).
          items: { $ref: '#/components/schemas/ContractReviewNote' }
        payment:
          description: >-
            What the review owes. Null while its first version is still
            uploading, and for a review that reached our lawyer before billing.
          oneOf:
            - $ref: '#/components/schemas/ContractReviewPayment'
            - type: 'null'
      additionalProperties: false
    ContractReviewUpload:
      type: object
      required: [review, version_id, upload]
      description: In upload, document_id is the review's id and version_id the new version's.
      properties:
        review: { $ref: '#/components/schemas/ContractReview' }
        version_id: { $ref: '#/components/schemas/Uuid' }
        upload: { $ref: '#/components/schemas/UploadGrant' }
      additionalProperties: false
    ContractReviewSummary:
      type: object
      required:
        [
          id,
          title,
          counterparty_name,
          status,
          latest_version,
          latest_version_state,
          due_at,
          open_notes,
          updated_at,
          version,
          payment,
        ]
      properties:
        id: { $ref: '#/components/schemas/Uuid' }
        title: { type: string, minLength: 1, maxLength: 200 }
        counterparty_name:
          type: 'null'
          deprecated: true
          description: Always null, and removed in a later release.
        status: { $ref: '#/components/schemas/ContractReviewStatus' }
        latest_version: { type: integer, minimum: 1 }
        latest_version_state: { $ref: '#/components/schemas/ContractReviewVersionState' }
        due_at:
          type: [string, 'null']
          format: date-time
          description: The due date of the newest ready version while it is unsent.
        open_notes:
          type: integer
          minimum: 0
          description: Sent notes nobody has resolved.
        updated_at: { $ref: '#/components/schemas/Timestamp' }
        version: { type: integer, format: int64, minimum: 1 }
        payment:
          oneOf:
            - $ref: '#/components/schemas/ContractReviewPaymentSummary'
            - type: 'null'
      additionalProperties: false
    ContractReviewList:
      type: object
      required: [items]
      properties:
        items:
          type: array
          maxItems: 500
          items: { $ref: '#/components/schemas/ContractReviewSummary' }
      additionalProperties: false
    GuestInvitationOpen:
      type: object
      required: [state, scope, intended_for, seeded_data, expires_at]
      properties:
        state:
          type: string
          const: OPEN
        scope:
          type: string
          enum: [INTAKE_FORM, PRESENTATION_RESPONSE]
        intended_for:
          type: object
          required: [name, company, work_email]
          properties:
            name:
              type: string
              maxLength: 200
            company:
              type: string
              maxLength: 200
            work_email:
              type: string
              format: email
              maxLength: 320
          additionalProperties: false
        seeded_data:
          type: object
          maxProperties: 64
          additionalProperties: true
          description: >-
            Customer-safe fields derived by the service from the related intake draft or fixed
            presentation snapshot. Raw source evidence and counterparty identity are never present.
        expires_at:
          $ref: '#/components/schemas/Timestamp'
      additionalProperties: false
    GuestInvitationClosed:
      type: object
      required: [state, message]
      properties:
        state:
          type: string
          const: CLOSED
        message:
          type: string
          const: Thanks. This link is closed.
      additionalProperties: false
    GuestSubmissionRequest:
      type: object
      required: [submission]
      properties:
        submission:
          type: object
          maxProperties: 64
          additionalProperties: true
          description: >-
            Scope-specific intake terms or private presentation response. The service validates
            the closed shape and records it for staff; it is never forwarded to another party.
            Intake terms state one term, month-to-month among them, and the interconnect; they
            exclude desk-owned terms (quoted date, commercial expiry and technical
            specifications), for which the draft keeps its stored values. A term type,
            interconnect or interconnect speed the terms omit also keeps the draft's value,
            as does an omitted country while the location stays the same, and an omitted
            service_window_start_precision (DAY or MONTH) while the start date does.
      additionalProperties: false
    GuestSubmissionAccepted:
      type: object
      required: [state, message]
      properties:
        state:
          type: string
          const: SUBMITTED
        message:
          type: string
          const: Thanks. Your information was submitted.
      additionalProperties: false
    DeskWindow:
      type: string
      pattern: '^[1-9][0-9]{0,2}d$'
      description: A whole number of days from 1d through 365d.
    DeskTermBucket:
      type: string
      enum: [lt3, 3to6, 7to12, 13to24, gt24]
      description: Delivery term in months, under 3 through over 24; month-to-month is lt3.
    DeskRegion:
      type: string
      enum: [US, EU, APAC, OTHER, UNKNOWN]
      description: Region of a structured delivery country; UNKNOWN when none is recorded.
    DeskPriceBand:
      type: object
      additionalProperties: false
      required: [low, high]
      description: The $0.50 range holding a USD per GPU-hour price; low is inclusive.
      properties:
        low: { type: number, minimum: 0, multipleOf: 0.5 }
        high: { type: number, exclusiveMinimum: 0, multipleOf: 0.5 }
    DeskTermCount:
      type: object
      additionalProperties: false
      required: [bucket, count]
      properties:
        bucket: { $ref: '#/components/schemas/DeskTermBucket' }
        count: { type: integer, minimum: 0 }
    DeskRegionCount:
      type: object
      additionalProperties: false
      required: [region, count]
      properties:
        region: { $ref: '#/components/schemas/DeskRegion' }
        count: { type: integer, minimum: 0 }
    DeskQuoteCounts:
      type: object
      additionalProperties: false
      required: [total, supply, buy, by_term, by_region]
      properties:
        total: { type: integer, minimum: 0 }
        supply: { type: integer, minimum: 0, description: Sell quotes. }
        buy: { type: integer, minimum: 0 }
        by_term:
          type: array
          minItems: 5
          maxItems: 5
          description: Every term bucket, shortest first.
          items: { $ref: '#/components/schemas/DeskTermCount' }
        by_region:
          type: array
          minItems: 5
          maxItems: 5
          description: Every region, in enum order.
          items: { $ref: '#/components/schemas/DeskRegionCount' }
    DeskCurvePoint:
      type: object
      additionalProperties: false
      required: [bucket, quotes, relative, median_usd_per_gpu_hour]
      properties:
        bucket: { $ref: '#/components/schemas/DeskTermBucket' }
        quotes: { type: integer, minimum: 0 }
        relative:
          type: [number, 'null']
          exclusiveMinimum: 0
          description: >-
            Bucket median over the median of the first bucket with quotes, to two
            decimals; null unless both buckets meet the privacy minimum.
        median_usd_per_gpu_hour:
          type: [number, 'null']
          exclusiveMinimum: 0
          description: Bucket median to the cent; null without the desk-quotes grant or below the privacy minimum.
    DeskRecentQuote:
      type: object
      additionalProperties: false
      required:
        [
          side,
          size_gpus,
          size_nodes,
          tenor_months,
          term_type,
          region,
          quoted_date,
          band,
          exact_usd_per_gpu_hour,
        ]
      properties:
        side: { type: string, enum: [BUY, SELL] }
        size_gpus: { type: integer, minimum: 1, description: 'The size in whole nodes, as GPUs.' }
        size_nodes: { type: integer, minimum: 1 }
        tenor_months:
          oneOf: [{ $ref: '#/components/schemas/TenorMonths' }, { type: 'null' }]
          description: Null exactly for a month-to-month term.
        term_type: { $ref: '#/components/schemas/TermType' }
        region: { $ref: '#/components/schemas/DeskRegion' }
        quoted_date: { $ref: '#/components/schemas/Date' }
        band:
          oneOf: [{ $ref: '#/components/schemas/DeskPriceBand' }, { type: 'null' }]
        exact_usd_per_gpu_hour:
          type: [number, 'null']
          minimum: 0
          description: >-
            The quote's own price per GPU-hour, for a caller that also holds the
            live desk-quote-prices grant; null wherever the band is null.
    DeskQuoteFlow:
      type: object
      additionalProperties: false
      required: [chip, window_days, counts, curve, recent, entitled]
      properties:
        chip: { type: string, enum: [a100, h100, h200, b200, b300] }
        window_days: { type: integer, minimum: 1, maximum: 365 }
        counts: { $ref: '#/components/schemas/DeskQuoteCounts' }
        curve:
          type: array
          minItems: 5
          maxItems: 5
          description: The desk commitment-length curve, every term bucket shortest first.
          items: { $ref: '#/components/schemas/DeskCurvePoint' }
        recent:
          type: array
          maxItems: 20
          description: The newest quotes in the window, newest first.
          items: { $ref: '#/components/schemas/DeskRecentQuote' }
        entitled:
          { type: boolean, description: Whether the caller holds the live desk-quotes grant. }
    DeskDealRow:
      type: object
      additionalProperties: false
      required: [size_gpus, tenor_months, region, month, band, exact_usd_per_gpu_hour]
      properties:
        size_gpus:
          type: [integer, 'null']
          minimum: 1
          description: The size in whole nodes of 8 GPUs, as GPUs; null when the agreed terms carry none.
        tenor_months:
          oneOf: [{ $ref: '#/components/schemas/TenorMonths' }, { type: 'null' }]
        region: { $ref: '#/components/schemas/DeskRegion' }
        month:
          {
            type: string,
            pattern: '^[0-9]{4}-(0[1-9]|1[0-2])$',
            description: UTC month the terms were agreed.,
          }
        band:
          oneOf: [{ $ref: '#/components/schemas/DeskPriceBand' }, { type: 'null' }]
        exact_usd_per_gpu_hour:
          type: [number, 'null']
          minimum: 0
          description: >-
            The deal's own price per GPU-hour, for a caller that also holds the
            live desk-quote-prices grant; null wherever the band is null.
    DeskDeals:
      type: object
      additionalProperties: false
      required: [chip, window_days, count, rows, next_cursor, entitled]
      properties:
        chip: { type: string, enum: [a100, h100, h200, b200, b300] }
        window_days: { type: integer, minimum: 1, maximum: 365 }
        count: { type: integer, minimum: 0, description: Every deal in the window. }
        rows:
          type: array
          maxItems: 50
          description: One page of the window's deals, newest first.
          items: { $ref: '#/components/schemas/DeskDealRow' }
        next_cursor:
          $ref: '#/components/schemas/PageCursor'
        entitled:
          { type: boolean, description: Whether the caller holds the live desk-quotes grant. }
    DeskBookDepth:
      type: object
      additionalProperties: false
      required: [quotes, gpus]
      description: >-
        What rests on one side: the quotes and their GPUs rounded to whole
        nodes, counting alternative offers of one capacity pool once.
      properties:
        quotes: { type: integer, minimum: 0 }
        gpus: { type: integer, minimum: 0 }
    DeskOrderBookCell:
      type: object
      additionalProperties: false
      required: [bucket, supply, buy, median_usd_per_gpu_hour]
      properties:
        bucket: { $ref: '#/components/schemas/DeskTermBucket' }
        supply: { $ref: '#/components/schemas/DeskBookDepth' }
        buy: { $ref: '#/components/schemas/DeskBookDepth' }
        median_usd_per_gpu_hour:
          type: [number, 'null']
          exclusiveMinimum: 0
          description: >-
            Median of the cell's priced quotes, both sides, to the cent; null
            without the desk-quotes grant or below the privacy minimum.
    DeskOrderBookRow:
      type: object
      additionalProperties: false
      required: [month, cells]
      properties:
        month:
          {
            type: string,
            pattern: '^[0-9]{4}-(0[1-9]|1[0-2])$',
            description: The UTC start month the row holds.,
          }
        cells:
          type: array
          minItems: 5
          maxItems: 5
          description: Every term bucket, shortest first.
          items: { $ref: '#/components/schemas/DeskOrderBookCell' }
    DeskOrderBook:
      type: object
      additionalProperties: false
      required: [chip, rows, supply, buy, later, entitled]
      properties:
        chip: { type: string, enum: [a100, h100, h200, b200, b300] }
        rows:
          type: array
          minItems: 6
          maxItems: 12
          description: A row per start month from the current UTC month, soonest first.
          items: { $ref: '#/components/schemas/DeskOrderBookRow' }
        supply: { $ref: '#/components/schemas/DeskBookDepth' }
        buy: { $ref: '#/components/schemas/DeskBookDepth' }
        later:
          type: integer
          minimum: 0
          description: Resting quotes that start after the last row.
        entitled:
          { type: boolean, description: Whether the caller holds the live desk-quotes grant. }
    DeskBookOrder:
      type: object
      additionalProperties: false
      required:
        [
          size_gpus,
          size_nodes,
          month,
          term_type,
          tenor_months,
          region,
          quoted_date,
          prepayment_percentage,
          band,
          exact_usd_per_gpu_hour,
        ]
      properties:
        size_gpus:
          { type: integer, minimum: 1, description: 'What rests, in whole nodes, as GPUs.' }
        size_nodes: { type: integer, minimum: 1 }
        month:
          {
            type: string,
            pattern: '^[0-9]{4}-(0[1-9]|1[0-2])$',
            description: The book row the quote rests in.,
          }
        term_type: { $ref: '#/components/schemas/TermType' }
        tenor_months:
          oneOf: [{ $ref: '#/components/schemas/TenorMonths' }, { type: 'null' }]
          description: Null exactly for a month-to-month term.
        region: { $ref: '#/components/schemas/DeskRegion' }
        quoted_date: { $ref: '#/components/schemas/Date' }
        prepayment_percentage:
          oneOf: [{ $ref: '#/components/schemas/Percentage' }, { type: 'null' }]
          description: The prepayment the quote is priced at; null when none is stated, which is not 0.
        band:
          oneOf: [{ $ref: '#/components/schemas/DeskPriceBand' }, { type: 'null' }]
        exact_usd_per_gpu_hour:
          type: [number, 'null']
          minimum: 0
          description: >-
            The quote's own price per GPU-hour, for a caller that also holds the
            live desk-quote-prices grant; null wherever the band is null.
    DeskBookOrders:
      type: object
      additionalProperties: false
      required: [chip, side, total, items, next_cursor, entitled]
      properties:
        chip: { type: string, enum: [a100, h100, h200, b200, b300] }
        side: { type: string, enum: [SELL, BUY] }
        total: { type: integer, minimum: 0, description: Every quote on this side of the book. }
        items:
          type: array
          maxItems: 50
          items: { $ref: '#/components/schemas/DeskBookOrder' }
        next_cursor:
          $ref: '#/components/schemas/PageCursor'
        entitled:
          { type: boolean, description: Whether the caller holds the live desk-quotes grant. }
    DeskPriceRange:
      type: object
      additionalProperties: false
      required: [low, high]
      description: >-
        Whole $0.50 bands from the one holding the lowest price to the one
        holding the highest, in USD per GPU-hour; low is inclusive.
      properties:
        low: { type: number, minimum: 0, multipleOf: 0.5 }
        high: { type: number, exclusiveMinimum: 0, multipleOf: 0.5 }
    DeskDealOHLC:
      type: object
      additionalProperties: false
      required: [open, high, low, close]
      description: >-
        A month's first, highest, lowest and last deal price in USD per GPU-hour
        to the cent, first and last by when each deal was agreed.
      properties:
        open: { type: number, exclusiveMinimum: 0 }
        high: { type: number, exclusiveMinimum: 0 }
        low: { type: number, exclusiveMinimum: 0 }
        close: { type: number, exclusiveMinimum: 0 }
    DeskDealCandle:
      type: object
      additionalProperties: false
      required: [month, deals, gpus, median_usd_per_gpu_hour, range, exact]
      properties:
        month:
          {
            type: string,
            pattern: '^[0-9]{4}-(0[1-9]|1[0-2])$',
            description: UTC month the deals' terms were agreed.,
          }
        deals: { type: integer, minimum: 0 }
        gpus:
          type: integer
          minimum: 0
          description: The GPUs of the deals whose agreed terms carry a size, in whole nodes of 8.
        median_usd_per_gpu_hour:
          type: [number, 'null']
          exclusiveMinimum: 0
          description: Median of the month's priced deals to the cent; null without the desk-quotes grant or below the privacy minimum.
        range:
          oneOf: [{ $ref: '#/components/schemas/DeskPriceRange' }, { type: 'null' }]
          description: Null wherever the median is null.
        exact:
          oneOf: [{ $ref: '#/components/schemas/DeskDealOHLC' }, { type: 'null' }]
          description: For a caller that also holds the live desk-quote-prices grant; null wherever the median is null.
    DeskDealCandles:
      type: object
      additionalProperties: false
      required: [chip, candles, entitled]
      properties:
        chip: { type: string, enum: [a100, h100, h200, b200, b300] }
        candles:
          type: array
          minItems: 12
          maxItems: 12
          description: The 12 calendar months ending with the current UTC month, oldest first.
          items: { $ref: '#/components/schemas/DeskDealCandle' }
        entitled:
          { type: boolean, description: Whether the caller holds the live desk-quotes grant. }
    SignatureKind: { type: string, enum: [ECP_ATTESTATION, MNDA, ORDER_FORM] }
    SignatureMechanism: { type: string, enum: [ENVELOPE, CLICKWRAP] }
    SignatureSubjectType: { type: string, enum: [PARTY, DEAL_SIDE] }
    SignatureRequestStatus:
      type: string
      enum: [READY, CREATING, SENT, AWAITING_LIQUIDCOMPUTE, COMPLETED, DECLINED, VOIDED]
    ECPQualification:
      type: object
      required: [category]
      properties:
        category:
          type: string
          enum:
            [
              FINANCIAL_INSTITUTION,
              INSURANCE_COMPANY,
              BROKER_DEALER,
              FUTURES_COMMISSION_MERCHANT,
              FLOOR_BROKER_TRADER,
              INVESTMENT_COMPANY,
              COMMODITY_POOL,
              ENTITY_ASSETS,
              ENTITY_SUPPORT,
              ENTITY_NET_WORTH,
              BENEFIT_PLAN_ASSETS,
              BENEFIT_PLAN_FIDUCIARY,
              GOVERNMENTAL_ENTITY,
              INDIVIDUAL_INVESTMENTS,
              INDIVIDUAL_RISK_MANAGEMENT,
              ECP_AGENT,
              ECP_FIDUCIARY,
              OTHER,
            ]
        details:
          type: string
          maxLength: 2000
          description: Required for ENTITY_SUPPORT, ENTITY_NET_WORTH, INDIVIDUAL_RISK_MANAGEMENT and OTHER; otherwise omitted.
      additionalProperties: false
    ECPAttestation:
      type: object
      required: [version, qualifications]
      properties:
        version: { type: string, enum: [ecp-categories-v1] }
        qualifications:
          type: array
          minItems: 1
          maxItems: 18
          items: { $ref: '#/components/schemas/ECPQualification' }
      additionalProperties: false
    SignatureRequest:
      type: object
      description: >-
        One document for one counterparty. The status is the only input to any gate;
        DocuSign state moves it forward and is never read at gate time.
      required:
        [
          id,
          kind,
          mechanism,
          subject_type,
          subject_id,
          party_id,
          status,
          revision_no,
          counterparty_presigned,
          issued_at,
          version,
          created_at,
          updated_at,
        ]
      properties:
        id: { $ref: '#/components/schemas/Uuid' }
        kind: { $ref: '#/components/schemas/SignatureKind' }
        mechanism: { $ref: '#/components/schemas/SignatureMechanism' }
        subject_type: { $ref: '#/components/schemas/SignatureSubjectType' }
        subject_id: { $ref: '#/components/schemas/Uuid' }
        deal_side: { type: [string, 'null'], enum: [BUYER, SELLER, null] }
        party_id: { $ref: '#/components/schemas/Uuid' }
        status: { $ref: '#/components/schemas/SignatureRequestStatus' }
        revision_no: { type: integer, minimum: 1 }
        supersedes_request_id: { oneOf: [{ $ref: '#/components/schemas/Uuid' }, { type: 'null' }] }
        revision_note: { type: [string, 'null'] }
        issue_mode: { type: [string, 'null'], enum: [TEMPLATE, MANUAL_UPLOAD, null] }
        signing_order:
          { type: [string, 'null'], enum: [COUNTERPARTY_FIRST, LIQUIDCOMPUTE_FIRST, null] }
        source_document_version_id:
          { oneOf: [{ $ref: '#/components/schemas/Uuid' }, { type: 'null' }] }
        counterparty_presigned: { type: boolean }
        template_id: { type: [string, 'null'] }
        merge_data:
          type: [object, 'null']
          description: The merged tab values as sent to DocuSign, keyed by tab label.
          additionalProperties: { type: string }
        terms_snapshot_id: { oneOf: [{ $ref: '#/components/schemas/Uuid' }, { type: 'null' }] }
        terms_snapshot_hash_sha256: { type: [string, 'null'] }
        docusign_envelope_id: { type: [string, 'null'] }
        docusign_clickwrap_id: { type: [string, 'null'] }
        docusign_clickwrap_version_id: { type: [string, 'null'] }
        docusign_agreement_id: { type: [string, 'null'] }
        signer_seat_id: { oneOf: [{ $ref: '#/components/schemas/Uuid' }, { type: 'null' }] }
        signer_name: { type: [string, 'null'] }
        signer_email: { type: [string, 'null'] }
        liquidcompute_signer_staff_sub: { type: [string, 'null'] }
        liquidcompute_signed_at:
          { oneOf: [{ $ref: '#/components/schemas/Timestamp' }, { type: 'null' }] }
        counterparty_signed_at:
          { oneOf: [{ $ref: '#/components/schemas/Timestamp' }, { type: 'null' }] }
        issued_at: { $ref: '#/components/schemas/Timestamp' }
        sent_at: { oneOf: [{ $ref: '#/components/schemas/Timestamp' }, { type: 'null' }] }
        completed_at: { oneOf: [{ $ref: '#/components/schemas/Timestamp' }, { type: 'null' }] }
        declined_at: { oneOf: [{ $ref: '#/components/schemas/Timestamp' }, { type: 'null' }] }
        decline_reason: { type: [string, 'null'] }
        voided_at: { oneOf: [{ $ref: '#/components/schemas/Timestamp' }, { type: 'null' }] }
        void_reason: { type: [string, 'null'] }
        voided_by_staff_sub: { type: [string, 'null'] }
        effective_at: { oneOf: [{ $ref: '#/components/schemas/Timestamp' }, { type: 'null' }] }
        expires_at: { oneOf: [{ $ref: '#/components/schemas/Timestamp' }, { type: 'null' }] }
        attestation_basis: { type: [string, 'null'], enum: [ECP_ENTITY, HNW_INDIVIDUAL, null] }
        ecp_attestation:
          oneOf: [{ $ref: '#/components/schemas/ECPAttestation' }, { type: 'null' }]
        signed_document_version_id:
          { oneOf: [{ $ref: '#/components/schemas/Uuid' }, { type: 'null' }] }
        certificate_document_version_id:
          { oneOf: [{ $ref: '#/components/schemas/Uuid' }, { type: 'null' }] }
        issued_by_staff_sub: { type: [string, 'null'] }
        issued_by_system: { type: [string, 'null'] }
        version: { type: integer, format: int64 }
        created_at: { $ref: '#/components/schemas/Timestamp' }
        updated_at: { $ref: '#/components/schemas/Timestamp' }
      additionalProperties: false
    LegalStatus:
      type: object
      required:
        [
          party_id,
          attestation,
          mnda,
          attestation_complete,
          mnda_effective,
          mnda_expires_at,
          physical_settlement_approved,
          physical_settlement_allowed,
          blocking,
        ]
      properties:
        party_id: { $ref: '#/components/schemas/Uuid' }
        attestation:
          { oneOf: [{ $ref: '#/components/schemas/SignatureRequest' }, { type: 'null' }] }
        mnda: { oneOf: [{ $ref: '#/components/schemas/SignatureRequest' }, { type: 'null' }] }
        attestation_complete: { type: boolean }
        mnda_effective:
          { type: boolean, description: A completed MNDA whose validity window covers now. }
        mnda_expires_at: { oneOf: [{ $ref: '#/components/schemas/Timestamp' }, { type: 'null' }] }
        physical_settlement_approved:
          type: boolean
          description: Historical staff meeting approval, retained for compatibility; does not gate physical compute.
        physical_settlement_allowed:
          type: boolean
          description: Physical standing requires an effective MNDA. ECP attestation and staff meeting approval are not required.
        blocking:
          type: array
          description: The physical settlement documents not yet satisfied; ECP attestation and meeting approval do not block physical compute.
          items: { type: string, enum: [MNDA] }
      additionalProperties: false
    ErrorEnvelope:
      type: object
      required: [error]
      properties:
        error:
          type: object
          required: [code, message, retryable]
          properties:
            code:
              type: string
              pattern: '^[a-z][a-z0-9_]{1,63}$'
            message:
              type: string
              maxLength: 500
            retryable:
              type: boolean
            signature_request_id:
              $ref: '#/components/schemas/Uuid'
              description: Committed replacement attestation request; retry signing-session against this ID.
            current_version:
              type: [integer, 'null']
              format: int64
              minimum: 1
            blocking:
              description: >-
                On `legal_prerequisites_missing`, MNDA identifies the required agreement
                that is not yet effective.
              type: array
              items: { type: string, enum: [MNDA] }
          additionalProperties: false
      additionalProperties: false
    TradingMoney:
      type: [string, 'null']
      pattern: '^-?[0-9]+\.[0-9]{2}$'
      description: Exact USD at cents; null means incomplete or undisclosed.
    TradingPnLDay:
      type: object
      additionalProperties: false
      required:
        [
          date,
          realized_usd,
          costs_usd,
          unrealized_usd,
          open_count,
          closed_count,
          pending_economics,
          pending_costs,
          missing_prices,
          stale_prices,
          oldest_price_at,
          latest_price_at,
        ]
      properties:
        date: { type: string, format: date }
        realized_usd: { $ref: '#/components/schemas/TradingMoney' }
        costs_usd: { $ref: '#/components/schemas/TradingMoney' }
        unrealized_usd: { $ref: '#/components/schemas/TradingMoney' }
        open_count: { type: integer, minimum: 0 }
        closed_count: { type: integer, minimum: 0 }
        pending_economics: { type: integer, minimum: 0 }
        pending_costs: { type: integer, minimum: 0 }
        missing_prices: { type: integer, minimum: 0 }
        stale_prices: { type: integer, minimum: 0 }
        oldest_price_at: { type: [string, 'null'], format: date-time }
        latest_price_at: { type: [string, 'null'], format: date-time }
    TradingFinancialRevision:
      type: object
      additionalProperties: false
      required: [revision, kind, recorded_at, economic_date, gross_usd, costs_usd, net_usd]
      properties:
        revision: { type: integer, format: int64, minimum: 1 }
        kind: { type: string, enum: [COSTS, ADJUSTMENT, SETTLEMENT, TERMINATION, CORRECTION] }
        recorded_at: { type: string, format: date-time }
        economic_date: { type: string, format: date }
        gross_usd:
          allOf: [{ $ref: '#/components/schemas/TradingMoney' }]
          description: Cumulative signed cash PnL from this company's side, excluding principal and costs.
        costs_usd:
          allOf: [{ $ref: '#/components/schemas/TradingMoney' }]
          description: Cumulative incurred costs for this company, null if unconfirmed or undisclosed.
        net_usd:
          allOf: [{ $ref: '#/components/schemas/TradingMoney' }]
          description: Cumulative gross less costs; null unless both amounts are known and authorized. Excludes open valuation.
    TradingPnL:
      type: object
      additionalProperties: false
      required: [method, as_of, history_start, legacy_count, days, deals]
      properties:
        method: { type: string, const: DAILY_INDEX_FULL_HOURS_V1 }
        as_of: { type: string, format: date-time }
        history_start: { type: [string, 'null'], format: date }
        legacy_count: { type: integer, minimum: 0 }
        days:
          type: array
          items: { $ref: '#/components/schemas/TradingPnLDay' }
        deals:
          type: array
          items:
            type: object
            additionalProperties: false
            required:
              [
                deal_id,
                reference,
                status,
                phase,
                legacy,
                terms_version,
                effective_at,
                financial_postings,
                financial_revisions,
              ]
            properties:
              deal_id: { $ref: '#/components/schemas/Uuid' }
              reference: { type: string }
              status:
                type: string
                description: >-
                  PENDING_SIGNATURE and SUPERSEDED retain previously opened positions
                  present at history cutover; their legacy economics remain incomplete.
                enum: [OPEN, SETTLED, VOID, UNRECORDED, PENDING_SIGNATURE, SUPERSEDED]
              phase: { $ref: '#/components/schemas/DealPhase' }
              legacy: { type: boolean }
              terms_version: { type: [string, 'null'], format: uuid }
              effective_at: { type: [string, 'null'], format: date-time }
              trade_date: { type: [string, 'null'], format: date }
              tracking_started_at:
                {
                  type: [string, 'null'],
                  format: date-time,
                  description: Earliest recorded opened position version. A legacy imported trade has incomplete performance between its original trade date and this baseline.,
                }
              financial_postings: { type: integer, minimum: 0 }
              financial_revisions:
                type: array
                description: Authorized cumulative revisions, oldest first. Empty means no financial postings, never confirmed zero.
                items: { $ref: '#/components/schemas/TradingFinancialRevision' }
    QualificationInstallerStep:
      type: object
      additionalProperties: false
      required: [name, state]
      properties:
        name: { type: string, pattern: '^[a-z0-9][a-z0-9_.-]{0,63}$' }
        state: { type: string, enum: [APPLIED, SKIPPED, FAILED] }
        detail: { type: string, maxLength: 2000 }
    QualificationBuyerAccessSpec:
      type: object
      additionalProperties: false
      required:
        [
          enrollment_id,
          allocation_id,
          machine_id,
          revision,
          accounts,
          sshd_match_block,
          relay_source_cidrs,
          expires_at,
        ]
      properties:
        enrollment_id: { $ref: '#/components/schemas/Uuid' }
        allocation_id: { $ref: '#/components/schemas/Uuid' }
        machine_id: { $ref: '#/components/schemas/Uuid' }
        revision: { type: integer, minimum: 1 }
        accounts:
          type: array
          items:
            type: object
            additionalProperties: false
            required: [name, principal, ca_public_key]
            properties:
              name: { type: string }
              principal: { type: string }
              ca_public_key: { type: string }
        sshd_match_block: { type: string }
        relay_source_cidrs: { type: array, items: { type: string } }
        expires_at: { $ref: '#/components/schemas/Timestamp' }
    QualificationBuyerAccessReport:
      type: object
      additionalProperties: false
      required: [revision, outcome, steps]
      properties:
        revision: { type: integer, minimum: 1 }
        outcome: { type: string, enum: [APPLIED, FAILED, REMOVED] }
        steps:
          type: array
          minItems: 1
          maxItems: 64
          items: { $ref: '#/components/schemas/QualificationInstallerStep' }
    QualificationBuyerAccessStatus:
      type: object
      additionalProperties: false
      required: [state]
      properties:
        state: { type: string, enum: [NOT_REQUESTED, PENDING, APPLIED, FAILED, REMOVAL_PENDING] }
        revision: { type: integer, minimum: 1 }
        command: { type: string }
        outcome: { type: string, enum: [APPLIED, FAILED, REMOVED] }
        reported_at: { $ref: '#/components/schemas/Timestamp' }
        steps:
          type: array
          items: { $ref: '#/components/schemas/QualificationInstallerStep' }
        expires_at: { $ref: '#/components/schemas/Timestamp' }
    QualificationNextAction:
      type: object
      additionalProperties: false
      required: [code, owner, scope, deadline_at, next_automatic_event, waiting, affected_ids]
      description: Derived for the current audience. Buyer projections omit machine
        identities and supplier remedies; supplier projections may name their machines.
      properties:
        code:
          type: string
          enum:
            [
              COMPLETE_TERMS,
              INVITE_TECHNICIAN,
              CONNECT_MACHINES,
              REPAIR_MACHINES,
              REPLACE_MACHINE,
              APPROVE_WINDOW,
              WAITING_FOR_WINDOW,
              RELEASE_HOLD,
              TESTING_RUNNING,
              VERIFY_CLOSURE,
              REVIEW_RESULTS,
              SHARE_REPORT,
              PROVISION_BUYER_ACCESS,
              VERIFY_BUYER_ACCESS,
              WAITING_FOR_ACCESS_WINDOW,
              REMOVE_BUYER_ACCESS,
              DELIVER_ACCESS,
              ACCEPT_DELIVERY,
              RESOLVE_ROUTING,
              REVIEW_DEADLINE_REQUEST,
              LICO_PLATFORM_ISSUE,
              EVALUATION_ONLY_PROFILE,
              REQUEST_CANCELLED,
              COMPLETE,
            ]
        owner: { type: string, enum: [LICO, SUPPLIER, TECHNICIAN, BUYER, SYSTEM] }
        scope:
          type: object
          additionalProperties: false
          required: [machine_ids, expected_count, ready_count]
          properties:
            machine_ids:
              type: array
              items: { $ref: '#/components/schemas/Uuid' }
            expected_count: { type: integer, minimum: 0 }
            ready_count: { type: integer, minimum: 0 }
        deadline_at: { type: [string, 'null'], format: date-time }
        next_automatic_event: { type: [string, 'null'] }
        waiting: { type: boolean }
        affected_ids:
          type: array
          items: { $ref: '#/components/schemas/Uuid' }
    CustomerQualificationSummary:
      type: object
      additionalProperties: false
      required: [request_id, state, expected_node_count, next_action]
      properties:
        request_id: { $ref: '#/components/schemas/Uuid' }
        state:
          type: string
          enum:
            [DRAFT, INVITED, ONBOARDING, READY, ARMED, HELD, RUNNING, CLOSING, COMPLETED, CANCELLED]
        expected_node_count: { type: integer, minimum: 1, maximum: 16 }
        next_action: { $ref: '#/components/schemas/QualificationNextAction' }
    ShortTermPrice:
      type: string
      pattern: '^(0|[1-9][0-9]{0,5})(\.[0-9]{1,4})?$'
      description: A short-term price's precision, USD per GPU-hour with at most six
        integer and four fractional digits. A price field pairs it with
        PositiveUsdPrice; responses print two to four fractional digits.
    ShortTermPaymentPlan:
      type: string
      enum: [DEPOSIT, UPFRONT, INSTALLMENTS, ON_ACCEPTANCE]
      description: How the seller requires buyers to pay. DEPOSIT takes 25% when the
        seller confirms and the rest when the buyer accepts the nodes; UPFRONT
        takes everything when the seller confirms; INSTALLMENTS takes one
        payment per installment_weeks, each due before its period starts;
        ON_ACCEPTANCE takes everything when the buyer accepts.
    ShortTermInstallmentWeeks:
      type: integer
      enum: [1, 2, 4]
      description: The weeks one INSTALLMENTS payment covers.
    ShortTermPlanCadence:
      description: A payment plan and its cadence are one unit. INSTALLMENTS states
        installment_weeks; every other plan has none.
      oneOf:
        - required: [installment_weeks]
          properties:
            payment_plan: { const: INSTALLMENTS }
            installment_weeks:
              $ref: '#/components/schemas/ShortTermInstallmentWeeks'
        - properties:
            payment_plan: { enum: [DEPOSIT, UPFRONT, ON_ACCEPTANCE] }
            installment_weeks: { type: 'null' }
    ShortTermEvidenceKind:
      type: string
      enum: [GCAT_RUN, STANDALONE_TEST, TEST_WAIVER]
      description: GCAT_RUN is a GCAT run; STANDALONE_TEST a standalone GPU test;
        TEST_WAIVER a chip's waiver of the test, accepted on staging only.
        GCAT runs and, on staging, waivers are listable today.
    ShortTermEvidenceRef:
      type: object
      description: The passing GPU test an ask lists from.
      required: [kind, id]
      properties:
        kind:
          $ref: '#/components/schemas/ShortTermEvidenceKind'
        id:
          $ref: '#/components/schemas/Uuid'
      additionalProperties: false
    ShortTermEvidence:
      type: object
      description: A passing GPU test the company may list an ask from.
      required: [kind, id, gpu_chip, nodes, tested_at, passed]
      properties:
        kind:
          $ref: '#/components/schemas/ShortTermEvidenceKind'
        id:
          $ref: '#/components/schemas/Uuid'
        gpu_chip:
          $ref: '#/components/schemas/GpuChip'
        nodes:
          type: integer
          minimum: 1
          description: Nodes of eight GPUs the test ran on.
        interconnect:
          $ref: '#/components/schemas/Interconnect'
          description: The network the test measured; omitted when it states none.
        tested_at:
          $ref: '#/components/schemas/Timestamp'
        passed:
          type: boolean
          const: true
      additionalProperties: false
    ShortTermEvidenceList:
      type: object
      required: [items]
      properties:
        items:
          type: array
          maxItems: 50
          items:
            $ref: '#/components/schemas/ShortTermEvidence'
      additionalProperties: false
    ShortTermWeek:
      type: object
      required: [week_start, free_nodes]
      properties:
        week_start:
          $ref: '#/components/schemas/Date'
          description: The week's Monday.
        free_nodes:
          type: integer
          minimum: 0
          description: Nodes no booking takes that week; 0 for a week the ask does not
            list.
      additionalProperties: false
    ShortTermLengthUnit:
      type: string
      enum: [HOUR, DAY, WEEK]
      description: What a booking's length counts. HOUR and DAY are booked at once
        and paid by card; WEEK waits for the seller and follows its payment
        plan.
    ShortTermLength:
      type: object
      required: [unit, price_usd]
      properties:
        unit:
          $ref: '#/components/schemas/ShortTermLengthUnit'
        price_usd:
          allOf:
            - $ref: '#/components/schemas/PositiveUsdPrice'
            - $ref: '#/components/schemas/ShortTermPrice'
          description: The price per GPU-hour of a booking of this unit.
      additionalProperties: false
    ShortTermFreeDay:
      type: object
      required: [date, free_gpus]
      properties:
        date:
          $ref: '#/components/schemas/Date'
          description: A UTC day the ask lists free.
        free_gpus:
          type: integer
          minimum: 0
          description: GPUs no booking holds at any time that day.
      additionalProperties: false
    ShortTermTakenGpus:
      type: object
      description: GPUs another booking of the ask holds, without saying whose.
      required: [starts_at, ends_at, gpus]
      properties:
        starts_at:
          $ref: '#/components/schemas/Timestamp'
        ends_at:
          $ref: '#/components/schemas/Timestamp'
        gpus:
          type: integer
          minimum: 1
      additionalProperties: false
    ShortTermMarketGpu:
      type: object
      description: One GPU chip's supply in the market.
      required: [gpu_chip, offers, nodes_free, gpus_free, from_price_usd]
      properties:
        gpu_chip:
          $ref: '#/components/schemas/GpuChip'
        offers:
          type: integer
          minimum: 0
          description: Asks of this chip in the market.
        nodes_free:
          type: integer
          minimum: 0
          description: gpus_free in whole nodes.
        gpus_free:
          type: integer
          minimum: 0
          description: The sum over those asks of each one's most GPUs free for an
            hour of any listed day.
        from_price_usd:
          description: The cheapest price of those asks' lengths; null with no
            offers.
          oneOf:
            - allOf:
                - $ref: '#/components/schemas/PositiveUsdPrice'
                - $ref: '#/components/schemas/ShortTermPrice'
            - type: 'null'
      additionalProperties: false
    ShortTermMarketAsk:
      type: object
      description: A live ask another company listed, without its seller.
      required:
        [
          id,
          reference,
          gpu_chip,
          nodes,
          min_nodes,
          min_weeks,
          max_weeks,
          interconnect,
          interconnect_gbps_per_gpu,
          location,
          country,
          price_usd,
          payment_plan,
          installment_weeks,
          tested_at,
          weeks,
          gpus,
          min_gpus,
          lengths,
          days,
          taken,
          server_gpus,
        ]
      allOf:
        - $ref: '#/components/schemas/ShortTermPlanCadence'
      properties:
        id:
          $ref: '#/components/schemas/Uuid'
          description: The ask a booking names as ask_id.
        reference:
          type: string
        gpu_chip:
          $ref: '#/components/schemas/GpuChip'
        nodes:
          type: integer
          minimum: 1
          maximum: 1000
          description: Nodes of eight GPUs the ask lists.
        min_nodes:
          type: integer
          minimum: 1
          description: The fewest nodes one booking takes.
        min_weeks:
          type: integer
          minimum: 1
          maximum: 12
        max_weeks:
          type: integer
          minimum: 1
          maximum: 12
        interconnect:
          $ref: '#/components/schemas/Interconnect'
        interconnect_gbps_per_gpu:
          oneOf:
            - $ref: '#/components/schemas/InterconnectSpeed'
            - type: 'null'
        location:
          type: string
          minLength: 1
          maxLength: 500
        country:
          $ref: '#/components/schemas/DeliveryCountry'
        price_usd:
          allOf:
            - $ref: '#/components/schemas/PositiveUsdPrice'
            - $ref: '#/components/schemas/ShortTermPrice'
        payment_plan:
          $ref: '#/components/schemas/ShortTermPaymentPlan'
        installment_weeks:
          oneOf:
            - $ref: '#/components/schemas/ShortTermInstallmentWeeks'
            - type: 'null'
        tested_at:
          $ref: '#/components/schemas/Timestamp'
          description: When the GPU test the ask lists from completed.
        weeks:
          type: array
          minItems: 12
          maxItems: 12
          description: Nodes free in each of the market's weeks, in week_starts order.
          items:
            $ref: '#/components/schemas/ShortTermWeek'
        gpus:
          type: integer
          minimum: 8
          description: nodes times eight.
        min_gpus:
          type: integer
          minimum: 1
          description: The fewest GPUs one booking takes.
        lengths:
          type: array
          minItems: 1
          maxItems: 3
          description: The lengths the ask takes, shortest first, each at its price.
          items:
            $ref: '#/components/schemas/ShortTermLength'
        days:
          type: array
          description: Each day the ask lists free from today through the market's
            last week, with the GPUs free all of it.
          items:
            $ref: '#/components/schemas/ShortTermFreeDay'
        taken:
          type: array
          description: Open bookings of the ask over the same days, earliest first, so
            the free hours of a partly booked day show.
          items:
            $ref: '#/components/schemas/ShortTermTakenGpus'
        server_gpus:
          description: Set on a listing whose servers Kliq runs, where each booking
            rents exactly one whole server of this many GPUs; null on a listing
            Liquid Compute delivers.
          oneOf:
            - type: integer
              const: 8
            - type: 'null'
      additionalProperties: false
    ShortTermMarket:
      type: object
      required: [week_starts, gpus, asks]
      properties:
        week_starts:
          type: array
          minItems: 12
          maxItems: 12
          description: This week's Monday and the eleven Mondays after it.
          items:
            $ref: '#/components/schemas/Date'
        gpus:
          type: array
          description: One entry per GpuChip, in GpuChip order.
          items:
            $ref: '#/components/schemas/ShortTermMarketGpu'
        asks:
          type: array
          description: Cheapest first; listing order breaks ties.
          items:
            $ref: '#/components/schemas/ShortTermMarketAsk'
      additionalProperties: false
    ShortTermAvailableWeeks:
      type: array
      minItems: 1
      maxItems: 52
      uniqueItems: true
      description: The Mondays the ask can be booked in, sorted, none before this
        week's Monday.
      items:
        $ref: '#/components/schemas/Date'
    ShortTermAvailableDays:
      type: array
      minItems: 1
      maxItems: 364
      uniqueItems: true
      description: The UTC days the ask is free, sorted, none before today and none
        52 weeks or more after this week's Monday.
      items:
        $ref: '#/components/schemas/Date'
    ShortTermMinGpus:
      type: integer
      minimum: 1
      description: The fewest GPUs one booking takes, 1, 2, 4 or whole nodes of 8,
        at most the ask's GPUs.
    ShortTermLengths:
      type: array
      minItems: 1
      maxItems: 3
      uniqueItems: true
      items:
        $ref: '#/components/schemas/ShortTermLengthUnit'
    ShortTermAsk:
      type: object
      description: One of the company's asks. version guards its update and
        withdrawal.
      required:
        [
          id,
          reference,
          status,
          gpu_chip,
          nodes,
          interconnect,
          interconnect_gbps_per_gpu,
          location,
          country,
          available_weeks,
          available_days,
          gpus,
          min_nodes,
          min_gpus,
          min_weeks,
          max_weeks,
          lengths,
          price_usd,
          hour_price_usd,
          day_price_usd,
          week_price_usd,
          payment_plan,
          installment_weeks,
          evidence,
          tested_at,
          version,
        ]
      allOf:
        - $ref: '#/components/schemas/ShortTermPlanCadence'
      properties:
        id:
          $ref: '#/components/schemas/Uuid'
        reference:
          type: string
        status:
          type: string
          enum: [ACTIVE, CLOSED]
          description: ACTIVE is on the market; CLOSED was withdrawn.
        gpu_chip:
          $ref: '#/components/schemas/GpuChip'
        nodes:
          type: integer
          minimum: 1
          maximum: 1000
        interconnect:
          $ref: '#/components/schemas/Interconnect'
        interconnect_gbps_per_gpu:
          oneOf:
            - $ref: '#/components/schemas/InterconnectSpeed'
            - type: 'null'
        location:
          type: string
          minLength: 1
          maxLength: 500
        country:
          $ref: '#/components/schemas/DeliveryCountry'
        available_weeks:
          $ref: '#/components/schemas/ShortTermAvailableWeeks'
          description: The Mondays of the weeks available_days fall in.
        available_days:
          $ref: '#/components/schemas/ShortTermAvailableDays'
        gpus:
          type: integer
          minimum: 8
          description: nodes times eight.
        min_nodes:
          type: integer
          minimum: 1
          description: min_gpus in whole nodes, at least one.
        min_gpus:
          $ref: '#/components/schemas/ShortTermMinGpus'
        min_weeks:
          type: integer
          minimum: 1
          maximum: 12
        max_weeks:
          type: integer
          minimum: 1
          maximum: 12
        lengths:
          $ref: '#/components/schemas/ShortTermLengths'
        price_usd:
          allOf:
            - $ref: '#/components/schemas/PositiveUsdPrice'
            - $ref: '#/components/schemas/ShortTermPrice'
        hour_price_usd:
          description: The ask's own price per GPU-hour for this length; null takes
            price_usd.
          oneOf:
            - allOf:
                - $ref: '#/components/schemas/PositiveUsdPrice'
                - $ref: '#/components/schemas/ShortTermPrice'
            - type: 'null'
        day_price_usd:
          description: The ask's own price per GPU-hour for this length; null takes
            price_usd.
          oneOf:
            - allOf:
                - $ref: '#/components/schemas/PositiveUsdPrice'
                - $ref: '#/components/schemas/ShortTermPrice'
            - type: 'null'
        week_price_usd:
          description: The ask's own price per GPU-hour for this length; null takes
            price_usd.
          oneOf:
            - allOf:
                - $ref: '#/components/schemas/PositiveUsdPrice'
                - $ref: '#/components/schemas/ShortTermPrice'
            - type: 'null'
        payment_plan:
          $ref: '#/components/schemas/ShortTermPaymentPlan'
        installment_weeks:
          oneOf:
            - $ref: '#/components/schemas/ShortTermInstallmentWeeks'
            - type: 'null'
        evidence:
          $ref: '#/components/schemas/ShortTermEvidenceRef'
        tested_at:
          $ref: '#/components/schemas/Timestamp'
        version:
          type: integer
          format: int64
          minimum: 1
      additionalProperties: false
    ShortTermAskList:
      type: object
      required: [items]
      properties:
        items:
          type: array
          maxItems: 500
          items:
            $ref: '#/components/schemas/ShortTermAsk'
      additionalProperties: false
    CreateShortTermAskRequest:
      type: object
      required:
        [
          gpu_chip,
          nodes,
          interconnect,
          location,
          country,
          min_weeks,
          max_weeks,
          price_usd,
          payment_plan,
          evidence,
          rights_confirmed,
        ]
      description: available_days, or available_weeks as every day of them, and
        min_gpus, or min_nodes as whole nodes. lengths defaults to every unit.
      allOf:
        - $ref: '#/components/schemas/ShortTermPlanCadence'
        - anyOf:
            - required: [available_days]
            - required: [available_weeks]
        - anyOf:
            - required: [min_gpus]
            - required: [min_nodes]
      properties:
        gpu_chip:
          $ref: '#/components/schemas/GpuChip'
          description: The chip the evidence proves.
        nodes:
          type: integer
          minimum: 1
          maximum: 1000
          description: Nodes of eight GPUs.
        interconnect:
          $ref: '#/components/schemas/Interconnect'
        interconnect_gbps_per_gpu:
          description: Only on a networked block, never with NONE.
          oneOf:
            - $ref: '#/components/schemas/InterconnectSpeed'
            - type: 'null'
        location:
          type: string
          minLength: 1
          maxLength: 500
          description: Trimmed; never blank.
        country:
          $ref: '#/components/schemas/DeliveryCountry'
        available_weeks:
          $ref: '#/components/schemas/ShortTermAvailableWeeks'
        available_days:
          $ref: '#/components/schemas/ShortTermAvailableDays'
        min_nodes:
          type: integer
          minimum: 1
          description: The fewest nodes one booking takes, at most nodes.
        min_gpus:
          $ref: '#/components/schemas/ShortTermMinGpus'
        min_weeks:
          type: integer
          minimum: 1
          maximum: 12
          description: The shortest booking of weeks.
        max_weeks:
          type: integer
          minimum: 1
          maximum: 12
          description: The longest booking of weeks, at least min_weeks.
        lengths:
          $ref: '#/components/schemas/ShortTermLengths'
        price_usd:
          allOf:
            - $ref: '#/components/schemas/PositiveUsdPrice'
            - $ref: '#/components/schemas/ShortTermPrice'
          description: The price per GPU-hour of every length without its own.
        hour_price_usd:
          description: The ask's own price per GPU-hour for this length; omitted or
            empty takes price_usd.
          oneOf:
            - allOf:
                - $ref: '#/components/schemas/PositiveUsdPrice'
                - $ref: '#/components/schemas/ShortTermPrice'
            - type: string
              const: ''
        day_price_usd:
          description: The ask's own price per GPU-hour for this length; omitted or
            empty takes price_usd.
          oneOf:
            - allOf:
                - $ref: '#/components/schemas/PositiveUsdPrice'
                - $ref: '#/components/schemas/ShortTermPrice'
            - type: string
              const: ''
        week_price_usd:
          description: The ask's own price per GPU-hour for this length; omitted or
            empty takes price_usd.
          oneOf:
            - allOf:
                - $ref: '#/components/schemas/PositiveUsdPrice'
                - $ref: '#/components/schemas/ShortTermPrice'
            - type: string
              const: ''
        payment_plan:
          $ref: '#/components/schemas/ShortTermPaymentPlan'
        installment_weeks:
          oneOf:
            - $ref: '#/components/schemas/ShortTermInstallmentWeeks'
            - type: 'null'
        evidence:
          $ref: '#/components/schemas/ShortTermEvidenceRef'
        rights_confirmed:
          type: boolean
          const: true
          description: Confirms the company has the right to rent out these machines.
      additionalProperties: false
    UpdateShortTermAskRequest:
      type: object
      minProperties: 1
      description: An omitted field keeps its value. payment_plan INSTALLMENTS needs
        installment_weeks; installment_weeks alone changes a stored
        INSTALLMENTS plan's cadence; any other plan clears it. The result
        follows CreateShortTermAskRequest's rules.
      properties:
        available_weeks:
          $ref: '#/components/schemas/ShortTermAvailableWeeks'
        available_days:
          $ref: '#/components/schemas/ShortTermAvailableDays'
        price_usd:
          allOf:
            - $ref: '#/components/schemas/PositiveUsdPrice'
            - $ref: '#/components/schemas/ShortTermPrice'
        hour_price_usd:
          description: The ask's own price per GPU-hour for this length; omitted or
            empty takes price_usd.
          oneOf:
            - allOf:
                - $ref: '#/components/schemas/PositiveUsdPrice'
                - $ref: '#/components/schemas/ShortTermPrice'
            - type: string
              const: ''
        day_price_usd:
          description: The ask's own price per GPU-hour for this length; omitted or
            empty takes price_usd.
          oneOf:
            - allOf:
                - $ref: '#/components/schemas/PositiveUsdPrice'
                - $ref: '#/components/schemas/ShortTermPrice'
            - type: string
              const: ''
        week_price_usd:
          description: The ask's own price per GPU-hour for this length; omitted or
            empty takes price_usd.
          oneOf:
            - allOf:
                - $ref: '#/components/schemas/PositiveUsdPrice'
                - $ref: '#/components/schemas/ShortTermPrice'
            - type: string
              const: ''
        min_nodes:
          type: integer
          minimum: 1
        min_gpus:
          $ref: '#/components/schemas/ShortTermMinGpus'
        lengths:
          $ref: '#/components/schemas/ShortTermLengths'
        min_weeks:
          type: integer
          minimum: 1
          maximum: 12
        max_weeks:
          type: integer
          minimum: 1
          maximum: 12
        payment_plan:
          $ref: '#/components/schemas/ShortTermPaymentPlan'
        installment_weeks:
          $ref: '#/components/schemas/ShortTermInstallmentWeeks'
      additionalProperties: false
    ShortTermPayment:
      type: object
      description: One amount a booking owes. The buyer also sees a bank-transfer
        booking's card hold as a HOLD payment, and pay_url while an amount is
        ISSUED; the seller sees payout_status instead.
      required: [id, kind, amount_usd, credit_usd, due_at, status, method]
      properties:
        id:
          $ref: '#/components/schemas/Uuid'
          description: The payment's id; a HOLD's is its checkout's.
        kind:
          type: string
          enum: [HOLD, DEPOSIT, BALANCE, FULL, INSTALLMENT, EXTENSION, DAY]
          description: A booking of hours pays FULL when booked; one of days pays
            each DAY two hours before it starts.
        amount_usd:
          $ref: '#/components/schemas/PositiveUsdAmount'
        credit_usd:
          $ref: '#/components/schemas/NonNegativeUsdAmount'
          description: The captured card hold counted against this amount.
        due_at:
          description: Null on a HOLD. An installment covers its period's share of
            the weeks. A bank-transfer amount falls due no sooner than its
            invoice, two days after it is issued. Access is suspended 24 hours
            after an issued amount falls due unpaid, and the booking ends 72
            hours after.
          oneOf:
            - $ref: '#/components/schemas/Timestamp'
            - type: 'null'
        status:
          type: string
          enum: [SCHEDULED, ISSUED, PAID, FAILED, CANCELLED, AUTHORIZED]
          description: AUTHORIZED is a HOLD's authorized card hold; a HOLD reads
            SCHEDULED while pending, PAID once captured and CANCELLED once
            released.
        method:
          type: string
          enum: [CARD, BANK_TRANSFER]
          description: The booking's method; a HOLD is always CARD.
        pay_url:
          type: string
          format: uri
          maxLength: 2048
          description: Buyer only, while the amount is ISSUED; omitted otherwise.
        payout_status:
          type: string
          enum: [NOT_DUE, DUE, PAID]
          description: Seller only; whether Liquid Compute has wired the seller's share
            of the paid amount. Omitted for the buyer.
        payout_usd:
          $ref: '#/components/schemas/NonNegativeUsdAmount'
          description: Seller only; the seller's share of the amount after Liquid
            Compute's 2% fee. Omitted for the buyer.
      additionalProperties: false
    ShortTermExtension:
      type: object
      description: A buyer's request for more weeks. version guards its approve,
        decline and withdraw commands.
      required: [id, weeks, status, total_usd, requested_at, decided_at, version]
      properties:
        id:
          $ref: '#/components/schemas/Uuid'
        weeks:
          type: integer
          enum: [1, 2, 4]
        status:
          type: string
          enum: [REQUESTED, APPROVED, DECLINED, WITHDRAWN]
        total_usd:
          $ref: '#/components/schemas/PositiveUsdAmount'
        requested_at:
          $ref: '#/components/schemas/Timestamp'
        decided_at:
          description: Null while REQUESTED.
          oneOf:
            - $ref: '#/components/schemas/Timestamp'
            - type: 'null'
        version:
          type: integer
          format: int64
          minimum: 1
      additionalProperties: false
    ShortTermBooking:
      type: object
      description: A booking as the company's side sees it. The counterparty is
        never named. version guards the confirm, decline, cancel, stop, start
        and terminate commands.
      required:
        [
          id,
          reference,
          role,
          ask_id,
          source,
          status,
          phase,
          gpu_chip,
          nodes,
          gpus,
          start_date,
          end_date,
          weeks,
          length_unit,
          length,
          starts_at,
          ends_at,
          problem_window_ends_at,
          price_usd,
          total_usd,
          payment_plan,
          installment_weeks,
          method,
          hold_usd,
          respond_by,
          location,
          interconnect,
          deal_stage,
          payments,
          extensions,
          counterparty_disclosed,
          version,
        ]
      allOf:
        - $ref: '#/components/schemas/ShortTermPlanCadence'
      properties:
        id:
          $ref: '#/components/schemas/Uuid'
          description: The booking's deal id.
        reference:
          type: string
          description: The deal's reference.
        role:
          type: string
          enum: [BUYER, SELLER]
          description: The reading company's side.
        ask_id:
          $ref: '#/components/schemas/Uuid'
        source:
          type: string
          enum: [REQUEST, AUTO_BOOK]
          description: Whether the buyer requested it or its AUTO_BOOK bid did.
        status:
          type: string
          enum: [AWAITING_PAYMENT, REQUESTED, CONFIRMED, DECLINED, LAPSED, CANCELLED, TERMINATED]
          description: The stored lifecycle. AWAITING_PAYMENT waits for the buyer's
            checkout and is CANCELLED after an hour unpaid; a booking of weeks
            is then REQUESTED from the seller and LAPSED unanswered at
            respond_by, while one of hours or days is CONFIRMED at once.
            TERMINATED ended for a payment left unpaid, 72 hours after it fell
            due for weeks, a day after the unpaid day started for days, and an
            hour after booking for hours.
        phase:
          type: string
          enum:
            [
              AWAITING_PAYMENT,
              REQUESTED,
              CONFIRMED,
              READY,
              RUNNING,
              SUSPENDED,
              ENDED,
              DECLINED,
              LAPSED,
              CANCELLED,
              TERMINATED,
            ]
          description: What the booking is doing now, from its status, the clock and
            its machine access. A CONFIRMED booking is CONFIRMED while Liquid
            Compute prepares it, READY with access open for the buyer's check,
            RUNNING once the buyer accepts, SUSPENDED for a missed payment, and
            ENDED after end_date.
        gpu_chip:
          $ref: '#/components/schemas/GpuChip'
        nodes:
          type: integer
          minimum: 1
        gpus:
          type: integer
          minimum: 1
          description: 1, 2 or 4 GPUs of one node, or whole nodes of 8.
        start_date:
          $ref: '#/components/schemas/Date'
          description: The UTC day starts_at falls on.
        end_date:
          $ref: '#/components/schemas/Date'
          description: The day after the last UTC day the booking touches.
        weeks:
          type: integer
          minimum: 0
          maximum: 52
          description: A booking of weeks' length, approved extensions included; 0 for
            hours and days.
        length_unit:
          $ref: '#/components/schemas/ShortTermLengthUnit'
        length:
          type: integer
          minimum: 1
          maximum: 52
          description: Hours, days or weeks of length_unit.
        starts_at:
          $ref: '#/components/schemas/Timestamp'
          description: When access starts; a UTC midnight for weeks.
        ends_at:
          $ref: '#/components/schemas/Timestamp'
        problem_window_ends_at:
          $ref: '#/components/schemas/Timestamp'
          description: Until when, after starts_at, the buyer may report GPUs that
            fail for a full refund, 30 minutes for hours, 4 hours for days, 48
            hours for weeks.
        price_usd:
          allOf:
            - $ref: '#/components/schemas/PositiveUsdPrice'
            - $ref: '#/components/schemas/ShortTermPrice'
        total_usd:
          $ref: '#/components/schemas/PositiveUsdAmount'
        payment_plan:
          $ref: '#/components/schemas/ShortTermPaymentPlan'
        installment_weeks:
          oneOf:
            - $ref: '#/components/schemas/ShortTermInstallmentWeeks'
            - type: 'null'
        method:
          type: string
          enum: [CARD, BANK_TRANSFER]
          description: CARD below a $10,000 total, BANK_TRANSFER at or above it.
        hold_usd:
          description: A bank-transfer booking's card hold; null on a card booking.
          oneOf:
            - $ref: '#/components/schemas/PositiveUsdAmount'
            - type: 'null'
        respond_by:
          description: The seller's deadline to answer a REQUESTED booking.
          oneOf:
            - $ref: '#/components/schemas/Timestamp'
            - type: 'null'
        location:
          type: [string, 'null']
          maxLength: 500
        interconnect:
          oneOf:
            - $ref: '#/components/schemas/Interconnect'
            - type: 'null'
        deal_stage:
          type: string
          enum:
            [
              EXPLORING,
              NEGOTIATING,
              TERMS_AGREED,
              CONTRACTING,
              CONTRACTED,
              FULFILLMENT,
              SETTLEMENT,
              COMPLETED,
              LOST,
              CANCELLED,
            ]
          description: The booking's deal stage.
        access_status:
          type: string
          enum:
            [UNAVAILABLE, EXPIRED, SUSPENDED, AWAITING_ACCEPTANCE, PREPARING, SCHEDULED, AVAILABLE]
          description: The machine access status of the deal's first allocation;
            omitted until Liquid Compute prepares access.
        payments:
          type: array
          items:
            $ref: '#/components/schemas/ShortTermPayment'
        extensions:
          type: array
          items:
            $ref: '#/components/schemas/ShortTermExtension'
        vms:
          type: array
          maxItems: 16
          items:
            $ref: '#/components/schemas/ShortTermVM'
          description: The buyer's VMs on a listing whose servers Kliq runs, one
            whole server each. Omitted for the seller and for a booking whose
            machines Liquid Compute delivers.
        counterparty_disclosed:
          type: boolean
          const: false
        version:
          type: integer
          format: int64
          minimum: 1
      additionalProperties: false
    ShortTermVM:
      type: object
      description: One of a booking's VMs as its buyer sees it.
      required: [id, phase, stopped, ready_at, ssh]
      properties:
        id:
          $ref: '#/components/schemas/Uuid'
        phase:
          type: string
          enum: [PENDING, PROVISIONING, READY, STOPPED, RELEASING, FAILED]
          description: What the VM is doing, as the cluster last reported. A VM
            is PENDING until its provisioning starts shortly before the
            booking, and FAILED while the cluster can't run it.
        stopped:
          type: boolean
          description: Whether the buyer stopped it.
        ready_at:
          description: When it was first ready, which is when paid time starts.
          oneOf:
            - $ref: '#/components/schemas/Timestamp'
            - type: 'null'
        ssh:
          description: How to log in while the VM is READY; null otherwise.
          oneOf:
            - $ref: '#/components/schemas/ShortTermSSH'
            - type: 'null'
      additionalProperties: false
    ShortTermSSH:
      type: object
      description: Where a ready VM's SSH listens, and the account the company's
        registered SSH keys log in to.
      required: [host, port, user]
      properties:
        host:
          type: string
          description: The server's public IP address.
        port:
          type: integer
          minimum: 1
          maximum: 65535
        user:
          type: string
          pattern: '^[a-z_][a-z0-9_-]{0,31}$'
      additionalProperties: false
    ShortTermBookingList:
      type: object
      required: [items]
      properties:
        items:
          type: array
          maxItems: 200
          items:
            $ref: '#/components/schemas/ShortTermBooking'
      additionalProperties: false
    ShortTermReturnPath:
      type: string
      maxLength: 500
      pattern: '^/compute/short-term(/[^?#]*)?(\?[^#]*)?$'
      not:
        pattern: '^[^?]*\.\.'
      description: Where a checkout returns the buyer, a customer-app path at or
        under /compute/short-term with an optional query and no fragment.
    CreateShortTermBookingRequest:
      type: object
      description: Books gpus for length length_unit from starts_at; a request
        without length_unit books nodes for weeks from start_date. The GPUs
        must be free throughout and the ask free every UTC day the booking
        touches.
      required: [ask_id, terms_accepted, return_path]
      oneOf:
        - required: [gpus, length_unit, length, starts_at]
          not: { anyOf: [{ required: [nodes] }, { required: [start_date] }, { required: [weeks] }] }
        - required: [nodes, start_date, weeks]
          not:
            {
              anyOf:
                [
                  { required: [gpus] },
                  { required: [length_unit] },
                  { required: [length] },
                  { required: [starts_at] },
                ],
            }
      properties:
        ask_id:
          $ref: '#/components/schemas/Uuid'
          description: An ask from the market.
        gpus:
          type: integer
          minimum: 1
          description: 1, 2 or 4, or whole nodes of 8, from the ask's min_gpus
            through its GPUs.
        length_unit:
          $ref: '#/components/schemas/ShortTermLengthUnit'
        length:
          type: integer
          minimum: 1
          maximum: 30
          description: 1 to 24 hours, 1 to 30 days, or weeks from the ask's min_weeks
            through its max_weeks.
        starts_at:
          $ref: '#/components/schemas/Timestamp'
          description: Hours and days start on a whole UTC hour from the next one;
            weeks at a UTC midnight from today.
        nodes:
          type: integer
          minimum: 1
          description: Whole nodes, from the ask's smallest booking through its nodes.
        start_date:
          $ref: '#/components/schemas/Date'
          description: Today or later, in UTC.
        weeks:
          type: integer
          minimum: 1
          maximum: 12
          description: From the ask's min_weeks through its max_weeks.
        terms_accepted:
          type: boolean
          const: true
        return_path:
          $ref: '#/components/schemas/ShortTermReturnPath'
      additionalProperties: false
    ShortTermBookingCreated:
      type: object
      required: [booking]
      properties:
        booking:
          $ref: '#/components/schemas/ShortTermBooking'
        payment_url:
          type: string
          format: uri
          description: The checkout that saves the buyer's card, or holds it for a
            bank-transfer booking. Present only while the booking is
            AWAITING_PAYMENT.
      additionalProperties: false
    ShortTermPaymentLink:
      type: object
      required: [pay_url]
      properties:
        pay_url:
          type: string
          format: uri
          description: The invoice page that takes a bank transfer or a card.
      additionalProperties: false
    CreateShortTermExtensionRequest:
      type: object
      required: [weeks]
      properties:
        weeks:
          type: integer
          enum: [1, 2, 4]
      additionalProperties: false
    ShortTermBid:
      type: object
      description: One of the company's bids. version guards its cancel command.
      required:
        [
          id,
          mode,
          status,
          gpu_chips,
          min_nodes,
          max_nodes,
          min_weeks,
          max_weeks,
          start_from,
          start_to,
          max_price_usd,
          require_infiniband,
          regions,
          accepted_plans,
          hold_usd,
          hold_expires_at,
          matched_deal_id,
          version,
        ]
      properties:
        id:
          $ref: '#/components/schemas/Uuid'
        mode:
          type: string
          enum: [EXACT, AUTO_BOOK, NOTIFY]
          description: EXACT is the bid behind a booking request and is never listed.
        status:
          type: string
          enum: [PENDING_HOLD, ACTIVE, MATCHED, CANCELLED, EXPIRED]
          description: PENDING_HOLD waits for an AUTO_BOOK bid's card hold; MATCHED
            booked an ask; EXPIRED outlived its start window.
        gpu_chips:
          type: array
          minItems: 1
          maxItems: 10
          uniqueItems: true
          items:
            $ref: '#/components/schemas/GpuChip'
        min_nodes:
          type: integer
          minimum: 1
          maximum: 1000
        max_nodes:
          type: integer
          minimum: 1
          maximum: 1000
        min_weeks:
          type: integer
          minimum: 1
          maximum: 12
        max_weeks:
          type: integer
          minimum: 1
          maximum: 12
        start_from:
          $ref: '#/components/schemas/Date'
        start_to:
          $ref: '#/components/schemas/Date'
        max_price_usd:
          allOf:
            - $ref: '#/components/schemas/PositiveUsdPrice'
            - $ref: '#/components/schemas/ShortTermPrice'
        require_infiniband:
          type: boolean
        regions:
          type: array
          maxItems: 50
          uniqueItems: true
          description: Delivery countries a fitting ask may be in; empty for any.
          items:
            $ref: '#/components/schemas/DeliveryCountry'
        accepted_plans:
          type: array
          minItems: 1
          maxItems: 4
          uniqueItems: true
          items:
            $ref: '#/components/schemas/ShortTermPaymentPlan'
        hold_usd:
          description: An AUTO_BOOK bid's card hold; null otherwise.
          oneOf:
            - $ref: '#/components/schemas/PositiveUsdAmount'
            - type: 'null'
        hold_expires_at:
          description: When the authorized card hold lapses, seven days after it was
            authorized; null until then.
          oneOf:
            - $ref: '#/components/schemas/Timestamp'
            - type: 'null'
        matched_deal_id:
          description: The deal of the booking a MATCHED bid made.
          oneOf:
            - $ref: '#/components/schemas/Uuid'
            - type: 'null'
        version:
          type: integer
          format: int64
          minimum: 1
      additionalProperties: false
    ShortTermBidList:
      type: object
      required: [items]
      properties:
        items:
          type: array
          maxItems: 200
          items:
            $ref: '#/components/schemas/ShortTermBid'
      additionalProperties: false
    CreateShortTermBidRequest:
      type: object
      required:
        [
          mode,
          gpu_chips,
          min_nodes,
          max_nodes,
          min_weeks,
          max_weeks,
          start_from,
          start_to,
          max_price_usd,
          accepted_plans,
          return_path,
        ]
      properties:
        mode:
          type: string
          enum: [AUTO_BOOK, NOTIFY]
        gpu_chips:
          type: array
          minItems: 1
          maxItems: 10
          uniqueItems: true
          items:
            $ref: '#/components/schemas/GpuChip'
        min_nodes:
          type: integer
          minimum: 1
          maximum: 1000
        max_nodes:
          type: integer
          minimum: 1
          maximum: 1000
          description: At least min_nodes.
        min_weeks:
          type: integer
          minimum: 1
          maximum: 12
        max_weeks:
          type: integer
          minimum: 1
          maximum: 12
          description: At least min_weeks.
        start_from:
          $ref: '#/components/schemas/Date'
          description: Today or later, in UTC.
        start_to:
          $ref: '#/components/schemas/Date'
          description: On or after start_from and at most twelve weeks after today.
        max_price_usd:
          allOf:
            - $ref: '#/components/schemas/PositiveUsdPrice'
            - $ref: '#/components/schemas/ShortTermPrice'
        require_infiniband:
          type: boolean
          default: false
        regions:
          type: array
          maxItems: 50
          uniqueItems: true
          description: Delivery countries a fitting ask may be in; omitted or empty
            for any.
          items:
            $ref: '#/components/schemas/DeliveryCountry'
        accepted_plans:
          type: array
          minItems: 1
          maxItems: 4
          uniqueItems: true
          items:
            $ref: '#/components/schemas/ShortTermPaymentPlan'
        return_path:
          $ref: '#/components/schemas/ShortTermReturnPath'
      additionalProperties: false
    ShortTermBidCreated:
      type: object
      required: [bid]
      properties:
        bid:
          $ref: '#/components/schemas/ShortTermBid'
        payment_url:
          type: string
          format: uri
          description: The checkout that holds the buyer's card for an AUTO_BOOK bid
            when the company has no saved card; omitted otherwise.
      additionalProperties: false
    InferenceDeployment:
      type: object
      required:
        [
          id,
          model_id,
          model_name,
          state,
          steps,
          failure,
          supply_tier,
          endpoint,
          created_at,
          updated_at,
        ]
      properties:
        id: { $ref: '#/components/schemas/Uuid' }
        model_id: { type: string }
        model_name: { type: string }
        state:
          type: string
          enum: [PROVISIONING, REGISTERING, LIVE, FAILED, STOPPING, STOPPED]
        steps:
          type: array
          description: Every setup step in order, with how far it got.
          minItems: 6
          maxItems: 6
          items:
            $ref: '#/components/schemas/InferenceDeploymentStep'
        failure:
          oneOf: [{ $ref: '#/components/schemas/InferenceFailure' }, { type: 'null' }]
        supply_tier:
          type: string
          enum: [open, vetted]
          description: >-
            vetted for a machine its company switched on under the supply terms,
            which serves customers; open for a setup started before the switch,
            which is probed but never serves them.
        endpoint:
          description: The router's last view of the machine; null before it was read and once the machine left the router.
          oneOf: [{ $ref: '#/components/schemas/InferenceEndpoint' }, { type: 'null' }]
        created_at: { $ref: '#/components/schemas/Timestamp' }
        updated_at: { $ref: '#/components/schemas/Timestamp' }
      additionalProperties: false
    InferenceDeploymentStep:
      type: object
      required: [key, state]
      properties:
        key: { type: string, enum: [connect, engine, model, certificate, port, health] }
        state: { type: string, enum: [DONE, RUNNING, PENDING, FAILED] }
        detail: { type: string }
      additionalProperties: false
    InferenceFailure:
      type: object
      required: [step, message]
      properties:
        step: { type: string, enum: [connect, engine, model, certificate, port, health] }
        message: { type: string, minLength: 1, maxLength: 1000 }
      additionalProperties: false
    InferenceEndpoint:
      type: object
      required: [healthy, serving]
      properties:
        healthy: { type: boolean, description: The router's probe reaches the model server. }
        serving: { type: boolean, description: The router's probe sees it serve the model. }
      additionalProperties: false
