Skip to main content
GET
Get a demand exchange (proposal pass)

Authorizations

Authorization
string
header
required

API key or access token

Path Parameters

runId
integer
required

Surrogate id of the chef compose run that anchors this exchange (the demand-inbox ledger row id).

Required range: 1 <= x <= 9007199254740991
Example:

42

Response

Get a demand exchange (proposal pass)

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.

exchangeId
string
required

The compose-run id anchoring this exchange.

buyer
string | null
required
provenance
string | null
required

How this brief reached the ledger: 'live' = an AdCP get_products call from a buyer agent, 'uploaded' = a seller-uploaded RFP/brief confirmed in chat (AI-4728). Reflects this run's own intake origin (AI-5025), falling back to the linked brief artifact's source column only for historical runs recorded before that origin was captured. Null when neither is available. An uploaded exchange is never presented as live buyer demand.

runPurpose
enum<string> | null
required

The compose run's own run_purpose. null means the run predates classification (an absent historical value, treated as real demand); a persisted value outside this build's known set surfaces as 'unknown', which consumers must treat exactly like a synthetic purpose — never as a live buyer. Together with provenance this is how the pass classifies the exchange into a matrix-ruling cell: any purpose other than 'live'/'unclassified' (including 'unknown') is never real buyer demand, regardless of what provenance says.

Available options:
live,
setup_test,
simulation,
probe,
evaluation,
unclassified,
unknown
briefText
string | null
required
briefFacts
object | null
required

Condensed brief facts read from the persisted brief artifact request (never reconstructed). Any field the buyer did not send is null.

briefArtifactId
string | null
required
briefArtifactAvailable
boolean
required

False when no brief artifact was captured (historical run). Readers must say "brief unavailable" rather than reconstruct it.

proposalArtifactId
string | null
required

The most recent proposal artifact id (from the full persisted pass list, not the capped slice), for reference.

passesTruncated
boolean
required

True when the run had more persisted proposal artifacts than the pass cap; the widget discloses the cap rather than silently dropping older passes.

result
enum<string>
required

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.

Available options:
closed_won,
closed_lost,
pending
wonValue
object[] | null
required

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.

statedBudget
object | null
required

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).

ledBy
enum<string> | null
required
Available options:
agent,
human
grade
enum<string> | null
required
Available options:
A,
B,
C,
D,
F
feedback
string | null
required
receivedAt
string
required
respondedAt
string | null
required
grouping
object
required
passes
object[]
required
adjustable
boolean
required

Whether Adjust is available on this exchange right now, from the shared eligibility rule the compose mutation re-checks (AI-5571). False for anything but classified live demand with a retained brief, active operating instructions, and lifetime adjustments remaining.

adjustabilityReason
enum<string> | null
required

Null when adjustable is true. Otherwise the server's own named reason — never a bare not-found — so the pass renders Adjust disabled with the reason instead of a button that fails when pressed.

Available options:
not_live_demand,
brief_not_retained,
no_operating_instructions,
adjustment_limit_reached,
open_revision_exists
openRevision
object | null
required

The exchange's open draft or awaiting-approval revision, surfaced separately from the pass list — a seller-composed revision is never mistaken for a captured pass until it is actually sent.