Codex API Adapter v1.0.48 Enter your Codex API username to load and test protected API calls.

API Documentation

This service exposes a versioned HTTP adapter over the local Codex app-server. The stable surface for browser-based coding agents is under /api/v1. Search is enabled by default, managed projects resume the latest Codex thread for the same workspace, and approval-aware coding flows remain explicit rather than hidden.

Version: v1.0.48. Customer API authentication accepts a System Token or User API Key. Admin API endpoints remain hidden until an authorized admin signs in on this page.

Power AI API guide

Exact model ID: power-ai

Power AI is a managed model alias. Send model=power-ai to use the service's configured Power AI destination. The selected provider can change without changing this model ID. Existing account billing and key restrictions apply. This is a service-managed alias, not the official name of a separate foundation model.

  1. Sign in to your CodexAPI.pro dashboard and open API Keys. Create a User API Key for your integration, or use your System Token.
  2. Use https://codexapi.pro as the API host. SDKs that append endpoint names normally need https://codexapi.pro/v1 as their base URL; do not append /v1 twice.
  3. Send Authorization: Bearer YOUR_API_KEY and model: power-ai. A dashboard login session, provider key or reseller-management key is not a substitute for the customer's model API key.
  4. Read the returned answer and usage. API calls use the account's existing wallet and saved Codex rate card. Applicable Unlimited coverage, key permissions, spending limits and expiry still apply; this model does not create free credit.

Power AI accepts Chat Completions, Responses and Anthropic-style Messages requests. The public model ID stays power-ai even when its managed destination changes. Reasoning behavior follows the current routing policy; specifying this alias does not pin a particular upstream or effort level.

Download customer OpenAPI schema. Import this schema into your API client and choose a Power AI request example. Customer inference and reseller management use separate schemas.

List available models

Bash: set API_KEY to a System Token or User API Key from this site's dashboard before running these commands.

curl --fail-with-body https://codexapi.pro/v1/models \
  -H "Authorization: Bearer $API_KEY"
Chat Completions

Read the assistant reply from choices[0].message.content. Inspect tool_calls if the reply asks to use a function.

curl --fail-with-body --max-time 180 https://codexapi.pro/v1/chat/completions \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"power-ai","max_tokens":64,"messages":[{"role":"user","content":"Reply exactly OK."}]}'
Responses

Read text from output items containing output_text blocks. Response IDs and usage totals vary by request.

curl --fail-with-body --max-time 180 https://codexapi.pro/v1/responses \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"power-ai","input":"Reply exactly OK.","max_output_tokens":64}'
Anthropic-style Messages

Use /v1/messages and include the anthropic-version header. Read text blocks from content. No dashboard model change is needed when the request explicitly names power-ai.

curl --fail-with-body --max-time 180 https://codexapi.pro/v1/messages \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -H "anthropic-version: 2023-06-01" \
  -d '{"model":"power-ai","max_tokens":64,"messages":[{"role":"user","content":"Reply exactly OK."}]}'
Streaming

This Responses example keeps the connection open for server-sent events. Handle text deltas, response.completed and error events; HTTP 200 alone does not prove a successful generation. Messages streams use content_block_delta and message_stop instead.

curl --fail-with-body --max-time 180 -N https://codexapi.pro/v1/responses \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"power-ai","input":"Reply exactly OK.","max_output_tokens":64,"stream":true}'
Function tools

Your application must execute get_project_name locally. Return a role=tool message with the matching tool_call_id after the complete assistant tool_calls message, then submit the conversation again. Do not omit a tool result or execute an unapproved function. Tools and images remain subject to the active destination's capabilities.

curl --fail-with-body --max-time 180 https://codexapi.pro/v1/chat/completions \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"power-ai","messages":[{"role":"user","content":"Use get_project_name to read the current project name."}],"tools":[{"type":"function","function":{"name":"get_project_name","description":"Read the name of the current project.","parameters":{"type":"object","properties":{},"additionalProperties":false}}}],"tool_choice":{"type":"function","function":{"name":"get_project_name"}},"max_tokens":256}'
Python

Python 3, standard library only. Set API_KEY in your environment. This sends one request and prints the answer and support request ID.

import json
import os
from urllib.request import Request, urlopen

payload = {'model': 'power-ai', 'max_tokens': 64, 'messages': [{'role': 'user', 'content': 'Reply exactly OK.'}]}
request = Request(
    "https://codexapi.pro/v1/chat/completions",
    data=json.dumps(payload).encode("utf-8"),
    headers={"Authorization": "Bearer " + os.environ["API_KEY"],
             "Content-Type": "application/json"},
    method="POST",
)
with urlopen(request, timeout=180) as response:
    result = json.load(response)
    print(result["choices"][0]["message"]["content"])
    print("Request ID:", response.headers.get("x-request-id", "not supplied"))
JavaScript / Node.js

Run with Node.js 18 or later. Keep the key on your server, never in public browser JavaScript. Set API_KEY before running the script.

async function main() {
  if (!process.env.API_KEY) throw new Error("Set API_KEY first");
  const response = await fetch("https://codexapi.pro/v1/chat/completions", {
    method: "POST",
    headers: {Authorization: `Bearer ${process.env.API_KEY}`, "Content-Type": "application/json"},
    body: JSON.stringify({"model": "power-ai", "max_tokens": 64, "messages": [{"role": "user", "content": "Reply exactly OK."}]}),
    signal: AbortSignal.timeout(180000),
  });
  const requestId = response.headers.get("x-request-id");
  if (!response.ok) throw new Error(`HTTP ${response.status}; request ID ${requestId}`);
  const result = await response.json();
  console.log(result.choices[0].message.content);
  console.log("Request ID:", requestId);
}
main().catch(error => { console.error(error.message); process.exitCode = 1; });
Windows PowerShell

Set the API_KEY environment variable to your own key, then paste this into PowerShell. This example does not overwrite any CLI configuration.

$ErrorActionPreference = 'Stop'
if (-not $env:API_KEY) { throw 'Set API_KEY first' }
$body = @{
  model = 'power-ai'
  max_tokens = 64
  messages = @(@{ role = 'user'; content = 'Reply exactly OK.' })
} | ConvertTo-Json -Depth 10
$result = Invoke-RestMethod -Method Post -Uri 'https://codexapi.pro/v1/chat/completions' -TimeoutSec 180 -Headers @{ Authorization = "Bearer $env:API_KEY" } -ContentType 'application/json' -Body $body
$result.choices[0].message.content

Troubleshooting

401: Check that the key belongs to this site and has not been revoked. 402: Check available wallet credit or matching Unlimited coverage. 403: Check key permissions and VIP entitlement if using VIP. 429: Respect Retry-After and avoid overlapping retries. 502/503: The current upstream could not complete the request; save the request ID for support. Do not resend a completed request blindly.

Image input uses OpenAI image_url blocks on Chat Completions and Anthropic image blocks on Messages. Never strip an image to make a request appear successful; verify support through the configured destination. Local file, shell and editor tools run in your client, not automatically on the API server.

Restricted operator access

Show Admin API endpoints

Sign in with the authorized admin account to add the protected Admin API endpoints to this page. Customer documentation remains public.

Auth Model

Official-Compatible Model APIs

CodexAPI.pro exposes additive compatibility endpoints for coding clients that expect the official OpenAI and Anthropic-style HTTP surfaces. These endpoints use the same wallet, internal rate cards, model-selection policy, and live routing source of truth as the existing CLI endpoints.

API Test Zone

These examples automatically use the bearer value in the field above. When a client opens this page from the dashboard, that value is their Codex API username. Replace the prompt text and project names as needed.

1. Verify your wallet account

          
2. Verify stock Codex model discovery

          
3. Start a browser-agent project

          

Official OpenAI Codex CLI

Use the customer's Codex API username with the official OpenAI package installed by npm install -g @openai/codex@0.147.0. The API compatibility target is Codex CLI 0.147.0. Do not run codex login for CodexAPI.pro; configure CodexAPI.pro as a custom Responses provider and save the username in ~/.codex/config.toml using the dashboard's one-time setup command.

After the one-time setup has written CodexAPI.pro into the normal Codex config, users can return with codex, codex --search, codex resume --search, or any other normal Codex opening command without repeating API setup.

For non-interactive live-search runs, use codex --search exec .... The current official CLI rejects codex exec --search ....

The dashboard returns a token-specific command after creation; the generic provider shape is:

