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

# Quick Start

> Get started with Doshi webview authentication in minutes

## Overview

Doshi webview authentication uses a secure two-step process:

1. **Generate a custom token** by calling the Doshi API with your API key
2. **Pass the token to the iframe** using postMessage or query parameters

<Warning>
  **IP Whitelisting Required**: Your server's IP address must be whitelisted to access the Doshi API. Contact [hello@doshi.app](mailto:hello@doshi.app) with your server IP addresses to get started.
</Warning>

<Steps>
  <Step title="Get API Key & Whitelist IP">
    Contact [hello@doshi.app](mailto:hello@doshi.app) to:

    * Receive your API key
    * Whitelist your server IP address(es)
  </Step>

  <Step title="Generate Custom Token">
    Call the authentication endpoint with your API key from your whitelisted server
  </Step>

  <Step title="Embed Webview">
    Pass the custom token to the Doshi Frontend using postMessage or query parameters
  </Step>

  <Step title="User Signs In">
    Doshi handles the sign-in process, including 2FA if enabled
  </Step>
</Steps>

<Note>
  These examples use the production hosts. To test against sandbox, use
  `https://sandbox.embed.doshi.app` (and `https://sandbox.api.doshi.app` for the
  API) — your client token works in both. See [Environments](/webview/environments).
</Note>

## Authentication Flow

```mermaid theme={null}
sequenceDiagram
    participant Client as Your Application
    participant API as Doshi API
    participant Frontend as Doshi Frontend
    participant User as End User

    Client->>API: POST /client/auth/custom-token<br/>(with API Key)
    API->>Client: Returns custom token
    Client->>Frontend: Pass token + user data
    Frontend->>User: Shows login/2FA screen if needed
    User->>Frontend: Completes authentication
    Frontend->>Frontend: Session active (1hr + 12hr refresh)
```

## Step 1: Generate Custom Token

Call the authentication endpoint with your API key:

```bash theme={null}
curl -X POST https://api.doshi.app/client/auth/custom-token \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Access-Type: client" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com",
    "partnerUserId": "partner_123",
    "firstName": "John",
    "lastName": "Doe",
    "branchId": "branch_789"
  }'
```

<Warning>
  **Required Headers:**

  * `Authorization: Bearer YOUR_API_KEY`
  * `Access-Type: client`
  * `Content-Type: application/json`
</Warning>

### API Parameters

**Required (at least one):**

* `email` - User's email address
* `partnerUserId` - Your internal user ID

**Optional:**

* `firstName` - User's first name
* `lastName` - User's last name
* `branchId` - Branch or location identifier

<Note>
  You must provide either `email` OR `partnerUserId` (or both). All other fields are optional.
</Note>

**Response:**

```json theme={null}
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
```

See the [full API reference](/api-reference/auth/get-custom-authentication-token) for details.

### Setting the Starting Learning Path

By default, a new user picks their own learning path the first time they open Doshi. To skip that screen and drop them straight into a specific set of lessons, pass a `pathId` when you mint the token.

<Warning>
  `pathId` is only accepted by `/client/auth/token` — **not** by
  `/client/auth/custom-token`, which ignores it. Use `/client/auth/token` when
  you need a starting path.
</Warning>

<Steps>
  <Step title="Create the path">
    In the dashboard, go to **Paths** → **Create**, then add the courses and lessons you want the user to see.
  </Step>

  <Step title="Copy the path ID">
    On the path's card, open the **⋯** menu and choose **Copy ID**. The ID is also the last segment of the path's URL (`/paths/<pathId>`).
  </Step>

  <Step title="Pass it when generating the token">
    Include the ID as `pathId` in the token request.
  </Step>
</Steps>

```bash theme={null}
curl -X POST https://api.doshi.app/client/auth/token \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Access-Type: client" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com",
    "pathId": "YOUR_PATH_ID"
  }'
```

The user now lands directly in the lessons on that path, with no path-selection screen.

