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

# Get a demand exchange (proposal pass)

> The full story of one exchange for the calling storefront: condensed brief facts read from the persisted brief artifact, ordered passes with product allocation + pricing rows and expand-to-inspect snapshots from the persisted proposal artifacts, per-pass commercial outcome, and an exchange-level result (won if any pass won, else lost if the most recent decided pass lost, else pending). Grouped from persisted linkage only — one compose run and its proposal artifacts, never the content-addressed brief digest — so an exchange reads as a single composition pass until persisted cross-run lineage exists. A run with no captured artifact renders as unavailable, never reconstructed; a metric with no input is null, never 0. Scoped to the caller's storefront — a run outside it returns 404.



## OpenAPI

````yaml /v2/storefront-api-v2.yaml get /demand-inbox/{runId}/exchange
openapi: 3.0.0
info:
  title: Scope3 Storefront API
  version: 2.0.0
  description: |-
    REST API for partners to manage storefronts, inventory sources, and billing.

    ## Authentication

    All endpoints require a Bearer token in the Authorization header:
    ```
    Authorization: Bearer your-api-key
    ```

    ## Base URL

    `https://api.interchange.io/api/v2/storefront`

    ## For AI Agents

    AI agents can use the MCP endpoint at `/mcp/v2/storefront` with three tools:
    - `initialize`: Start an MCP session
    - `api_call`: Make REST API calls
    - `ask_about_capability`: Learn about API features
servers:
  - url: https://api.interchange.io/api/v2/storefront
    description: Production server
security: []
tags:
  - name: Account
    description: Account management, service tokens, and preferences
  - name: Storefront
    description: Manage storefront and inventory sources
  - name: Storefront Agents
    description: List and manage registered sales, signals, and outcomes agents
  - name: Storefront Activity
    description: Audit log of configuration and inventory changes on the storefront
  - name: Storefront Billing
    description: Payout bank details and billing configuration for storefronts
  - name: AI Usage
    description: Storefront AI token usage visibility by model
  - name: MCP
    description: Model Context Protocol endpoints
paths:
  /demand-inbox/{runId}/exchange:
    get:
      tags:
        - Storefront
      summary: Get a demand exchange (proposal pass)
      description: >-
        The full story of one exchange for the calling storefront: condensed
        brief facts read from the persisted brief artifact, ordered passes with
        product allocation + pricing rows and expand-to-inspect snapshots from
        the persisted proposal artifacts, per-pass commercial outcome, and an
        exchange-level result (won if any pass won, else lost if the most recent
        decided pass lost, else pending). Grouped from persisted linkage only —
        one compose run and its proposal artifacts, never the content-addressed
        brief digest — so an exchange reads as a single composition pass until
        persisted cross-run lineage exists. A run with no captured artifact
        renders as unavailable, never reconstructed; a metric with no input is
        null, never 0. Scoped to the caller's storefront — a run outside it
        returns 404.
      operationId: getDemandExchange
      parameters:
        - in: path
          name: runId
          schema:
            description: >-
              Surrogate id of the chef compose run that anchors this exchange
              (the demand-inbox ledger row id).
            example: 42
            type: integer
            maximum: 9007199254740991
            minimum: 1
          required: true
          description: >-
            Surrogate id of the chef compose run that anchors this exchange (the
            demand-inbox ledger row id).
      responses:
        '200':
          description: Get a demand exchange (proposal pass)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DemandExchange'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: No demand-inbox exchange with that run id in the calling storefront.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
        - bearerAuth: []
