Login with ChatGPT
Reference

AI SDK providers

createChatGPTProxyProvider for browser/server proxy mode, createChatGPT for direct token mode, and the shared provider API.

@opencoredev/loginwithchatgpt-ai adapts the ChatGPT-backed Codex responses endpoint to the Vercel AI SDK (ai and @ai-sdk/openai are peer dependencies). Two factories, one provider shape:

  • Proxy provider: default for browser and app-server routes. Sends requests to your server handler, which injects credentials from the session cookie.
  • Direct provider: headless/server-only escape hatch. Holds a user's tokens and calls upstream directly.

Provider API

Both factories return a ChatGPTProvider:

const model = chatgpt("gpt-5.5");            // a responses model
const same = chatgpt.responses("gpt-5.5");   // explicit accessor
const model2 = chatgpt();                    // uses defaultModel ("gpt-5.5")
const slugs = await chatgpt.listModels();    // string[]
const generated = await chatgpt.images.generate({ prompt, model: slugs[0] });
const edited = await chatgpt.images.edit({ prompt, images, model: slugs[0] });
const openai = chatgpt.openai;               // underlying @ai-sdk/openai provider

Models work with streamText, generateText, tool calling, and structured output, plus anything else the AI SDK responses model supports. The provider's images client generates and edits images through the signed-in user's ChatGPT plan. Embeddings and audio are not provided.

Images

Both proxy and direct providers expose the same chatgpt.images client. It uses the existing /responses route, so it does not expose tokens or require an API key.

Always discover a mainline model from the signed-in account first:

const models = await chatgpt.listModels();
const model = models[0];
if (!model) throw new Error("No ChatGPT models were returned for this account.");

Generate

const result = await chatgpt.images.generate({
  model,
  prompt: "An editorial photograph of a cobalt chair in a sunlit concrete room",
  size: "2048x2048",
  quality: "high",
  format: "webp",
  compression: 80,
  background: "opaque",
  n: 2,
  partialImages: 2,
  onPartialImage(partial) {
    preview.src = partial.dataUrl;
  },
});

const first = result.data[0];
first.base64;   // raw base64 bytes
first.dataUrl;  // data:image/webp;base64,...
first.mediaType;

Edit

Editing is stateless: the client sends every source image with the edit request. It accepts remote URLs, data URLs, raw base64, Blob, ArrayBuffer, and Uint8Array inputs.

const result = await chatgpt.images.edit({
  model,
  prompt: "Replace only the background with a warm plaster wall.",
  images: [{ data: sourceBlob, detail: "high" }],
  mask: { data: maskBytes, mediaType: "image/png" },
  inputFidelity: "high",
  size: "1536x1024",
  quality: "high",
  format: "png",
});

image.src = result.data[0].dataUrl;

Image options

OptionTypeNotes
promptstringRequired generation or editing instruction.
modelstringMainline model returned by listModels().
imageModelstringOptional GPT Image model override; otherwise the tool selects it.
size"auto" or dimensionsPopular sizes include 1024x1024, 2048x2048, and 3840x2160.
qualityauto | low | medium | highRendering quality.
formatpng | jpeg | webpDefault png.
compression0..100JPEG/WebP output compression.
backgroundauto | opaque | transparentSupport depends on the selected image model.
n1..10Runs independent image requests and returns all results in order.
partialImages0..3Requests intermediate previews for each result.
onPartialImagecallbackReceives base64, data URL, result index, and partial index.
signalAbortSignalCancels the request.
imagesChatGPTImageInput[]Required by edit(); multiple reference images are supported.
maskChatGPTImageInputOptional edit mask.
inputFidelitylow | highDetail preservation for edits.

Raw base64 and byte inputs require an image/* mediaType. A Blob can use its own image MIME type. ChatGPTImageError exposes status, code, and detail when the upstream request fails.

createChatGPTProxyProvider(options?)

browser
import { createChatGPTProxyProvider } from "@opencoredev/loginwithchatgpt-ai";
import { streamText } from "ai";

const chatgpt = createChatGPTProxyProvider();

const result = streamText({
  model: chatgpt("gpt-5.5"),
  prompt: "Explain cache invalidation in two sentences.",
});
OptionTypeDefaultNotes
basePathstring"/api/chatgpt"Handler mount path. Use an absolute URL for cross-origin setups.
fetchFetchLikeglobal fetchWraps the AI SDK's own requests to /responses.
credentialsRequestCredentials"same-origin"Used by listModels() and images.*() requests; use "include" cross-origin.
headersRecord<string, string>n/aExtra headers on every request.
defaultModelstring"gpt-5.5"Used by chatgpt() with no argument.

listModels() throws a ChatGPTProxyError (with a status field) on non-OK responses. Check status === 401 to detect a signed-out user. Cross-origin setups need both credentials and a fetch wrapper; see the cross-origin guide.

The same provider is also the recommended app-server route pattern. Bind it to the current request with auth.proxyFetch(request):

app/api/chat/route.ts
import { createChatGPTProxyProvider } from "@opencoredev/loginwithchatgpt-ai";
import { streamText } from "ai";

export async function POST(request: Request) {
  const chatgpt = createChatGPTProxyProvider({
    fetch: auth.proxyFetch(request),
  });
  const { prompt } = await request.json();
  const result = streamText({ model: chatgpt(), prompt });
  return result.toUIMessageStreamResponse();
}

createChatGPT(options)

Use this only when you intentionally manage token custody yourself, such as a CLI/headless app or a migration path. Normal web apps should use createChatGPTProxyProvider() with /responses or auth.proxyFetch(request).

headless-server
import { createChatGPT } from "@opencoredev/loginwithchatgpt-ai";
import { streamText } from "ai";

const chatgpt = createChatGPT({ credentials: tokens });
const result = streamText({ model: chatgpt(), prompt });
OptionTypeDefaultNotes
credentialsChatGPTTokens | () => ChatGPTTokens | Promise<ChatGPTTokens>requiredStatic tokens or a loader.
onRefresh(tokens: ChatGPTTokens) => void | Promise<void>n/aPersist rotated tokens when you manage them yourself.
headersRecord<string, string>n/aExtra headers on upstream requests.
defaultModelstring"gpt-5.5"Used by chatgpt() with no argument.
+ CodexResponsesOptionsn/an/ainstructions, reasoningEffort, reasoningSummary, textVerbosity, serviceTier; see server defaults.
+ ChatGPTConfign/an/aUpstream overrides; see core reference.

Refresh behavior

  • Tokens with a refreshToken refresh in place. Pass onRefresh to persist rotations (see Headless & CLI logins).
  • Tokens without a refreshToken cannot self-refresh. If credentials is a function, the provider calls it again when the access token expires, so pass a loader for long-lived direct providers.
  • Tokens without an accountId throw ChatGPTAuthError with code invalid_token.

What the transport does

Both providers route through createCodexFetch() from core, which adds auth headers and the client_version query parameter, then rewrites the body for the stateless Codex backend. The full rule list is in Response proxy.

Attachments

Send files through the standard AI SDK messages shape; the transport passes them through untouched:

const result = streamText({
  model: chatgpt(model),
  messages: [
    {
      role: "user",
      content: [
        { type: "text", text: "Summarize this file." },
        { type: "file", data: dataUrl, mediaType: "application/pdf", filename: "report.pdf" },
      ],
    },
  ],
});

The proxy's maxRequestBytes (40 MiB default) counts the full JSON body, including base64 file data.

On this page