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

# JavaScript SDK Quickstart

> Create an Anam session token, install the JavaScript SDK, and run your first interactive avatar conversation in a browser.

export const javaScriptAgentPrompt = ["# Build an Anam JavaScript SDK quickstart", "", "Build a minimal browser application where I can start a conversation with an Anam avatar, speak through my microphone, hear and see its response, and stop the session.", "", "Before writing code, read https://anam.ai/docs/javascript-sdk/quickstart, https://anam.ai/docs/javascript-sdk/authentication, and https://anam.ai/docs/llms.txt. Use the current docs and SDK source as the source of truth. Follow the authentication guide's server-side API-key pattern, even where the quickstart uses a key directly in a local HTML demo. Do not invent SDK methods or configuration fields.", "", "## Set up the project", "", "1. Inspect the current workspace and use its existing framework, package manager, and server routes. If it is empty, create a small Node.js server with a browser page. Keep the implementation focused on the quickstart.", "2. Install @anam-ai/js-sdk using the project's package manager, or use the documented browser module import for a plain HTML page. Check the installed package and its Node.js requirements rather than assuming a version.", "3. Create a server-only ANAM_API_KEY environment variable with a placeholder. Have me fill it in locally using https://lab.anam.ai/api-keys. Never ask for the key in chat, print it, commit it, or expose it through a public environment variable or browser bundle.", "4. Use the documented personaConfig from the current quickstart as a starting point, or my existing persona configuration. Keep its avatarId, avatarModel, voiceId, llmId, and systemPrompt together so I can change them. Do not invent resource IDs. Explain how to choose my own avatar and voice in Anam Lab.", "", "## Create a session token on the server", "", "Add a POST /api/session-token route. It calls https://api.anam.ai/v1/auth/session-token with Authorization: Bearer followed by the server's ANAM_API_KEY, Content-Type: application/json, and a JSON body containing personaConfig. Return only the sessionToken to the browser. Handle missing configuration, network failures, and non-success API responses without exposing credentials. If the application already has authentication, keep its existing route protections.", "", "## Connect the browser", "", "- Render a responsive video element with id persona-video and the autoplay and playsinline attributes. Include Start and Stop controls and a readable status message.", "- When I click Start, request a fresh token from the local server, call createClient(sessionToken), then await client.streamToVideoElement(\"persona-video\"). Let the SDK handle microphone capture and audio playback.", "- Disable Start while connecting or connected so repeated clicks cannot create overlapping sessions. Handle denied microphone permission and connection failures with an actionable message and a way to retry.", "- On Stop, await client.stopStreaming(), clear the client reference, and reset the UI. Clean up an active client when the component is removed or the page closes.", "- Use localhost for development and HTTPS when deployed. Do not use file:// for this example.", "", "## Verify", "", "Run the project's available build, lint, and test commands. Check the token route's missing-key and failure paths. With a configured key and microphone access, open the app and verify a complete start, spoken exchange, stop, and restart. Confirm there is only one active session and that the API key never reaches the browser.", "", "Provide exact install/run instructions, the local URL, and the files I need to configure. Report the checks you actually completed and any steps that still require credentials or microphone access. Do not claim a live conversation worked unless you tested it."].join("\n");

