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

# Demand inbox

> The front-of-house ledger of every advertiser brief your storefront answered — product offer or proposal, feedback, and commercial lifecycle.

## Overview

The **demand inbox** is your storefront's front-of-house ledger: one place that
answers *what demand came in, how did my agent respond, and did we win?* Each
row is one buyer brief your Merchandising Agent answered, joined to the seller
response it produced and the commercial result that followed. A response is
called a **Proposal** only when the persisted response carries a proposal id;
otherwise returned products are a **product offer**. It is the human-readable
scoreboard on top of your [intelligence runs](/v2/storefront/intelligence-runs/overview).

Every number is computed **only from what actually happened**. A metric with no
inputs yet reads as *unavailable* — never a fabricated `0`. A historical brief
captured before canonical artifact persistence reads as *artifact unavailable*
rather than being reconstructed.

The Page reads the v3 demand-loop records directly. Its list is
`search({ "kind": "rfp" })`, row drill-in is `get({ "kind": "rfp_turn" })`,
feedback and revisions use `save_rfp`, and aggregate analytics come from
`get_rfp_performance`. The Page does not call a hidden demand-inbox list
endpoint or compute its own analytics rollups.

## The metrics strip

| Metric         | Meaning                                                                       | When unavailable                  |
| -------------- | ----------------------------------------------------------------------------- | --------------------------------- |
| Briefs YTD     | Briefs received this calendar year                                            | Never — a true count (may be `0`) |
| Answered       | Share of this year's briefs the agent answered with a seller response         | No briefs yet                     |
| Win rate       | Won ÷ (won + lost) across briefs with a decided result (see scope note below) | No result recorded yet            |
| Avg grade      | Mean seller grade (A–F) rounded to the nearest letter                         | Nothing graded yet                |
| Agent vs human | Split of briefs led by the agent vs a human who took over                     | No run has recorded attribution   |

