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

# POST /widget/session

> Create a widget session and get parameters to launch the Proof widget

Creates a session for one widget transaction and returns every parameter the widget needs.

This endpoint must be called from your **backend server**, not from the browser. Your server adds the `client_token`, reads the user's real IP from the incoming request, and forwards it as `client_ip`. The response parameters are then passed to the frontend to launch the widget.

The response is shaped to be passed directly to `proofWidget.run({...})`. Do not modify the returned values; pass them through verbatim.

## Request

### Headers

<ParamField header="Authorization" type="string" required>
  Bearer token. Format: `Bearer <client_token>`.
</ParamField>

<ParamField header="Content-Type" type="string" required>
  `application/json`
</ParamField>

<ParamField header="Origin" type="string">
  The request `Origin` header must match one of your registered origins, or it is rejected with `403 forbidden`. See [Onboarding → Authentication](/on-off-ramp/onboarding#authentication).
</ParamField>

### Body

<ParamField body="partner_user_id" type="string" required>
  Your stable internal user identifier. Used for KYC mapping and transaction attribution.
</ParamField>

<ParamField body="user_email" type="string" required>
  User's email. Used to find or create the user identity.
</ParamField>

<ParamField body="network" type="string" required>
  Blockchain network for the crypto leg. Examples: `TRC20`, `ERC20`, `BEP20`.
</ParamField>

<ParamField body="currency" type="string" required>
  Crypto currency. Must be one of the currencies enabled for your integration. Proof confirms the list with you at onboarding.
</ParamField>

<ParamField body="amount" type="string" required>
  Crypto amount as a decimal string (e.g. `"100"`, `"99.50"`).
</ParamField>

<ParamField body="type" type="string">
  `"buy"` or `"sell"`. Overrides the partner default from `widget_config.type`. Off-ramp (`sell`) must be enabled for your integration.
</ParamField>

<ParamField body="redirect_url" type="string">
  Overrides the partner default `redirect_url` for this session only.
</ParamField>

<ParamField body="client_ip" type="string">
  The end user's real IP address, as seen by your backend from the incoming request (`X-Forwarded-For` or the connection's remote address). Mixed into the session signature. Recommended — omitting it degrades fraud-signal quality.
</ParamField>

<ParamField body="address" type="string">
  Destination wallet address. Send this only if Proof told you to during onboarding. Otherwise omit it — Proof resolves the address for you and any value you send is ignored.
</ParamField>

## Response

<ResponseField name="merchant_transaction_id" type="string">
  Stable identifier for this session. Save it — every status surface keys off it.
</ResponseField>

<ResponseField name="widget_id" type="string">
  Your widget identifier. Pass as `widgetId` to the widget.
</ResponseField>

<ResponseField name="address" type="string">
  Resolved destination address. Pass as `address` to the widget verbatim.
</ResponseField>

<ResponseField name="signature" type="string">
  Cryptographic signature, format `v2:<128-hex-chars>`. Computed by Proof. Pass as `signature` to the widget verbatim.
</ResponseField>

<ResponseField name="init_token" type="string">
  Single-use session token. Single-use, expires after first use or one hour. Pass as `initToken` to the widget.
</ResponseField>

<ResponseField name="init_token_type" type="string">
  Pass as `initTokenType` to the widget.
</ResponseField>

<ResponseField name="currency" type="string">
  Crypto currency. Echo of the request.
</ResponseField>

<ResponseField name="currencies" type="string[]">
  Currencies offered in the widget's currency selector. From `widget_config.currencies`.
</ResponseField>

<ResponseField name="fix_currency" type="boolean">
  Always `true`. The currency is locked at the value you requested.
</ResponseField>

<ResponseField name="network" type="string">
  Network. Echo of the request.
</ResponseField>

<ResponseField name="networks" type="string[]">
  Networks offered in the widget's network selector. From `widget_config.networks`.
</ResponseField>

<ResponseField name="fix_network" type="string[]">
  Always `[network]` — the network is locked at the value you requested.
</ResponseField>

<ResponseField name="amount" type="string">
  Crypto amount. Echo of the request.
</ResponseField>

<ResponseField name="fix_amount" type="boolean">
  Always `true`. The amount is locked at the value you requested.
</ResponseField>

<ResponseField name="fiat_currency" type="string">
  Default fiat currency, from `widget_config.fiat_currency`.
</ResponseField>

<ResponseField name="fiat_currencies" type="string[]">
  Fiat currencies offered, from `widget_config.fiat_currencies`.
</ResponseField>

<ResponseField name="fix_fiat_currency" type="boolean">
  Always `false`. The user can change the fiat currency inside the widget.
</ResponseField>

<ResponseField name="fix_fiat_amount" type="boolean">
  Always `true`.
</ResponseField>

<ResponseField name="type" type="string">
  `buy` or `sell`. Resolved from the request body or `widget_config.type`.
</ResponseField>

<ResponseField name="lang" type="string">
  Widget language, from `widget_config.lang`.
</ResponseField>

<ResponseField name="theme" type="string">
  Widget theme, from `widget_config.theme`.
</ResponseField>

<ResponseField name="redirect_url" type="string">
  Redirect URL after the transaction completes. Resolved from the request or `widget_config.redirect_url`.
</ResponseField>

## Examples

<CodeGroup>
  ```bash cURL theme={null}
  # Called from your backend server
  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"
    }'
  ```

  ```javascript Node.js (backend) theme={null}
  // userIp — read from the incoming request on your server,
  // e.g. req.headers["x-forwarded-for"]?.split(",")[0] || 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: "user-123",
      user_email: "user@example.com",
      network: "TRC20",
      currency: "USDT",
      amount: "100",
      type: "buy",
      client_ip: userIp
    })
  }).then(r => r.json());
  ```
</CodeGroup>

```json Response 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",
  "currencies": ["USDT"],
  "fix_currency": true,
  "network": "TRC20",
  "networks": ["TRC20", "ERC20"],
  "fix_network": ["TRC20"],
  "amount": "100",
  "fix_amount": true,
  "fiat_currency": "EUR",
  "fiat_currencies": ["EUR", "USD"],
  "fix_fiat_currency": false,
  "fix_fiat_amount": true,
  "type": "buy",
  "lang": "en",
  "theme": "light",
  "redirect_url": "https://app.example.com/done"
}
```

## Errors

See [Error Handling](/on-off-ramp/errors). The most common session-creation errors:

| HTTP | `error`                | `code`                 | Cause                                                                                      |
| ---- | ---------------------- | ---------------------- | ------------------------------------------------------------------------------------------ |
| 400  | `invalid_request`      | `validation_error`     | A required field is missing or has the wrong type                                          |
| 400  | `invalid_request`      | `currency_not_allowed` | `currency` is not enabled for your integration                                             |
| 400  | `wallet_not_supported` | `WALLET_NOT_SUPPORTED` | No active wallet for the requested `currency` + `network` — contact Proof to provision one |
| 401  | `unauthorised`         | `invalid_token`        | `client_token` is missing or inactive                                                      |
| 403  | `forbidden`            | `forbidden_origin`     | The request `Origin` is not on your allowlist                                              |
| 502  | `upstream_error`       | `provider_error`       | An external service returned an error; retry with the same body                            |