{
  "providerId": "codexapi",
  "providerName": "CodexAPI.pro",
  "officialCliVersion": "0.147.0",
  "officialInstallTag": "0.147.0",
  "officialInstallCommand": "npm install -g @openai/codex@0.147.0",
  "baseUrl": "https://api-canada.codexapi.pro/v1",
  "envKey": "CODEXAPI_CODEX_API_KEY",
  "wireApi": "responses",
  "model": "gpt-5.6",
  "reasoningEffort": "xhigh",
  "modelsUrl": "https://api-canada.codexapi.pro/v1/models",
  "responsesUrl": "https://api-canada.codexapi.pro/v1/responses",
  "codexHome": "$HOME/.codex",
  "configArgs": [
    "-c 'model_provider=\"codexapi\"'",
    "-c 'model_providers.codexapi.name=\"CodexAPI.pro\"'",
    "-c 'model_providers.codexapi.base_url=\"https://api-canada.codexapi.pro/v1\"'",
    "-c 'model_providers.codexapi.env_key=\"CODEXAPI_CODEX_API_KEY\"'",
    "-c 'model_providers.codexapi.wire_api=\"responses\"'",
    "-c 'model_providers.codexapi.supports_websockets=false'",
    "-c 'model_reasoning_effort=\"xhigh\"'",
    "-c 'features.apps=false'",
    "-c 'approval_policy=\"never\"'",
    "-c 'sandbox_mode=\"danger-full-access\"'"
  ],
  "persistentConfigToml": "model = \"gpt-5.6\"\nmodel_provider = \"codexapi\"\nmodel_reasoning_effort = \"xhigh\"\napproval_policy = \"never\"\nsandbox_mode = \"danger-full-access\"\n\n[features]\napps = false\n\n[model_providers.codexapi]\nname = \"CodexAPI.pro\"\nbase_url = \"https://api-canada.codexapi.pro/v1\"\nexperimental_bearer_token = \"<CODEXAPI_API_KEY>\"\nwire_api = \"responses\"\nsupports_websockets = false\n",
  "linuxSetupAndStartCommand": "set -e\nif ! command -v npm >/dev/null 2>&1; then\n  if command -v apt-get >/dev/null 2>&1; then\n    sudo apt-get update\n    sudo apt-get install -y ca-certificates curl gnupg\n    curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -\n    sudo apt-get install -y nodejs git\n  else\n    echo \"Install Node.js LTS and npm first, then rerun this block.\" >&2\n    exit 1\n  fi\nfi\nnpm install -g @openai/codex@0.147.0\nCODEX_HOME_DIR=\"$HOME/.codex\"\nCONFIG_PATH=\"$CODEX_HOME_DIR/config.toml\"\nmkdir -p \"$CODEX_HOME_DIR\" 2>/dev/null || true\nif [ -e \"$CODEX_HOME_DIR\" ] && [ ! -w \"$CODEX_HOME_DIR\" ]; then\n  echo \"Fixing ownership of $CODEX_HOME_DIR for this user...\"\n  if command -v sudo >/dev/null 2>&1; then sudo chown -R \"$(id -u):$(id -g)\" \"$CODEX_HOME_DIR\"; fi\nfi\nif [ -e \"$CONFIG_PATH\" ] && [ ! -w \"$CONFIG_PATH\" ]; then\n  echo \"Fixing ownership of $CONFIG_PATH for this user...\"\n  if command -v sudo >/dev/null 2>&1; then sudo chown \"$(id -u):$(id -g)\" \"$CONFIG_PATH\"; fi\nfi\nmkdir -p \"$CODEX_HOME_DIR\"\nif [ ! -w \"$CODEX_HOME_DIR\" ] || { [ -e \"$CONFIG_PATH\" ] && [ ! -w \"$CONFIG_PATH\" ]; }; then\n  echo \"Cannot write Codex config at $CONFIG_PATH. Run: sudo chown -R \\\"$(id -u):$(id -g)\\\" \\\"$CODEX_HOME_DIR\\\"\" >&2\n  exit 1\nfi\nrm -rf \"$CODEX_HOME_DIR/cache/codex_apps_tools\" 2>/dev/null || true\nCODEXAPI_BACKUP_TS=\"$(date +%Y%m%d%H%M%S)\"\nCODEXAPI_CONFIG_SNIPPET=\"$(mktemp)\"\ncat > \"$CODEXAPI_CONFIG_SNIPPET\" <<'TOML'\nmodel = \"gpt-5.6\"\nmodel_provider = \"codexapi\"\nmodel_reasoning_effort = \"xhigh\"\napproval_policy = \"never\"\nsandbox_mode = \"danger-full-access\"\n\n[features]\napps = false\n\n[model_providers.codexapi]\nname = \"CodexAPI.pro\"\nbase_url = \"https://api-canada.codexapi.pro/v1\"\nexperimental_bearer_token = \"<CODEXAPI_API_KEY>\"\nwire_api = \"responses\"\nsupports_websockets = false\nTOML\npython3 - \"$CONFIG_PATH\" \"$CODEXAPI_CONFIG_SNIPPET\" <<'PY'\nimport os, re, shutil, sys\nfrom pathlib import Path\n\npath = Path(sys.argv[1]).expanduser()\nsnippet = Path(sys.argv[2]).read_text()\npath.parent.mkdir(parents=True, exist_ok=True)\nif path.exists():\n    shutil.copy2(path, f\"{path}.backup.{os.environ.get('CODEXAPI_BACKUP_TS', 'manual')}\")\noriginal = path.read_text() if path.exists() else \"\"\ntop_keys = {\"model\", \"model_provider\", \"model_reasoning_effort\", \"approval_policy\", \"sandbox_mode\"}\nsection_re = re.compile(r\"^\\s*\\[[^\\]]+\\]\\s*$\")\nkey_re = re.compile(r\"^\\s*([A-Za-z0-9_-]+)\\s*=\")\nstale_mcp_re = re.compile(r\"^\\s*\\[mcp_servers\\.(codex_apps|memories|blender)(?:\\.|\\])\")\nsnippet_lines = snippet.strip().splitlines()\nfirst_section = next((i for i, line in enumerate(snippet_lines) if section_re.match(line.strip())), len(snippet_lines))\ntop = snippet_lines[:first_section]\nprovider = []\nfeature_apps = 'apps = false'\ni = first_section\nwhile i < len(snippet_lines):\n    line = snippet_lines[i]\n    if re.match(r\"^\\[model_providers\\.codexapi\\]\\s*$\", line.strip()):\n        provider = snippet_lines[i:]\n        break\n    i += 1\nclean = []\nskip_codexapi = False\nskip_stale_mcp = False\nin_section = False\nin_features = False\nfeatures_seen = False\nfor line in original.splitlines():\n    stripped = line.strip()\n    if stale_mcp_re.match(stripped):\n        skip_stale_mcp = True\n        in_features = False\n        continue\n    if skip_stale_mcp and section_re.match(stripped):\n        skip_stale_mcp = False\n    if skip_stale_mcp:\n        continue\n    if re.match(r\"^\\[model_providers\\.codexapi\\]\\s*$\", stripped):\n        skip_codexapi = True\n        in_features = False\n        continue\n    if skip_codexapi and section_re.match(stripped):\n        skip_codexapi = False\n    if skip_codexapi:\n        continue\n    if section_re.match(stripped):\n        in_section = True\n        in_features = stripped == '[features]'\n        features_seen = features_seen or in_features\n    if not in_section:\n        m = key_re.match(line)\n        if m and m.group(1) in top_keys:\n            continue\n    if in_features:\n        m = key_re.match(line)\n        if m and m.group(1) == 'apps':\n            continue\n    clean.append(line)\nparts = []\nparts.extend(top)\nparts.append('')\ninserted_feature = False\nfor line in clean:\n    parts.append(line)\n    if line.strip() == '[features]':\n        parts.append(feature_apps)\n        inserted_feature = True\nif not features_seen:\n    if parts and parts[-1] != '':\n        parts.append('')\n    parts.extend(['[features]', feature_apps])\nif provider:\n    if parts and parts[-1] != '':\n        parts.append('')\n    parts.extend(provider)\npath.write_text('\\n'.join(line for line in parts if line is not None).rstrip() + '\\n')\nPY\nrm -f \"$CODEXAPI_CONFIG_SNIPPET\"\necho \"CodexAPI.pro is configured. Future sessions can start with codex or codex resume --search.\"\ncodex --search",
  "macosSetupAndStartCommand": "set -e\nBREW_BIN=\"$(command -v brew 2>/dev/null || true)\"\nif [ -z \"$BREW_BIN\" ] && [ -x /opt/homebrew/bin/brew ]; then BREW_BIN=\"/opt/homebrew/bin/brew\"; fi\nif [ -z \"$BREW_BIN\" ] && [ -x /usr/local/bin/brew ]; then BREW_BIN=\"/usr/local/bin/brew\"; fi\nif [ -z \"$BREW_BIN\" ]; then\n  /bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"\n  if [ -x /opt/homebrew/bin/brew ]; then BREW_BIN=\"/opt/homebrew/bin/brew\"; fi\n  if [ -z \"$BREW_BIN\" ] && [ -x /usr/local/bin/brew ]; then BREW_BIN=\"/usr/local/bin/brew\"; fi\nfi\nif [ -z \"$BREW_BIN\" ]; then echo \"Homebrew installation finished but brew was not found. Open a new Terminal and rerun this setup.\" >&2; exit 1; fi\neval \"$(\"$BREW_BIN\" shellenv)\"\nbrew list node >/dev/null 2>&1 || brew install node\nbrew list git >/dev/null 2>&1 || brew install git\nhash -r 2>/dev/null || true\nif ! npm install -g @openai/codex@0.147.0; then\n  echo \"Global npm install failed. Retrying with a user-owned npm prefix at $HOME/.npm-global.\"\n  mkdir -p \"$HOME/.npm-global\"\n  npm config set prefix \"$HOME/.npm-global\"\n  export PATH=\"$HOME/.npm-global/bin:$PATH\"\n  npm install -g @openai/codex@0.147.0\nfi\nexport PATH=\"$(npm config get prefix)/bin:$PATH\"\nhash -r 2>/dev/null || true\nappend_codexapi_path() {\n  profile=\"$1\"\n  mkdir -p \"$(dirname \"$profile\")\"\n  touch \"$profile\"\n  if ! grep -q \"# BEGIN CODEXAPI_PRO_NPM_PATH\" \"$profile\" 2>/dev/null; then\n    cat >> \"$profile\" <<'ENV'\n# BEGIN CODEXAPI_PRO_NPM_PATH\nexport PATH=\"$HOME/.npm-global/bin:/opt/homebrew/bin:/usr/local/bin:$PATH\"\n# END CODEXAPI_PRO_NPM_PATH\nENV\n  fi\n}\nappend_codexapi_path \"$HOME/.zshrc\"\nappend_codexapi_path \"$HOME/.zprofile\"\nappend_codexapi_path \"$HOME/.bashrc\"\nappend_codexapi_path \"$HOME/.profile\"\ncodex --version\nCODEX_HOME_DIR=\"$HOME/.codex\"\nCONFIG_PATH=\"$CODEX_HOME_DIR/config.toml\"\nmkdir -p \"$CODEX_HOME_DIR\" 2>/dev/null || true\nif [ -e \"$CODEX_HOME_DIR\" ] && [ ! -w \"$CODEX_HOME_DIR\" ]; then\n  echo \"Fixing ownership of $CODEX_HOME_DIR for this user...\"\n  if command -v sudo >/dev/null 2>&1; then sudo chown -R \"$(id -u):$(id -g)\" \"$CODEX_HOME_DIR\"; fi\nfi\nif [ -e \"$CONFIG_PATH\" ] && [ ! -w \"$CONFIG_PATH\" ]; then\n  echo \"Fixing ownership of $CONFIG_PATH for this user...\"\n  if command -v sudo >/dev/null 2>&1; then sudo chown \"$(id -u):$(id -g)\" \"$CONFIG_PATH\"; fi\nfi\nmkdir -p \"$CODEX_HOME_DIR\"\nif [ ! -w \"$CODEX_HOME_DIR\" ] || { [ -e \"$CONFIG_PATH\" ] && [ ! -w \"$CONFIG_PATH\" ]; }; then\n  echo \"Cannot write Codex config at $CONFIG_PATH. Run: sudo chown -R \\\"$(id -u):$(id -g)\\\" \\\"$CODEX_HOME_DIR\\\"\" >&2\n  exit 1\nfi\nrm -rf \"$CODEX_HOME_DIR/cache/codex_apps_tools\" 2>/dev/null || true\nCODEXAPI_BACKUP_TS=\"$(date +%Y%m%d%H%M%S)\"\nCODEXAPI_CONFIG_SNIPPET=\"$(mktemp)\"\ncat > \"$CODEXAPI_CONFIG_SNIPPET\" <<'TOML'\nmodel = \"gpt-5.6\"\nmodel_provider = \"codexapi\"\nmodel_reasoning_effort = \"xhigh\"\napproval_policy = \"never\"\nsandbox_mode = \"danger-full-access\"\n\n[features]\napps = false\n\n[model_providers.codexapi]\nname = \"CodexAPI.pro\"\nbase_url = \"https://api-canada.codexapi.pro/v1\"\nexperimental_bearer_token = \"<CODEXAPI_API_KEY>\"\nwire_api = \"responses\"\nsupports_websockets = false\nTOML\npython3 - \"$CONFIG_PATH\" \"$CODEXAPI_CONFIG_SNIPPET\" <<'PY'\nimport os, re, shutil, sys\nfrom pathlib import Path\n\npath = Path(sys.argv[1]).expanduser()\nsnippet = Path(sys.argv[2]).read_text()\npath.parent.mkdir(parents=True, exist_ok=True)\nif path.exists():\n    shutil.copy2(path, f\"{path}.backup.{os.environ.get('CODEXAPI_BACKUP_TS', 'manual')}\")\noriginal = path.read_text() if path.exists() else \"\"\ntop_keys = {\"model\", \"model_provider\", \"model_reasoning_effort\", \"approval_policy\", \"sandbox_mode\"}\nsection_re = re.compile(r\"^\\s*\\[[^\\]]+\\]\\s*$\")\nkey_re = re.compile(r\"^\\s*([A-Za-z0-9_-]+)\\s*=\")\nstale_mcp_re = re.compile(r\"^\\s*\\[mcp_servers\\.(codex_apps|memories|blender)(?:\\.|\\])\")\nsnippet_lines = snippet.strip().splitlines()\nfirst_section = next((i for i, line in enumerate(snippet_lines) if section_re.match(line.strip())), len(snippet_lines))\ntop = snippet_lines[:first_section]\nprovider = []\nfeature_apps = 'apps = false'\ni = first_section\nwhile i < len(snippet_lines):\n    line = snippet_lines[i]\n    if re.match(r\"^\\[model_providers\\.codexapi\\]\\s*$\", line.strip()):\n        provider = snippet_lines[i:]\n        break\n    i += 1\nclean = []\nskip_codexapi = False\nskip_stale_mcp = False\nin_section = False\nin_features = False\nfeatures_seen = False\nfor line in original.splitlines():\n    stripped = line.strip()\n    if stale_mcp_re.match(stripped):\n        skip_stale_mcp = True\n        in_features = False\n        continue\n    if skip_stale_mcp and section_re.match(stripped):\n        skip_stale_mcp = False\n    if skip_stale_mcp:\n        continue\n    if re.match(r\"^\\[model_providers\\.codexapi\\]\\s*$\", stripped):\n        skip_codexapi = True\n        in_features = False\n        continue\n    if skip_codexapi and section_re.match(stripped):\n        skip_codexapi = False\n    if skip_codexapi:\n        continue\n    if section_re.match(stripped):\n        in_section = True\n        in_features = stripped == '[features]'\n        features_seen = features_seen or in_features\n    if not in_section:\n        m = key_re.match(line)\n        if m and m.group(1) in top_keys:\n            continue\n    if in_features:\n        m = key_re.match(line)\n        if m and m.group(1) == 'apps':\n            continue\n    clean.append(line)\nparts = []\nparts.extend(top)\nparts.append('')\ninserted_feature = False\nfor line in clean:\n    parts.append(line)\n    if line.strip() == '[features]':\n        parts.append(feature_apps)\n        inserted_feature = True\nif not features_seen:\n    if parts and parts[-1] != '':\n        parts.append('')\n    parts.extend(['[features]', feature_apps])\nif provider:\n    if parts and parts[-1] != '':\n        parts.append('')\n    parts.extend(provider)\npath.write_text('\\n'.join(line for line in parts if line is not None).rstrip() + '\\n')\nPY\nrm -f \"$CODEXAPI_CONFIG_SNIPPET\"\necho \"CodexAPI.pro is configured. Future macOS sessions can start with codex --search or codex resume --search.\"\ncodex --search",
  "futureStartCommand": "codex\ncodex --search",
  "startCommand": "set -e\nif ! command -v npm >/dev/null 2>&1; then\n  if command -v apt-get >/dev/null 2>&1; then\n    sudo apt-get update\n    sudo apt-get install -y ca-certificates curl gnupg\n    curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -\n    sudo apt-get install -y nodejs git\n  else\n    echo \"Install Node.js LTS and npm first, then rerun this block.\" >&2\n    exit 1\n  fi\nfi\nnpm install -g @openai/codex@0.147.0\nCODEX_HOME_DIR=\"$HOME/.codex\"\nCONFIG_PATH=\"$CODEX_HOME_DIR/config.toml\"\nmkdir -p \"$CODEX_HOME_DIR\" 2>/dev/null || true\nif [ -e \"$CODEX_HOME_DIR\" ] && [ ! -w \"$CODEX_HOME_DIR\" ]; then\n  echo \"Fixing ownership of $CODEX_HOME_DIR for this user...\"\n  if command -v sudo >/dev/null 2>&1; then sudo chown -R \"$(id -u):$(id -g)\" \"$CODEX_HOME_DIR\"; fi\nfi\nif [ -e \"$CONFIG_PATH\" ] && [ ! -w \"$CONFIG_PATH\" ]; then\n  echo \"Fixing ownership of $CONFIG_PATH for this user...\"\n  if command -v sudo >/dev/null 2>&1; then sudo chown \"$(id -u):$(id -g)\" \"$CONFIG_PATH\"; fi\nfi\nmkdir -p \"$CODEX_HOME_DIR\"\nif [ ! -w \"$CODEX_HOME_DIR\" ] || { [ -e \"$CONFIG_PATH\" ] && [ ! -w \"$CONFIG_PATH\" ]; }; then\n  echo \"Cannot write Codex config at $CONFIG_PATH. Run: sudo chown -R \\\"$(id -u):$(id -g)\\\" \\\"$CODEX_HOME_DIR\\\"\" >&2\n  exit 1\nfi\nrm -rf \"$CODEX_HOME_DIR/cache/codex_apps_tools\" 2>/dev/null || true\nCODEXAPI_BACKUP_TS=\"$(date +%Y%m%d%H%M%S)\"\nCODEXAPI_CONFIG_SNIPPET=\"$(mktemp)\"\ncat > \"$CODEXAPI_CONFIG_SNIPPET\" <<'TOML'\nmodel = \"gpt-5.6\"\nmodel_provider = \"codexapi\"\nmodel_reasoning_effort = \"xhigh\"\napproval_policy = \"never\"\nsandbox_mode = \"danger-full-access\"\n\n[features]\napps = false\n\n[model_providers.codexapi]\nname = \"CodexAPI.pro\"\nbase_url = \"https://api-canada.codexapi.pro/v1\"\nexperimental_bearer_token = \"<CODEXAPI_API_KEY>\"\nwire_api = \"responses\"\nsupports_websockets = false\nTOML\npython3 - \"$CONFIG_PATH\" \"$CODEXAPI_CONFIG_SNIPPET\" <<'PY'\nimport os, re, shutil, sys\nfrom pathlib import Path\n\npath = Path(sys.argv[1]).expanduser()\nsnippet = Path(sys.argv[2]).read_text()\npath.parent.mkdir(parents=True, exist_ok=True)\nif path.exists():\n    shutil.copy2(path, f\"{path}.backup.{os.environ.get('CODEXAPI_BACKUP_TS', 'manual')}\")\noriginal = path.read_text() if path.exists() else \"\"\ntop_keys = {\"model\", \"model_provider\", \"model_reasoning_effort\", \"approval_policy\", \"sandbox_mode\"}\nsection_re = re.compile(r\"^\\s*\\[[^\\]]+\\]\\s*$\")\nkey_re = re.compile(r\"^\\s*([A-Za-z0-9_-]+)\\s*=\")\nstale_mcp_re = re.compile(r\"^\\s*\\[mcp_servers\\.(codex_apps|memories|blender)(?:\\.|\\])\")\nsnippet_lines = snippet.strip().splitlines()\nfirst_section = next((i for i, line in enumerate(snippet_lines) if section_re.match(line.strip())), len(snippet_lines))\ntop = snippet_lines[:first_section]\nprovider = []\nfeature_apps = 'apps = false'\ni = first_section\nwhile i < len(snippet_lines):\n    line = snippet_lines[i]\n    if re.match(r\"^\\[model_providers\\.codexapi\\]\\s*$\", line.strip()):\n        provider = snippet_lines[i:]\n        break\n    i += 1\nclean = []\nskip_codexapi = False\nskip_stale_mcp = False\nin_section = False\nin_features = False\nfeatures_seen = False\nfor line in original.splitlines():\n    stripped = line.strip()\n    if stale_mcp_re.match(stripped):\n        skip_stale_mcp = True\n        in_features = False\n        continue\n    if skip_stale_mcp and section_re.match(stripped):\n        skip_stale_mcp = False\n    if skip_stale_mcp:\n        continue\n    if re.match(r\"^\\[model_providers\\.codexapi\\]\\s*$\", stripped):\n        skip_codexapi = True\n        in_features = False\n        continue\n    if skip_codexapi and section_re.match(stripped):\n        skip_codexapi = False\n    if skip_codexapi:\n        continue\n    if section_re.match(stripped):\n        in_section = True\n        in_features = stripped == '[features]'\n        features_seen = features_seen or in_features\n    if not in_section:\n        m = key_re.match(line)\n        if m and m.group(1) in top_keys:\n            continue\n    if in_features:\n        m = key_re.match(line)\n        if m and m.group(1) == 'apps':\n            continue\n    clean.append(line)\nparts = []\nparts.extend(top)\nparts.append('')\ninserted_feature = False\nfor line in clean:\n    parts.append(line)\n    if line.strip() == '[features]':\n        parts.append(feature_apps)\n        inserted_feature = True\nif not features_seen:\n    if parts and parts[-1] != '':\n        parts.append('')\n    parts.extend(['[features]', feature_apps])\nif provider:\n    if parts and parts[-1] != '':\n        parts.append('')\n    parts.extend(provider)\npath.write_text('\\n'.join(line for line in parts if line is not None).rstrip() + '\\n')\nPY\nrm -f \"$CODEXAPI_CONFIG_SNIPPET\"\necho \"CodexAPI.pro is configured. Future sessions can start with codex or codex resume --search.\"\ncodex --search",
  "execSmokeTestCommand": "codex --search exec --skip-git-repo-check \"Say hello from CodexAPI.pro.\"",
  "loggedInOverrideCommand": "set -e\nif ! command -v npm >/dev/null 2>&1; then\n  if command -v apt-get >/dev/null 2>&1; then\n    sudo apt-get update\n    sudo apt-get install -y ca-certificates curl gnupg\n    curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -\n    sudo apt-get install -y nodejs git\n  else\n    echo \"Install Node.js LTS and npm first, then rerun this block.\" >&2\n    exit 1\n  fi\nfi\nnpm install -g @openai/codex@0.147.0\nCODEX_HOME_DIR=\"$HOME/.codex\"\nCONFIG_PATH=\"$CODEX_HOME_DIR/config.toml\"\nmkdir -p \"$CODEX_HOME_DIR\" 2>/dev/null || true\nif [ -e \"$CODEX_HOME_DIR\" ] && [ ! -w \"$CODEX_HOME_DIR\" ]; then\n  echo \"Fixing ownership of $CODEX_HOME_DIR for this user...\"\n  if command -v sudo >/dev/null 2>&1; then sudo chown -R \"$(id -u):$(id -g)\" \"$CODEX_HOME_DIR\"; fi\nfi\nif [ -e \"$CONFIG_PATH\" ] && [ ! -w \"$CONFIG_PATH\" ]; then\n  echo \"Fixing ownership of $CONFIG_PATH for this user...\"\n  if command -v sudo >/dev/null 2>&1; then sudo chown \"$(id -u):$(id -g)\" \"$CONFIG_PATH\"; fi\nfi\nmkdir -p \"$CODEX_HOME_DIR\"\nif [ ! -w \"$CODEX_HOME_DIR\" ] || { [ -e \"$CONFIG_PATH\" ] && [ ! -w \"$CONFIG_PATH\" ]; }; then\n  echo \"Cannot write Codex config at $CONFIG_PATH. Run: sudo chown -R \\\"$(id -u):$(id -g)\\\" \\\"$CODEX_HOME_DIR\\\"\" >&2\n  exit 1\nfi\nrm -rf \"$CODEX_HOME_DIR/cache/codex_apps_tools\" 2>/dev/null || true\nCODEXAPI_BACKUP_TS=\"$(date +%Y%m%d%H%M%S)\"\nCODEXAPI_CONFIG_SNIPPET=\"$(mktemp)\"\ncat > \"$CODEXAPI_CONFIG_SNIPPET\" <<'TOML'\nmodel = \"gpt-5.6\"\nmodel_provider = \"codexapi\"\nmodel_reasoning_effort = \"xhigh\"\napproval_policy = \"never\"\nsandbox_mode = \"danger-full-access\"\n\n[features]\napps = false\n\n[model_providers.codexapi]\nname = \"CodexAPI.pro\"\nbase_url = \"https://api-canada.codexapi.pro/v1\"\nexperimental_bearer_token = \"<CODEXAPI_API_KEY>\"\nwire_api = \"responses\"\nsupports_websockets = false\nTOML\npython3 - \"$CONFIG_PATH\" \"$CODEXAPI_CONFIG_SNIPPET\" <<'PY'\nimport os, re, shutil, sys\nfrom pathlib import Path\n\npath = Path(sys.argv[1]).expanduser()\nsnippet = Path(sys.argv[2]).read_text()\npath.parent.mkdir(parents=True, exist_ok=True)\nif path.exists():\n    shutil.copy2(path, f\"{path}.backup.{os.environ.get('CODEXAPI_BACKUP_TS', 'manual')}\")\noriginal = path.read_text() if path.exists() else \"\"\ntop_keys = {\"model\", \"model_provider\", \"model_reasoning_effort\", \"approval_policy\", \"sandbox_mode\"}\nsection_re = re.compile(r\"^\\s*\\[[^\\]]+\\]\\s*$\")\nkey_re = re.compile(r\"^\\s*([A-Za-z0-9_-]+)\\s*=\")\nstale_mcp_re = re.compile(r\"^\\s*\\[mcp_servers\\.(codex_apps|memories|blender)(?:\\.|\\])\")\nsnippet_lines = snippet.strip().splitlines()\nfirst_section = next((i for i, line in enumerate(snippet_lines) if section_re.match(line.strip())), len(snippet_lines))\ntop = snippet_lines[:first_section]\nprovider = []\nfeature_apps = 'apps = false'\ni = first_section\nwhile i < len(snippet_lines):\n    line = snippet_lines[i]\n    if re.match(r\"^\\[model_providers\\.codexapi\\]\\s*$\", line.strip()):\n        provider = snippet_lines[i:]\n        break\n    i += 1\nclean = []\nskip_codexapi = False\nskip_stale_mcp = False\nin_section = False\nin_features = False\nfeatures_seen = False\nfor line in original.splitlines():\n    stripped = line.strip()\n    if stale_mcp_re.match(stripped):\n        skip_stale_mcp = True\n        in_features = False\n        continue\n    if skip_stale_mcp and section_re.match(stripped):\n        skip_stale_mcp = False\n    if skip_stale_mcp:\n        continue\n    if re.match(r\"^\\[model_providers\\.codexapi\\]\\s*$\", stripped):\n        skip_codexapi = True\n        in_features = False\n        continue\n    if skip_codexapi and section_re.match(stripped):\n        skip_codexapi = False\n    if skip_codexapi:\n        continue\n    if section_re.match(stripped):\n        in_section = True\n        in_features = stripped == '[features]'\n        features_seen = features_seen or in_features\n    if not in_section:\n        m = key_re.match(line)\n        if m and m.group(1) in top_keys:\n            continue\n    if in_features:\n        m = key_re.match(line)\n        if m and m.group(1) == 'apps':\n            continue\n    clean.append(line)\nparts = []\nparts.extend(top)\nparts.append('')\ninserted_feature = False\nfor line in clean:\n    parts.append(line)\n    if line.strip() == '[features]':\n        parts.append(feature_apps)\n        inserted_feature = True\nif not features_seen:\n    if parts and parts[-1] != '':\n        parts.append('')\n    parts.extend(['[features]', feature_apps])\nif provider:\n    if parts and parts[-1] != '':\n        parts.append('')\n    parts.extend(provider)\npath.write_text('\\n'.join(line for line in parts if line is not None).rstrip() + '\\n')\nPY\nrm -f \"$CODEXAPI_CONFIG_SNIPPET\"\necho \"CodexAPI.pro is configured. Future sessions can start with codex or codex resume --search.\"\ncodex --search",
  "loggedInOverrideExecSmokeTestCommand": "codex --search exec --skip-git-repo-check \"Say hello from CodexAPI.pro.\"",
  "resumeLastCommand": "codex resume --search\ncodex resume --last --search",
  "windowsDependencyInstallCommand": "# Run PowerShell as Administrator for this dependency step.\nwinget source update\nwinget install --id OpenJS.NodeJS.LTS -e --accept-package-agreements --accept-source-agreements\nwinget install --id Git.Git -e --accept-package-agreements --accept-source-agreements\nSet-ExecutionPolicy -Scope CurrentUser RemoteSigned -Force\n# Close PowerShell, open a new PowerShell window, then run the verification step.",
  "windowsVerifyDependenciesCommand": "node --version\nnpm --version\ngit --version",
  "windowsCodexInstallCommand": "npm install -g @openai/codex@0.147.0\ncodex --version",
  "windowsFullInstallCommand": "# Run PowerShell as Administrator for this dependency step.\nwinget source update\nwinget install --id OpenJS.NodeJS.LTS -e --accept-package-agreements --accept-source-agreements\nwinget install --id Git.Git -e --accept-package-agreements --accept-source-agreements\nSet-ExecutionPolicy -Scope CurrentUser RemoteSigned -Force\n# Close PowerShell, open a new PowerShell window, then run the verification step.\n\n# After reopening PowerShell:\nnode --version\nnpm --version\ngit --version\nnpm install -g @openai/codex@0.147.0\ncodex --version",
  "windowsSetupAndStartCommand": "$ErrorActionPreference = \"Stop\"\nwinget source update\nif (-not (Get-Command node -ErrorAction SilentlyContinue)) { winget install --id OpenJS.NodeJS.LTS -e --accept-package-agreements --accept-source-agreements }\nif (-not (Get-Command git -ErrorAction SilentlyContinue)) { winget install --id Git.Git -e --accept-package-agreements --accept-source-agreements }\nnpm install -g @openai/codex@0.147.0\n$CodexHome = \"$env:USERPROFILE\\.codex\"\n$ConfigPath = Join-Path $CodexHome \"config.toml\"\nNew-Item -ItemType Directory -Force -Path $CodexHome | Out-Null\n$ConfigProbe = Join-Path $CodexHome \".codexapi-write-test\"\ntry {\n  \"ok\" | Set-Content -Path $ConfigProbe -Encoding utf8 -Force\n  Remove-Item $ConfigProbe -Force\n} catch {\n  Write-Error \"Cannot write to $CodexHome. Open PowerShell as your normal Windows user, or fix folder ownership if this .codex folder was created by an Administrator shell.\"\n  throw\n}\nif (Test-Path $ConfigPath) {\n  Copy-Item $ConfigPath \"$ConfigPath.backup.$((Get-Date).ToString('yyyyMMddHHmmss'))\" -Force\n}\n$CodexAppsCache = Join-Path $CodexHome \"cache\\codex_apps_tools\"\nRemove-Item -Recurse -Force -ErrorAction SilentlyContinue $CodexAppsCache\n$CodexApiBackupStamp = (Get-Date).ToString('yyyyMMddHHmmss')\n$CodexApiConfigSnippet = @'\nmodel = \"gpt-5.6\"\nmodel_provider = \"codexapi\"\nmodel_reasoning_effort = \"xhigh\"\napproval_policy = \"never\"\nsandbox_mode = \"danger-full-access\"\n\n[features]\napps = false\n\n[model_providers.codexapi]\nname = \"CodexAPI.pro\"\nbase_url = \"https://api-canada.codexapi.pro/v1\"\nexperimental_bearer_token = \"<CODEXAPI_API_KEY>\"\nwire_api = \"responses\"\nsupports_websockets = false\n'@\nif (Test-Path $ConfigPath) { Copy-Item $ConfigPath \"$ConfigPath.backup.$CodexApiBackupStamp\" -Force }\n$ExistingConfig = if (Test-Path $ConfigPath) { Get-Content $ConfigPath -Raw } else { \"\" }\n$Lines = @()\n$SkipCodexApi = $false\n$SkipStaleMcp = $false\n$InSection = $false\n$InFeatures = $false\n$FeaturesSeen = $false\nforeach ($Line in ($ExistingConfig -split \"`r?`n\")) {\n  $Trimmed = $Line.Trim()\n  if ($Trimmed -match '^\\[mcp_servers\\.(codex_apps|memories|blender)(?:\\.|\\])') { $SkipStaleMcp = $true; $InFeatures = $false; continue }\n  if ($SkipStaleMcp -and $Trimmed -match '^\\[[^\\]]+\\]\\s*$') { $SkipStaleMcp = $false }\n  if ($SkipStaleMcp) { continue }\n  if ($Trimmed -match '^\\[model_providers\\.codexapi\\]\\s*$') { $SkipCodexApi = $true; $InFeatures = $false; continue }\n  if ($SkipCodexApi -and $Trimmed -match '^\\[[^\\]]+\\]\\s*$') { $SkipCodexApi = $false }\n  if ($SkipCodexApi) { continue }\n  if ($Trimmed -match '^\\[[^\\]]+\\]\\s*$') { $InSection = $true; $InFeatures = ($Trimmed -eq '[features]'); if ($InFeatures) { $FeaturesSeen = $true } }\n  if (-not $InSection -and $Trimmed -match '^(model|model_provider|model_reasoning_effort|approval_policy|sandbox_mode)\\s*=') { continue }\n  if ($InFeatures -and $Trimmed -match '^apps\\s*=') { continue }\n  if ($Line.Trim().Length -gt 0) { $Lines += $Line }\n}\n$SnippetLines = $CodexApiConfigSnippet.Trim() -split \"`r?`n\"\n$FirstSection = $SnippetLines.Count\nfor ($i = 0; $i -lt $SnippetLines.Count; $i++) { if ($SnippetLines[$i].Trim() -match \"^\\[[^\\]]+\\]\\s*$\") { $FirstSection = $i; break } }\n$Top = @($SnippetLines[0..($FirstSection - 1)]) | Where-Object { $_ -ne $null }\n$ProviderStart = -1\nfor ($i = 0; $i -lt $SnippetLines.Count; $i++) { if ($SnippetLines[$i].Trim() -match \"^\\[model_providers\\.codexapi\\]\\s*$\") { $ProviderStart = $i; break } }\n$Provider = if ($ProviderStart -ge 0) { @($SnippetLines[$ProviderStart..($SnippetLines.Count - 1)]) } else { @() }\n$Out = @()\n$Out += $Top\n$Out += ''\n$InsertedFeatures = $false\nforeach ($Line in $Lines) { $Out += $Line; if ($Line.Trim() -eq '[features]') { $Out += 'apps = false'; $InsertedFeatures = $true } }\nif (-not $FeaturesSeen) { $Out += ''; $Out += '[features]'; $Out += 'apps = false' }\n$Out += ''\n$Out += $Provider\n($Out -join \"`n\").Trim() + \"`n\" | Set-Content -Path $ConfigPath -Encoding UTF8\nWrite-Host \"CodexAPI.pro is configured. Future sessions can start with codex or codex resume --search.\"\ncodex --search",
  "windowsSetupCommand": "$ErrorActionPreference = \"Stop\"\nwinget source update\nif (-not (Get-Command node -ErrorAction SilentlyContinue)) { winget install --id OpenJS.NodeJS.LTS -e --accept-package-agreements --accept-source-agreements }\nif (-not (Get-Command git -ErrorAction SilentlyContinue)) { winget install --id Git.Git -e --accept-package-agreements --accept-source-agreements }\nnpm install -g @openai/codex@0.147.0\n$CodexHome = \"$env:USERPROFILE\\.codex\"\n$ConfigPath = Join-Path $CodexHome \"config.toml\"\nNew-Item -ItemType Directory -Force -Path $CodexHome | Out-Null\n$ConfigProbe = Join-Path $CodexHome \".codexapi-write-test\"\ntry {\n  \"ok\" | Set-Content -Path $ConfigProbe -Encoding utf8 -Force\n  Remove-Item $ConfigProbe -Force\n} catch {\n  Write-Error \"Cannot write to $CodexHome. Open PowerShell as your normal Windows user, or fix folder ownership if this .codex folder was created by an Administrator shell.\"\n  throw\n}\nif (Test-Path $ConfigPath) {\n  Copy-Item $ConfigPath \"$ConfigPath.backup.$((Get-Date).ToString('yyyyMMddHHmmss'))\" -Force\n}\n$CodexAppsCache = Join-Path $CodexHome \"cache\\codex_apps_tools\"\nRemove-Item -Recurse -Force -ErrorAction SilentlyContinue $CodexAppsCache\n$CodexApiBackupStamp = (Get-Date).ToString('yyyyMMddHHmmss')\n$CodexApiConfigSnippet = @'\nmodel = \"gpt-5.6\"\nmodel_provider = \"codexapi\"\nmodel_reasoning_effort = \"xhigh\"\napproval_policy = \"never\"\nsandbox_mode = \"danger-full-access\"\n\n[features]\napps = false\n\n[model_providers.codexapi]\nname = \"CodexAPI.pro\"\nbase_url = \"https://api-canada.codexapi.pro/v1\"\nexperimental_bearer_token = \"<CODEXAPI_API_KEY>\"\nwire_api = \"responses\"\nsupports_websockets = false\n'@\nif (Test-Path $ConfigPath) { Copy-Item $ConfigPath \"$ConfigPath.backup.$CodexApiBackupStamp\" -Force }\n$ExistingConfig = if (Test-Path $ConfigPath) { Get-Content $ConfigPath -Raw } else { \"\" }\n$Lines = @()\n$SkipCodexApi = $false\n$SkipStaleMcp = $false\n$InSection = $false\n$InFeatures = $false\n$FeaturesSeen = $false\nforeach ($Line in ($ExistingConfig -split \"`r?`n\")) {\n  $Trimmed = $Line.Trim()\n  if ($Trimmed -match '^\\[mcp_servers\\.(codex_apps|memories|blender)(?:\\.|\\])') { $SkipStaleMcp = $true; $InFeatures = $false; continue }\n  if ($SkipStaleMcp -and $Trimmed -match '^\\[[^\\]]+\\]\\s*$') { $SkipStaleMcp = $false }\n  if ($SkipStaleMcp) { continue }\n  if ($Trimmed -match '^\\[model_providers\\.codexapi\\]\\s*$') { $SkipCodexApi = $true; $InFeatures = $false; continue }\n  if ($SkipCodexApi -and $Trimmed -match '^\\[[^\\]]+\\]\\s*$') { $SkipCodexApi = $false }\n  if ($SkipCodexApi) { continue }\n  if ($Trimmed -match '^\\[[^\\]]+\\]\\s*$') { $InSection = $true; $InFeatures = ($Trimmed -eq '[features]'); if ($InFeatures) { $FeaturesSeen = $true } }\n  if (-not $InSection -and $Trimmed -match '^(model|model_provider|model_reasoning_effort|approval_policy|sandbox_mode)\\s*=') { continue }\n  if ($InFeatures -and $Trimmed -match '^apps\\s*=') { continue }\n  if ($Line.Trim().Length -gt 0) { $Lines += $Line }\n}\n$SnippetLines = $CodexApiConfigSnippet.Trim() -split \"`r?`n\"\n$FirstSection = $SnippetLines.Count\nfor ($i = 0; $i -lt $SnippetLines.Count; $i++) { if ($SnippetLines[$i].Trim() -match \"^\\[[^\\]]+\\]\\s*$\") { $FirstSection = $i; break } }\n$Top = @($SnippetLines[0..($FirstSection - 1)]) | Where-Object { $_ -ne $null }\n$ProviderStart = -1\nfor ($i = 0; $i -lt $SnippetLines.Count; $i++) { if ($SnippetLines[$i].Trim() -match \"^\\[model_providers\\.codexapi\\]\\s*$\") { $ProviderStart = $i; break } }\n$Provider = if ($ProviderStart -ge 0) { @($SnippetLines[$ProviderStart..($SnippetLines.Count - 1)]) } else { @() }\n$Out = @()\n$Out += $Top\n$Out += ''\n$InsertedFeatures = $false\nforeach ($Line in $Lines) { $Out += $Line; if ($Line.Trim() -eq '[features]') { $Out += 'apps = false'; $InsertedFeatures = $true } }\nif (-not $FeaturesSeen) { $Out += ''; $Out += '[features]'; $Out += 'apps = false' }\n$Out += ''\n$Out += $Provider\n($Out -join \"`n\").Trim() + \"`n\" | Set-Content -Path $ConfigPath -Encoding UTF8\nWrite-Host \"CodexAPI.pro is configured. Future sessions can start with codex or codex resume --search.\"\ncodex --search",
  "windowsStartCommand": "codex\ncodex --search",
  "windowsResumeLastCommand": "codex resume --search\ncodex resume --last --search",
  "windowsExecSmokeTestCommand": "codex --search exec --skip-git-repo-check \"Say hello from CodexAPI.pro.\"",
  "macosStartCommand": "codex\ncodex --search",
  "macosResumeLastCommand": "codex resume --search\ncodex resume --last --search",
  "macosExecSmokeTestCommand": "codex --search exec --skip-git-repo-check \"Say hello from CodexAPI.pro.\"",
  "configToml": "model = \"gpt-5.6\"\nmodel_provider = \"codexapi\"\nmodel_reasoning_effort = \"xhigh\"\napproval_policy = \"never\"\nsandbox_mode = \"danger-full-access\"\n\n[features]\napps = false\n\n[model_providers.codexapi]\nname = \"CodexAPI.pro\"\nbase_url = \"https://api-canada.codexapi.pro/v1\"\nenv_key = \"CODEXAPI_CODEX_API_KEY\"\nwire_api = \"responses\"\nsupports_websockets = false\n",
  "clientModelLabel": "Deepseek v4 Budget",
  "displayModel": "Deepseek v4 Budget",
  "modelDisplayName": "Deepseek v4 Budget"
}

