The Codex API in Practice: Automating Routine Engineering Work
By Sebastiaan Jansen · · 8 min read
The codex api sends a bounded coding task to OpenAI Codex and returns a patch, a plan, or a pull request. Use it when "done" is defined: a failing test, a dependency bump, a changelog, a small refactor with a spec. Product calls, security-sensitive changes, and fuzzy "done" stay with a person.
What the Codex API should own
Codex reads a repository, runs allowed commands, edits files, and can open a pull request. The Codex API puts that agent behind a script, a CI job, or a ticket worker. Shapes and limits are in the OpenAI API documentation. Treat samples as a pattern, not a contract.
A task needs three parts:
- Scope: repo, ref, paths, and commands.
- Done: a machine check, such as tests, a clean linter, or the ticket's acceptance lines.
- Stop: no unnamed dependencies, no edits outside a directory, no force-push, no merge.
If those three don't fit in a short brief, the task isn't ready. Chat is for exploring. The call is for a teammate who never saw the thread.
Tasks that fit
- A localized test failure. Send the log, the test file, and the module under test.
- A dependency patch. Bump one pin, run the suite, write a short risk note.
- A ticket with acceptance criteria. Implement those lines and list what was left alone.
- Release notes from merged pull requests. A person publishes the draft.
- Drifted lint or types. Fix the mechanical diff and stop.
Tasks to keep with a person
- A bug with no reproduction.
- A redesign, a new public interface, or a migration with more than one sensible design.
- Auth, billing, permissions, cryptography, or secrets.
- A green suite you hope will "also clean this up while you are there."
Run these with a person in the session, not on a schedule.
The unread diff
The run that fails you is green and wide. "Make InvoiceRetryTest pass" becomes an unused-import cleanup in billing.ts, a queue-consumer type "fix," and a helper bump. Tests pass, the diff runs to hundreds of lines, and the reviewer rewrites three of them by hand.
That is a worker bug. The allowlist should reject billing.ts. A note in the prompt still lets the extra files ship, because a prompt sentence can't delete a file from a diff.
Write the brief the way you write a ticket
The model follows the brief, gaps included.
Repo: payments-service @ origin/main
Goal: Make InvoiceRetryTest pass.
Constraint: Edit only src/retry.ts and src/retry.test.ts.
Do not add dependencies. Do not change public function signatures.
Done when: pnpm test src/retry.test.ts exits 0, and pnpm typecheck exits 0.
Report: what failed, what you changed, and any case you did not cover.
Stop and return a note if the failure is outside those two files.
Store that next to the job. Client field names differ; the content shouldn't.
- One outcome per run. The retry test and a billing cleanup are two runs.
- Pin the SHA, the test command, and the ticket id. You can't reproduce a moving branch.
- Ask for the failing assertion, the change, and the remaining risk.
Put the limits in the worker. AI coding agents and OpenAI Codex for software teams use the same rules in chat.
Worked example: red CI to a reviewed patch
The failure. main is red. InvoiceRetryTest expected three attempts and saw one, at scheduleRetry in src/retry.ts. Nothing else failed.
The trigger. A ticket, "Retry test fails after the backoff change," plus the log. Then call. A red build alone is not a brief, so require a label, a path, or a click.
The run. That SHA only. Allow the test and pnpm typecheck, with no production credential and no registry.
The result. The patch restores the loop bound and asserts the delay. The note says the queue consumer was untouched: if it drops the third attempt, the test passes and production still retries once. Green doesn't mean fixed.
The review. A pull request or attached diff, merged by a person. Same handoff as Codex and GitHub. The agent can't merge.
The stop. If the bug is in the consumer, end the run. That call still succeeded. Widening the scope makes a diff nobody reads.
The record. Brief, SHA, commands, note, and pull request stay on the ticket, so next time you can tell a new bug from a rerun.
How a codex api worker rejects a bad diff
Judge the diff in code, on a clean checkout of the recorded SHA, before review.
- If the diff doesn't apply, attach the reject. Don't retry an unpinned tree.
- If
git diff --name-onlyshows an unnamed file, fail, delete the branch, and name the path. - Re-run the done command yourself. The transcript can be wrong; the command on the SHA is the record.
- Open a pull request only when both checks pass. Otherwise return the note without a diff.
That sequence makes a codex api call reviewable. Skip the path check and billing.ts lands. Skip the re-run and a flaky transcript turns a broken SHA green.
Retries aren't free. A path failure means a bad brief or a wide task, so rewrite it or assign a person. The same text buys the same extra file.
A dependency bump, one pin at a time
"Upgrade the mail client" hides the version and whatever else the solver moves.
Repo: notifications @ the SHA currently in production
Goal: bump only the direct pin for the mail client from 4.2.1 to 4.2.3,
the version named in the ticket.
Constraint: edit the manifest and the lockfile. Do not change call sites.
Do not bump any other direct dependency.
Done when: the install command exits 0 and the notifications test script exits 0.
Stop if the changelog mentions a breaking change, or if the solver
wants a second package you did not name.
4.2.3 is an example. Version and package come from the ticket, never the model.
- No target version means stop. Never pick "latest."
- Record the production SHA. A moving default branch isn't what you shipped.
- Allow install and the named test. No publish, migrate, or push.
- Reject any file besides the manifest and lockfile, and any other direct pin that moved.
- The pull request body gets the note plus the ticket's changelog link. A person reads the breaking-change line and merges.
One pin, one version, one suite. Three packages are three runs, so one bad solve can't hide in the others.
Guardrails to put in the worker
Enforce these in code.
- A third file fails the path allowlist.
- Commands: named tests and typecheck. No migrate, no publish.
- No secrets in the prompt. The checkout and CI log are enough, so don't paste tokens to "check the dashboard."
- Record the SHA used.
- On this job, anything over 80 lines goes to a person. Keep the ceiling small and per job.
- A bot authors commits, and branch protection doesn't count the bot as a reviewer.
- Store the brief the run received, even after the template changes.
Before the call and after it
Before: a ticket, a done command, and today's reviewer. Slack, Sentry, and call notes are sources, but write the brief first. After: attach the patch, and close it only when a person accepts.
AI project management that holds up is a state, a reviewer, and a link to the change. Falrow is one such path: Slack, call notes, and Sentry become tickets, and Codex plans from git under team rules, checking writes against the version it read. The agent setup and the MCP tool catalogue show repo and ticket together. Another tracker is fine if it keeps that link. An MCP server lets agents read a ticket without the admin API.
Pricing without a stale number
A search for openai codex pricing won't find a blog number you can trust. Prices, cache discounts, and allowed models change. Check OpenAI's site the week you enable a job, and again when the allowlist or model changes.
- Every red build spends calls on flakes and on failures no patch should touch. Filter first.
- Cap at two attempts. A third run on the same SHA means the brief is wrong.
- Fifteen minutes on 20 lines is fine. Rewriting the diff isn't, at any token rate.
- A bad merge costs more than the invoice, so path limits and branch protection come first.
On a routine job, the openai codex api is often cheaper than the chore. As a way to skip the ticket, it isn't.
What to measure
When reviewers rewrite heavily, the job leaves the unattended path.
| Signal | Fine for a routine job | Look again |
|---|---|---|
| Acceptance check | Command passes on the recorded SHA | Green tests; the note says the real bug is elsewhere |
| Diff size | Inside your ceiling | The ceiling trips often |
| Edits after review | Little or no rewrite | Most lines rewritten |
| Reopens | Rare | Same test fails within a week |
| Attempts | One, sometimes two | Reruns hoping for a better diff |
| Path rejects | Occasional; the brief tightens | Most runs die on an extra file |
High rejects mean the worker is catching what the prompt couldn't. Low rejects plus heavy rewrites mean a loose allowlist, and the reviewers have become the filter.
A one-week rollout
One repo, one job: a retry-test failure that's obvious and touches few files.
Replay three old failures by hand, from a label or a button. If two of three merge with minor comments, you've earned a second manual job. Otherwise, fix the brief before a second repo. No schedule until the manual path feels dull.
Match each run against the merged human fix: same files, same outcome, though lines may differ. An extra file tightens the allowlist before ticket four. A stop the human later confirmed is a pass. Scoring only on green hides useful stops.
Add the bump after a week of those manual runs, same repo, one package. The production SHA check belongs in the worker.
FAQ
What is the Codex API?
The programmatic interface to OpenAI Codex: it reads a repo, runs allowed commands, and proposes a change. Call it from scripts, CI, or tickets when you don't need a chat. The rules are the same either way: pinned state, a done check, and human review before merge.
How is an API run different from pasting a ticket into chat?
Chat finds the task. An API run executes one that's already bounded: fixed SHA, paths, commands, and a stored brief. If you still need a conversation to choose the approach, it isn't ready.
Where should I check OpenAI Codex pricing?
OpenAI's pricing pages, the week the job turns on, since posts go stale. Budget for filtered volume, a retry cap, and review time. Recheck if the model changes or a schedule outruns the manual job.
Can these runs merge their own pull requests?
They may open one, but they shouldn't merge it. Keep human review on branch protection, bot-authored commits, and the brief stored with the diff. A stop outside the allowlist means the run finished.