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

# Retained Session timeline

> The seller-only, retained Session read contract.

<Warning>
  A signed-in, non-impersonated seller can directly search and get the
  explicitly selected `storefront_rfp_v1` retained source. The staged compose
  exchange rollout additionally enables `storefront_compose_run_v1` for its
  selected seller. Unqualified Session discovery, Murph conversation reads, and
  every `save_session` action remain restricted to a signed-in platform operator
  in a seller account. Buyer Session access is not yet available: no source
  currently implements the required buyer relationship checks. Catalogue
  visibility is unchanged from the current release; buyer Session search still
  requires a seller account and direct Session reads are denied. `tools/list`
  for the active account remains authoritative.
</Warning>

## Seller Sessions workspace

The seller workspace exposes **Sessions** beside Advertisers when the
`sessionsPageEntry` feature flag is enabled. It shows retained conversation
history in ledger sequence as a chat-style timeline. Sellers can search the
admitted history and narrow it with the **All**, **Agents**, and **People**
filter. Source and lifecycle filters remain available in the compact filter
menu.

An exact RFP request or response hands off to Proposal Pass once the seller's
response for that turn exists. The handoff is typed and authorised for the
selected record; the workspace does not infer an RFP from displayed text. Brief
and media-buy references remain visible in the timeline. The workspace shows an
open action only when an event supplies the exact brief or media-buy reference;
otherwise the chip remains a label.

## Sessions Page access

The Sessions Page has separate access checks. Code may select it from the
existing Demand entry only for a signed-in seller who is not impersonating
another user and who already has both existing Demand access and the retained
Sessions Page access. The server makes that choice and checks access again for
each Page call. This does not enrol customer accounts or prove that the Page is
deployed for any account. Its access rules do not change generic Session
access: source-qualified Storefront RFP reads remain available to the seller
described below, while unqualified reads, Murph reads, and `save_session`
remain operator-only.

The Page keeps existing Demand access separate from retained conversations. A
seller who can use Demand can continue to read and work on authorised RFPs,
including opening Proposal Pass for the exact RFP and turn, even when retained
conversations are unavailable. Existing RFP write permissions still apply.

The Sessions Page opens its native RFP browser first. On larger screens, the
RFP list remains beside the selected Proposal Pass; on smaller screens, Back
returns to that list. Its filters and continuation stay with Demand. The
selected RFP also keeps its eligible native ledger actions, such as response
linking, pairing and seller feedback, beside that exact exchange; those actions
remain subject to the existing RFP write permissions. The
separate Sessions tab defaults to the Page-authorised Murph conversation
source. A seller who also has current Demand access can explicitly select
recorded RFP exchanges; each source keeps its own cursors and states.

When an exact native RFP turn is open, the ordinary bottom Murph composer can
show that selected RFP and turn as removable private coaching context. It does
not send a buyer message, alter the RFP, create a retained Session note, or
change the composer into a shared room. The server checks the exact current
RFP/turn pair, seller access, and private room ownership again for every
composer send. Closing or replacing the selected exchange clears the context.

The Sessions tab defaults to admitted Murph conversations. A seller who also
has existing Demand access can select **Recorded RFP exchanges** to read the
admitted `storefront_rfp_v1` source through the same Page. That selection is
explicit: the lists keep separate cursors, the RFP source does not appear in
the default conversation search, and it cannot create private notes or change
the RFP. Page conversation search and reads require a signed-in seller who is
not impersonating another user, Page enablement for that seller, and current
native room access. Private notes remain an operator-only `save_session`
operation and show read-only when present in the Sessions timeline. RFP history
additionally requires current Demand access and rechecks current seller
ownership for every page and read. The Page fails closed when any of those
checks is absent. The server checks room sharing again for each conversation
action. Existing read and write permissions still apply.

The staging seller enrolled in the compose exchange rollout can also select
`storefront_compose_run_v1`. It returns captured compose exchanges as
structured-only retained history. The server checks the rollout for every
search and read.

RFP and conversation lists keep separate cursors and metrics. Sellers can no
longer create private notes from the Sessions view. Existing private-note events
remain visible as read-only timeline events. The only remaining write path is
the separate operator `save_session` `record_note` action under its existing
authorisation; the Sessions view does not invoke it.

