API reference

Build with Codex.

An OpenAI-compatible API backed by the Codex account connected to each API key. Existing OpenAI SDKs work by changing the base URL and key.

Base URLhttps://authapi.dev/v1
ChatGPT-plan auth outside the Codex application is unofficial and may change. Each user must connect their own Codex account from the dashboard.

Authentication

Every API request requires a authapi.dev key in the HTTP bearer authorization header. Create a key from your dashboard after connecting Codex.

Keys are scoped to the account that created them. Requests use that account’s connected Codex session.

HTTP headerrequired
Authorization: Bearer $KEY

Quick start

Send your first request with curl, or point the official OpenAI SDK at this API.

OpenAI Python SDK

Set base_url to the AuthAPI and pass your dashboard key. The rest of the client interface remains unchanged.

curlchat completion
curl https://authapi.dev/v1/chat/completions \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-5.6-sol",
    "messages": [
      {"role": "user", "content": "Hello"}
    ]
  }'

Streaming

Set stream: true to receive output as server-sent events while it is generated.

Chat Completions emits chat.completion.chunk objects and ends with data: [DONE]. Tool calls use indexed delta.tool_calls argument fragments and finish with finish_reason: tool_calls. Responses preserves typed events including response.output_item.added/done and response.function_call_arguments.delta/done.

Pythonstreaming
from openai import OpenAI

client = OpenAI(
    base_url="https://authapi.dev/v1",
    api_key=KEY,
)

stream = client.chat.completions.create(
    model="gpt-5.6-sol",
    messages=[{"role": "user", "content": "Hello"}],
    stream=True,
)

for chunk in stream:
    text = chunk.choices[0].delta.content
    print(text or "", end="")
GET

/v1/health

Checks both your API key and its owner’s connected Codex authorization. Send the API key in the bearer authorization header, never in the URL.

StatusMeaning
200The API key is valid and Codex is connected.
401The API key is missing, invalid, or revoked.
503The API key is valid, but Codex must be reconnected.
Requestcurl
curl https://authapi.dev/v1/health \
  -H "Authorization: Bearer $CODEX...e>
          
GET

/v1/models

Lists the models currently exposed by Codex in OpenAI-compatible model-list format.

ReturnsDescription
objectAlways list.
data[]Available model objects. Use each object’s id in generation requests.
Requestcurl
curl https://authapi.dev/v1/models \
  -H "Authorization: Bearer $KEY"
POST

/v1/chat/completions

Creates an OpenAI-compatible chat completion with native function tools. The gateway relays calls and results but never executes client tools.

ParameterDescription
modelModel ID. Defaults to gpt-5.6-sol.
messagesRequired system, developer, user, assistant, and tool messages. Assistant calls use tool_calls; results use role: tool and tool_call_id.
toolsUp to 128 nested OpenAI function definitions.
tool_choicenone, auto, required, or a forced function.
parallel_tool_callsAllow one response to contain multiple calls.
reasoning_effortReasoning level supported by the selected model.
streamWhen true, returns OpenAI-compatible SSE chunks.
Request bodyJSON
{
  "model": "gpt-5.6-sol",
  "messages": [{"role": "user", "content": "Weather in Paris?"}],
  "tools": [{
    "type": "function",
    "function": {
      "name": "get_weather",
      "description": "Get current weather",
      "parameters": {"type": "object", "properties": {
        "city": {"type": "string"}
      }}
    }
  }],
  "tool_choice": "auto",
  "parallel_tool_calls": true
}
Tool-only output has content: null, stable call IDs, and finish_reason: tool_calls. Send results back as tool messages.
POST

/v1/responses

Creates an OpenAI Responses response with flat function tools and canonical function_call/function_call_output items.

ParameterDescription
modelModel ID. Defaults to gpt-5.6-sol.
inputRequired string or structured message/function item array.
instructionsOptional developer-level instructions.
toolsUp to 128 flat function definitions.
tool_choicenone, auto, required, or a forced function.
parallel_tool_callsAllow multiple function calls.
reasoning.effortReasoning level for the selected model.
streamWhen true, preserves typed Responses API events.
Request bodyJSON
{
  "model": "gpt-5.6-sol",
  "input": [{
    "role": "user",
    "content": [{"type": "input_text", "text": "Weather in Oslo?"}]
  }],
  "tools": [{
    "type": "function",
    "name": "get_weather",
    "description": "Get current weather",
    "parameters": {"type": "object", "properties": {
      "city": {"type": "string"}
    }}
  }],
  "tool_choice": "auto",
  "parallel_tool_calls": true,
  "stream": true
}
Calls are output items with a call_id. Execute them client-side, then submit a function_call_output item using that ID. Final event: response.completed.
POST

