# Quickstart (/docs/quickstart)





This page wires the default path: a server route at `/api/chatgpt/*`, a React
sign-in button, and a browser-safe AI SDK provider that streams through your
backend.

<Steps>
  <Step>
    ### Install the packages [#install-the-packages]

    <CodeBlockTabs defaultValue="bun">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="bun">
          bun
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="npm">
          npm
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="pnpm">
          pnpm
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="bun">
        ```bash
        bun add @opencoredev/loginwithchatgpt-server @opencoredev/loginwithchatgpt-react @opencoredev/loginwithchatgpt-ai ai @ai-sdk/openai
        ```
      </CodeBlockTab>

      <CodeBlockTab value="npm">
        ```bash
        npm install @opencoredev/loginwithchatgpt-server @opencoredev/loginwithchatgpt-react @opencoredev/loginwithchatgpt-ai ai @ai-sdk/openai
        ```
      </CodeBlockTab>

      <CodeBlockTab value="pnpm">
        ```bash
        pnpm add @opencoredev/loginwithchatgpt-server @opencoredev/loginwithchatgpt-react @opencoredev/loginwithchatgpt-ai ai @ai-sdk/openai
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    `ai` and `@ai-sdk/openai` are peer dependencies of `@opencoredev/loginwithchatgpt-ai`.
  </Step>

  <Step>
    ### Set a secret [#set-a-secret]

    The secret signs the session cookie and encrypts tokens before they enter
    your session store. Without one, the SDK generates an ephemeral secret and
    logs a warning, and every restart logs everyone out.

    ```bash title=".env"
    LWC_SECRET="use-the-output-of: openssl rand -hex 32"
    ```
  </Step>

  <Step>
    ### Mount the server handler [#mount-the-server-handler]

    `createChatGPTHandler()` returns one Web-standard handler that owns login,
    polling, logout, model listing, and the streaming proxy.

    <CodeBlockTabs defaultValue="Bun">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="Bun">
          Bun
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="Next.js">
          Next.js
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="Bun">
        ```ts  title="index.ts"
        import { createChatGPTHandler } from "@opencoredev/loginwithchatgpt-server";
        import index from "./index.html";

        const auth = createChatGPTHandler({
          secret: process.env.LWC_SECRET,
          responsesProxy: {
            allowedModels: ["gpt-5.5", "gpt-5.4", "gpt-5.4-mini"],
          },
        });

        Bun.serve({
          routes: {
            "/": index,
            "/api/chatgpt/*": (req) => auth.handler(req),
          },
        });
        ```
      </CodeBlockTab>

      <CodeBlockTab value="Next.js">
        ```ts  title="app/api/chatgpt/[...lwc]/route.ts"
        import { createChatGPTHandler } from "@opencoredev/loginwithchatgpt-server";

        const auth = createChatGPTHandler({
          secret: process.env.LWC_SECRET,
          responsesProxy: {
            allowedModels: ["gpt-5.5", "gpt-5.4", "gpt-5.4-mini"],
          },
        });

        export const GET = (request: Request) => auth.handler(request);
        export const POST = (request: Request) => auth.handler(request);
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    The handler only needs Web-standard `Request`, `Response`, `fetch`, and
    `crypto`, so any runtime with those works.
  </Step>

  <Step>
    ### Render the sign-in button [#render-the-sign-in-button]

    ```tsx title="components/sign-in.tsx"
    "use client";

    import { LoginWithChatGPT } from "@opencoredev/loginwithchatgpt-react";

    export function SignIn() {
      return (
        <LoginWithChatGPT
          consent={{ appName: "Acme" }}
          onAuthenticated={() => console.log("ChatGPT session connected")}
        />
      );
    }
    ```

    Clicking the button opens a consent popup first. It cannot be disabled, only
    customized. If the user continues, the same popup goes to OpenAI's
    verification page, the widget copies the user code, polls for completion, and
    renders the signed-in state:

        <img alt="The sign-in flow: consent, then the device code and verification window" src="__img0" />
  </Step>

  <Step>
    ### Discover models and stream [#discover-models-and-stream]

    Model availability depends on the account and plan, so ask instead of
    hardcoding a list. The proxy provider sends everything to your handler, which
    injects credentials from the session cookie.

    ```ts title="lib/chat.ts"
    import { createChatGPTProxyProvider } from "@opencoredev/loginwithchatgpt-ai";
    import { streamText } from "ai";

    const chatgpt = createChatGPTProxyProvider();

    export async function ask(prompt: string) {
      const models = await chatgpt.listModels(); // GET /api/chatgpt/models
      const model = models.includes("gpt-5.5") ? "gpt-5.5" : models[0];
      if (!model) throw new Error("No ChatGPT models returned for this account.");

      return streamText({ model: chatgpt(model), prompt });
    }
    ```

    ```ts
    const result = await ask("Explain HTTP cookies in one paragraph.");

    for await (const delta of result.textStream) {
      process.stdout.write(delta);
    }
    ```
  </Step>

  <Step>
    ### Verify [#verify]

    1. Click the button, continue past consent, and authorize in the OpenAI
       window.
    2. `GET /api/chatgpt/session` returns `{ "status": "authenticated" }`.
    3. `GET /api/chatgpt/models` returns at least one model slug.
    4. A prompt streams text back through `POST /api/chatgpt/responses`.
  </Step>
</Steps>

## Next [#next]

<Cards>
  <Card title="Build a chat page" href="/docs/guides/chat-app">
    Turn this into a real chat UI with a model picker and streaming output.
  </Card>

  <Card title="Production checklist" href="/docs/guides/production">
    Shared session store, guardrails, and a smoke test before you deploy.
  </Card>
</Cards>