## Scope and access

`search({"kind":"session"})`, `get({"kind":"session","id":"..."})`,
and `save_session({"action":"record_note", ...})` retain one canonical
owner. A signed-in, non-impersonated seller may use `search` with
`filter.sessionSource: "storefront_rfp_v1"`, or with
`"storefront_compose_run_v1"` when the staged compose rollout has selected
their seller. They may `get` only a matching source Session ID. Each source
independently rechecks current seller ownership on every page and read.
Unqualified discovery, Murph conversation reads, and private notes still
require a platform operator acting in a seller account. Operator access does
not bypass source permissions. A member can read their own Murph room; a shared
room is readable only while the server's current room-sharing policy permits
that member. The server checks that policy again for every page and note retry.
Changing a participant, RFP owner, or private-practice marker does not grant
access.

Generic intake, buyer message delivery, lifecycle updates, approvals, chat
execution, and evaluations are not available through this admitted slice.

## Buyer read boundary

Buyer Session access is not yet available. No retained Session source currently
opts into buyer reads, so catalogue visibility remains unchanged from the
current release, buyer Session search requires a seller account, and direct
Session reads are denied.

When a source is approved for buyer reads, a signed-in buyer who is not
impersonating another user will be able to read only exchanges with a recorded
counterparty membership and a current relationship that the source verifies.
The source will check that relationship again for every search page and read. A
buyer will never see another buyer's exchange or an unrelated seller's exchange.
A revoked relationship will return no result, and `get` will not reveal whether
a Session exists.

The service returns only events explicitly classified as buyer-visible. It
withholds seller private notes, coaching, agent-only events, Murph context,
internal reasoning, and any event classification not explicitly approved for
buyers, even if a source mistakenly adds one during read enrichment. Buyers
cannot use `save_session`, add private notes, edit proposals, send a buyer
message, or use seller coaching controls. Sources that do not yet implement the
buyer relationship boundary are empty to buyers, rather than falling back to
their seller read path.

## Find and read retained history

Only conversations with a complete-history publication admission appear in
Session search. A retained root alone is not admission. Search and get never
create a Murph room, import history, allocate an event position, or repair
source data.

An eligible newly created Storefront RFP can admit its recorded initial request
to the retained timeline. Historical RFP rows are not imported by a read: until
a bounded server-side import admits their recorded history, they remain RFP
rows and have no Session result. After admission, later recorded RFP events
continue in the same retained sequence. RFPs owned by private practice never
appear as Sessions.

No filter searches admitted Murph conversations, RFPs, and standalone media-buy
Sessions for an operator. Lifecycle-filtered searches retain their existing
Murph/RFP behavior; media buys do not support lifecycle filtering. A text query
or the `archived` lifecycle filter selects the authorized Murph source only; it
never sends that request to the RFP source. A seller's direct RFP read must select
`filter.sessionSource: "storefront_rfp_v1"`. That source accepts exact
`filter.buyer` and `filter.advertiser` discovery values. They are trimmed
strings matched without case-folding or alias resolution against any of
`storefront_rfp.origin.buyer`,
`storefront_rfp.origin.preset.buyer`, and the latest recorded turn's
`request.dimensions.buyer` (and the equivalent `advertiser` paths). There is
no precedence: a row matches when any exact recorded path matches. Missing
values do not match. The latest-turn path is deliberately the current recorded
dimension, not a search across every historical participant or turn.

Those buyer and advertiser values are discovery metadata only. They are not
customer, user, or advertiser-account identities and do not grant access.
Sessions never derive an identity from brief, message, or discovery text.

| Filter | Storefront RFP | Murph conversation | Media buy |
| - | - | - | - |
| `advertiserId` / `advertiserIds` | Linked buyer proposal or recorded media buy | Stored advertiser association | Native media-buy advertiser |
| `buyerAccountIds` | Linked buyer proposal or recorded media buy | Unsupported: no stored buyer-account association | Native media-buy buyer |

