> For a complete page index, fetch https://docs.transak.com/llms.txt

# How to use Authentication APIs

In this tutorial guide, we will be exploring the user onboarding flow for Transak's Whitelabel APIs. Before a user can place an order, they must be onboarded and authenticated. Transak supports two distinct paths for this.
Transak can either own the user's identity and authentication lifecycle, or your platform can rely on its own authentication and pass a signed token to Transak. This guide will walk you through both paths.

## Two Onboarding Paths

| Path                                              | Who authenticates the user | Best for                                                                            |
| ------------------------------------------------- | -------------------------- | ----------------------------------------------------------------------------------- |
| **Transak Onboarding APIs**                       | Transak                    | Partners who want Transak to own the full user identity and auth lifecycle          |
| **[Auth Reliance Flow](/features/auth-reliance)** | Partner                    | Partners who already have a verified user base and want to bring their own identity |

## Path 1: Transak Onboarding APIs

In this path, your app calls Transak APIs to create a user account and issue a session token. Transak owns the user's identity record. Users authenticate through Transak.

### How it Works

Trigger OTP delivery. Transak emails a time-limited one-time code to the user and returns a `stateToken`. Store this token — it is required for the verification step.

```bash
curl -X POST https://api-gateway-stg.transak.com/api/v2/user/otp/send \
  -H "x-api-key: YOUR_API_KEY" \
  -H "x-user-ip: USER_IP_ADDRESS" \
  -H "Content-Type: application/json" \
  -d '{ "userId": "USER_ID" }'
```

The user enters the code from their email. Pass the `stateToken` from the previous step along with the OTP. On success, Transak returns an `accessToken` that authenticates all subsequent API calls for this user session.

```bash
curl -X POST https://api-gateway-stg.transak.com/api/v2/user/otp/verify \
  -H "x-api-key: YOUR_API_KEY" \
  -H "x-user-ip: USER_IP_ADDRESS" \
  -H "Content-Type: application/json" \
  -d '{ "stateToken": "STATE_TOKEN", "otp": "123456" }'
```

Store the `accessToken` in memory (not localStorage). Attach it to every user-scoped API call as `Authorization: <accessToken>`.

After the user is authenticated, you can call the following APIs to retrieve user information, check transaction limits, or log out the user.

**Get user details**

Retrieve the authenticated user's profile, KYC status, and account information.

```bash
curl -X GET https://api-gateway-stg.transak.com/api/v2/user \
  -H "x-api-key: YOUR_API_KEY" \
  -H "x-user-ip: USER_IP_ADDRESS" \
  -H "Authorization: ACCESS_TOKEN"
```

**Get user transaction limits**

Check how much the user can buy or sell based on their KYC level and jurisdiction.

```bash
curl -X GET https://api-gateway-stg.transak.com/api/v2/user/limits \
  -H "x-api-key: YOUR_API_KEY" \
  -H "x-user-ip: USER_IP_ADDRESS" \
  -H "Authorization: ACCESS_TOKEN"
```

**Log out the user**

Invalidate the current session token. Call this when the user explicitly signs out of your app.

```bash
curl -X POST https://api-gateway-stg.transak.com/api/v2/user/logout \
  -H "x-api-key: YOUR_API_KEY" \
  -H "x-user-ip: USER_IP_ADDRESS" \
  -H "Authorization: ACCESS_TOKEN"
```

### User Onboarding APIs

Triggers email delivery of a one-time password and returns a `stateToken`.

Validates the OTP using the `stateToken` and returns an `accessToken` for the session.

Retrieves the authenticated user's profile and KYC status.

Returns the user's transaction limits based on their KYC level and jurisdiction.

Invalidates the current user session token.

### Sample Code

