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

# Connect a customer

> Connect a customer's MoonPay account to your app.

Connect a customer's MoonPay account so you can list payment methods, get executable quotes, and execute transactions. Before you start, review the [requirements](/platform/overview/requirements).

## Prerequisites

* A server that can create session tokens with your secret key.
* A client that can render frames (SDK or manual integration).

Connecting is a one or two-step process depending on whether the customer is new or returning:

* If the customer has never connected, render the co-branded connect frame.
* If the customer has connected before, first check whether their connection is still valid. You only need to render UI again if the connection has [expired](#connection-required).

For all cases, first [create a session](#create-a-session), then [check if a connection exists](#check-connection). If a connection is required, initialize the [connect flow](#connect-flow).

```mermaid theme={null} theme={null}
sequenceDiagram
    autonumber
    actor c as customer

    participant s as Your server

    box Your app
        participant fe as Your frontend
        participant cf as MoonPay frame<br/>(check connection)
        participant cnf as MoonPay frame<br />(connection UI)
    end

    participant api as MoonPay API

    %% -----------------

    c ->> fe: Customer visits app and signs in
    fe ->> s: Request session token
    s ->> api: POST /platform/v1/sessions
    api ->> s: { sessionToken: "c3N0XzAwMQ" }
    s ->> fe: Send session token

    activate cf
    fe ->> cf: getConnection()
    cf -->> fe: Dispatch result + credentials
    deactivate cf

    fe ->> fe: Store credentials

    alt active connection
    fe ->> fe: Continue to<br />buy flow
    else requires connect flow
        activate cnf
        fe ->> cnf: connect(clientToken)
        c ->> cnf: Sign in or<br />onboard to MoonPay
        cnf -->> fe: Dispatch result + credentials
        deactivate cnf

        fe ->> fe: Replace stored<br />credentials

        alt active connection?
        fe ->> fe: Continue to<br />buy flow
        end
    end



```

## Create a session

To initiate a session on your server, provide:

1. A unique identifier from your system for the customer (`externalCustomerId`).
2. The IP address of the customer's device. This is used across frames to ensure the integrity of the session.

Once initiated, you receive a [`sessionToken`](/platform/guides/api-and-sdk-credentials#session-token) to send to your frontend.

<CodeGroup>
  ```ts Create session token theme={null} theme={null}
  // Server-side code example
  const url = "https://api.moonpay.com/platform/v1/sessions";

  const res = await fetch(url, {
    headers: {
      "Content-Type": "application/json",
      "X-Api-Key": "sk_test_123",
    },
    method: "POST",
    body: JSON.stringify({
      externalCustomerId: "your_user_id",
      deviceIp: "...ip address from client",
    }),
  });

  console.log(await res.json());
  ```

  ```json Result theme={null} theme={null}
  {
    "sessionToken": "c3N0XzAwMQ=="
  }
  ```
</CodeGroup>

<Callout icon="book" iconType="regular">
  Check the [API reference](/api-reference/platform/endpoints/sessions/create)
  for detailed usage.
</Callout>

## Check connection

Using the `sessionToken`, check if the customer already has an active connection. No UI appears in this step, but it runs in a frame. You can check the session using the SDK or [manually](/platform/frames/check).

The check frame always returns encrypted credentials. Hold them in memory only and do not persist them, regardless of the status returned. For an `active` connection these are authenticated credentials. For `connectionRequired`, these are anonymous credentials whose `clientToken` you pass into the connect flow.

<CodeGroup>
  ```ts Check the connection theme={null} theme={null}
  import { createClient } from "@moonpay/platform-sdk-web";

  // Create the client with your session token
  const client = createClient({
    sessionToken: "c3N0XzAwMQ==", // The session token from your server
  });

  // Check if the customer has an active connection
  const connectionResult = await client.getConnection();

  if (!connectionResult.ok) {
    // Handle error
  }

  console.log(connectionResult.value);
  ```

  ```ts Result (active) theme={null} theme={null}
  {
    status: "active",
    customer: {
      id: "Y3VzX2FiYzEyMw=="
    },
    // Encrypted credentials — the SDK decrypts and stores them internally
    credentials: "ZW5jXzAwMQ==",
    capabilities: {}
  }
  ```

  ```ts Result (requires connection) theme={null} theme={null}
  {
    status: "connectionRequired",
    // Encrypted credentials — the SDK decrypts and stores them internally
    credentials: "ZW5jXzAwMQ=="
  }
  ```
</CodeGroup>

The SDK injects an invisible frame to check the connection. If you are integrating without the SDK, load the frame directly and listen for the [`postMessage` events](/platform/frames/check#events).

## Low-friction authentication

You can simplify login for returning customers by passing the optional `email` and `phoneNumber` parameters when you [create a session](/api-reference/platform/endpoints/sessions/create).

If these values match an existing MoonPay account, the [connect frame](#connect-flow) skips the full login and prompts the customer to enter an OTP code sent to their phone.

## Connect flow

If you need to create or revalidate a connection, initialize the connect flow with the `clientToken` from the anonymous credentials returned by the check frame. The SDK provides hooks to coordinate rendering the connect UI (for example, to animate a modal or sheet). You can also do this [manually](/platform/frames/connect).

When the connect flow completes, replace the anonymous credentials from the check step with the authenticated credentials from the `complete` event.

The resulting connection has one of the following [statuses](#connection-statuses): `active`, `pending`, `unavailable`, or `failed`.

<Callout icon="book" iconType="regular">
  In mobile apps, present the connect flow as a full sheet. See [presentation
  and appearance](/platform/guides/presentation-and-appearance) for UI guidance.
</Callout>

<CodeGroup>
  ```ts Initialize connect with SDK theme={null}
  import { createClient, type ConnectEvent } from "@moonpay/platform-sdk-web";

  // Create the client
  const client = createClient({
    sessionToken: "c3N0XzAwMQ==", // The session token from your server
  });

  // Initialize the connect flow
  const connectResult = await client.connect({
    container: connectContainer, // DOM element to render the connect frame

    theme: { appearance: "dark" }, // Optional: force dark or light mode

    onEvent: (event: ConnectEvent) => {
      switch (event.kind) {
        case "ready":
          // The frame is ready and rendered
          break;

        case "complete":
          // The connection is complete
          console.log(event.connection);
          // { status: "active", customer: { id: "..." }, credentials: { accessToken: "...", clientToken: "...", expiresAt: "..." } }

          // You can unmount the frame using the reference in the payload
          event.payload.frame.dispose();
          break;

        case "error":
          // Handle error
          console.error(event.payload.message);
          break;
      }
    },
  });

  // If there is an error setting up the connect frame, no events are
  // dispatched via the `onEvent` callback, and you receive an error here.
  if (!connectResult.ok) {
    // Handle error
  }

  // If the frame is successfully mounted, the returned value provides a reference for disposal at any time.
  connectResult.value.dispose();
  ```

  ```ts Result (complete event) theme={null}
  {
    kind: "complete",
    connection: {
      status: "active",
      customer: {
        id: "Y3VzX2FiYzEyMw=="
      },
      credentials: {
        accessToken: "c2F0XzAwMQ==",
        clientToken: "c2N0XzAwMQ==",
        expiresAt: "2026-12-09T07:16:57Z"
      },
      capabilities: {
        ramps: {
          requirements: {}
        }
      }
    },
    payload: {
      frame: {
        dispose: [Function]
      }
    }
  }
  ```
</CodeGroup>

When you receive the `complete` event, the payload includes authenticated client
credentials and a `customer` object identifying the connected MoonPay customer.
Discard any anonymous credentials from the check step and use the new ones instead.
Use them to make scoped API calls from your frontend (for example, listing
payment methods and getting quotes). See [Pay with Apple
Pay](/platform/guides/pay-with-apple-pay) for a complete example flow.

```ts Active connection theme={null}
{
  kind: "complete",
  connection: {
    status: "active",
    customer: {
      id: "Y3VzX2FiYzEyMw=="
    },
    credentials: {
      accessToken: "c2F0XzAwMQ==",
      clientToken: "c2N0XzAwMQ==",
      expiresAt: "2026-12-09T07:16:57Z"
    }
  },
  payload: {
    frame: {
      dispose: [Function]
    }
  }
}
```

<Warning>
  [Client
  credentials](/platform/guides/api-and-sdk-credentials#client-credentials)
  should never be persisted and should only be held in memory as this could pose
  a security risk.
</Warning>

## Connection statuses

### Active

An `active` status means the connection is valid and can be used. Active connections typically remain live for 180 days without revalidation. If the connection expires, refresh it via the connect flow.

### Unavailable

An `unavailable` status means the connection cannot be used at the current time. This typically occurs when a KYC-verified customer is using a device or application from a restricted location.

### Pending

A `pending` status typically occurs for customers whose KYC decisions are delayed. Often these cases are resolved out of band and the customer can connect on a subsequent visit to your app.

### Failed

A `failed` status is a terminal state. This usually happens if the customer fails KYC or cannot be onboarded to MoonPay. It can also happen if the customer rejects the connection. In these cases, direct the customer to an alternate flow in your app.

### Connection required

The `connectionRequired` status is returned from the [check frame](#check-connection) as a signal to guide the customer through the full [connect flow](#connect-flow). This status is returned for new customers who have not connected to your app, or returning customers whose connections have expired. The response also includes anonymous `credentials` — keep them only in memory and use the `clientToken` to initialize the connect flow. If the payload includes `mismatch: true`, the session's email and phone number resolve to two different MoonPay customers — route the customer through the connect flow proactively, before rendering payment UI such as Apple Pay.