export const PrebuiltPrompt = ({name, prompt, promptVersion, title = `Build this ${name} quickstart with a coding agent`, description = "Paste this prompt into Cursor, Claude Code, Codex, or another coding agent.", children}) => {
  const copyPrompt = async event => {
    const button = event.currentTarget;
    if (button.dataset.copyState === "copying") return;
    const label = button.querySelector("[data-copy-label]");
    const status = button.parentElement.querySelector("[data-copy-status]");
    window.clearTimeout(Number(button.dataset.copyResetTimer));
    delete button.dataset.copyResetTimer;
    button.dataset.copyState = "copying";
    button.setAttribute("aria-disabled", "true");
    label.textContent = "Copying…";
    status.textContent = "";
    const copyWithSelection = () => {
      const textArea = document.createElement("textarea");
      textArea.value = prompt;
      textArea.setAttribute("readonly", "");
      textArea.style.position = "fixed";
      textArea.style.opacity = "0";
      document.body.appendChild(textArea);
      try {
        textArea.select();
        if (!document.execCommand("copy")) throw new Error("Copy command failed");
      } finally {
        textArea.remove();
        button.focus({
          preventScroll: true
        });
      }
    };
    try {
      if (navigator.clipboard && window.isSecureContext) {
        try {
          await navigator.clipboard.writeText(prompt);
        } catch {
          copyWithSelection();
        }
      } else {
        copyWithSelection();
      }
      button.dataset.copyState = "copied";
      label.textContent = "Copied";
      status.textContent = "Prompt copied. Paste it into your coding agent.";
      button.dataset.copyResetTimer = String(window.setTimeout(() => {
        delete button.dataset.copyState;
        label.textContent = "Copy prompt";
        delete button.dataset.copyResetTimer;
      }, 2000));
    } catch {
      button.dataset.copyState = "error";
      label.textContent = "Try again";
      status.textContent = "Couldn't copy the prompt. Try again.";
    } finally {
      button.removeAttribute("aria-disabled");
    }
    try {
      window.posthog?.capture?.("docs_quickstart_prompt_copy_clicked", {
        quickstart: name.toLowerCase(),
        prompt_version: promptVersion,
        prompt_length: prompt.length,
        source_page: window.location.pathname
      });
    } catch {}
  };
  return <aside className="prebuilt-prompt" data-prompt-version={promptVersion} data-prompt-length={prompt.length} aria-label={children ? `${name} demo` : `${name} agent prompt`}>
      <div className="prebuilt-prompt-copy">
        <p className="prebuilt-prompt-title">{title}</p>
        <p className="prebuilt-prompt-description">
          {description}
        </p>
      </div>
      <button className="prebuilt-prompt-button" type="button" onClick={copyPrompt} data-attr="prebuilt-prompt-copy" aria-label={`Copy prompt for ${name}`}>
        <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" focusable="false">
          <g className="prebuilt-prompt-icon-copy">
            <rect x="9" y="9" width="13" height="13" rx="2" />
            <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
          </g>
          <path className="prebuilt-prompt-icon-check" d="m5 12 4 4L19 6" />
          <g className="prebuilt-prompt-icon-error">
            <circle cx="12" cy="12" r="9" />
            <path d="M12 8v4m0 4h.01" />
          </g>
        </svg>
        <span data-copy-label>Copy prompt</span>
      </button>
      {children}
      <span className="prebuilt-prompt-status" data-copy-status role="status" aria-live="polite" aria-atomic="true" />
    </aside>;
};

<PrebuiltPrompt name="JavaScript" prompt={javaScriptAgentPrompt} promptVersion="javascript-agent-prompt-v1" />

The Anam API provides a simple interface for implementing digital AI personas within your web applications. This guide will walk you through the process of setting up a minimal example of an interactive AI persona. By the end, you'll have a working persona that can have real-time conversations in your web browser.

## Prerequisites

