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

# Decision trace

> The machine-readable Transactability reading an agent logs, gates on, and audits — quote.transactabilityTrace, its attribution buckets, and the never-blended network vs first-party readings.

The [Transactability](/docs/transactability) score is a reading a human can eyeball on the terminal. The **decision trace** is the same reading in machine-readable form — the object an agent parses to gate its own spend at runtime.

<Note>
  This is a **Developer Preview** surface. The shape and its meaning are stable; exact field paths under `quote.transactabilityTrace` may change during preview.
</Note>

The [SELAT CLI](/docs/selat-cli) renders the score as a one-line summary for a human — `selat run "<intent>" --dry-run --live-probe` prints the network reading in the pick preview, before any spend. For an agent that needs to **log, annotate, rank, or audit** the full reading, add `--json` to the same free dry run (selat-cli 0.16.12+) and the machine-readable **decision trace** rides along as `transactabilityTrace`, next to the plan and the human score line:

```bash theme={null}
selat run "<intent>" --dry-run --live-probe --json
```

For a specific endpoint's trace rather than a generic pick, pin it — `--endpoint <url>` (plus `--method` / `--param` as needed). On the wire the engine emits the same object as `quote.transactabilityTrace`; the JSON dry run surfaces it top-level as `transactabilityTrace`.

A live probe may invoke the target endpoint's method and body — only probe an endpoint you have already confirmed is safe to call. Rather than one blended rate, the trace separates failures by **who is actually responsible** — because a single "success rate" conflates three different owners:

| Bucket         | Owner        | What it counts                                                                     | Upstream paid?                                                |
| -------------- | ------------ | ---------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| `counterparty` | **endpoint** | captured payment, then a `5xx` — the upstream took the money and failed to deliver | yes → the only failure that reflects **endpoint** reliability |
| `yourRequests` | **agent**    | a `4xx` — your request was rejected                                                | no → fix the request (the charge is refundable — see below)   |
| `settlement`   | **rail**     | a `402`/settlement rejection — the payment never settled                           | no → a rail/router issue                                      |

The network index on the 402 measures **only** the counterparty view: `paidNum` counts *captured* payments (see [What each reading means](/docs/transactability#what-each-reading-means)), so a `4xx`/`402` that never settled cannot enter it. The other two buckets come from **your own SELAT payment ledger** — the record of your paid calls (see [`selat history`](/docs/selat-cli)) — which the CLI folds in at probe time (best-effort, 90-day evidence window). Pick-time quarantine runs on a separate, shorter clock — see [First-Party Transactability](/docs/transactability/first-party#honest-limits).

The counterparty bucket carries **two independent readings that are never blended**. Both are SELAT's own settlement-delivery observations — never a merchant self-report — but they cover different populations: `network` is aggregated across all SELAT users; it carries every window the payload has — `24h`, `7d`, and `all` — under `windows`, with the headline `window`/`deliveryRate` recency-first (`24h`) and the confidence weight taken from the widest window's sample (SELAT-authored; the router emits it from SELAT's settlement record and strips any merchant-supplied index), while `firstParty` is **your own settled payments**. They are different populations over different windows, so their samples are never summed and their rates never averaged. `primarySource` names which reading drives `signal`/`primaryCaution` — **first-party whenever it has evidence, because it is your own usage** (your inputs, rails, recency), not because the network reading is untrusted. `divergence` compares the two and marks whether they **agree** (it is present even when they do, with `agree: true`); the network-wide reading stays worth weighing as a broader prior, rather than hiding the comparison in one number. See [First-Party Transactability](/docs/transactability/first-party) for why your own ledger is the authoritative reading.

```json theme={null}
{
  "metric": "transactability",
  "endpointUrl": "https://api.exa.ai/search",
  "dataStatus": "measured",
  "attribution": {
    "counterparty": {
      "owner": "endpoint",
      "primarySource": "first-party",
      "signal": "below-100pct",
      "network":    { "scope": "network-wide", "window": "24h", "deliveryRate": 1, "capturedPayments": 2, "windows": [ { "window": "24h", "deliveryRate": 1, "capturedPayments": 2 }, { "window": "7d", "deliveryRate": 1, "capturedPayments": 9 }, { "window": "all", "deliveryRate": 1, "capturedPayments": 90 } ], "lastStatus": "200", "signal": "ok" },
      "firstParty": { "scope": "first-party", "settled": 24, "deliver5xxFails": 1, "deliveryRate": 0.96, "sampleSize": 25, "signal": "below-100pct" },
      "divergence": { "networkRate": 1, "firstPartyRate": 0.96, "agree": true, "note": "network-wide and your first-party rate agree" }
    },
    "yourRequests": { "owner": "agent", "firstParty": { "count": 4 } },
    "settlement":   { "owner": "rail",  "firstParty": { "count": 2 } }
  },
  "primaryCaution": "delivery-below-100pct",
  "advisories": [
    { "owner": "agent", "bucket": "yourRequests", "count": 4, "refundable": true, "remedy": "fix-request-then-refund", "note": "4 of your recent request(s) were rejected 4xx — check your request body/schema. You paid SELAT but the upstream did not capture, so the charge is refundable (refund requests coming soon)." },
    { "owner": "rail",  "bucket": "settlement",   "count": 2, "note": "2 payment(s) did not settle (402/rejected) — a rail/router issue. The upstream was not paid." }
  ]
}
```

`primaryCaution` is resolved from the **counterparty bucket only** — the agent's `4xx` and the rail's `402` never move it. Precedence: `unmeasured` → `last-attempt-5xx` → `delivery-below-100pct` → `low-confidence-sample` → `null`. The `yourRequests` and `settlement` counts surface as `advisories` instead, so the agent can act on them (*"fix my request"*, *"retry the settlement"*) without misreading them as the endpoint being unreliable.

<Warning>
  **The endpoint is charged only for what it did.** In the example above, `api.exa.ai/search` shows a `0.96` counterparty rate (one real `5xx` over 25 captured payments) — the four `4xx` and two `402` are pulled out as advisories, not held against the endpoint. A blended rate would have read `~0.83` — 24 delivered over 29 **charged** attempts, folding your four rejected requests into the endpoint's record (the two `402`s never charged anyone, so even a naive blend has no basis to count them) — and wrongly told the agent to avoid a reliable endpoint.
</Warning>

On a `4xx`, you paid SELAT but the upstream never captured — so the charge is **recoverable**. The `yourRequests` bucket carries `refundable: true` (with `refundStatus: "coming-soon"`); agents will be able to file a refund request for these charges. This is the practical reason a `4xx` must never count against the endpoint: it is not the endpoint's failure, and it is not even a completed payment to the endpoint.
