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

# Transaction Status

> Three ways to track on-ramp and off-ramp transactions

Proof exposes three independent channels for delivering transaction status. All three carry the same normalized status enum.

## Status enum

| Status       | Terminal | Description                               |
| ------------ | -------- | ----------------------------------------- |
| `pending`    | No       | Session created; payment not yet captured |
| `processing` | No       | Payment captured; crypto leg in flight    |
| `completed`  | Yes      | Transaction finalized successfully        |
| `failed`     | Yes      | Payment, deposit, or payout failed        |
| `cancelled`  | Yes      | User cancelled inside the widget          |

Stop polling and close WebSocket connections when you receive a terminal status.

## Channel 1 — Polling

```bash theme={null}
curl https://DOMAIN/widget/transactions/{merchant_transaction_id} \
  -H "Authorization: Bearer <client_token>"
```

**Response shape:** see [`GET /widget/transactions/{id}`](/on-off-ramp/api-transaction).

**Recommended cadence:** every 5–10 seconds while `status` is `pending` or `processing`. Stop on terminal states.

## Channel 2 — WebSocket

```javascript theme={null}
// Node.js — server-side only; client_token must not go to the browser
const WebSocket = require("ws");
const ws = new WebSocket(
  "wss://DOMAIN/ws?tx_id=550e8400-e29b-41d4-a716-446655440000",
  { headers: { Authorization: "Bearer <client_token>" } }
);

ws.on("message", (data) => {
  const msg = JSON.parse(data);
  if (msg.event === "tx.update") {
    console.log(msg.status);
  }
});
```

One connection subscribes to one `merchant_transaction_id` via the `tx_id` query parameter. See [WebSocket](/on-off-ramp/api-websocket) for reconnection patterns and payload reference.

## Channel 3 — Outbound webhook

If you provided a `webhook_url` at onboarding, Proof POSTs the normalized transaction event to that URL on every status change. Delivery is best-effort — always pair the webhook with polling or WebSocket for authoritative state. See [Partner Webhook](/on-off-ramp/api-partner-webhook).

## Widget callbacks

The widget fires browser-side callbacks (`onStatusChange`, `onLoad`, `onReady`, `onPaymentFinished`, `onSellTransferEnabled`) as the user progresses. These are informational only — never use them as the authoritative source for business logic, as they can be lost if the user closes the tab.

See [Widget Callbacks](/on-off-ramp/widget-callbacks) for the full reference and payload shapes.

## Retry behavior

If a payment fails inside the widget, the user can retry without requesting a new session, as long as the `init_token` is still valid. The `merchant_transaction_id` stays the same across retries.
