Codex CLI JSON Output: Events and Structured Results
The Codex CLI can stream its work as JSON events — one object per line. Here is what each event type contains, how to parse them, and what to store for a useful audit trail.
Updated 4 min read
Passing --json to Codex CLI switches it from a human-readable terminal display to a stream of JSON objects, one per line. Each object describes one event in the session: a message from the model, a file the CLI proposes to write, a shell command it wants to run, or the final result. This format is designed for scripting, logging, and piping the output into other tools without parsing terminal color codes or progress indicators.
The raw event stream captures everything the CLI did, but that is often more than you need later. A tool like fenbs is a better home for the outcomes that matter — the tasks that were completed, the decisions that were confirmed, and the next step — separate from the raw execution log.
Enable JSON output
Add --json to any Codex CLI invocation:
codex --json "list all exported functions in src/api.ts"
Each event appears as a single JSON line as it happens, not at the end of the session. The stream ends when the session exits. You can redirect it to a file or pipe it to another process:
codex --json "add JSDoc comments to src/utils.ts" > session.jsonl
Event types in the stream
Each event object has a type field that identifies what it represents. The main event types you will encounter are:
message: a chunk of text from the model, with acontentfield. These arrive incrementally as the model streams its response.file_write: a proposed file change, withpathandcontent. Insuggestmode, this event arrives before the user approves it.shell_command: a shell command the CLI wants to run, with acommandfield.approval_request: sent when the CLI is waiting for the user to approve or reject a step.result: the final summary at the end of a session, with asummaryfield and anexit_code.error: an unexpected failure, with amessagefield and sometimes acode.
The exact schema can change across releases. For the authoritative event list, see the OpenAI Codex repository (opens in a new tab) and its changelog.
Not every event type appears in every session. A session that only reads files and produces a text answer will not emit file_write or shell_command events. A session that requests approval will include approval_request events at each decision point. The mix of events reflects the structure of the task and the approval mode in use.
Parse the stream with jq
Because each event is a complete JSON object on its own line, jq can filter and transform the stream in real time:
codex --json "summarize the README" | jq 'select(.type == "message") | .content'
codex --json "add tests" | jq 'select(.type == "file_write") | .path'
codex --json "fix the linting errors" | jq 'select(.type == "result")'
These patterns also work on a saved .jsonl file after the session. Replace the pipe from codex with a cat of the file.
Handle error events
Error events appear when something the CLI tried failed — an API error, a shell command that exited with a non-zero code, or a file it could not read. The error object includes a message field and sometimes a code:
cat session.jsonl | jq 'select(.type == "error")'
When an error event arrives mid-session, the CLI may continue or may stop, depending on whether the error was recoverable. Check the exit_code in the final result event to confirm whether the session completed successfully. An exit code of 0 means the session ended without a fatal error. Any non-zero code is worth investigating before acting on the session's output, since the model may have stopped partway through the task.
What to keep and what to discard
A complete event log captures everything, but most of it is intermediate reasoning. For a useful audit trail, keep the result events, any error events, and the file_write events for changes that were approved. Streaming model tokens (message events) are rarely worth storing at full length.
For a project-level record, the most useful things to save are not events at all: they are the completed tasks, the decisions that were confirmed during the session, and the next step. Those belong in a project record, not in a raw execution log. The codex cli commands guide covers the full flag reference.
Consider writing a small script that processes the .jsonl log after each session: count the file_write events, check the final exit_code, and write a one-line summary. That summary is far more useful in a team log than the full event stream, and it takes seconds to scan. Store those summaries in a session-log/ directory alongside the project so the team can see what changed across sessions without wading through raw JSON.
Keep outcomes, not raw logs
fenbs records what was done, by whom, and when, as a clean History attached to tasks and decisions. Ask Codex CLI to save the task outcome and any confirmed decisions to fenbs at the end of each session. The session's JSON log is for debugging; fenbs is where the useful record lives. Create a free account and connect Codex CLI from Integrations.
Create a free accountFree for one person · No credit card · Works with Claude, ChatGPT, Cursor and more
How to do this with fenbs
- Create a free account and add your project with the tasks Codex CLI is working on.
- Connect Codex CLI to fenbs over MCP from Integrations. Sign in with a browser when prompted.
- Run sessions with
--jsonand save the.jsonlfile locally for debugging purposes. - At the end of each session, ask Codex CLI to update the relevant tasks in fenbs: what was completed, what was checked, and what comes next.
- Review the task History in fenbs for a readable record of what changed across sessions.
The agent harness topic hub has more guides on scripting and logging AI coding agents, including the codex-app-vs-cli-vs-ide-extension comparison for choosing which interface fits your workflow.