/v1/images/generations

Generates an image and returns OpenAI-compatible base64 image data. Every response includes X-Request-Id. A supplied canonical UUID is accepted; invalid or unsafe values are replaced. Disconnects and the bounded image timeout cancel upstream work.

ParameterDescription
promptRequired description of the image.
size1024x1024, 1536x1024, or 1024x1536.
qualitylow, medium, or high.
output_formatOutput format, defaulting to png.
Request bodyJSON
{
  "model": "gpt-image-1",
  "prompt": "A technical blueprint of a lunar rover",
  "size": "1536x1024",
  "quality": "medium"
}
POST

/v1/images/edits

Edits one or more supplied images. Send multipart form data for files, or JSON with image data URLs.

ParameterDescription
imageRequired source image file or data URL. Multiple images are supported.
promptRequired edit instruction.
sizeRequested output dimensions.
qualityRequested image quality.
Multipart requestcurl
curl https://authapi.dev/v1/images/edits \
  -H "Authorization: Bearer $KEY" \
  -F "image=@source.png" \
  -F "prompt=Make the background transparent" \
  -F "size=1024x1024"
GET

/v1/limits (alias: /limits)

Requires a bearer API key and returns normalized provider-reported Codex quota windows for that key’s owner. Browser session identity is not used. Account IDs, email, OAuth tokens, and key data are omitted. Successes and errors use Cache-Control: private, no-store.

Owner scoped. The key determines whose 5-hour, weekly, other, and additional model-specific windows are returned. Missing windows are never fabricated.
Request
curl https://authapi.dev/v1/limits \
  -H "Authorization: Bearer $AUTHAPI_KEY"
POST

/v1/audio/transcriptions

Transcribes one uploaded audio file using the API key owner’s connected Codex account and the same private batch route used by the Codex prompt box. Audio and transcripts are not logged or stored by AuthAPI.

ParameterDescription
fileRequired audio file up to 25 MB: FLAC, M4A, MP3/MP4/MPEG/MPGA, OGA/OGG, WAV, or WebM.
modelRequired. codex-transcribe or gpt-4o-mini-transcribe.
languageOptional ISO language code such as en or en-US.
response_formatjson (default) or text.

This compatibility subset does not support prompts, temperature, timestamps, or verbose JSON. Successful responses use Cache-Control: no-store.

Try it in your browser. The authenticated Speech-to-Text page can record from your microphone or upload a supported file without exposing an API key to the page.
Multipart requestcurl
curl https://authapi.dev/v1/audio/transcriptions \
  -H "Authorization: Bearer $KEY" \
  -F "file=@recording.webm" \
  -F "model=codex-transcribe" \
  -F "language=en"
200 responseJSON
{"text":"Transcribed speech."}
POST

/v1/audio/speech

Generates spoken audio using the API key owner’s connected Codex account. The server uses Codex’s realtime output-audio stream and returns audio only after the spoken transcript is verified against the requested text. Input text, transcripts, and audio are not logged or stored.

ParameterDescription
inputRequired text, from 1 to 1,000 characters.
modelcodex-tts (default), gpt-4o-mini-tts, or gpt-realtime-1.5.
voicemarin (default), alloy, ash, ballad, cedar, coral, echo, sage, shimmer, or verse.
response_formatwav (default) or raw pcm. Both are mono signed 16-bit little-endian PCM at 24 kHz.
instructionsOptional voice-style guidance up to 500 characters.
speedOptional and currently must be 1.

This is a restricted OpenAI speech compatibility subset. MP3 and other encoded formats are not supported. A transcript mismatch fails with 502 incomplete_speech rather than returning known-wrong audio.

Try it in your browser. Use the authenticated Text-to-Speech page to select a voice, add style guidance, and play the generated WAV.
JSON requestcurl
curl https://authapi.dev/v1/audio/speech \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "codex-tts",
    "input": "Hello from Codex.",
    "voice": "marin",
    "response_format": "wav"
  }' \
  --output speech.wav
The response body is audio, not JSON. Save it to a file or stream it to an audio player.
GET

/api/provider-usage

Returns current provider-reported Codex quota windows for the logged-in account. This browser-account endpoint requires the cxa_session cookie, removes upstream account identity and OAuth fields, and responds with Cache-Control: private, no-store.

