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

# Confirming what happened

> Check whether an order actually landed, using the live page, the session's run report and the merchant's receipt, before you tell anyone their card was charged.

Establish whether an order went through, and tell the user exactly what is known before you report a charge or a failure. Adding these checks to an agent takes about 10 minutes.

No single signal proves an order. Each check answers a different question:

| Check | Answers | Limit |
| - | - | - |
| `get_session_outcome` | Did a success or failure marker appear after the step I submitted? | Not a record of what the session did |
| `get_run_report` (SDK: `browser.outcome()`) | What did this session do, from the proxy's own record? | Not proof of a charge |
| Merchant state | Does an order exist? | None. It settles an unclear outcome |

## Before you start

* You submitted the order **once**. See [Run a checkout end to end](/guides/first-checkout).
* You know the submit selector you clicked, a selector that appears only on success, and one that appears only on failure.
* Any 3DS or wallet popup is handled. See [Handle challenges](/guides/challenges).

## Confirm an order

<Steps>
  <Step title="Race the success and failure markers">
    ```ts theme={null}
    // MCP: get_session_outcome
    {
      successSelector: "#order-success",
      failureSelector: ".checkout-error",
      timeout: 15000,            // maximum 30000
      confirmsCheckout: true,
      submitSelector: "#place-order"
    }
    ```

    `outcome` is `success` or `failure` (named in `matched`), `timeout` (neither yet, **not** a failed checkout), or `cancelled` (a handoff took the page). Use this instead of reading the confirmation page, which redaction blacks out.
  </Step>

  <Step title="Check corroborated is true">
    Pass `confirmsCheckout: true` only for a checkout, with the `submitSelector` you clicked. The confirmation is recorded only when:

    1. The submit control has left the screen.
    2. No tab this page opened is waiting for `wait_for_popup`.
    3. The page shows no error, rate limit, sign-in step, sent-code notice or challenge.
    4. The success selector is still there, and the failure selector is not.

    Then the reply says `corroborated: true`. A `false` names the failed check: do not report an order on it.
  </Step>

  <Step title="Read the run report">
    Call `get_run_report`, or `close_session`, which returns the same report. It still answers after you navigate away.

    | Field | Meaning |
    | - | - |
    | `filledFieldKinds` | Which **kinds** of token were filled, for example `card`. Never a value |
    | `deniedCount` | How many fills were refused |
    | `confirmationDetected` | Whether a checkout confirmation was recorded |
    | `orderTotal` | The total the proxy read, when it could |
    | `coversWholeRun` | `false` when the connection dropped and the record restarted |
    | `earlierReports` | Present when `open_page` replaced the proxy session partway through and could still read the old one. One record per earlier session, with the same fields. Count every one: a `card` or a confirmation in any of them holds for the run. See [After a restart](/mcp/tool-reference#after-a-restart) |

    A `timeout` from the last step does not mean nothing was filled. If `filledFieldKinds` contains `card`, the card went in.
  </Step>

  <Step title="Settle anything unclear with the merchant">
    If a card went in and nothing was confirmed, check the order page, the account's order list or the receipt email. The emailed receipt is the source of truth.
  </Step>

  <Step title="Tell the user">
    Say three things:

    1. What the run report says was filled, by kind.
    2. Whether a confirmation was corroborated.
    3. If a card was filled with no corroborated confirmation: a card number reached this merchant, whether it was charged cannot be established from here, and where they can check.
  </Step>
</Steps>

Your agent now reports only what it can prove.

## What the account holder sees

Their [Activity](/dashboard/activity) page normally shows each session: the kinds of token filled, any refusals, and whether a confirmation was recorded. Recording is best effort, so an event that fails to save is missing rather than retried, and a gap does not prove nothing happened.

<Frame caption="A confirmed session on the Activity page.">
  <img className="block dark:hidden" src="https://mintcdn.com/self-labs/VeDWRFlNWGO5jQN6/images/guides/activity-confirmed-light.webp?fit=max&auto=format&n=VeDWRFlNWGO5jQN6&q=85&s=7261188616856cfdff2916063b38bcc4" alt="Activity page with a session expanded to show a card fill, a security code fill and a confirmed checkout, with no receipt image." width="2332" height="924" data-path="images/guides/activity-confirmed-light.webp" />

  <img className="hidden dark:block" src="https://mintcdn.com/self-labs/VeDWRFlNWGO5jQN6/images/guides/activity-confirmed-dark.webp?fit=max&auto=format&n=VeDWRFlNWGO5jQN6&q=85&s=3902329ae6271716758b954f0a3896c2" alt="Activity page with a session expanded to show a card fill, a security code fill and a confirmed checkout, with no receipt image." width="2332" height="924" data-path="images/guides/activity-confirmed-dark.webp" />
</Frame>

* **Most checkouts show no receipt image.** One is stored only when the session filled no sensitive value, because a confirmation page can repeat filled details where redaction cannot cover them. See [Redaction](/security/redaction).
* **Spend counts when the card is filled, not when it is charged.** An abandoned checkout still counts toward the agent card's spend and is not reversed. Reconcile against the card statement.

## In the SDK

`browser.outcome()` returns the run report. Its `confirmationDetected` is set **only** by your own `page.recordOutcome()` call, and it is not corroborated. `recordOutcome()` records a confirmation on the current page whenever you call it. It does not race success and failure markers or check that the submit control left the screen, as `get_session_outcome` does over MCP.

So `true` means your agent decided the order landed, nothing more. Make that call from your own check of the page (a success selector present, the failure selector absent), and settle any doubt with the merchant before you report a charge. `false` is inconclusive. Never call `recordOutcome()` for a login. Its optional order total accepts a money format only.

## If something goes wrong

| Symptom | Cause | Fix |
| - | - | - |
| `outcome: "timeout"` | Neither marker appeared in time | Not a failure. Call again, then read the run report |
| `corroborated: false` | One of the four checks failed, named in the reply | Do not report an order. Fix what it names, or check the merchant |
| `outcome: "cancelled"` | A challenge handoff took over the page | Ask again once the handoff returns |
| `popup_pending` from `recordOutcome()` | A tab this page opened is still waiting | Take it with `waitForPopup()`, then record from the receipt page |
| The run report is refused with a message | The proxy could not complete it | Read the message. A refusal is not zeros, and a report of zeros is real |
| `coversWholeRun: false` | The proxy connection dropped and the record restarted | Fills before the drop are missing from it, except what `earlierReports` carries. Read every segment, then check the merchant |
| `outcome()` unavailable | The session already closed | Use the report `close_session` returned |

**Next:** [Contribute a recipe](/registry/contribute) to share a checkout that worked, with its receipt.


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