#### Behaviour and limits

* **Sets the initial path only.** If the user already has a path, `pathId` is ignored — existing users are never moved. It is not a way to switch someone's path later.
* **Don't combine it with `segment`.** The two write to different places, so sending both leaves the path-selection screen in place. Use one or the other.
* **The ID isn't validated.** An incorrect ID is accepted without an error and leaves the user without usable content, so confirm the flow on [sandbox](/webview/environments) before going live.
* Must be 2–50 characters.
* Works with the 2FA flow.

<Note>
  `pathId` decides **which lessons** the user starts on. `segment` is different —
  it narrows which paths a user is offered when you run several paths under one
  organization, and still lets them choose.
</Note>

## Step 2: Pass Token to Doshi Frontend

Pass the nonce token to the Doshi Frontend using one of two methods:

<CardGroup cols={2}>
  <Card title="postMessage (Recommended)" icon="message" href="/webview/postmessage">
    Secure, real-time communication for sensitive data
  </Card>

  <Card title="Query Parameters" icon="link" href="/webview/query-parameters">
    Simple URL-based authentication
  </Card>
</CardGroup>

### Frontend Parameters

**Required:**

* `token` - The nonce token from the API

**Optional:**

* `email` - User's email address
* `segment` - For handling multiple learning paths
* `branchId` - Branch or location identifier

### 2FA Parameters (Optional)

When 2FA is enabled, pass these additional parameters:

**`All 2FA fields are required when is2FaEnabled is true:`**

* `is2FaEnabled` - Set to `true`
* `dob` - Date of birth (YYYY-MM-DD)
* `organizationId` - Your organization ID
* `partnerUserId` - Your internal user ID
* `firstName` - User's first name
* `lastName` - User's last name

<ParamField path="token" type="string" required>
  The nonce token received from the API
</ParamField>

<ParamField path="email" type="string">
  User's email address (optional)
</ParamField>

<ParamField path="segment" type="string">
  Used for handling multiple learning paths under the same organization (optional)
</ParamField>

<ParamField path="branchId" type="string">
  Branch or location identifier (optional)
</ParamField>

<ParamField path="is2FaEnabled" type="boolean">
  Set to `true` to enable 2FA flow (optional, but if true, all 2FA fields below are required)
</ParamField>

<ParamField path="dob" type="string">
  User's date of birth - YYYY-MM-DD format (required when is2FaEnabled is true)
</ParamField>

<ParamField path="organizationId" type="string">
  Your organization ID (required when is2FaEnabled is true)
</ParamField>

<ParamField path="partnerUserId" type="string">
  Your internal user ID (required when is2FaEnabled is true)
</ParamField>

<ParamField path="firstName" type="string">
  User's first name (required when is2FaEnabled is true)
</ParamField>

<ParamField path="lastName" type="string">
  User's last name (required when is2FaEnabled is true)
</ParamField>

<Note>
  When 2FA is enabled, the Doshi Frontend automatically handles the OTP send and verification flow. You don't need to call the OTP endpoints separately.
</Note>

## Step 3: Session Management

Once authenticated, the Doshi Frontend automatically manages the session:

* **ID Token TTL**: 1 hour
* **Refresh Token TTL**: 12 hours
* **Auto-refresh**: Tokens are refreshed automatically

No additional action required from your application!

## Quick Example

### React with postMessage