```tsx title="useTransakAuth.ts"
import { useState } from "react";

const BASE = "https://api-gateway-stg.transak.com/api/v2/user";

interface AuthState {
  step: "email" | "otp" | "done";
  userId: string;
  stateToken: string;
  accessToken: string;
  error: string | null;
  loading: boolean;
}

export function useTransakAuth(apiKey: string, userIp: string) {
  const [state, setState] = useState<AuthState>({
    step: "email",
    userId: "",
    stateToken: "",
    accessToken: "",
    error: null,
    loading: false,
  });

  const headers = {
    "x-api-key": apiKey,
    "x-user-ip": userIp,
    "Content-Type": "application/json",
  };

  async function createUserAndSendOtp(email: string) {
    setState(s => ({ ...s, loading: true, error: null }));
    try {
      // Step 1 — create user
      const createRes = await fetch(BASE, {
        method: "POST",
        headers,
        body: JSON.stringify({ email }),
      });
      const createData = await createRes.json();
      if (!createRes.ok) throw new Error(createData.message ?? `HTTP ${createRes.status}`);
      const userId: string = createData.data.userId;

      // Step 2 — send OTP, receive stateToken
      const otpRes = await fetch(`${BASE}/otp/send`, {
        method: "POST",
        headers,
        body: JSON.stringify({ userId }),
      });
      const otpData = await otpRes.json();
      if (!otpRes.ok) throw new Error(otpData.message ?? `HTTP ${otpRes.status}`);
      const stateToken: string = otpData.data.stateToken;

      setState(s => ({ ...s, userId, stateToken, step: "otp", loading: false }));
    } catch (err: unknown) {
      setState(s => ({
        ...s,
        loading: false,
        error: err instanceof Error ? err.message : "Failed to create user",
      }));
    }
  }

  async function verifyOtp(otp: string) {
    setState(s => ({ ...s, loading: true, error: null }));
    try {
      const res = await fetch(`${BASE}/otp/verify`, {
        method: "POST",
        headers,
        body: JSON.stringify({ stateToken: state.stateToken, otp }),
      });
      const data = await res.json();
      if (!res.ok) throw new Error(data.message ?? `HTTP ${res.status}`);

      const accessToken: string = data.data.accessToken;
      setState(s => ({ ...s, accessToken, step: "done", loading: false }));
      return accessToken;
    } catch (err: unknown) {
      setState(s => ({
        ...s,
        loading: false,
        error: err instanceof Error ? err.message : "OTP verification failed",
      }));
      return null;
    }
  }

  return { state, createUserAndSendOtp, verifyOtp };
}
```

```tsx title="OnboardingScreen.tsx"
import { useState } from "react";
import { useTransakAuth } from "../hooks/useTransakAuth";

const API_KEY = "YOUR_PARTNER_API_KEY";
const USER_IP = "USER_IP_ADDRESS";

export function OnboardingScreen({ onAuthenticated }: { onAuthenticated: (token: string) => void }) {
  const { state, createUserAndSendOtp, verifyOtp } = useTransakAuth(API_KEY, USER_IP);
  const [email, setEmail] = useState("");
  const [otp, setOtp] = useState("");

  async function handleEmailSubmit(e: React.FormEvent) {
    e.preventDefault();
    await createUserAndSendOtp(email);
  }

  async function handleOtpSubmit(e: React.FormEvent) {
    e.preventDefault();
    const token = await verifyOtp(otp);
    if (token) onAuthenticated(token);
  }

  if (state.step === "email") {
    return (
      <form onSubmit={handleEmailSubmit}>
        <h2>Create your account</h2>
        <input
          type="email"
          placeholder="your@email.com"
          value={email}
          onChange={e => setEmail(e.target.value)}
          required
        />
        {state.error && <p style={{ color: "red" }}>{state.error}</p>}
        <button type="submit" disabled={state.loading}>
          {state.loading ? "Sending…" : "Continue"}
        </button>
      </form>
    );
  }

  return (
    <form onSubmit={handleOtpSubmit}>
      <h2>Enter verification code</h2>
      <p>We sent a code to {email}</p>
      <input
        type="text"
        inputMode="numeric"
        maxLength={6}
        placeholder="000000"
        value={otp}
        onChange={e => setOtp(e.target.value)}
        required
      />
      {state.error && <p style={{ color: "red" }}>{state.error}</p>}
      <button type="submit" disabled={state.loading}>
        {state.loading ? "Verifying…" : "Verify"}
      </button>
    </form>
  );
}
```