<Note>
  The scoreboard covers this year's briefs and counts your agent's **first
  response** to each brief. Imported briefs are excluded: the scoreboard measures
  work your agent did in this year, rather than historical work you added for
  reference. Renegotiation (refine) passes are not yet rolled up to
  their originating brief, so the win rate reflects first-response briefs rather
  than the full negotiation. A seller-adjusted revision you Send (see
  [Adjusting a proposal](#adjusting-a-proposal)) is never counted here either —
  composing and sending a revision writes no new brief-response record, only a
  new pass on the exchange that already exists, so the agent-vs-human split
  moves but the first-response metrics above cannot be inflated or deflated by
  your own edits.
</Note>

## The ledger row

Each row carries the brief, advertiser, buying operator, actual response kind,
when it came in and went out, response elapsed time when recorded, who led the
response (agent or human), the buyer's feedback, commercial lifecycle, and your
letter grade.

* **Result** derives from the brief run's own persisted state. Live demand uses
  the commercial outcome: a forwarded or delivered buy is `closed_won`, a
  rejected buy is `closed_lost`, and anything still in flight (or not yet
  attributed) remains undecided. For those live rows, the UI names the most
  specific state, such as **Product offer sent · awaiting advertiser decision**,
  **Proposal sent · awaiting advertiser decision**, or **Media buy submitted ·
  awaiting seller approval**. Imported references instead read **Imported
  reference · not sent to a buyer**. A non-live row gets a purpose badge:
  **Draft response · not sent to a buyer** for `draft`, **Evaluation response ·
  no buyer** for `evaluation`, **Practice response · no buyer** for practice,
  **Setup test response · no buyer** for `setup_test`, **Simulated response ·
  no buyer** for `simulation`, and **Probe response · no buyer** for `probe`.
  A failed or unavailable response keeps its recorded failure state rather than
  being relabeled by purpose. A booking attributed to a later renegotiation
  pass is not rolled up here (see the scope note above). There is no separate
  result to keep in sync — it always reflects the run's real state.
* **Grade**, **feedback**, and **led-by** are seller-authored annotations you
  record against a row. Grade and feedback default to empty; led-by defaults to
  the agent, since every composed response is agent-led until a human takes it
  over.

## Advertiser, operator, and account identity

The primary commercial identity is the **advertiser brand**. The **buying
operator** — the organization operating the buy — appears secondarily. Together
they form the advertiser relationship described by AI-5080: **BrandRef ×
Operator**. A direct buy can therefore show the same organization on both axes;
an agency-operated buy keeps the brand and agency distinct.

The surface reads these values from the persisted AdCP request first:
`brand.domain` (or `account.brand.domain`) for the advertiser,
`account.operator` for the buying operator, and optional `account.account_id`
as the buyer-declared account reference. When the request uses an opaque
`account_id`, the Seller authorizes that account before discovery and records
the approved relationship's canonical brand and operator on the compose run;
those values fill identity fields the opaque wire reference cannot contain.
If a request pairs the account id with a different brand or operator, the
Seller rejects it before discovery instead of recording split identity.
Other historical compose-run snapshots remain fallback evidence only. Missing
values read **Advertiser not supplied** or **Buying operator not supplied** —
never *Unknown*, and never a customer display name substituted for a missing
brand.

<Note>
  An AdCP `account_id` is not a CRM record and is not a seller-native ad-server
  mapping. CRM evidence is optional context; source-native account mappings are
  execution configuration. Neither is joined into the advertiser name on this
  surface, so “no CRM evidence,” “no required source mapping,” and “advertiser not
  supplied” remain three different facts.
</Note>

## Product offer, Proposal, and money

The response label follows the persisted response contract:

| Label                    | Persisted evidence                                                                 |
| ------------------------ | ---------------------------------------------------------------------------------- |
| **Product offer**        | One or more products returned, with no non-empty `proposal_id`.                    |
| **Proposal**             | At least one persisted proposal carries a non-empty `proposal_id`.                 |
| **No offer**             | The persisted response explicitly returned an empty products list and no Proposal. |
| **Response unavailable** | No response payload was retained; the UI does not reconstruct one.                 |

The budget remains the buyer's stated range and currency. CPM or another product
price remains the selected persisted pricing option and its own currency. A
proposed allocation becomes an amount only against a point budget where
`min === max`; an open range or one-sided bound stays percentage-only. Won value
comes from delivered spend when recorded, otherwise the booked media-buy budget,
and only when amount and currency were persisted together. These figures are
never converted or filled from one another.

## Where a brief came from

Not every brief arrives as a live AdCP call from a buyer agent. Each row carries
its **provenance** so you always know what you're looking at:

| Provenance   | What it means                                                                                                                                          |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Live**     | An AdCP `get_products` call from a buyer's agent — real inbound demand.                                                                                |
| **Uploaded** | A brief you handed your agent yourself — you dropped an RFP, spreadsheet, or forwarded agency email into chat and confirmed it.                        |
| **Imported** | A historical RFP you added with `origin: imported`, optionally with the response you sent. It is reference evidence, not a live or agent-run exchange. |
| *(none)*     | No brief artifact was captured — a practice pitch or a historical pre-capture run.                                                                     |

An **uploaded** brief never masquerades as live buyer demand: no buyer is
recorded unless you confirmed the buyer from the document, no buyer is contacted,
and nothing is booked. It counts on the scoreboard by the same rules as any other
brief — an uploaded brief has no commercial outcome until you record one, so it
stays `pending` and never inflates your win rate.

### Uploading a brief into chat

When a brief reaches your sales lead by email or attachment instead of by API,
you don't need a form. Drop the document into the Murph composer. Murph reads it,
shows you the demand brief it recognized (unconfirmed), and — if the upload
includes a media kit — tells you in chat what the kit claims that your storefront
can and can't back. Nothing is written or run until you confirm. When you
confirm, the brief runs through the same product-discovery and composition path a
buyer's agent would trigger, and the exchange appears here as an **uploaded** row
that drills into the proposal pass like any other.

To change an uploaded exchange, upload or paste a corrected brief and confirm it
— that produces a new exchange. The original keeps its honest record and stays
annotatable (grade, feedback, result).

### Importing a historical brief

Add a historical RFP when you want a past buyer request and your response to
teach the agent. From the [Library](/v2/storefront/library), add the response,
choose the brief it answered, or create the imported brief inline. The record
carries `origin: imported` and appears in this ledger so it can be paired,
annotated, and endorsed. It is excluded from **Briefs YTD**, **Answered**, and
**Win rate**, because those measures describe live and agent-run demand rather
than a reference you added from history.

### Link a response to a brief

On an eligible brief, choose an uploaded Library document or historical
proposal and select **Link to brief**. Direct file upload in the Demand Inbox
is out of scope. If the file is not in the Library yet, add it there, then
return to choose it here. The row then names the linked response. A brief that
already has a response explains why linking is unavailable; the existing pair
is not silently replaced.

You can start from the Library instead. Open an uploaded document and choose
**Save as a response**, then choose the brief it answered. Use **Import
historical brief** there when a past brief is not recorded yet. The Responses
section shows sent and imported proposals, including a response without a brief
as a standalone shape example, not an evaluation.

### Commentary and endorsement

Grade and feedback on a row are the commentary on its brief-response pair.
After a response is attached or already present, choose **Endorse** to mark the
pair as the answer you want your agent to learn from. Removing the endorsement
leaves the brief, response, commentary, and commercial outcome in place; it
only stops that pair from being offered as an endorsed example.

## The seller-response pass

Open a ledger row to see the **full story of that exchange** — the response
pass. It answers *what exactly did my agent return, from what brief, and how did
the exchange evolve?* The pass shows:

* The **brief**, as a business document. It leads with the advertiser, who it
  came through, and the buyer's ask in their own words, then the few facts a
  commercial decision turns on: budget, flight, markets, channels, how they want
  to transact, and how long you have to respond. Requirements follow as plain
  sentences — placements, exclusivity, currencies, policies, the metrics they
  expect back, performance standards and verification vendors — then where it has
  to run: regions, metros, an approved property list by name, keywords, and the
  advertiser catalog. A field the buyer did not send is simply absent; you are
  never shown a row that says nothing.
* **Condensed brief facts** appear only while the complete brief is loading, if
  its read fails, or for historical rows with no artifact link. They are an
  honest fallback, never a reconstructed substitute for the request.
* **A composed pitch, when your storefront has one for this pass.** The pass
  opens on a written argument, read as a story: what the buyer's brief asked
  for, why you, the plan told as roles instead of rows, proof from a matched
  case study, the terms, and the ask — before the product table, which moves
  under a Plan appendix heading beneath it. A pass with no composed pitch
  renders exactly as it always has: the plan, and nothing invented around it.
  See [The pitch](/v2/storefront/pitch) and
  [The story-first proposal](/v2/storefront/proposal-story).
* The **plan**, as the products you sent. The plan's name, its scale, and its
  argument for the brief come first, then one line per product with its price and
  its share of the plan. Expand a product to see why it is in the plan, how it is
  priced (including every option offered and where the plan's selection sat
  against market guidance), where it runs, who it reaches, and what it reports.
* **What fed each decision.** Expand a product row to see which of *your own*
  ingredients your agent consulted when it built that line: the inventory it
  selected, the audience it layered on, and the rate-card entries it priced
  against. Tap one to open the thing that owns it — the source it came from, or
  your Playbook — change it there, and the pass refreshes. Underneath the table,
  a second group names what fed the *whole* response rather than one product:
  the Playbook version it composed under, and the terms resolved for that buyer.
  See [Where "fed by" comes from](#where-fed-by-comes-from).
* **Per-product money** appears when the buyer committed a single exact budget —
  each product's share of that amount, in the plan view and in the pass history.
  Against an open range or a lone ceiling/floor you see shares only: a
  proposal commits to a split, and money arrives when the buy is created.
* **Technical details**, one disclosure at the foot of each. Artifact id, schema,
  digest, source, capture size, run linkage, redactions, truncation evidence, and
  the exact structured request or response — copyable, and identical to what an
  agent reads. Both the human view and the exact artifact come from the same
  immutable capture; there is no separately maintained presentation model.
* **Pass history** for every recorded response, with the buyer-facing price and
  plan allocation per product. Selecting a pass opens its full plan above.
* A **refine-pass timeline** and the **commercial outcome** for the exchange.

Every number on the pass is traceable to a persisted brief/response artifact or
outcome record; an input that wasn't captured reads as *unavailable* rather than
being reconstructed. A capture can also be **truncated**: the response was
recorded but part of it exceeded the storage cap. The pass says which — product
rows still render when the response survived and only the per-product snapshots
were dropped, and a pass whose response itself wasn't stored is labeled as a
truncated capture, never described as predating capture.

## Where "fed by" comes from

"Fed by" answers the question a proposal has to survive: *why these products, at
these prices?* It is a record of what your agent **actually consulted** while
composing, written at the moment it composed. It is not a summary the agent
writes about itself afterwards, and it is not reconstructed from the finished
proposal.

Two things follow from that, and they are the reason you can trust it.

**It is scoped as honestly as the decision was.** Some ingredients are chosen per
product — the inventory a line is built from, the audience layered on it, the
rate-card entries priced against that exact combination. Those appear on the
product row. Others are resolved once for the whole response — the Playbook
version in force, and the terms that applied to that buyer. Those appear under
the table, labelled as feeding the whole pass. Nothing is promoted from one group
to the other to make a row look better-explained than it is.

**It is history, and history is not rewritten.** A chip names the ingredient as
it stood when the proposal was composed, including its name at the time. Renaming
a rate-card entry later, editing your Playbook, or deleting a source never
changes what an earlier pass says fed it. If you tap a chip for something you
have since deleted, you are told plainly that it no longer exists — you are never
sent to a stand-in for it. That check is exact: removing one product or audience
from a source you otherwise kept is enough, and a same-named entry on a different
source is never treated as the one that fed the decision. And it only ever says
"deleted" when it actually checked and found nothing: if the check itself cannot
complete, the chip simply becomes un-tappable rather than telling you something
was removed when it may not have been.

What each chip names:

| Chip           | The ingredient                                                                               | Where tapping it takes you                                   |
| -------------- | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
| Inventory      | The wholesale inventory the product was built from                                           | The inventory source it came from, when that source is yours |
| Audience       | An audience or contextual signal layered on the product                                      | The inventory source it came from, when that source is yours |
| Rate card      | A pricing entry that anchored or floored this product's price                                | Your Playbook                                                |
| Playbook       | The guidance version the response composed under                                             | Your Playbook                                                |
| Your marketing | A selling point from your product marketing that shaped how a product was named or described | Your product marketing page, anchored on that selling point  |
| Buyer terms    | The terms resolved for this buyer (notes and any discount)                                   | Shown for context; managed on the buyer                      |

A few honest limits:

* **A pass that predates this shows no chips.** Responses composed before your
  agent started recording what it consulted have nothing to show, and the row
  simply does not expand. Nothing is inferred for them.
* **A response you adjusted yourself shows the inventory and audience it
  composed from, and no rate-card or marketing chips.** An adjustment
  re-resolves your inventory but is priced from the adjustment you declared, so
  there are no rate-card entries to name; it also composes without your product
  marketing, so no selling point shaped it and none is named. Both absences are
  the honest record of what that pass actually consulted, not a gap in the
  display.
* **Your marketing material feeds composition at the whole-response level.**
  Once you've confirmed material into your
  [product marketing](/v2/storefront/product-marketing/overview), an
  eligible selling point can shape how products in the response are named or
  described, and the row's chips show "Your marketing" alongside the exact
  selling point consulted. Unlike inventory and audience, this is recorded
  once per response rather than per product line — the same way Playbook
  guidance is. The chip opens your product marketing page anchored on that
  exact selling point, so you can see it with its source and evidence.
* **Some chips name something you cannot open.** Inventory reached through
  another seller's storefront belongs to them, not to a source of yours, so its
  chip names what fed the decision without a place to go. Buyer terms are the
  same: they are shown for context and managed on the buyer. A chip whose
  owner we momentarily could not look up behaves the same way. None of these is
  described as deleted, because none of them was.
* **Buyer terms appear only when they actually applied.** Terms that matched the
  buyer but were held back — because the request carried no account, or because
  the row set neither a discount nor a note — did not shape the response, so they
  are not listed as having fed it.

## Adjusting a proposal

Open a proposal pass and, if the exchange qualifies, you can **Adjust** it:
declare a posture, a price adjustment, and a product-count cap, and re-compose
the proposal through your storefront's real merchandising engine — without
leaving the page. This is the same declaration grammar the Merchandising
Simulator already taught you in a zero-risk room; Adjust reuses it rather than
asking you to learn a second way to describe a change.

**Adjust is available only on live, retained, adjustable demand.** When it
isn't, the button is disabled and names exactly why:

| Reason                    | What it means                                                                                         |
| ------------------------- | ----------------------------------------------------------------------------------------------------- |
| Not live demand           | This exchange isn't a classified live buyer brief (e.g. a historical or unclassified run).            |
| Brief not retained        | The original brief artifact wasn't captured, so there's nothing to re-compose against.                |
| No operating instructions | Your storefront has no active operating instructions for the merchandising engine to compose against. |
| Adjustment limit reached  | This exchange has used up its lifetime adjustment allowance.                                          |

A stale page or a direct API call gets the same named reason — never a bare
"not found."

### Draft, then Send

Composing an adjustment produces a **draft revision**. A draft is silent by
construction: it holds no proposal artifact and no delivery state, so nothing
is visible to the buyer until you send it. Declining the sheet leaves no
record at all; discarding a draft leaves only its own terminal audit entry —
neither counts against anything you'd notice.

When you're ready, **Send** routes the revision through the same approval
setting you already use for media buys — the campaign-approval capability and
its manual/auto dial. There is no separate approval system to configure.

* If your storefront auto-approves, the send clears immediately.
* An enabled existing deprecated V2 per-buyer auto-approval override also
  clears the send immediately. You can [inspect or revoke that
  override](/v2/storefront/approvals/overview#deprecated-v2-per-buyer-approval);
  do not use it for new integrations.
* Otherwise it queues for a colleague to **approve** or **reject** — and
  **whoever drafted the revision cannot clear their own send**. You can always
  reject or withdraw your own submission (that creates no buyer-visible
  evidence), but sending it requires a second person. A rejected revision
  returns to draft, and resubmitting it clears the earlier decision entirely —
  a revision awaiting a decision never shows a decision that hasn't actually
  been made against it.
* A cleared send becomes a real pass on the exchange, labeled **seller-adjusted**
  and attributed to the person who sent it — it renders through the same pass
  card as everything your agent proposed, never a second layout.

Every exchange has a **lifetime limit** on how many adjustments it can produce
— counted over every revision you've ever composed on it, including ones you
discarded. Compose-and-discard doesn't reset the count.

### What "sent" actually means to the buyer

Be direct with yourself here: **sending a revision does not notify the
buyer's agent.** We investigated whether a follow-up proposal could reach the
buyer through the existing exchange, and today it can't — there is no
delivery lane for a subsequent proposal, and no honest staging/preview lane
either. A sent revision is recorded truthfully: it appears in your pass
history as sent, not delivered, and the page says so outright. If you need
the buyer to actually see the new terms, you still have to reach them the way
you would today — a message, a call, or a fresh brief exchange.

### What Adjust doesn't do

A draft never issues a rate hold and never converts into the buyer's stated
currencies — that's what an actual buyer-facing response does, and a
discardable draft must not create that kind of durable, buyer-facing state.
The draft does record the settlement currency and the buyer's stated
currencies as of when you composed it, purely for your own reference.

### What counts as one exchange

An exchange is grouped from **persisted linkage only**: one composition pass and
the proposal artifacts recorded against it. Byte-identical or refine briefs are
**not** grouped by their content hash (a hash collides across unrelated pursuits
and diverges across refines), and there is no persisted cross-run lineage yet —
so a renegotiation is its own ledger row and its own exchange today. Each
exchange therefore reads as a single composition pass, disclosed honestly on the
pass itself.

The **exchange-level result** follows an explicit rule: *won* if any pass in the
exchange is won; otherwise *lost* if the most recent decided pass is lost;
otherwise *pending*. With one pass per exchange today this is simply that pass's
outcome.

## Money

Each row shows the buyer's **stated budget range** exactly as it arrived on the
brief (buyers state it in `filters.budget_range`; when a buyer doesn't, the
column reads absence rather than a guess). A won row also shows the **won
value** — the delivered spend when reporting exists, otherwise the booked
media-buy budget — always in the money's own currency, never converted. The
**Booked YTD** tile totals this year's won money per currency; multiple
currencies are disclosed rather than summed together.

On the proposal pass, the exchange carries a per-currency **won value** when it
won (one entry per currency, never summed across currencies). A **lost** exchange
still surfaces the buyer's stated budget as **lost demand** — the size of the
opportunity you didn't close — so a loss is legible, not blank.

## Filters

Filter the ledger by **buyer** from the widget header to focus on one
relationship. Sector filtering is not available yet — sector is not captured
per brief today.

## Related

<CardGroup cols={2}>
  <Card title="Intelligence runs" href="/v2/storefront/intelligence-runs/overview" icon="microscope">
    The per-request detail behind each ledger row.
  </Card>

  <Card title="Seller analytics" href="/v2/storefront/analytics" icon="chart-line">
    The aggregate performance view across many runs.
  </Card>
</CardGroup>