Separate measurements. Plan-limit percentages come from Codex. They are not calculated from the AuthAPI request and token history shown on Usage.
FieldDescription
kindfive_hour, weekly, or other, classified from the provider’s reported window duration.
usedPercentPercentage consumed, clamped to 0–100.
remainingPercentPercentage available, calculated as 100 minus used.
resetsAtAccount-specific absolute reset time supplied by Codex.

Primary and secondary positions are not assumed to mean 5-hour and weekly. Missing windows are not fabricated, and additional model-specific limits are returned as independent entries. Results are cached per owner/account for 30 seconds.

200 responseJSON
{
  "available": true,
  "fetchedAt": "2026-08-08T12:00:00.000Z",
  "planType": "pro",
  "limits": [{
    "id": "codex",
    "name": "Codex",
    "allowed": true,
    "limitReached": false,
    "windows": [
      { "kind": "five_hour", "label": "5-hour",
        "usedPercent": 25, "remainingPercent": 75,
        "windowSeconds": 18000,
        "resetsAt": "2026-08-08T17:00:00.000Z" },
      { "kind": "weekly", "label": "Weekly",
        "usedPercent": 60, "remainingPercent": 40,
        "windowSeconds": 604800,
        "resetsAt": "2026-08-12T12:00:00.000Z" }
    ]
  }]
}
POST

/api/voice/session

Exchanges a complete WebRTC audio SDP offer for an answer using the authenticated owner’s connected Codex/ChatGPT OAuth account. Browser requests require the logged-in session cookie and same-origin Origin; origin-less clients may use a cxa bearer key whose owner has connected Codex.

Caller-defined functions are supported. Supply flat OpenAI function definitions in tools. Frameless Bidi emits delegation.created over oai-events; your backend selects the function through Responses SSE, executes it, and the browser returns result context. AuthAPI never executes your tools.
ParameterDescription
sdpRequired bounded, ICE-complete WebRTC SDP offer with audio.
voiceOptional Codex V3 voice. Defaults to cove.
instructionsOptional assistant instructions, up to 16,384 characters. Requires tools.
toolsOne to 128 flat OpenAI function definitions with type, name, and optional description, parameters, and strict. The client-delegation Responses adapter selects a function; your executor must allowlist names and validate arguments.
tool_choiceauto, none, required, or {"type":"function","name":"…"}. Defaults to auto.
response_modelOptional model used by the Responses adapter. Defaults to gpt-5.5.

Audio uses browser WebRTC media tracks. Create oai-events before the offer and parse each message as one UTF-8 JSON object. The response never includes OAuth credentials, account ID, or the validated upstream call ID.

Request bodyJSON
{
  "sdp": "v=0\\r\\n…",
  "voice": "cove",
  "instructions": "Use functions when needed.",
  "tools": [{
    "type": "function",
    "name": "get_weather",
    "description": "Get current weather for a city.",
    "parameters": {
      "type": "object",
      "properties": { "city": { "type": "string" } },
      "required": ["city"],
      "additionalProperties": false
    },
    "strict": true
  }],
  "tool_choice": "auto"
}
201 responseWebRTC answer
{
  "sdp": "v=0\\r\\n…",
  "protocol": { "transport": "webrtc", "architecture": "frameless-bidi", "version": 3,
                "experimental": true, "dataChannel": "oai-events",
                "model": "gpt-live-1-codex", "voice": "cove",
                "delegation": "client", "functionTransport": "responses-adapter",
                "responseModel": "gpt-5.5", "toolChoice": "auto", "toolCount": 1 }
}

Realtime transcript events

Select text by exact event type. Do not look for transcript fragments in top-level delta, text, or transcript properties.

EventRead and render
input_transcript.addedAppend event.item.text to the current user draft.
output_transcript.addedAppend event.item.text to the current assistant draft.
turn.doneUse event.turn.role; replace its draft with the complete event.turn.transcript, then finalize it.
Avoid duplicate or delayed text. Keep separate user and assistant drafts. An *.added payload is a fragment and should be appended once. A turn.done transcript is the authoritative complete value and must replace, not append to, the draft.

Ignore unknown event types and sanitize error events rather than displaying raw upstream details. A valid delegation.created may be handled only by an explicitly registered client function. See the complete transcript mapper and function adapter contract.

