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

# Quickstart

> Create a Boxpressd Payments session on your backend and open hosted checkout.

This guide creates a marketplace checkout session and redirects the browser to its one-time URL.

## Prerequisites

* An approved Payments service client ID and secret
* A billing account your client is allowed to access
* A ready merchant account for the current environment
* Registered return and cancel URLs

<Steps>
  <Step title="Store the service credential">
    Keep both values in your backend environment:

    ```env theme={null}
    BOXPRESSD_PAYMENTS_CLIENT_ID=your-service-client
    BOXPRESSD_PAYMENTS_CLIENT_SECRET=replace_me
    BOXPRESSD_PAYMENTS_ORIGIN=https://payments-dev.boxpressd.com
    ```

    Never expose the secret through browser or native application code.
  </Step>

  <Step title="Create a hosted session">
    Call Payments from your backend. Supply a stable idempotency key for the checkout attempt.

    ```javascript theme={null}
    const response = await fetch(
      `${process.env.BOXPRESSD_PAYMENTS_ORIGIN}/api/v1/payment-sessions`,
      {
        method: "POST",
        headers: {
          Authorization: `Bearer ${process.env.BOXPRESSD_PAYMENTS_CLIENT_SECRET}`,
          "X-Boxpressd-Client-Id": process.env.BOXPRESSD_PAYMENTS_CLIENT_ID,
          "Idempotency-Key": order.checkoutAttemptId,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          action: "purchase",
          actor: { type: "boxpressd_user", id: String(user.id) },
          billingAccountId: order.billingAccountId,
          marketplace: {
            merchantAccountId: order.merchantAccountId,
            orderReference: order.id,
            money: { amountMinor: order.totalMinor, currency: "USD" },
          },
          returnUrl: `https://shop.example/orders/${order.id}/complete`,
          cancelUrl: `https://shop.example/orders/${order.id}`,
          presentation: "page",
        }),
      },
    );

    const { data } = await response.json();
    ```
  </Step>

  <Step title="Use the first returned URL">
    Redirect the browser to `data.url` or open it with the [embed SDK](/payments/embed-sdk).

    Persist the session ID before redirecting. An idempotency replay returns the existing session with `url: null` because Payments cannot reproduce the raw one-time token.
  </Step>

  <Step title="Verify authoritative status">
    After the user returns, query the session from your backend:

    ```javascript theme={null}
    const response = await fetch(
      `${process.env.BOXPRESSD_PAYMENTS_ORIGIN}/api/v1/payment-sessions/${sessionId}/status`,
      {
        headers: {
          Authorization: `Bearer ${process.env.BOXPRESSD_PAYMENTS_CLIENT_SECRET}`,
          "X-Boxpressd-Client-Id": process.env.BOXPRESSD_PAYMENTS_CLIENT_ID,
        },
      },
    );

    const { data: status } = await response.json();
    ```
  </Step>
</Steps>

Browser status messages improve the interface but do not prove fulfillment. Use the status endpoint or the signed fulfillment callback as your source of truth.