* An Anam API key ([get one here](/docs/api-key))
* A modern web browser with microphone access
* A local web server (we'll show you how)

## Run this with your own persona

Create a persona and API key in Anam Lab, then use them in the quickstart.

<CardGroup cols={2}>
  <div data-attr="marketing.sdk-quickstart.persona-cta">
    <Card title="Create a persona" icon="user-plus" href="https://lab.anam.ai/build?action=create&tab=persona&cta_id=sdk-quickstart-persona" horizontal>
      Configure the avatar, voice, model, and instructions for your persona.
    </Card>
  </div>

  <div data-attr="marketing.sdk-quickstart.api-key-cta">
    <Card title="Get an API key" icon="key" href="https://lab.anam.ai/api-keys?cta_id=sdk-quickstart-api-key" horizontal>
      Create the key used to authenticate the quickstart request.
    </Card>
  </div>
</CardGroup>

## Create Your First Persona

<Steps>
  <Step title="Create the HTML structure">
    Create a new file called `index.html` and add this basic structure:

    ```html index.html theme={"system"}
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Hello Cara</title>
    </head>
    <body>
        <div style="text-align: center; padding: 20px;">
            <h1>Chat with Cara</h1>
            <p>Your AI persona will appear below and start automatically</p>
            <video id="persona-video" autoplay playsinline style="max-width: 100%; border-radius: 8px;"></video>
            <div id="status" style="margin-top: 15px; font-size: 14px; color: #666;">Loading...</div>
        </div>
    </body>
    </html>
    ```
  </Step>

  <Step title="Add the JavaScript">
    Now add the script that will automatically start your persona. Add this `<script>` tag just before the closing `</body>` tag:

    ```javascript theme={"system"}
    <script type="module">
        import { createClient } from "https://esm.sh/@anam-ai/js-sdk@latest";

        // Replace with your actual API key
        const API_KEY = "your-api-key-here";

        const videoElement = document.getElementById("persona-video");
        const statusElement = document.getElementById("status");

        async function createSessionToken() {
            const response = await fetch("https://api.anam.ai/v1/auth/session-token", {
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                    Authorization: `Bearer ${API_KEY}`,
                },
                body: JSON.stringify({
                    personaConfig: {
                        name: "Cara",
                        avatarId: "30fa96d0-26c4-4e55-94a0-517025942e18",
                        avatarModel: "cara-4",
                        voiceId: "6bfbe25a-979d-40f3-a92b-5394170af54b",
                        llmId: "a7cf662c-2ace-4de1-a21e-ef0fbf144bb7",
                        systemPrompt: "You are Cara, a helpful and friendly AI assistant. Keep responses conversational and concise.",
                    },
                }),
            });

            if (!response.ok) {
                throw new Error(`Failed to create session token: ${response.status}`);
            }

            const data = await response.json();
            return data.sessionToken;
        }

        async function startChat() {
            try {
                statusElement.textContent = "Creating session...";

                const sessionToken = await createSessionToken();
                statusElement.textContent = "Connecting...";

                const anamClient = createClient(sessionToken);
                await anamClient.streamToVideoElement("persona-video");

                statusElement.textContent = "Connected! Start speaking to Cara";

            } catch (error) {
                console.error("Failed to start chat:", error);
                statusElement.textContent = "Failed to connect. Check your API key.";
            }
        }

        // Auto-start when page loads
        startChat();
    </script>
    ```
  </Step>

  <Step title="Add your API key">
    Replace `your-api-key-here` with your actual Anam API key in the script.
  </Step>

  <Step title="Complete file example">
    Your final `index.html` file should look like this:

    ```html index.html [expandable] theme={"system"}
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Hello Cara</title>
    </head>
    <body>
        <div style="text-align: center; padding: 20px;">
            <h1>Chat with Cara</h1>
            <p>Your AI persona will appear below and start automatically</p>
            <video id="persona-video" autoplay playsinline style="max-width: 100%; border-radius: 8px;"></video>
            <div id="status" style="margin-top: 15px; font-size: 14px; color: #666;">Loading...</div>
        </div>

        <script type="module">
            import { createClient } from "https://esm.sh/@anam-ai/js-sdk@latest";

            // Replace with your actual API key
            const API_KEY = "your-api-key-here";

            const videoElement = document.getElementById("persona-video");
            const statusElement = document.getElementById("status");

            async function createSessionToken() {
                const response = await fetch("https://api.anam.ai/v1/auth/session-token", {
                    method: "POST",
                    headers: {
                        "Content-Type": "application/json",
                        Authorization: `Bearer ${API_KEY}`,
                    },
                    body: JSON.stringify({
                        personaConfig: {
                            name: "Cara",
                            avatarId: "30fa96d0-26c4-4e55-94a0-517025942e18",
                            avatarModel: "cara-4",
                            voiceId: "6bfbe25a-979d-40f3-a92b-5394170af54b",
                            llmId: "a7cf662c-2ace-4de1-a21e-ef0fbf144bb7",
                            systemPrompt: "You are Cara, a helpful and friendly AI assistant. Keep responses conversational and concise.",
                        },
                    }),
                });

                if (!response.ok) {
                    throw new Error(`Failed to create session token: ${response.status}`);
                }

                const data = await response.json();
                return data.sessionToken;
            }

            async function startChat() {
                try {
                    statusElement.textContent = "Creating session...";

                    const sessionToken = await createSessionToken();
                    statusElement.textContent = "Connecting...";

                    const anamClient = createClient(sessionToken);
                    await anamClient.streamToVideoElement("persona-video");

                    statusElement.textContent = "Connected! Start speaking to Cara";

                } catch (error) {
                    console.error("Failed to start chat:", error);
                    statusElement.textContent = "Failed to connect. Check your API key.";
                }
            }

            // Auto-start when page loads
            startChat();
        </script>
    </body>
    </html>
    ```
  </Step>

  <Step title="Start a local server">
    To serve your html file, you'll need a local web server. You can use one of
    the following methods depending on which development tools you have installed.

    <CodeGroup>
      ```bash Node.js theme={"system"}
      npx http-server -p 8000
      ```

      ```bash Python 3 theme={"system"}
      python -m http.server 8000
      ```

      ```bash Python 2 theme={"system"}
      python -m SimpleHTTPServer 8000
      ```

      ```bash PHP theme={"system"}
      php -S localhost:8000
      ```
    </CodeGroup>
  </Step>

  <Step title="Open and test">
    1. Navigate to `http://localhost:8000` in your browser
    2. Allow microphone access when prompted
    3. Cara will appear automatically and you can start talking!
    4. When you're done, close the browser tab to end the session.
  </Step>
</Steps>

## What just happened?

* **Obtaining a Session Token**: Your API key was exchanged for a temporary session token that enables the persona connection
* **Establishing a WebRTC Stream**: The Anam SDK established a real-time video/audio connection to display your persona
* **Voice Interaction**: Cara listens to your microphone and responds with natural speech and facial expressions

## What's next?

Now that you have Cara running, here are some directions to explore:

<CardGroup cols={3}>
  <Card title="Customize your persona" icon="paintbrush" href="https://anam.ai/docs/personas/overview">
    Change your persona's appearance, voice, and personality
  </Card>

  <Card title="Build a full application" icon="code" href="examples/basic-app">
    Create a complete app with proper client/server architecture
  </Card>

  <Card title="Handle conversation events" icon="bolt" href="https://anam.ai/docs/javascript-sdk/reference/event-types">
    React when your persona speaks, listens, or encounters errors
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.