An empty `advertiserIds` list does not constrain a search. When the list is
non-empty and callers also supply `advertiserId`, every source uses their
intersection. The singular ID must occur in the list; if it does not, the
search returns no Sessions.

When a mixed search includes `buyerAccountIds`, Sessions skips the unsupported
Murph source and returns matching RFP and media-buy results. `actorKind: "agent"` selects
recorded AdCP/RFP and media-buy activity, while `actorKind: "human"` selects Murph
conversations; classifications are source-derived rather than inferred from
text. RFP turns expose deterministic readable text from the recorded brief,
budget, flight, audience, and proposal commercial facts; no model call is made
when a timeline is read.

With the explicitly selected Storefront RFP source, one Session `purpose` value
and `updatedAfter` are supported. Live status, attention, operator domain, and
transport remain rejected rather than becoming a partial RFP search. A rejected
filter never produces a partial unfiltered page.

`storefront_media_buy_v1` exposes retained, seller-operator-only Sessions for
media buys that are not linked to an admitted RFP Session. It records the
buyer-facing media-buy identifier on every event so the media-buy document can
be opened from the timeline. The timeline records each forwarded media-buy
creation, update, and creative sync request and response. Search supports the recorded agent classification
and native buyer and advertiser identities; it does not infer identity from
free-form request fields. Buyer reads remain unavailable.

Media-buy Sessions cover only media buys created or replayed after this release;
earlier media buys will not appear until the historical import lands. A seller's
unqualified Session search includes retained standalone media-buy Sessions. A
linked media buy remains in its RFP Session and is never returned as a second,
standalone result. A caller can still select `storefront_media_buy_v1` directly.

Mixed-source search visits admitted Murph conversations first, then admitted
RFPs, then retained media buys. This is source order, not a
global chronology or relevance ranking. Each source uses its immutable retained
publication order, and a page contains rows from one source only. A short Murph
page can therefore retain a continuation for RFPs or the media-buy source. The
signed cursor binds the account, actor, access policy, canonical filters and
limit, the selected source versions and order, and one retained-publication
scope captured at the first page. That scope contains a committed publication-
sequence ceiling and a generation. Each source reads membership against that
fixed scope rather than mutable native activity. A Session first retained after
the ceiling is excluded until a fresh search. An old cursor or a cursor whose
generation no longer matches must restart rather than be reinterpreted.

Each continuation binds the account, actor, room-sharing policy, complete
canonical filter set, selected source version and one retained-publication
scope. Each continuation also binds the complete matching retained set for the
source it is reading at that ceiling. For Murph, that includes supported
lifecycle and current room access. For an RFP continuation, it includes the
current seller owner, private-practice marker, lifecycle, and selected latest
buyer/advertiser dimensions. If one of those changes the matching set between
pages, the next page
requires a fresh search rather than silently skipping or adding a result.
Native activity that does not change the set, and publications after the
ceiling, do not invalidate the traversal.

When an admitted RFP turn becomes terminal, the native turn and its retained
terminal action are written together. If that retained write cannot complete,
the native terminal transition is rolled back. An unadmitted RFP remains
native-only until the bounded importer admits its complete history.

Search cursors are separate from the event cursors returned by `get`. Current
account and native source access are checked again on every page.

For example, this discovers one seller-owned retained RFP source without
claiming a buyer-account relationship:

```json theme={null}
{
  "kind": "session",
  "filter": {
    "sessionSource": "storefront_rfp_v1",
    "buyer": ["Example Buyer"],
    "advertiser": ["Example Advertiser"]
  }
}
```

The returned `sessionId` can be read with `get(kind: "session", id: ...)`.
Its request, response, and terminal events remain `structured_only`: request
and response text can be `null`, while each event carries its exact RFP and
turn references and version digest. Use the existing
`get(kind: "rfp_turn", id: "...")` owner for the authoritative shared Brief
or Proposal document and representation references. A multi-turn RFP therefore
returns recorded request/response/action events in retained sequence, not a
fabricated chat transcript.

## Open a linked Proposal Pass

When a retained Session shows a linked RFP request or response with both an RFP
ID and a turn ID, a seller can select **Open exact Proposal Pass** to open that
exact turn. Proposal Pass checks the seller's current access again before it
opens; seeing a Session link does not grant new access.