## Path 2: Auth Reliance Flow

In this path, your platform authenticates the user. You call Transak's Onboard User Auth API to register that user with Transak. Because the partner owns the authentication, the user never receives an OTP from Transak — your partner access token is the credential for all subsequent API calls.

| Use this path when                                                                |
| --------------------------------------------------------------------------------- |
| You already have a verified and authenticated user base.                          |
| You want a seamless, no-extra-login experience for your users.                    |
| You are responsible for your users' identity verification at your platform level. |

### How it Works

Your existing auth system verifies the user (session cookie, JWT, OAuth — whatever your platform uses). This step happens entirely on your side.

Call Transak's Onboard User Auth API from your **server**, passing the user's email in the request body and your partner access token in the `x-access-token` header.

```bash
curl -X POST https://api-gateway-stg.transak.com/api/v2/user/auth-reliance/onboard \
  -H "x-api-key: YOUR_API_KEY" \
  -H "x-access-token: YOUR_PARTNER_ACCESS_TOKEN" \
  -H "x-user-ip: USER_IP_ADDRESS" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com"
  }'
```

Once the user is onboarded, all subsequent Transak API calls on behalf of this user are authenticated by passing the user's **email** and your **partner access token** in the request headers. No OTP or user-specific token is required.

```bash
curl -X GET https://api-gateway-stg.transak.com/api/v2/user \
  -H "x-api-key: YOUR_API_KEY" \
  -H "x-access-token: YOUR_PARTNER_ACCESS_TOKEN" \
  -H "x-user-ip: USER_IP_ADDRESS" \
  -H "x-user-email: user@example.com"
```

### Auth Reliance User Onboarding API

Registers a pre-authenticated user with Transak using your partner access token. No OTP is sent to the user.

### Sample Code

```ts title="server/transakAuth.ts"
// Express route — runs on your server, never in the browser
import express from "express";

const router = express.Router();

const TRANSAK_API_KEY = process.env.TRANSAK_API_KEY!;
const TRANSAK_PARTNER_ACCESS_TOKEN = process.env.TRANSAK_PARTNER_ACCESS_TOKEN!;
const TRANSAK_BASE = "https://api-gateway-stg.transak.com";

router.post("/api/transak-onboard", async (req, res) => {
  // Your existing session/auth middleware must gate this route
  const currentUser = req.user; // populated by your auth middleware
  if (!currentUser) return res.status(401).json({ error: "Unauthorized" });

  try {
    const response = await fetch(`${TRANSAK_BASE}/api/v2/user/auth-reliance/onboard`, {
      method: "POST",
      headers: {
        "x-api-key": TRANSAK_API_KEY,
        "x-access-token": TRANSAK_PARTNER_ACCESS_TOKEN,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ email: currentUser.email }),
    });

    const data = await response.json();
    if (!response.ok) throw new Error(data.message ?? `HTTP ${response.status}`);

    res.json({ success: true });
  } catch (err: unknown) {
    res.status(500).json({ error: err instanceof Error ? err.message : "Onboarding failed" });
  }
});

export default router;
```

```ts title="server/transakUserApi.ts"
// All user-scoped Transak API calls use the partner access token + user email in headers
const TRANSAK_API_KEY = process.env.TRANSAK_API_KEY!;
const TRANSAK_PARTNER_ACCESS_TOKEN = process.env.TRANSAK_PARTNER_ACCESS_TOKEN!;
const TRANSAK_BASE = "https://api-gateway-stg.transak.com";

async function getUserDetails(userEmail: string) {
  const response = await fetch(`${TRANSAK_BASE}/api/v2/user`, {
    method: "GET",
    headers: {
      "x-api-key": TRANSAK_API_KEY,
      "x-access-token": TRANSAK_PARTNER_ACCESS_TOKEN,
      "x-user-email": userEmail,
    },
  });
  return response.json();
}
```