```tsx theme={null}
import React, { useEffect, useState } from "react";

const DoshiEmbed = () => {
  const [token, setToken] = useState<string | null>(null);

  useEffect(() => {
    // Step 1: Get nonce token from API
    async function authenticate() {
      const response = await fetch('https://api.doshi.app/client/auth/custom-token', {
        method: 'POST',
        headers: {
          'Authorization': 'Bearer YOUR_API_KEY',
          'Access-Type': 'client',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          email: 'user@example.com',  // Required (or partnerUserId)
          partnerUserId: 'partner_123', // Optional (but one of email/partnerUserId required)
          branchId: 'branch_789'        // Optional
        })
      });
      
      const data = await response.json();
      setToken(data.token);
    }

    authenticate();
  }, []);

  useEffect(() => {
    if (!token) return;

    // Step 2: Listen for PING from Doshi Frontend
    const handleMessage = (event: MessageEvent) => {
      if (event.origin !== 'https://embed.v2.doshi.app') return;
      
      const data = typeof event.data === "string" 
        ? JSON.parse(event.data) 
        : event.data;

      if (data.type === "PING") {
        // Step 3: Send token to Doshi Frontend
        const iframe = document.querySelector("iframe");
        if (iframe?.contentWindow) {
          iframe.contentWindow.postMessage(
            JSON.stringify({
              token,               // Required
              email: 'user@example.com',  // Optional
              segment: 'premium',         // Optional
              branchId: 'branch_789',     // Optional
              type: "AUTH"
            }),
            "https://embed.v2.doshi.app"
          );
        }
      }
    };

    window.addEventListener("message", handleMessage);
    return () => window.removeEventListener("message", handleMessage);
  }, [token]);

  if (!token) {
    return <div>Loading...</div>;
  }

  return (
    <iframe
      src="https://embed.v2.doshi.app"
      className="w-full h-screen"
      frameBorder="0"
      allowFullScreen
    />
  );
};

export default DoshiEmbed;
```

### React with Query Parameters

```tsx theme={null}
import React, { useEffect, useState } from "react";

const DoshiEmbed = () => {
  const [iframeUrl, setIframeUrl] = useState<string | null>(null);

  useEffect(() => {
    async function authenticate() {
      // Step 1: Get nonce token
      const response = await fetch('https://api.doshi.app/client/auth/custom-token', {
        method: 'POST',
        headers: {
          'Authorization': 'Bearer YOUR_API_KEY',
          'Access-Type': 'client',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          email: 'user@example.com',      // Required (or partnerUserId)
          partnerUserId: 'partner_123'    // Optional
        })
      });
      
      const data = await response.json();
      
      // Step 2: Build URL with token
      const params = new URLSearchParams({
        token: data.token,              // Required
        email: 'user@example.com',      // Optional
        segment: 'premium',             // Optional
        branchId: 'branch_789'          // Optional
      });
      
      setIframeUrl(`https://embed.v2.doshi.app?${params.toString()}`);
    }

    authenticate();
  }, []);

  if (!iframeUrl) {
    return <div>Loading...</div>;
  }

  return (
    <iframe
      src={iframeUrl}
      className="w-full h-screen"
      frameBorder="0"
      allowFullScreen
    />
  );
};

export default DoshiEmbed;
```

## With 2FA Enabled

```tsx theme={null}
const authData = {
  token: data.token,                    // Required
  email: 'user@example.com',            // Optional
  segment: 'premium',                   // Optional
  branchId: 'branch_789',               // Optional
  // All fields below are required when is2FaEnabled is true
  is2FaEnabled: true,
  dob: '1990-01-15',
  organizationId: 'org_123',
  partnerUserId: 'partner_123',
  firstName: 'John',
  lastName: 'Doe'
};

// The Doshi Frontend will automatically show OTP screen and handle verification
```

## Next Steps

<CardGroup cols={3}>
  <Card title="postMessage Method" icon="message" href="/webview/postmessage">
    Detailed postMessage implementation
  </Card>

  <Card title="Query Parameters" icon="link" href="/webview/query-parameters">
    URL-based authentication guide
  </Card>

  <Card title="Security" icon="shield" href="/webview/security">
    Security best practices
  </Card>
</CardGroup>

## Need Help?

<Card title="Contact Support" icon="envelope" href="mailto:hello@doshi.app">
  Get your API key or ask questions: [hello@doshi.app](mailto:hello@doshi.app)
</Card>
