# Automatic code review

> Run an automatic Codex review after every Claude Code turn with a Stop hook, sharing one MirAPI key and prepaid balance.

**Deliverable:** every time Claude Code finishes a turn with uncommitted changes, a Stop hook runs a Codex review of those changes and prints its verdict.

**Prerequisites:** Claude Code and Codex CLI both configured with the same `MIRAPI_API_KEY` (see [Claude Code](/docs/integrations/claude-code) and [Codex CLI](/docs/integrations/codex)); a git repository.

## 1. The review script

Create `~/.claude/review.sh`:

```bash
#!/usr/bin/env bash
# Run a Codex review of the uncommitted changes.
set -euo pipefail

DIFF_STAT=$(git diff HEAD --stat || true)

# Exit silently when there is nothing to review.
if [[ -z "$DIFF_STAT" ]]; then
  exit 0
fi

codex exec "Review the changes below for bugs, security issues, and broken invariants. Be concise.

$DIFF_STAT" 2>/dev/null || true
```

Make it executable:

```bash
chmod +x ~/.claude/review.sh
```

The script captures a one-line summary of the staged and unstaged changes (`git diff HEAD --stat`), hands it to Codex, and lets Codex print the review to the hook's output. The trailing `|| true` keeps a failed Codex call from surfacing as a hook error.

## 2. The Stop hook

Add the hook to your project's `.claude/settings.json`:

```json
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash ~/.claude/review.sh",
            "timeout": 120
          }
        ]
      }
    ]
  }
}
```

The `Stop` event fires whenever Claude Code pauses for your input — exactly the moment a review is useful. The hook fields are:

| Field | Purpose |
|---|---|
| `type` | `"command"` runs a shell command |
| `command` | The command to execute — here `bash ~/.claude/review.sh` |
| `timeout` | Maximum seconds the hook may run before being killed (120) |

To keep the hook out of git, use `.claude/settings.local.json` instead; global hooks live in `~/.claude/settings.json`.

## 3. Verify it fires

First test the script on its own: change a tracked file, then run it directly.

```bash
bash ~/.claude/review.sh
```

You should see the Codex review in the terminal. Then ask Claude Code to make a change and wait for the hook output — the verdict appears in the hook's output section once Claude Code pauses.

## Two protocols, one key

Claude Code and Codex CLI speak different protocols, so they use different base URLs on MirAPI:

| Agent | Protocol | Base URL | Authentication |
|---|---|---|---|
| Claude Code | Anthropic Messages (`POST /v1/messages`) | `https://api.mirapi.ai` (no `/v1`) | `ANTHROPIC_AUTH_TOKEN=$MIRAPI_API_KEY` |
| Codex CLI | OpenAI Responses (`POST /v1/responses`) | `https://api.mirapi.ai/v1` | `env_key = "MIRAPI_API_KEY"` |

### Claude Code

Point the Anthropic client at the bare origin and pass the key through the auth-token variable:

```bash
export ANTHROPIC_BASE_URL="https://api.mirapi.ai"
export ANTHROPIC_AUTH_TOKEN="$MIRAPI_API_KEY"
export ANTHROPIC_MODEL="deepseek-chat"
```

Do not append `/v1` — Claude Code builds the `/v1/messages` path itself. Full setup in [Claude Code](/docs/integrations/claude-code).

### Codex CLI

Register MirAPI as a provider in `~/.codex/config.toml`:

```toml
model = "deepseek-chat"
model_provider = "mirapi"

[model_providers.mirapi]
name = "MirAPI"
base_url = "https://api.mirapi.ai/v1"
env_key = "MIRAPI_API_KEY"
wire_api = "responses"
```

Codex appends `/responses` to the base URL, so it needs the `/v1` suffix. Full reference in [Codex CLI](/docs/integrations/codex).

Because both agents use the same key, they draw from the same prepaid balance. Watch the spend:

```bash
curl https://api.mirapi.ai/api/log/token -H "Authorization: Bearer $MIRAPI_API_KEY"
curl https://api.mirapi.ai/api/usage/token -H "Authorization: Bearer $MIRAPI_API_KEY"
```

`/api/log/token` lists per-request rows; `/api/usage/token` returns account-level totals. Text is metered per token (input / output / cache read), reasoning tokens bill as output, and prompt caching is enabled automatically on OpenAI-compatible models. Reconcile against the console billing log (https://console.mirapi.ai).

## Error handling

| Status | Meaning | What to do |
|---|---|---|
| 401 | Key missing, invalid, or unknown | Check the exported key |
| 403 | Key valid but refused — balance, whitelist, quota, or IP | Fix the cause, then retry |
| 413 | Payload too large | Trim the diff sent to the model |
| 429 | Rate limited | Jittered exponential backoff (1s→2s→4s, cap ~30s); no `Retry-After` header |
| 500 | Gateway or upstream error | Idempotent requests can be retried safely |

The gateway does not retry for you. Every error message ends with a MirAPI request ID — include it in support tickets.

## Pitfalls

- Hook stdout is shown to Claude Code — keep the script silent except for the verdict.
- The review runs inside the hook, so a long diff delays the next turn; keep the timeout tight and the prompt concise.
- `git diff HEAD --stat` covers staged and unstaged changes but not untracked files; add `git add -N .` (or switch to `git status --porcelain`) if new files matter.
- If Codex is not logged in or misconfigured, the script exits quietly (`|| true`) — test it standalone first.

## Approximate cost

Each review is one short Codex turn — typically a few thousand tokens, so a fraction of a cent to a few cents depending on the diff size and the model. Exact amounts show up in the console usage log and `GET /api/log/token`.

## Related links

- [Claude Code](/docs/integrations/claude-code)
- [Codex CLI](/docs/integrations/codex)
- [Claude Code advanced](/docs/tutorials/claude-code-advanced)
- [CI code review](/docs/tutorials/ci-code-review)
- [Cookbook](/docs/tutorials/index)
- [Billing & top-ups](/docs/billing)