Contents
- 01Start From Zero and Choose the Right Surface
- 02Turn Intent Into Verifiable Work
- 03Configure Codex and Teach It the Project
- 04Bound Authority With Sandbox and Approvals
- 05Make the Change and Prove It
- 06Work Across Environments and Worktrees
- 07Delegate With Subagents
- 08Build the Extension Stack
- 09Automate, Embed, and Run Long Work Safely
- 10Capstone: From Command to Receipt
Automate, Embed, and Run Long Work Safely
Turn a trusted workflow into non-interactive, scheduled, SDK-driven, app-server, or long-running work with idempotence, checkpoints, bounded authority, and receipts for success and failure.
By the end
A repeatable `codex exec` job, a working SDK proof, an app-server protocol map, and an automation contract that avoids duplicate side effects and hands control back with evidence.
Earn the right to automate
Automation magnifies the quality of the underlying workflow. If the manual process has unclear inputs, unstable decisions, broad permissions, or inconsistent evidence, scheduling it will produce failures faster and with less visibility. Repeat the workflow under supervision until its boundaries and common exceptions are known.
Choose an automation candidate with a clear trigger, bounded data, deterministic prechecks, recoverable actions, and an operator who owns exceptions. Read-only monitoring, classification, and draft preparation are safer first candidates than publishing, deletion, payments, or external communication.
Use non-interactive mode deliberately
Non-interactive execution lets a script or workflow invoke Codex and receive output without an ongoing chat. That makes inputs, environment, exit behavior, and machine-readable results more important. Use the current CLI help and official non-interactive documentation for exact flags because command options can evolve.
Bound the task with an explicit working directory, authority profile, expected output, and timeout or stopping condition. Capture standard output, standard error, exit status, and produced artifacts. A paragraph that sounds successful is not enough for a machine-run workflow; require a result shape that downstream logic can validate.
Choose `exec`, the SDK, or app-server for the right integration
Use `codex exec` for shell scripts, CI steps, scheduled jobs, and pipelines. It streams progress to standard error and prints the final message to standard output. `--json` changes standard output to JSON Lines events; `--output-schema` validates the final response against a JSON Schema; `-o` writes the last message to a file. It starts read-only by default, and `--sandbox workspace-write` is the explicit step when a controlled job must edit.
Use `@openai/codex-sdk` in server-side Node.js when your application needs to start, continue, or resume Codex threads programmatically. Use `codex app-server` only for a rich client that needs the lower-level JSON-RPC interface for authentication, conversation history, approvals, and streamed events. App-server powers clients such as the IDE extension; ordinary CI automation should use the SDK or `codex exec` instead.
Understand app-server before building a client
The default app-server transport is newline-delimited JSON over standard input and output. A client starts the process, sends `initialize`, sends the `initialized` notification, then starts a thread and a turn while continuously reading notifications. Generate TypeScript or JSON Schema files from the installed CLI so the client matches that exact Codex version instead of copying protocol shapes from a blog post.
A local WebSocket listener exists for remote terminal and client experiments, but the current documentation marks that transport experimental and unsupported. Do not expose a non-loopback listener without the documented authentication and TLS protections. For this guide's lab, stay on the default stdio transport and generate schemas; that teaches the protocol boundary without creating a network service.
Schedule only a workflow that already has receipts
The desktop app can schedule a task after the prompt, project, cadence, and permission boundary are defined. In a Git project, scheduled tasks can use dedicated background worktrees so they do not collide with foreground changes; in a non-version-controlled project they run directly in the project directory. That difference should influence whether the task is safe to schedule.
A schedule is an operator-owned product. Define how to disable it, where results appear, what counts as unchanged, and which failures notify a human. Start with read-only drift checks. Do not schedule publishing, deletion, messaging, or production mutation until supervised runs demonstrate idempotence, recovery, and explicit authority for the side effect.
Design recurrence around state
A scheduled task runs again. That single fact changes the design. The workflow must know which inputs are new, which work is already complete, and what should happen after an interrupted run. Idempotence means repeating the same operation does not create an unintended duplicate effect.
Use stable identifiers, checkpoints, last-success markers, and explicit deduplication where side effects are possible. Do not mark work complete until the receipt is durably stored. If a run fails after partially acting, the next run should reconcile what happened rather than starting blindly from the beginning.
Manage long-running objectives
Some work is not a schedule but an objective that requires many steps, checkpoints, or external waits. Break it into milestones with acceptance criteria and store progress outside transient reasoning. The agent should be able to resume from the last proven state and explain what is complete, active, pending, or blocked.
Long duration does not broaden authority. A goal to finish a release still does not authorize publishing, buying services, changing permissions, or contacting people unless those actions were explicitly included. Reconfirm consequential steps when the external state may have changed since the goal began.
Operate with receipts and human handoffs
Every run should produce a compact receipt containing the trigger, input window, environment, actions, outputs, checks, cost or usage signal when available, and remaining exceptions. Unchanged state is a valid monitoring result, not a reason to invent work.
Define escalation before launch: which failure retries automatically, which pauses, which pages a human, and what evidence accompanies the alert. A human should be able to resume without reconstructing the agent's hidden context.
Working reference
Commands and patterns
Inspect non-interactive options
codex exec --help
Use the live CLI output for current flags, output formats, working-directory controls, and automation behavior.
Run a bounded one-off task
codex exec --ephemeral "Inspect this repository and report documentation links that point to missing local files. Do not edit anything."
A read-only documentation check is a safer first non-interactive workflow than a task with side effects. `--ephemeral` avoids persisting session rollout files for this one-off run.
Capture machine-readable events
codex exec --json --ephemeral "Summarize the repository structure and do not edit files." > codex-events.jsonl jq -s 'map(.type) | unique' codex-events.jsonl
JSONL includes thread, turn, item, error, and usage events. Validate events rather than parsing prose progress; review the output file before using it downstream.
Install the TypeScript SDK package
npm install @openai/codex-sdk
Run this shell command in a disposable Node.js 18+ project. Confirm the installed package version in the lockfile before relying on an SDK behavior in production.
File contents: run-codex.mjs
import { writeFile } from "node:fs/promises";
import { Codex } from "@openai/codex-sdk";
const codex = new Codex();
const readOnlyOptions = {
workingDirectory: process.cwd(),
sandboxMode: "read-only",
approvalPolicy: "never",
};
const thread = codex.startThread(readOnlyOptions);
const first = await thread.run(
"List this repository's test commands and cite the files that define them."
);
const threadId = thread.id;
if (!threadId) throw new Error("Codex did not return a thread ID.");
await writeFile("codex-thread-id.txt", `${threadId}\n`, "utf8");
const resumed = codex.resumeThread(threadId, readOnlyOptions);
const second = await resumed.run(
"Continue from the prior evidence and identify the smallest safe test to run first."
);
await writeFile(
"codex-sdk-receipt.json",
JSON.stringify(
{ threadId, first: first.finalResponse, second: second.finalResponse },
null,
2
) + "\n",
"utf8"
);
console.log({ threadId, first: first.finalResponse, second: second.finalResponse });Save this block exactly as `run-codex.mjs`. `sandboxMode` makes the spawned Codex agent read-only, while the supervising Node process intentionally writes the two local receipt files; host-process writes are outside the agent sandbox and should stay limited to named evidence artifacts. This example deliberately selects the older sandbox engine, so its loaded configs must contain no newer permission-profile settings.
Run the SDK resume proof
node run-codex.mjs
A successful run creates `codex-thread-id.txt` and `codex-sdk-receipt.json`, then prints the same thread ID and both final responses. The second turn must be created with `resumeThread(threadId, readOnlyOptions)` rather than a new thread.
Generate app-server schemas for the installed version
codex app-server generate-ts --out ./codex-app-server-schemas codex app-server generate-json-schema --out ./codex-app-server-json-schema
These generated contracts match the Codex version that produced them. Use app-server for a rich client; use the SDK for job automation.
When the happy path breaks
Failure modes
Symptom
A scheduled run creates duplicate records, messages, issues, or deployments.
Likely cause
The workflow has no stable input identity, checkpoint, deduplication, or reconciliation after partial success.
Recovery
Add idempotency keys or stable identifiers, persist receipts only after verification, and make the next run reconcile prior effects before acting.
Symptom
Automation reports success because Codex returned fluent text, but no required artifact exists.
Likely cause
The runner validated narrative output rather than an explicit result contract and evidence.
Recovery
Require a machine-checkable output shape, validate files or external receipts directly, and treat missing evidence as failure.
Symptom
A long-running task continues acting after requirements or external state changed.
Likely cause
The objective has milestones but no freshness checks or renewed approval before consequential steps.
Recovery
Re-read current source and external state at milestone boundaries, invalidate stale assumptions, and reconfirm authority before irreversible actions.
Symptom
A failed run retries forever or leaves no useful handoff.
Likely cause
Retry count, blocker classification, stop conditions, and operator receipt were not designed.
Recovery
Bound retries, distinguish transient from structural failures, persist the last proven checkpoint, and alert the owner with exact evidence and safe next action.
Hands-on lab
Automate a read-only drift check
Turn a supervised repository check into a safe non-interactive workflow, then design its scheduled and long-running behavior.
- 01Choose a read-only check such as broken local documentation links. Run it interactively twice and document inputs, expected output, false positives, and completion evidence.
- 02Run the `codex exec --json --ephemeral` example inside the Git repository. Inspect standard error separately from the JSONL file and confirm the event stream reaches `turn.completed` or exposes a failure.
- 03Create a small JSON Schema for fields `status`, `findings`, and `checked_paths`; rerun with `--output-schema <schema-file>` and `-o <result-file>`, then validate that file with an ordinary JSON parser.
- 04For the SDK exercise, deliberately choose its typed older-sandbox lane: in the disposable setup, confirm that no loaded config contains `default_permissions` or `[permissions]`. Keep `sandboxMode: "read-only"` and `approvalPolicy: "never"` in `readOnlyOptions`; do not rely on prompt prose for authority and do not add newer permission-profile keys.
- 05Run the separate install command, save the dedicated file-content block as `run-codex.mjs`, and then run `node run-codex.mjs`. Prove that the first turn populated `thread.id`, that `codex-thread-id.txt` contains that exact value, that `resumeThread(threadId, readOnlyOptions)` completed the second turn, and that `codex-sdk-receipt.json` contains both final responses under one thread identity.
- 06Explain the authority boundary in the receipt: the Codex agent stayed read-only because both turns received `readOnlyOptions`; the supervising Node process—not the agent—wrote only the two named local receipt files.
- 07Generate TypeScript and JSON schemas from `codex app-server`. Open the generated thread and turn types and write the required sequence: start process, initialize, initialized, thread start, turn start, notifications.
- 08Run the same read-only input twice and prove no duplicate side effects. Then simulate a missing dependency and verify failure receipt and human handoff.
- 09Design, but do not enable, a desktop scheduled task for this check. Name its Git project, cadence, permission profile, dedicated-worktree behavior, unchanged receipt, notification rule, and disable path.
- 10Write what additional controls would be required before allowing edits or external actions.
Deliverable: A repeatable read-only `codex exec` job with structured output, a two-turn SDK proof, generated app-server schemas and protocol map, plus success, unchanged, failure, and recovery receipts.
Reusable artifact
Automation operating contract
A specification for recurring or long-running Codex work that can be operated by someone other than its author.
Objective: [bounded recurring outcome] Trigger: [schedule, event, or manual start] Input window and identity: [what is new and how it is keyed] Environment and working directory: [exact target] Authority: [read / draft / write scopes] Prechecks: [conditions required before action] Result contract: [machine-checkable fields or artifacts] Idempotence and deduplication: [mechanism] Checkpoint and resume: [durable state] Retries and timeout: [bounded policy] Escalate when: [structural blocker or consequential action] Run receipt: [trigger, actions, checks, changes, exceptions] Owner, pause, and disable path: [human operator]
Before you move on
Receipt checklist
- The workflow was proven manually before it was automated.
- Non-interactive execution has an explicit environment, authority boundary, output contract, and stop condition.
- Repeating the same input produces no unintended duplicate side effect.
- Success, unchanged, and failure paths each produce an operator-readable receipt.
- A simulated blocker stops safely and hands off the last proven checkpoint and next action.
Primary sources