Representation links and incomplete references stay read-only. They do not
offer an open action because they do not identify an exact RFP turn. If the
existing Proposal Pass bridge cannot open an eligible link, the retained Session
stays available and shows a retry action. Retrying does not alter the RFP,
representation, or retained record.

## Sessions Page hand-offs

The Sessions Page is available to sellers enrolled in its rollout. From an
eligible Session event, it can open the original brief for a recorded RFP
request that carries a brief-artifact evidence reference. It can also open a
media-buy timeline when an event carries a `media_buy` downstream reference.

These are Page-only hand-offs, not agent tools, so they do not appear in
`tools/list`. Opening a brief requires the seller's existing Demand access,
just as recorded RFP history does. The media-buy timeline hand-off has the
same Demand requirement and also rechecks the seller's existing timeline
access for that exact buy. An absent or inaccessible reference returns
`NOT_FOUND` or `ACCESS_DENIED`; the Page does not launch a timeline for it.

No suitable inspected source currently links a buyer account to these raw
dimensions or captures a buyer-to-seller human transcript for this Session
contract. That linkage, human-capture adapter, media-buy history, and
sync-creatives history remain separate requirements; private Murph coaching is
not substituted for any of them.

`get` returns an immutable page of retained events and three cursor values:

* `snapshotThrough` fixes the committed prefix being read.
* `pageNextAfter` is present only when that prefix has another page.
* `resumeAfter` remains at that prefix's boundary, including an empty or final
  page, so a later poll can receive publications that arrived while pages were
  being read.

Continue an existing snapshot with `pageNextAfter` as `after` and the original
`snapshotThrough` as `through`. Once it is consumed, poll with `resumeAfter`
as `after` and no `through`. Cursors identify positions, not permission:
current account and native source access are rechecked on every read.

## Complete result boundary

On a successful retained Session `get`, both the text-only MCP channel and
`structuredContent` carry the complete declared Session page: event bodies,
evidence and locators, participant/link-decision context, approvals,
capabilities, and cursor values. Text is fenced as untrusted data; it is not a
separate excerpt or a lossy summary.

The complete returned MCP result, including both channels, is limited to
128,000 characters. If that complete result does not fit, `get` returns a
`VALIDATION_ERROR` and attaches no Session page. For a multi-event page, retry
the same Session ID and `after`/`through` boundary with `limit: 1` or a smaller
valid limit. If a one-event minimum page still exceeds the boundary, repeating
the same request cannot recover it: this read has no text-offset continuation.

Historical import is an explicit server-side, bounded operation. Murph import
handles at most 100 native messages, and Storefront RFP import handles at most
100 source turns, in one transaction. A fresh Murph room may admit only with
its first native message. An older room can continue native conversation work
while unadmitted; if its history exceeds the bound or its retained root is
partial, Session search, get, and notes remain unavailable and the stored
timeline is not rewritten. Each import fails rather than returning partial
reconstructed history. Later native publications and private notes use the
same retained sequence. A message, recorded RFP event, or note never changes
an already returned event.

## Add a private coaching note

Use `save_session` with an exact Session ID, private note, and
`clientRequestId`:

```json theme={null}
{
  "action": "record_note",
  "sessionId": "conversation-id-from-search",
  "throughEventId": "optional-event-id-from-get",
  "note": "Explain the price before asking for a commitment.",
  "clientRequestId": "note-001"
}
```

The note is an authored private event; it does not send a buyer message, amend
the Murph conversation or recorded RFP, alter a response in progress, update
Playbook, approve an action, or create an evaluation. The same scoped request
ID and same content reuses the retained note without appending a duplicate.
Its delivery receipt remains `null`. Reusing that key with different content
fails. If current room or seller access has been revoked, both a new write and
its retry fail.

## Delivery status

This is retained-history and private-note infrastructure only. The code
selects the Sessions Page only for accounts that the existing server checks
authorise; it does not enrol accounts, claim deployment, enable message
delivery, response approval, or completed evaluation work. The native source
remains responsible for conversation execution and room permissions.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.