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

# Reading your agent's decisions

> How a seller reads each decision its storefront agent made: the proposals it sent, the briefs it declined, the terms it refined and accepted, and the checks that held an answer back.

Your storefront agent answers buyers for you. Each time it acts, Apostra keeps a
**decision**: what it did, for which buyer, whether it went out, and, if a check
held it back, which check. You read decisions with `get` and `search` and
`kind: "decision"`.

Only your account can read its decisions. A buyer never sees them.

> [!NOTE]
>
> Decisions are part of the seller beta feature profile. An account that is not on
> the beta is refused with `FEATURE_NOT_ENABLED`. Decisions are recorded from the
> release that added this read; answers your agent gave before it are not on the
> record.

## What a decision records

| Field | What it is |
| - | - |
| `id` | The decision's own ID, `dec_` followed by 24 letters and digits. |
| `action` | What the agent did: `propose` (answered a brief with a proposal), `decline_brief` (turned a brief down, or found nothing to propose), `refine_terms` (answered a buyer's refinement), `accept_terms` (committed a proposal's terms when the buyer finalized it, once per proposal), `change_price` or `change_product` (changed your catalog). |
| `status` | `sent` (the answer reached the buyer), `applied` (a catalog change took effect), `waiting_approval` (a change waits for your approval) or `blocked` (a check held the answer back). |
| `blockedBy` | The check that held it back, when `status` is `blocked`; otherwise `null`. |
| `brain` | Which agent decided: `{ kind, agentId }`, where `kind` is `chef` (Apostra's storefront agent), `prime` or `own` (your own model). |
| `shadow` | `true` for a shadow agent's decision, which is checked and recorded but never shown to a buyer. |
| `at` | When it was recorded, as an ISO 8601 date-time. |
| `target` | The object the decision changed, such as `{ type: "wholesale_product", id }`. `null` when the action has no object of its own, as for every answer to a buyer. |
| `buyer` | The buyer it answered: `{ accountId, displayName }`. `null` for a catalog change. |
| `sourceMaterialId` | The Library file a catalog change was read from, or `null`. |
| `approvalId` | The approval a catalog change waits for, or `null`. |
| `sessionId` | The [Session](https://docs.apostra.com/v3/sessions.md) holding the exchange with the buyer. It is `null` until that Session is published, which happens shortly after the answer. |

Today every decision is Apostra's storefront agent answering a buyer, so `brain`
is `{ kind: "chef", agentId: null }`, `shadow` is `false` and `target` is `null`.
To see the full exchange behind an answer, read its Session with
`get({ kind: "session", id: sessionId })`.

A brief whose every proposal a platform check held back is a `propose` decision
with `status: "blocked"`. Today the checks are `buyer_price_ceiling_unsatisfied`
(nothing could meet the buyer's maximum CPM) and `age_targeting_unavailable`
(nothing could meet the age targeting the buyer asked for).

One answer to a buyer is one decision, even when it carries several proposals.

## Read one decision

```json theme={null}
{
  "kind": "decision",
  "id": "dec_3f9c2a7b1e4d4c0a9b8e7f6a",
  "include": ["reasoning"]
}
```

Two includes add detail:

* `include: ["reasoning"]` adds `reasoning`: `{ text }` with the rationale
  Apostra's storefront agent gave for its answer, or `null` when it gave none.
  For `accept_terms` it is the rationale of the answer that proposed those
  terms. It is always `null` for your own model, which owes Apostra no reasons.
* `include: ["reads"]` adds `reads`: what the agent read to reach the decision,
  at most 10 reads, each `{ tool, returned: [{ id, version }] }`. Reads appear
  once read tracing ships; until then the list is empty.

An unknown `id` is refused with `NOT_FOUND`.

## List decisions

```json theme={null}
{
  "kind": "decision",
  "filter": { "action": "decline_brief", "buyerAccountId": 4821 },
  "limit": 20
}
```

Results come newest first, at most 50 a page. When there are more, the answer
carries `nextCursor`; pass it back as `cursor` with the same filter for the next
page. Decision search takes no `query`. Narrow it with `filter`:

| Filter | Keeps |
| - | - |
| `action` | Decisions of this action. |
| `status` | Decisions with this status. |
| `brain` | Decisions this agent made: `chef`, `prime` or `own`. |
| `shadow` | `true` for the shadow agent's decisions, `false` for the answering agent's. |
| `buyerAccountId` | Decisions for this buyer account. |
| `productId` | Decisions about this product: a product the answer proposed, or the product a catalog change targets. |
| `after`, `through` | Decisions recorded at or after, and at or before, these ISO 8601 date-times. |

Search results leave out `reads` and `reasoning`; read one decision with `get`
for those.

## Who may read decisions

* People with read access on your whole seller account, signed in or with their
  own API key.
* Your own agents with account-wide read: your account's API key, an
  organization key or a registered agent.
* Scope3 staff and Apostra's own test suites, and only with your account named
  as the customer on the call.

Every other caller is refused with `ACCESS_DENIED`: a Buyer account, a buyer's
agent, a person or credential limited to some advertisers, and the short-lived
read tokens Apostra gives an agent while it answers
one buyer, so an agent answering one buyer never sees your decisions about
another. Decisions made in the simulator and the benchmark are never listed.


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