components:
  schemas:
    DemandExchange:
      description: >-
        The full story of one brief → proposal → result exchange: brief facts,
        ordered passes with product allocation + pricing rows, per-pass outcome,
        and an exchange-level result. Every number is traceable to a persisted
        artifact or outcome record; missing inputs render unavailable, never 0.
      type: object
      properties:
        exchangeId:
          description: The compose-run id anchoring this exchange.
          type: string
        buyer:
          nullable: true
          type: string
        briefText:
          nullable: true
          type: string
        briefFacts:
          nullable: true
          allOf:
            - $ref: '#/components/schemas/DemandExchangeBriefFacts'
        briefArtifactId:
          nullable: true
          type: string
        briefArtifactAvailable:
          description: >-
            False when no brief artifact was captured (historical run). Readers
            must say "brief unavailable" rather than reconstruct it.
          type: boolean
        proposalArtifactId:
          description: The most recent proposal artifact id, for reference.
          nullable: true
          type: string
        result:
          description: >-
            Exchange-level commercial result. Derivation rule: won if ANY pass
            is won; else lost if the most recent decided pass is lost; else
            pending. With one run per exchange today this equals the run
            outcome.
          type: string
          enum:
            - closed_won
            - closed_lost
            - pending
        wonValue:
          description: >-
            Money won across the exchange, one entry per currency
            (both-or-nothing amount+currency per the AI-4726 money model; never
            summed cross-currency). Null when nothing won recorded a priced
            outcome.
          nullable: true
          type: array
          items:
            type: object
            properties:
              amount:
                type: number
              currency:
                type: string
            required:
              - amount
              - currency
            additionalProperties: false
        statedBudget:
          description: >-
            The buyer's stated budget range. Surfaced at the exchange level so a
            lost exchange still shows the demand it represented (the AI-4726
            lost-demand visibility).
          nullable: true
          allOf:
            - $ref: '#/components/schemas/DemandExchangeBudgetRange'
        ledBy:
          nullable: true
          type: string
          enum:
            - agent
            - human
        grade:
          nullable: true
          type: string
          enum:
            - A
            - B
            - C
            - D
            - F
        feedback:
          nullable: true
          type: string
        receivedAt:
          type: string
        respondedAt:
          nullable: true
          type: string
        grouping:
          $ref: '#/components/schemas/DemandExchangeGrouping'
        passes:
          type: array
          items:
            $ref: '#/components/schemas/DemandExchangePass'
      required:
        - exchangeId
        - buyer
        - briefText
        - briefFacts
        - briefArtifactId
        - briefArtifactAvailable
        - proposalArtifactId
        - result
        - wonValue
        - statedBudget
        - ledBy
        - grade
        - feedback
        - receivedAt
        - respondedAt
        - grouping
        - passes
      additionalProperties: false
    ErrorResponse:
      description: Standard error response
      type: object
      properties:
        data:
          type: string
          nullable: true
          enum:
            - null
        error:
          $ref: '#/components/schemas/ApiError'
      required:
        - data
        - error
      additionalProperties: false
    DemandExchangeBriefFacts:
      description: >-
        Condensed brief facts read from the persisted brief artifact request
        (never reconstructed). Any field the buyer did not send is null.
      type: object
      properties:
        channels:
          nullable: true
          type: array
          items:
            type: string
        countries:
          nullable: true
          type: array
          items:
            type: string
        budgetRange:
          nullable: true
          allOf:
            - $ref: '#/components/schemas/DemandExchangeBudgetRange'
        promotedOffering:
          nullable: true
          type: string
        brandDomain:
          nullable: true
          type: string
      required:
        - channels
        - countries
        - budgetRange
        - promotedOffering
        - brandDomain
      additionalProperties: false
    DemandExchangeBudgetRange:
      description: >-
        The buyer's stated budget range, exactly as sent in the brief's
        filters.budget_range. Null when the buyer did not state one.
      type: object
      properties:
        min:
          nullable: true
          type: number
        max:
          nullable: true
          type: number
        currency:
          type: string
      required:
        - min
        - max
        - currency
      additionalProperties: false
    DemandExchangeGrouping:
      type: object
      properties:
        basis:
          description: >-
            The persisted linkage the exchange is grouped on: a single compose
            run and its proposal artifacts.
          type: string
          enum:
            - compose_run
        passCount:
          type: integer
          minimum: -9007199254740991
          maximum: 9007199254740991
        multiPass:
          type: boolean
        note:
          description: >-
            Human-readable disclosure of the grouping honesty (e.g. that refine
            passes are not yet rolled up because no persisted cross-run lineage
            exists).
          type: string
      required:
        - basis
        - passCount
        - multiPass
        - note
      additionalProperties: false
    DemandExchangePass:
      type: object
      properties:
        passId:
          type: string
        sequence:
          description: 1-based order of this pass within the exchange.
          type: integer
          minimum: -9007199254740991
          maximum: 9007199254740991
        version:
          description: Proposal artifact version within its supersedes chain.
          nullable: true
          type: integer
          minimum: -9007199254740991
          maximum: 9007199254740991
        kind:
          description: >-
            The run's buying mode ('brief' for a first response, 'refine' for a
            renegotiation pass).
          type: string
        createdAt:
          type: string
        planName:
          nullable: true
          type: string
        planBriefAlignment:
          nullable: true
          type: string
        planCount:
          description: >-
            How many media plans the proposal carried. Allocation is read from
            one selected plan (the committed plan if present, else the first
            draft); when this exceeds 1, a product unallocated here may be
            allocated in another plan and the widget discloses that rather than
            conflating the two.
          type: integer
          minimum: -9007199254740991
          maximum: 9007199254740991
        captureState:
          description: >-
            How completely this pass was persisted: 'captured' = full response
            and snapshots stored; 'truncated' = a proposal artifact exists but
            its response or product snapshots exceeded the size limit, so some
            detail (e.g. per-product snapshots) is absent though product rows
            still render; 'unavailable' = no proposal artifact was captured for
            the run (historical / predates capture). Readers word the banner
            from this so a truncated pass is never described as predating
            capture.
          type: string
          enum:
            - captured
            - truncated
            - unavailable
        artifactAvailable:
          description: >-
            True when a proposal response was persisted and product rows can
            render (captureState captured or truncated). False when no response
            was captured (captureState unavailable) — products render as
            unavailable, never reconstructed.
          type: boolean
        products:
          type: array
          items:
            $ref: '#/components/schemas/DemandExchangeProduct'
        productsTruncated:
          description: >-
            True when the pass had more products than the response cap; the
            widget discloses the cap rather than silently dropping rows.
          type: boolean
        outcome:
          $ref: '#/components/schemas/DemandExchangePassOutcome'
      required:
        - passId
        - sequence
        - version
        - kind
        - createdAt
        - planName
        - planBriefAlignment
        - planCount
        - captureState
        - artifactAvailable
        - products
        - productsTruncated
        - outcome
      additionalProperties: false
    ApiError:
      description: Structured error object
      type: object
      properties:
        code:
          description: Machine-readable error code
          type: string
        message:
          description: Human-readable error message
          type: string
        field:
          description: Field path associated with the error
          type: string
        details:
          description: Additional error context
          type: object
          additionalProperties: {}
      required:
        - code
        - message
      additionalProperties: false
    DemandExchangeProduct:
      type: object
      properties:
        productId:
          type: string
        name:
          nullable: true
          type: string
        description:
          nullable: true
          type: string
        deliveryType:
          nullable: true
          type: string
        pricing:
          description: >-
            Buyer-facing price for the pricing option the plan priced this
            product against (matched by the allocation pricing_option_id); when
            the plan named none, the first fixed-price option is used, else the
            first floor-price option, else the first option. Within the chosen
            option a fixed price is shown, else the floor. Null when the
            persisted product carried no priced option.
          nullable: true
          type: object
          properties:
            model:
              nullable: true
              type: string
            amount:
              nullable: true
              type: number
            currency:
              nullable: true
              type: string
            kind:
              nullable: true
              type: string
              enum:
                - fixed
                - floor
          required:
            - model
            - amount
            - currency
            - kind
          additionalProperties: false
        allocationPercent:
          description: >-
            This product's share of the plan budget, exactly as the agent
            allocated it in the proposal (0–100). Null when the proposal carried
            no media plan for it.
          nullable: true
          type: number
        allocationValue:
          description: >-
            allocationPercent applied to the buyer's committed point budget
            (budget_range where min===max). Null unless the buyer stated a true
            point budget — never a guess against an open range or a single
            ceiling/floor bound.
          nullable: true
          type: object
          properties:
            amount:
              type: number
            currency:
              type: string
          required:
            - amount
            - currency
          additionalProperties: false
        formats:
          type: array
          items:
            type: object
            properties:
              agentUrl:
                nullable: true
                type: string
              id:
                nullable: true
                type: string
            required:
              - agentUrl
              - id
            additionalProperties: false
        snapshot:
          description: >-
            The persisted immutable product snapshot for expand-to-inspect. Null
            when the proposal artifact payload exceeded its size limit
            (complete=false).
          nullable: true
      required:
        - productId
        - name
        - description
        - deliveryType
        - pricing
        - allocationPercent
        - allocationValue
        - formats
        - snapshot
      additionalProperties: false
    DemandExchangePassOutcome:
      description: >-
        Commercial outcome attributed to this pass. Today one run backs an
        exchange, so every pass shares the run-level outcome.
      type: object
      properties:
        result:
          type: string
          enum:
            - closed_won
            - closed_lost
            - pending
        wonValue:
          nullable: true
          type: object
          properties:
            amount:
              type: number
            currency:
              type: string
          required:
            - amount
            - currency
          additionalProperties: false
      required:
        - result
        - wonValue
      additionalProperties: false
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: API key or access token

````