API Reference
View SourceComplete API documentation for all modules in the Elixir Codex SDK.
Module Overview
Module | Purpose |
---|---|
Codex | Main entry point for starting and resuming threads |
Codex.Thread | Manages conversation threads and turn execution |
Codex.Exec | GenServer managing codex-rs process lifecycle |
Codex.Events | Event type definitions |
Codex.Items | Thread item type definitions |
Codex.Options | Configuration structs |
Codex.OutputSchemaFile | JSON schema file management |
Codex
Main entry point for the Codex SDK. Use this module to create new threads or resume existing ones.
Functions
start_thread/2
Creates a new conversation thread with the Codex agent.
Signature:
@spec start_thread(Codex.Options.t(), Codex.Thread.Options.t()) ::
{:ok, Codex.Thread.t()} | {:error, term()}
Parameters:
codex_opts
(optional): Global Codex options. Defaults to%Codex.Options{}
thread_opts
(optional): Thread-specific options. Defaults to%Codex.Thread.Options{}
Returns:
{:ok, thread}
: New thread struct ready for turn execution{:error, reason}
: Configuration error
Examples:
# Start with defaults
{:ok, thread} = Codex.start_thread()
# Start with custom API key
codex_opts = %Codex.Options{api_key: "sk-..."}
{:ok, thread} = Codex.start_thread(codex_opts)
# Start with thread options
thread_opts = %Codex.Thread.Options{
model: "o1",
sandbox_mode: :read_only,
working_directory: "/path/to/project"
}
{:ok, thread} = Codex.start_thread(%Codex.Options{}, thread_opts)
resume_thread/3
Resumes an existing conversation thread from its persisted session.
Signature:
@spec resume_thread(String.t(), Codex.Options.t(), Codex.Thread.Options.t()) ::
{:ok, Codex.Thread.t()} | {:error, term()}
Parameters:
thread_id
: ID of the thread to resume (from~/.codex/sessions
)codex_opts
(optional): Global Codex optionsthread_opts
(optional): Thread-specific options
Returns:
{:ok, thread}
: Thread struct with existing thread_id{:error, reason}
: Thread not found or configuration error
Examples:
# Resume with thread ID
{:ok, thread} = Codex.resume_thread("thread_abc123")
# Resume with custom options
codex_opts = %Codex.Options{base_url: "https://custom.api"}
{:ok, thread} = Codex.resume_thread("thread_abc123", codex_opts)
Notes:
- Threads are persisted in
~/.codex/sessions
by codex-rs - Thread history and context are automatically restored
- The thread_id is available after the first turn completes
Codex.Thread
Manages individual conversation threads and turn execution. Threads maintain state across multiple turns.
Type: t()
@type t() :: %Codex.Thread{
thread_id: String.t() | nil,
codex_opts: Codex.Options.t(),
thread_opts: Codex.Thread.Options.t()
}
Fields:
thread_id
: Unique thread identifier (nil until first turn completes)codex_opts
: Global Codex configurationthread_opts
: Thread-specific configuration
Functions
run/3
Executes a turn and returns the complete result (blocking mode).
Signature:
@spec run(t(), String.t(), Codex.Turn.Options.t()) ::
{:ok, Codex.Turn.Result.t()} | {:error, term()}
Parameters:
thread
: Thread struct fromCodex.start_thread/2
orCodex.resume_thread/3
input
: Prompt or instruction for the agentturn_opts
(optional): Turn-specific options. Defaults to%Codex.Turn.Options{}
Returns:
{:ok, result}
: Complete turn result with items, response, and usage{:error, {:turn_failed, error}}
: Agent encountered an error{:error, reason}
: Other error (process, configuration, etc.)
Examples:
# Basic usage
{:ok, thread} = Codex.start_thread()
{:ok, result} = Codex.Thread.run(thread, "Explain GenServers")
IO.puts(result.final_response)
# => "GenServers are..."
# With structured output
schema = %{
"type" => "object",
"properties" => %{
"summary" => %{"type" => "string"},
"key_points" => %{
"type" => "array",
"items" => %{"type" => "string"}
}
}
}
turn_opts = %Codex.Turn.Options{output_schema: schema}
{:ok, result} = Codex.Thread.run(thread, "Summarize GenServers", turn_opts)
{:ok, data} = Jason.decode(result.final_response)
IO.inspect(data["key_points"])
# Continue conversation
{:ok, result2} = Codex.Thread.run(thread, "Give me an example")
Behavior:
- Blocks until turn completes
- Accumulates all events internally
- Returns final result with all items
- Thread struct is updated with thread_id after first turn
- Subsequent calls use the same thread_id for context
run_streamed/3
Executes a turn and returns a stream of events (streaming mode).
Signature:
@spec run_streamed(t(), String.t(), Codex.Turn.Options.t()) ::
{:ok, Enumerable.t()} | {:error, term()}
Parameters:
thread
: Thread structinput
: Prompt or instructionturn_opts
(optional): Turn-specific options
Returns:
{:ok, stream}
: Enumerable stream of events{:error, reason}
: Configuration or process error
Examples:
# Basic streaming
{:ok, thread} = Codex.start_thread()
{:ok, stream} = Codex.Thread.run_streamed(thread, "Analyze this codebase")
for event <- stream do
case event do
%Codex.Events.ItemStarted{item: item} ->
IO.puts("Started: #{item.type}")
%Codex.Events.ItemCompleted{item: %{type: :agent_message, text: text}} ->
IO.puts("Response: #{text}")
%Codex.Events.ItemCompleted{item: %{type: :command_execution} = cmd} ->
IO.puts("Command: #{cmd.command} (exit: #{cmd.exit_code})")
%Codex.Events.TurnCompleted{usage: usage} ->
IO.puts("Tokens: #{usage.input_tokens + usage.output_tokens}")
_ ->
:ok
end
end
# Process first N events
{:ok, stream} = Codex.Thread.run_streamed(thread, "Generate 100 files")
first_10 = Enum.take(stream, 10)
# Filter specific events
{:ok, stream} = Codex.Thread.run_streamed(thread, "Fix bugs")
commands = stream
|> Stream.filter(fn
%Codex.Events.ItemCompleted{item: %{type: :command_execution}} -> true
_ -> false
end)
|> Enum.to_list()
Behavior:
- Returns immediately with stream
- Events yielded as they arrive from codex-rs
- Stream is lazy (events fetched on demand)
- Automatic cleanup when stream completes or is halted
- Thread struct must be updated with thread_id from
ThreadStarted
event
Codex.Exec
GenServer that manages the codex-rs
process lifecycle. This module is typically used internally by Codex.Thread
, but can be used directly for advanced use cases.
Functions
start_link/1
Starts an Exec GenServer and spawns the codex-rs process.
Signature:
@spec start_link(keyword()) :: GenServer.on_start()
Options:
:input
(required): Input prompt for the turn:codex_path
(optional): Path to codex binary:thread_id
(optional): Existing thread ID to resume:base_url
(optional): OpenAI API base URL:api_key
(optional): OpenAI API key:model
(optional): Model name:sandbox_mode
(optional): Sandbox mode (:read_only
,:workspace_write
,:danger_full_access
):working_directory
(optional): Working directory for agent:skip_git_repo_check
(optional): Skip Git repository check:output_schema_file
(optional): Path to JSON schema file
Returns:
{:ok, pid}
: GenServer process ID{:error, reason}
: Spawn or configuration error
Examples:
# Basic usage
{:ok, pid} = Codex.Exec.start_link(
input: "Hello, Codex",
codex_path: "/usr/local/bin/codex"
)
# With all options
{:ok, pid} = Codex.Exec.start_link(
input: "Analyze code",
codex_path: "/usr/local/bin/codex",
thread_id: "thread_abc123",
api_key: "sk-...",
model: "o1",
sandbox_mode: :read_only,
working_directory: "/path/to/project",
output_schema_file: "/tmp/schema.json"
)
run_turn/2
Starts turn execution and returns a reference for event tracking.
Signature:
@spec run_turn(pid()) :: reference()
Parameters:
pid
: Exec GenServer process ID
Returns:
reference()
: Unique reference for this turn
Usage:
{:ok, pid} = Codex.Exec.start_link(input: "test")
ref = Codex.Exec.run_turn(pid)
# Receive events
receive do
{:event, ^ref, event} ->
IO.inspect(event)
{:done, ^ref} ->
IO.puts("Turn complete")
{:error, ^ref, error} ->
IO.puts("Error: #{inspect(error)}")
end
Codex.Events
Event type definitions for all events emitted during turn execution.
Event Types
ThreadStarted
Emitted when a new thread is started.
Type:
@type t() :: %Codex.Events.ThreadStarted{
type: :thread_started,
thread_id: String.t()
}
Fields:
type
: Always:thread_started
thread_id
: Unique identifier for the thread (format:"thread_*"
)
Example:
%Codex.Events.ThreadStarted{
type: :thread_started,
thread_id: "thread_abc123xyz"
}
TurnStarted
Emitted when a turn begins processing.
Type:
@type t() :: %Codex.Events.TurnStarted{
type: :turn_started
}
TurnCompleted
Emitted when a turn completes successfully.
Type:
@type t() :: %Codex.Events.TurnCompleted{
type: :turn_completed,
usage: Codex.Events.Usage.t()
}
Fields:
type
: Always:turn_completed
usage
: Token usage statistics
Usage Type:
@type usage() :: %Codex.Events.Usage{
input_tokens: non_neg_integer(),
cached_input_tokens: non_neg_integer(),
output_tokens: non_neg_integer()
}
Example:
%Codex.Events.TurnCompleted{
type: :turn_completed,
usage: %Codex.Events.Usage{
input_tokens: 1500,
cached_input_tokens: 500,
output_tokens: 800
}
}
TurnFailed
Emitted when a turn fails with an error.
Type:
@type t() :: %Codex.Events.TurnFailed{
type: :turn_failed,
error: Codex.Events.ThreadError.t()
}
Error Type:
@type thread_error() :: %Codex.Events.ThreadError{
message: String.t()
}
ItemStarted
Emitted when a new item is added to the thread.
Type:
@type t() :: %Codex.Events.ItemStarted{
type: :item_started,
item: Codex.Items.t()
}
ItemUpdated
Emitted when an item's state changes.
Type:
@type t() :: %Codex.Events.ItemUpdated{
type: :item_updated,
item: Codex.Items.t()
}
ItemCompleted
Emitted when an item reaches a terminal state.
Type:
@type t() :: %Codex.Events.ItemCompleted{
type: :item_completed,
item: Codex.Items.t()
}
Codex.Items
Thread item type definitions representing different actions and artifacts.
Item Types
AgentMessage
Text or JSON response from the agent.
Type:
@type t() :: %Codex.Items.AgentMessage{
id: String.t(),
type: :agent_message,
text: String.t()
}
Fields:
id
: Unique item identifiertype
: Always:agent_message
text
: Response text (natural language or JSON when using output schema)
Example:
%Codex.Items.AgentMessage{
id: "msg_abc123",
type: :agent_message,
text: "GenServers are process abstractions in Elixir..."
}
Reasoning
Agent's reasoning summary.
Type:
@type t() :: %Codex.Items.Reasoning{
id: String.t(),
type: :reasoning,
text: String.t()
}
Example:
%Codex.Items.Reasoning{
id: "reasoning_1",
type: :reasoning,
text: "To fix this issue, I need to first understand the error, then locate the relevant code..."
}
CommandExecution
Shell command executed by the agent.
Type:
@type t() :: %Codex.Items.CommandExecution{
id: String.t(),
type: :command_execution,
command: String.t(),
aggregated_output: String.t(),
exit_code: integer() | nil,
status: :in_progress | :completed | :failed
}
Fields:
command
: Command line that was executedaggregated_output
: Combined stdout and stderrexit_code
: Exit status (nil while running, integer when complete)status
: Current status of execution
Example:
%Codex.Items.CommandExecution{
id: "cmd_1",
type: :command_execution,
command: "mix test",
aggregated_output: "...\n42 tests, 0 failures\n",
exit_code: 0,
status: :completed
}
FileChange
File modifications made by the agent.
Type:
@type t() :: %Codex.Items.FileChange{
id: String.t(),
type: :file_change,
changes: [file_update_change()],
status: :completed | :failed
}
Change Type:
@type file_update_change() :: %{
path: String.t(),
kind: :add | :delete | :update
}
Example:
%Codex.Items.FileChange{
id: "patch_1",
type: :file_change,
changes: [
%{path: "lib/my_app.ex", kind: :update},
%{path: "lib/new_module.ex", kind: :add}
],
status: :completed
}
McpToolCall
Model Context Protocol tool invocation.
Type:
@type t() :: %Codex.Items.McpToolCall{
id: String.t(),
type: :mcp_tool_call,
server: String.t(),
tool: String.t(),
status: :in_progress | :completed | :failed
}
Example:
%Codex.Items.McpToolCall{
id: "mcp_1",
type: :mcp_tool_call,
server: "database_server",
tool: "query_records",
status: :completed
}
WebSearch
Web search query and results.
Type:
@type t() :: %Codex.Items.WebSearch{
id: String.t(),
type: :web_search,
query: String.t()
}
TodoList
Agent's running task list.
Type:
@type t() :: %Codex.Items.TodoList{
id: String.t(),
type: :todo_list,
items: [todo_item()]
}
Todo Item Type:
@type todo_item() :: %{
text: String.t(),
completed: boolean()
}
Example:
%Codex.Items.TodoList{
id: "todo_1",
type: :todo_list,
items: [
%{text: "Analyze codebase", completed: true},
%{text: "Identify issues", completed: true},
%{text: "Propose fixes", completed: false}
]
}
Error
Non-fatal error item.
Type:
@type t() :: %Codex.Items.Error{
id: String.t(),
type: :error,
message: String.t()
}
Codex.Options
Configuration structs for different levels of the SDK.
Codex.Options
Global Codex configuration.
Type:
@type t() :: %Codex.Options{
codex_path_override: String.t() | nil,
base_url: String.t() | nil,
api_key: String.t() | nil
}
Fields:
codex_path_override
: Custom path to codex binary (defaults to system PATH)base_url
: OpenAI API base URL (defaults to official URL)api_key
: OpenAI API key (overrides environment variable)
Example:
%Codex.Options{
codex_path_override: "/custom/path/to/codex",
base_url: "https://api.openai.com",
api_key: System.get_env("OPENAI_API_KEY")
}
Codex.Thread.Options
Thread-specific configuration.
Type:
@type t() :: %Codex.Thread.Options{
model: String.t() | nil,
sandbox_mode: sandbox_mode() | nil,
working_directory: String.t() | nil,
skip_git_repo_check: boolean()
}
Sandbox Modes:
:read_only
: Agent can read files but not modify them:workspace_write
: Agent can write within working directory:danger_full_access
: Agent has unrestricted filesystem access
Fields:
model
: Model name (e.g., "o1", "gpt-4")sandbox_mode
: File access restrictionsworking_directory
: Working directory for agent operationsskip_git_repo_check
: Skip Git repository check (default: false)
Example:
%Codex.Thread.Options{
model: "o1",
sandbox_mode: :read_only,
working_directory: "/home/user/project",
skip_git_repo_check: false
}
Codex.Turn.Options
Turn-specific configuration.
Type:
@type t() :: %Codex.Turn.Options{
output_schema: map() | nil
}
Fields:
output_schema
: JSON schema for structured output (nil for natural language)
Example:
%Codex.Turn.Options{
output_schema: %{
"type" => "object",
"properties" => %{
"summary" => %{"type" => "string"},
"status" => %{"type" => "string", "enum" => ["ok", "error"]}
},
"required" => ["summary", "status"]
}
}
Codex.OutputSchemaFile
Utility for managing JSON schema temporary files.
Functions
create/1
Creates a temporary file with the JSON schema.
Signature:
@spec create(map() | nil) :: {:ok, {String.t() | nil, function()}} | {:error, term()}
Parameters:
schema
: JSON schema map or nil
Returns:
{:ok, {path, cleanup}}
: Path to temp file and cleanup function{:error, reason}
: Error creating file
Examples:
# With schema
schema = %{"type" => "object", "properties" => %{}}
{:ok, {path, cleanup}} = Codex.OutputSchemaFile.create(schema)
# Use path...
# Clean up
cleanup.()
# Without schema
{:ok, {nil, cleanup}} = Codex.OutputSchemaFile.create(nil)
cleanup.() # No-op
Notes:
- Creates temp directory in system tmp folder
- Writes JSON to
schema.json
in that directory - Cleanup function removes entire directory
- Cleanup is idempotent (safe to call multiple times)
- Used internally by
Codex.Thread
Type Aliases
Codex.Turn.Result
Result of a completed turn (from run/3
).
Type:
@type t() :: %Codex.Turn.Result{
items: [Codex.Items.t()],
final_response: String.t(),
usage: Codex.Events.Usage.t() | nil
}
Fields:
items
: All items produced during the turnfinal_response
: Final agent message text (lastAgentMessage
item)usage
: Token usage statistics (nil if turn failed before completion)
Common Patterns
Error Handling
case Codex.Thread.run(thread, input) do
{:ok, result} ->
process_result(result)
{:error, {:turn_failed, error}} ->
Logger.error("Turn failed: #{error.message}")
{:error, :turn_failed}
{:error, {:process, reason}} ->
Logger.error("Process error: #{inspect(reason)}")
{:error, :process_error}
{:error, reason} ->
Logger.error("Unknown error: #{inspect(reason)}")
{:error, :unknown}
end
Streaming with Pattern Matching
{:ok, stream} = Codex.Thread.run_streamed(thread, input)
Enum.reduce(stream, %{commands: [], files: []}, fn
%ItemCompleted{item: %{type: :command_execution} = cmd}, acc ->
%{acc | commands: [cmd | acc.commands]}
%ItemCompleted{item: %{type: :file_change} = file}, acc ->
%{acc | files: [file | acc.files]}
_, acc ->
acc
end)
Structured Output with Validation
schema = %{
"type" => "object",
"properties" => %{
"status" => %{"type" => "string", "enum" => ["success", "failure"]},
"data" => %{"type" => "object"}
},
"required" => ["status"]
}
turn_opts = %Codex.Turn.Options{output_schema: schema}
{:ok, result} = Codex.Thread.run(thread, "Check system status", turn_opts)
case Jason.decode(result.final_response) do
{:ok, %{"status" => "success", "data" => data}} ->
process_data(data)
{:ok, %{"status" => "failure"}} ->
handle_failure()
{:error, _} ->
{:error, :invalid_json}
end
Migration from TypeScript SDK
For developers familiar with the TypeScript SDK:
TypeScript | Elixir |
---|---|
new Codex() | Codex module (no instance needed) |
codex.startThread() | Codex.start_thread() |
codex.resumeThread(id) | Codex.resume_thread(id) |
await thread.run(input) | Codex.Thread.run(thread, input) |
await thread.runStreamed(input) | Codex.Thread.run_streamed(thread, input) |
for await (const event of events) | Enum.each(stream, fn event -> ... end) |
CodexOptions | %Codex.Options{} |
ThreadOptions | %Codex.Thread.Options{} |
TurnOptions | %Codex.Turn.Options{} |