Files
codex/sdk/python/docs/getting-started.md
Ahmed Ibrahim 1a4096e273 Add untrusted external messages to the Python SDK (#44086)
## Why

Applications need to deliver content from other agents, tools, or services with tool-level authority, without treating it as user input or granting authorization.

## What changed

- Export `ExternalMessage` for sync and async `run(...)` and `turn(...)`, accepting text or structured content with a tool name and optional namespace. Send it through `toolOutput` and require CLI 0.151.0 or newer.
- Support starting a turn or joining an active regular turn while preserving external content as function output in history. Keep external messages separate from user-input lists and `steer(...)`.
- Give turn handles independent subscriptions, replaying completed items and latest usage to joining handles. Release consumed transient events and clean up subscriptions on closure, failure, or cancellation.
- Document the authority boundary and add sync and async examples.

## Testing

Add coverage for wire representations, input validation, runtime compatibility, tool authority across resume, active-turn joins, and tool-output truncation. Add subscription tests for replay, concurrent consumers, early completion, cancellation, and cleanup.

GitOrigin-RevId: 6106327085fd9c4bd11b71e20b3d8e74738b8bb5
2026-09-09 06:54:18 +00:00

4.2 KiB

Getting Started

This guide gets a published OpenAI Codex Python SDK installation running with a multi-turn thread.

1. Install

Install the SDK:

pip install openai-codex

Requirements:

  • Python >=3.10
  • An existing Codex account session, or one of the login flows below

The SDK installs its matching openai-codex-cli-bin runtime dependency automatically. Stable SDK releases track the corresponding stable Codex CLI release.

2. Authenticate When Needed

Existing Codex authentication is reused automatically. For ChatGPT browser login:

from openai_codex import Codex

with Codex() as codex:
    login = codex.login_chatgpt()
    print(login.auth_url)
    print(login.wait().success)

For device-code login:

with Codex() as codex:
    login = codex.login_chatgpt_device_code()
    print(login.verification_url, login.user_code)
    print(login.wait().success)

For API-key login:

with Codex() as codex:
    codex.login_api_key("sk-...")
    print(codex.account().account)

3. Run A Turn

from openai_codex import Codex, Sandbox

with Codex() as codex:
    thread = codex.thread_start(sandbox=Sandbox.workspace_write)
    result = thread.run("Say hello in one sentence.")

    print("Thread:", thread.id)
    print("Text:", result.final_response)
    print("Items:", len(result.items))

Thread.run(...) starts a turn, waits for completion, and returns TurnResult. Plain strings are shorthand for TextInput(...).

Use Thread.turn(...) when you need a TurnHandle for streaming, steering, or interrupting an active turn.

For untrusted content from another agent, tool, or application, pass an ExternalMessage. It retains tool-level authority and does not establish user authorization or approval. Plain strings and TextInput represent user input.

4. Choose Sandbox Access

Use one enum for the initial thread and later turn overrides:

from openai_codex import Codex, Sandbox

with Codex() as codex:
    thread = codex.thread_start(sandbox=Sandbox.workspace_write)
    thread.run("Make the requested changes.")
    review = thread.run("Review the diff only.", sandbox=Sandbox.read_only)

Available presets:

  • Sandbox.read_only: read files without allowing writes.
  • Sandbox.workspace_write: read files and write inside the workspace and configured writable roots; this is the normal default for workspace work.
  • Sandbox.full_access: run without filesystem access restrictions.

When sandbox= is omitted, Codex uses its configured default. A turn override also applies to subsequent turns on that thread.

5. Continue A Thread

from openai_codex import Codex

with Codex() as codex:
    thread = codex.thread_start()
    thread.run("Summarize Rust ownership in two bullets.")
    result = thread.run("Now explain it to a Python developer.")
    print(result.final_response)

To resume a stored thread later:

with Codex() as codex:
    thread = codex.thread_resume("thr_123")
    print(thread.run("Continue where we left off.").final_response)

6. Use The Async Client

import asyncio

from openai_codex import AsyncCodex, Sandbox


async def main() -> None:
    async with AsyncCodex() as codex:
        thread = await codex.thread_start(sandbox=Sandbox.workspace_write)
        result = await thread.run("Continue where we left off.")
        print(result.final_response)


asyncio.run(main())

7. Get Help

Python's built-in documentation tools cover the curated SDK surface:

import openai_codex
from openai_codex import Codex, CodexConfig

help(openai_codex)
help(Codex)
help(CodexConfig)
python -m pydoc openai_codex

Developing From This Repository

Contributors working from a checkout can install development dependencies from the repository:

cd sdk/python
uv sync --group dev
source .venv/bin/activate

Next Stops