Incremental fragmentsJSON
{"type":"input_transcript.added","item":{"text":"Can you "}}
{"type":"input_transcript.added","item":{"text":"hear me?"}}
{"type":"output_transcript.added","item":{"text":"Yes, I "}}
Authoritative final turnJSON
{
  "type": "turn.done",
  "turn": {
    "role": "assistant",
    "transcript": "Yes, I can."
  }
}
Exact field mappingJavaScript
if (event.type === 'input_transcript.added')
  appendDraft('user', event.item?.text || '');
else if (event.type === 'output_transcript.added')
  appendDraft('assistant', event.item?.text || '');
else if (event.type === 'turn.done')
  finalizeDraft(event.turn?.role, event.turn?.transcript || '');

Custom voice function calls

Custom voice functions use two connected transports. Frameless Bidi sends delegation.created over the WebRTC oai-events channel. Your backend submits the delegated text and fixed tool registry to bearer-authenticated POST /v1/responses, executes exactly one validated function, then the browser returns sanitized result context over the existing data channel.

External sites need their own backend. Do not place a cxa API key in browser JavaScript. External browser origins cannot call /api/voice/session directly. Send the browser's SDP to your authenticated, CSRF-protected backend, make the origin-less bearer request server-to-server, and bind later delegation work to a short-lived application voice-session ID. The same backend should call /v1/responses, rate-limit requests, and execute only fixed privileged functions.

Deployment topology

  1. The browser creates RTCPeerConnection, adds microphone tracks, creates oai-events before createOffer(), and waits for ICE gathering.
  2. Your backend injects its allowlisted tools and API key into POST /api/voice/session; the browser applies the returned SDP answer.
  3. On delegation.created, key work by event.item.id and send the joined input_text to your backend.
  4. Your backend calls https://authapi.dev/v1/responses with stream: true and parallel_tool_calls: false.
  5. Require response.completed, exactly one allowlisted call, complete JSON arguments, and local schema validation before execution.
  6. Return the stringified result to the browser, which sends delegation.context.append and waits for delegation.context.appended.

The session's response_model value becomes the Responses request's model field, and the normalized session tool_choice must also be retained for the Responses request. Voice instructions control when the live model delegates; separate Responses instructions control tool selection and arguments. Do not copy untrusted voice instructions into a privileged selector.

Correlation and fail-closed rules

VALUEUSE
event.item.idThe voice delegation_item_id. Cache work by this ID so duplicate events cannot execute a side effect twice.
Responses call_idIdentifies the selected function call and its string output.
handoff_idOptional voice transport metadata. Do not use it as either ID above.

Zero calls, multiple calls, unknown names, invalid arguments, failed or truncated SSE, timeouts, and execution exceptions must execute nothing. Send short failure context so the spoken turn is not left waiting. Never blindly re-execute because an acknowledgement is delayed.

Copy-paste implementation guide. The complete Markdown guide includes browser WebRTC negotiation, a backend session proxy, a bounded SSE collector, allowlisted dispatch, success and failure context, duplicate suppression, client cleanup, and backend timeouts.
External application flowtwo transports
Browser                         Your backend                 AuthAPI
RTCPeerConnection
POST /voice/session ----------> add tools + bearer key ----> /api/voice/session
<----------------------------- SDP answer <---------------- 201 SDP answer

oai-events: delegation.created
POST /voice/delegation -------> select + execute ----------> /v1/responses SSE
<----------------------------- sanitized result <---------- function_call

oai-events: delegation.context.append
         delegation.context.appended
Required terminal eventsJSON
{"type":"delegation.created","item":{
  "id":"delegation_123",
  "type":"delegation",
  "target":"client",
  "content":[{"type":"input_text","text":"Weather in Chicago?"}]
}}

{"type":"response.output_item.added","item":{
  "id":"fc_123","type":"function_call",
  "call_id":"call_123","name":"get_weather","arguments":""
}}
{"type":"response.function_call_arguments.done",
 "item_id":"fc_123","arguments":"{\"city\":\"Chicago\"}"}
{"type":"response.completed","response":{"status":"completed"}}
Responses events are SSE, not WebRTC messages. The complete guide shows bounded parsing and failure handling.

Errors

Errors use an OpenAI-compatible envelope. Authentication failures return HTTP 401 or 403; invalid input returns HTTP 400; upstream generation failures return HTTP 502.

Error responseJSON
{
  "error": {
    "message": "Incorrect API key provided.",
    "type": "invalid_request_error",
    "param": null,
    "code": "invalid_api_key"
  }
}