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

# Quickstart

> Get the widget running in your app in three steps

## Prerequisites

* You have completed [onboarding](/on-off-ramp/onboarding) and received a `client_token` and `widget_id`.
* You have a backend server whose IP is registered with Proof.
* You have a `partner_user_id` (your internal, stable user identifier) and the user's email.
* You know which `network` and `currency` the user wants to transact in (e.g. `TRC20` + `USDT`).

***

## Step 1 — Request a session from your backend

Your frontend calls your own backend. Your backend calls `POST /widget/session` with the `client_token` (kept server-side) and the user's real IP in `client_ip`.

```bash theme={null}
# Your backend → Proof API
curl -X POST https://DOMAIN/widget/session \
  -H "Authorization: Bearer <client_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "partner_user_id": "user-123",
    "user_email": "user@example.com",
    "network": "TRC20",
    "currency": "USDT",
    "amount": "100",
    "type": "buy",
    "client_ip": "203.0.113.42"
  }'
```

**Response:**

```json theme={null}
{
  "merchant_transaction_id": "550e8400-e29b-41d4-a716-446655440000",
  "widget_id": "fb359d09-fff6-4b1f-906c-b9062b135065",
  "address": "TRx1a2b3c4d5e6f...",
  "signature": "v2:a1b2c3d4e5f6...",
  "init_token": "eyJhbGciOi...",
  "init_token_type": "sdk_partner_authorization",
  "currency": "USDT",
  "network": "TRC20",
  "amount": "100",
  "fix_currency": true,
  "fix_network": ["TRC20"],
  "fix_amount": true,
  "fiat_currency": "EUR",
  "type": "buy"
}
```

<Note>
  Save `merchant_transaction_id` — you use it everywhere downstream (polling, WebSocket, webhook).
</Note>

Your backend returns the session parameters to the frontend (minus the `client_token`, which never leaves your server).

For the full request and response field reference, see [`POST /widget/session`](/on-off-ramp/api-session).

***

## Step 2 — Embed the widget

Your frontend receives the session parameters from your backend and passes them directly to `proofWidget.run()`. No `client_token` is needed on the frontend.

```html theme={null}
<div id="proof-widget"></div>

<script src="https://widget.proof.community/widget.2.0.js"></script>
<script>
  // `session` comes from your own backend endpoint, not from Proof directly
  proofWidget.run({
    widgetId:              session.widget_id,
    host:                  document.getElementById("proof-widget"),
    address:               session.address,
    signature:             session.signature,
    initToken:             session.init_token,
    initTokenType:         session.init_token_type,
    merchantTransactionId: session.merchant_transaction_id,
    currency:              session.currency,
    network:               session.network,
    amount:                session.amount,
    fixCurrency:           session.fix_currency,
    fixNetwork:            session.fix_network,
    fixAmount:             session.fix_amount,
    fiatCurrency:          session.fiat_currency,
    type:                  session.type,
    onStatusChange: function(data) {
      console.log("Widget status:", data.status);
    }
  });
</script>
```

The widget renders inside `#proof-widget`. The user completes identity verification (first time) and payment inside the widget.

***

## Step 3 — Track transaction status

All status calls use your `client_token` and must go through your backend. Pick one of three delivery channels:

### Polling (from your backend)

```bash theme={null}
# Your backend → Proof API
curl https://DOMAIN/widget/transactions/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer <client_token>"
```

Poll every 5–10 seconds while `status` is `pending` or `processing`. Stop on `completed`, `failed`, or `cancelled`. See [`GET /widget/transactions/{id}`](/on-off-ramp/api-transaction).

### WebSocket (from your backend)

```javascript theme={null}
// Node.js — server-side subscription
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`. See [WebSocket](/on-off-ramp/api-websocket) for details and reconnection patterns.

### Outbound webhook

If you provided a `webhook_url` at onboarding, Proof POSTs the normalized transaction event to that URL on every status change. See [Partner Webhook](/on-off-ramp/api-partner-webhook).

***

## Full example

<CodeGroup>
  ```javascript Backend (Node.js) theme={null}
  // POST /your-api/widget-session
  // Called by your frontend; client_token never leaves this file
  async function createWidgetSession(req, res) {
    const userIp =
      req.headers["x-forwarded-for"]?.split(",")[0].trim() ||
      req.socket.remoteAddress;

    const session = await fetch("https://DOMAIN/widget/session", {
      method: "POST",
      headers: {
        "Authorization": "Bearer <client_token>",
        "Content-Type": "application/json"
      },
      body: JSON.stringify({
        partner_user_id: req.body.userId,
        user_email:      req.body.userEmail,
        network:         "TRC20",
        currency:        "USDT",
        amount:          "100",
        type:            "buy",
        client_ip:       userIp
      })
    }).then(r => r.json());

    // Return session params — client_token is not included
    res.json(session);
  }
  ```

  ```javascript Frontend theme={null}
  // 1. Ask your backend for session params
  const session = await fetch("/your-api/widget-session", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ userId: "user-123", userEmail: "user@example.com" })
  }).then(r => r.json());

  // 2. Launch widget
  proofWidget.run({
    widgetId:              session.widget_id,
    host:                  document.getElementById("proof-widget"),
    address:               session.address,
    signature:             session.signature,
    initToken:             session.init_token,
    initTokenType:         session.init_token_type,
    merchantTransactionId: session.merchant_transaction_id,
    currency:              session.currency,
    network:               session.network,
    amount:                session.amount,
    fixCurrency:           session.fix_currency,
    fixNetwork:            session.fix_network,
    fixAmount:             session.fix_amount,
    fiatCurrency:          session.fiat_currency,
    type:                  session.type,
    onStatusChange:        (data) => console.log(data.status)
  });
  ```
</CodeGroup>

***

## Next steps

<CardGroup cols={2}>
  <Card title="Transaction Status" icon="signal" href="/on-off-ramp/transaction-status">
    Polling, WebSocket, and webhook delivery in detail.
  </Card>

  <Card title="Off-Ramp (Sell)" icon="arrow-right-arrow-left" href="/on-off-ramp/off-ramp">
    Enable sell flow for your users.
  </Card>

  <Card title="Mobile" icon="mobile" href="/on-off-ramp/mobile">
    iOS and Android setup for camera and payments.
  </Card>

  <Card title="Error Handling" icon="circle-exclamation" href="/on-off-ramp/errors">
    Real error codes and how to handle them.
  </Card>
</CardGroup>