Windows users must use PowerShell syntax, not Bash exports. The live /api/v1/cli/public-codex payload includes a complete PowerShell setup block that installs dependencies when needed, writes %USERPROFILE%\.codex\config.toml, saves the wallet username as the provider bearer token, and starts codex --search.

Managed Defaults

Codex 0.135.0 Notes

Primary Flow

Wallet Billing

Resume, Isolation, and Approvals

Each managed project_id maps to one stable workspace directory. When the same user returns to that project, the API resumes the latest Codex thread for that workspace instead of creating a fresh conversation.

Project-scoped approval handling stays explicit:

Customer API Calls

This catalogue is generated from the live FastAPI route table and filtered to customer-facing endpoints. Public calls need no bearer token, and API calls accept the System Token, customer username, or a user-created API key.

Method Path Access Tags Summary
GET/api/core/releases/currentapireleasesRead the current synced stable Codex release
GET/api/modelsapirpcList visible Codex models
POST/api/projects/openapithreadsOpen a project by cwd, resuming the latest matching thread when present
GET/api/status/regionspublicsystemRegional API availability
GET/api/threadsapithreadsList Codex threads
POST/api/threadsapithreadsStart a new thread
GET/api/threads/{thread_id}apithreadsRead a thread by id
POST/api/threads/{thread_id}/resumeapithreadsResume an existing thread
POST/api/turnsapiturnsStart a new turn on a thread
POST/api/turns/{turn_id}/interruptapiturnsInterrupt an active turn
GET/api/v1/agent/statusapiagentRead the managed Codex account state and default coding-agent configuration
GET/api/v1/auth/google/callbackpublicauthComplete Google sign-up or sign-in for a CodexAPI.pro wallet account
GET/api/v1/auth/google/startpublicauthStart Google sign-up or sign-in for a CodexAPI.pro wallet account
GET/api/v1/auth/human-checkpublicauthCreate a self-hosted signup human-verification challenge
POST/api/v1/auth/loginpublicauthAuthenticate a CodexAPI.pro CLI user and issue a wallet-linked API key
POST/api/v1/auth/password-reset/confirmpublicauthSet a new dashboard password from a reset link
POST/api/v1/auth/password-reset/requestpublicauthEmail a short-lived dashboard password reset link
GET/api/v1/auth/ssopublicauthOpen the customer dashboard from a signed notification link
POST/api/v1/auth/web-loginpublicauthAuthenticate a CodexAPI.pro dashboard user
POST/api/v1/auth/web-login/2fapublicauthComplete CodexAPI.pro dashboard login with a Google Authenticator code
GET/api/v1/billing/currency-estimatesapibillingRead estimated local-currency conversions while checkout remains USD
GET/api/v1/billing/token-usageapibillingRead token usage transactions and daily spend for the current wallet
POST/api/v1/cash-wallet/paymento/checkoutapibillingCreate a Paymento crypto checkout to load real-money Cash Wallet balance
POST/api/v1/cash-wallet/stripe/checkoutapibillingCreate a Stripe Checkout Session to load real-money Cash Wallet balance
POST/api/v1/cash-wallet/stripe/confirmapibillingConfirm a paid Stripe Checkout Session and reconcile Cash Wallet balance
POST/api/v1/cash-wallet/token-purchaseapibillingUse Cash Wallet balance to buy a Token Wallet top-up package
POST/api/v1/cash-wallet/unlimited-purchaseapibillingUse Cash Wallet balance to buy an Unlimited Coding plan
POST/api/v1/claude-desktop/object-workspaceapidesktopEnsure the current customer's private desktop object workspace
POST/api/v1/claude-desktop/object-workspace/projectsapidesktopCreate a private project folder in the current customer's desktop workspace
DELETE/api/v1/claude-desktop/object-workspace/projects/{project_id}apidesktopDelete the current customer's private desktop project marker
POST/api/v1/claude-desktop/object-workspace/repositories/{repository_name}apidesktopStore an authenticated customer's repository archive in their private desktop workspace
GET/api/v1/claude-desktop/pushapidesktopList persistent Claude Desktop service messages
POST/api/v1/claude-desktop/push/{message_key}/dismissapidesktopDismiss one Claude Desktop service message
GET/api/v1/claude-desktop/syncapidesktopRead synchronized Claude Desktop conversation history
PUT/api/v1/claude-desktop/syncapidesktopSynchronize Claude Desktop conversation history
GET/api/v1/claudecodex/versionpublicsystemRead the required CodexAPI.pro claudecodex launcher version
GET/api/v1/cli/versionpublicsystemRead the current CodexAPI.pro Codex CLI release metadata
GET/api/v1/codexclaude/versionpublicsystemRead the required CodexAPI.pro codexclaude launcher version
GET/api/v1/customer-engagementapidashboardRead promotion history, eligibility, and notification preferences
PUT/api/v1/customer-engagementapidashboardSave promotion, reminder, timezone, and motion preferences
POST/api/v1/customer/registerpublicauthCreate a CodexAPI.pro customer account and issue a wallet-linked API key
GET/api/v1/daily-checkinapibillingRead the current wallet's Daily Check-in reward status
POST/api/v1/daily-checkin/claimapibillingClaim today's Daily Check-in wallet reward
GET/api/v1/desktop-chat/bootstrapapidesktop-chatLoad CodexAPI.pro Desktop Chat conversations, models, and free daily quota
POST/api/v1/desktop-chat/conversationsapidesktop-chatCreate a CodexAPI.pro Desktop Chat conversation
GET/api/v1/desktop-chat/conversationsapidesktop-chatList CodexAPI.pro Desktop Chat conversations
DELETE/api/v1/desktop-chat/conversations/{conversation_id}apidesktop-chatDelete a CodexAPI.pro Desktop Chat conversation
GET/api/v1/desktop-chat/conversations/{conversation_id}/messagesapidesktop-chatRead CodexAPI.pro Desktop Chat conversation messages
POST/api/v1/desktop-chat/conversations/{conversation_id}/messagesapidesktop-chatSend a CodexAPI.pro Desktop Chat prompt with saved conversation context
GET/api/v1/desktop-chat/messages/{message_id}/research.pdfapidesktop-chatDownload a completed Desktop Chat Deep Research analysis as a PDF
GET/api/v1/desktop-chat/shoutbox/bootstrapapidesktop-chatLoad the shared CodexAPI.pro Desktop Chat Shoutbox
GET/api/v1/desktop-chat/shoutbox/messagesapidesktop-chatList shared CodexAPI.pro Desktop Chat Shoutbox messages
POST/api/v1/desktop-chat/shoutbox/messagesapidesktop-chatSend a shared CodexAPI.pro Desktop Chat Shoutbox message
POST/api/v1/desktop-chat/shoutbox/profileapidesktop-chatSet the user's shared Desktop Chat Shoutbox username
GET/api/v1/desktop/bootstrapapidesktopVerify a CodexAPI.pro token and return Windows desktop setup payloads
POST/api/v1/desktop/bug-reportapidesktopSend a customer desktop bug report to CodexAPI.pro support
GET/api/v1/desktop/coder/projectsapidesktopList Windows Desktop Coding Mode projects and managed databases
POST/api/v1/desktop/coder/projectsapidesktopCreate or resume a Windows Desktop Coding Mode project
POST/api/v1/desktop/coder/projects/purchase-slotapidesktopBuy one additional Windows Desktop Coding Mode project slot from Token Wallet credit
DELETE/api/v1/desktop/coder/projects/{project_name}apidesktopDelete a Windows Desktop Coding Mode project and its managed database
GET/api/v1/desktop/coding-sessionapidesktopLoad the saved CodexAPI.pro Desktop coding conversation history
PUT/api/v1/desktop/coding-sessionapidesktopPersist CodexAPI.pro Desktop coding conversation history
POST/api/v1/desktop/presenceapidesktopRecord a live Desktop Setup Client heartbeat
POST/api/v1/gumroad/registerpublicauthCreate a Gumroad.com buyer account and load $200 Token Wallet credit
GET/api/v1/meapiauthRead the current CodexAPI.pro customer wallet and CLI configuration
POST/api/v1/me/2fa/disableapiauthDisable Google Authenticator 2FA for the current dashboard user
POST/api/v1/me/2fa/enableapiauthEnable Google Authenticator 2FA after verifying the setup code
POST/api/v1/me/2fa/setupapiauthStart Google Authenticator 2FA setup for the current dashboard user
GET/api/v1/me/api-key-alertsapiauthList User API Key security and usage alerts
POST/api/v1/me/api-key-alerts/{event_id}/acknowledgeapiauthAcknowledge a User API Key alert
POST/api/v1/me/api-keysapiauthCreate a wallet-linked User API Key for the current customer
GET/api/v1/me/api-keysapiauthList the current customer's System Token and wallet-linked User API Keys
DELETE/api/v1/me/api-keys/{key_id}apiauthRevoke one of the current customer's User API Keys
PATCH/api/v1/me/api-keys/{key_id}apiauthUpdate a customer User API Key policy
GET/api/v1/me/api-keys/{key_id}/healthapiauthRun a non-billable User API Key integration health check
POST/api/v1/me/api-keys/{key_id}/replaceapiauthReplace a customer User API Key immediately
GET/api/v1/me/api-keys/{key_id}/usageapiauthRead usage for one customer User API Key
POST/api/v1/me/api-token/rotateapiauthRotate the current customer's Codex API username/token once every 24 hours
POST/api/v1/me/edge-regionapiauthSet the current customer's preferred London or Chicago API server
POST/api/v1/me/emailapiauthSave the required customer email address for the current CodexAPI.pro wallet
POST/api/v1/me/passwordapiauthChange the current CodexAPI.pro dashboard password
POST/api/v1/me/profile-nameapiauthSave the customer profile first name and surname
POST/api/v1/me/profile-name/dismissapiauthRecord that the customer skipped the profile name modal
POST/api/v1/me/sso-linkapiauthCreate a short-lived one-click dashboard sign-on link for the current customer
GET/api/v1/messagesapiauthList the current customer's inbox messages
POST/api/v1/messages/{message_id}/readapiauthMark a customer inbox message read
GET/api/v1/permissionsapiagentRead Codex permission profile presets for API clients
POST/api/v1/projectsapiprojectsCreate a managed project workspace for a web coding session
GET/api/v1/projectsapiprojectsList managed isolated project workspaces
GET/api/v1/projects/{project_id}apiprojectsRead managed project metadata and its latest Codex thread summary
GET/api/v1/projects/{project_id}/approvalsapiagentList pending approval requests for the latest managed project thread
POST/api/v1/projects/{project_id}/approvals/{request_id}/resolveapiagentResolve a pending project-scoped approval request
GET/api/v1/projects/{project_id}/eventsapiagentStream project-scoped Codex notifications over SSE
GET/api/v1/projects/{project_id}/files/contentapiprojectsRead a file from a managed project workspace
PUT/api/v1/projects/{project_id}/files/contentapiprojectsWrite a file inside a managed project workspace
GET/api/v1/projects/{project_id}/files/treeapiprojectsList files inside a managed project workspace
POST/api/v1/projects/{project_id}/openapiagentOpen or resume a managed project and optionally start an initial turn
GET/api/v1/projects/{project_id}/stateapiagentRead project metadata, latest thread state, and pending project-scoped approvals
GET/api/v1/projects/{project_id}/threadapiagentRead the latest Codex thread for a managed project
POST/api/v1/projects/{project_id}/turnsapiagentStart a project-scoped Codex turn using the web-coding defaults
POST/api/v1/projects/{project_id}/turns/{turn_id}/interruptapiagentInterrupt an active turn for a managed project
GET/api/v1/purchase-preferencesapibillingRead Deposit, low-balance, and business receipt preferences
PUT/api/v1/purchase-preferencesapibillingSave Deposit, low-balance, and business receipt preferences
GET/api/v1/raffleapibillingRead the current wallet's worldwide coding raffle status
POST/api/v1/reddit/conversionpublicanalyticsSend a server-side Reddit Conversions API website event
GET/api/v1/referralapibillingRead the current CodexAPI.pro VIP Referral Club and Cash Wallet status
POST/api/v1/setup/health-checkapiauthRun a non-billable account and setup readiness check
GET/api/v1/share-creditapibillingRead the one-time CodexAPI.pro share-credit offer for the current wallet
POST/api/v1/share-credit/claimapibillingClaim the one-time CodexAPI.pro share credit after sharing the public site
GET/api/v1/skillsapiagentList Codex skills available to API-backed Codex sessions
GET/api/v1/spin-wheelapibillingRead the current customer's Spin the Wheel promotion status
POST/api/v1/spin-wheel/spinapibillingUse the current customer's single Spin the Wheel entry
GET/api/v1/status/regionspublicsystemRegional API availability
POST/api/v1/stripe/webhookpublicbillingReceive Stripe wallet payment events
GET/api/v1/telegram/google/callbackpublicauthComplete Telegram Google account linking
GET/api/v1/telegram/google/startpublicauthStart Google sign-in for linking an existing CodexAPI.pro account to Telegram
POST/api/v1/telegram/webhookpublicauthReceive Telegram Bot API webhook updates for dashboard actions
GET/api/v1/toolboxapiauthRead the current customer Toolbox settings
POST/api/v1/toolboxapiauthUpdate the current customer Toolbox settings
GET/api/v1/unlimited-coding/plansapibillingList currently available Unlimited Coding plans
GET/api/v1/vippublicauthRead the current customer's VIP Priority server access
POST/api/v1/wallet/auto-topupapibillingConfigure automatic wallet top-up for the current CodexAPI.pro user
POST/api/v1/wallet/paymento/checkoutapibillingCreate a crypto checkout
GET/api/v1/wallet/paymento/optionsapibillingList crypto checkout options
GET/api/v1/wallet/paymento/orders/{order_id}apibillingCheck a crypto order
GET/api/v1/wallet/paymento/orders/{order_id}/receipt.pdfapibillingDownload a customer-owned crypto payment receipt
GET/api/v1/wallet/paymento/refund-addressesapibillingList saved refund addresses
POST/api/v1/wallet/paymento/refund-addressesapibillingSave a refund address
DELETE/api/v1/wallet/paymento/refund-addresses/{address_id}apibillingDelete a refund address
GET/api/v1/wallet/paymentsapibillingList Stripe wallet payments and receipt availability
GET/api/v1/wallet/statusapibillingRead a lightweight wallet balance snapshot for CLI launchers
POST/api/v1/wallet/stripe/checkoutapibillingCreate a Stripe Checkout Session for a CodexAPI.pro wallet credit pack
POST/api/v1/wallet/stripe/confirmapibillingConfirm a paid Stripe Checkout Session and reconcile wallet credit
POST/api/v1/wallet/top-upapibillingLoad credit onto the current CodexAPI.pro wallet after a plan payment
POST/api/v1/wallet/topup-discountapibillingValidate a user-scoped top-up bonus code
POST/api/v1/wallet/topupemail-session-discountapibillingIssue a time-boxed top-up email discount
POST/v1/chat/completionsapiopenai-compatibleCreate an OpenAI-compatible Chat Completion using the active CodexAPI.pro routing policy
GET/v1/chat/completionsapiopenai-compatibleList locally cached Chat Completions for this bearer
DELETE/v1/chat/completions/{completion_id}apiopenai-compatibleDelete a locally cached Chat Completion
GET/v1/chat/completions/{completion_id}apiopenai-compatibleRetrieve a locally cached Chat Completion
PATCH/v1/chat/completions/{completion_id}apiopenai-compatibleUpdate metadata for a locally cached Chat Completion
POST/v1/completionsapiopenai-compatibleCreate a legacy OpenAI-compatible text completion using the active Responses route
POST/v1/messagesapianthropic-compatibleProxy Claude Code CLI Messages API traffic through CodexAPI.pro wallet billing
POST/v1/messages/{subpath:path}apianthropic-compatibleProxy Claude Code CLI auxiliary Messages API traffic
GET/v1/modelsapiopenai-compatibleList CodexAPI.pro models for stock public Codex CLI
GET/v1/models/{model_id:path}apiopenai-compatibleRetrieve one CodexAPI.pro model descriptor
POST/v1/responsesapiopenai-compatibleProxy stock public Codex CLI Responses API traffic through CodexAPI.pro wallet billing
POST/v1/responses/count_tokensapiopenai-compatibleEstimate input tokens for an OpenAI-compatible Responses payload
DELETE/v1/responses/{response_id}apiopenai-compatibleDelete a locally cached OpenAI-compatible Responses object
GET/v1/responses/{response_id}apiopenai-compatibleRetrieve a locally cached OpenAI-compatible Responses object
POST/v1/responses/{response_id}/cancelapiopenai-compatibleCancel a Responses request if it is still pending
POST/v1/responses/{response_id}/compactapiopenai-compatibleReturn a compacted summary of a locally cached Responses object
GET/v1/responses/{response_id}/input_itemsapiopenai-compatibleList input items captured for a locally cached Responses object