SpyBara
Go Premium Account
2026
24 Apr 2026, 18:20
14 May 2026, 21:00 14 May 2026, 07:00 13 May 2026, 00:57 12 May 2026, 01:59 11 May 2026, 18:00 7 May 2026, 20:02 7 May 2026, 17:08 5 May 2026, 23:00 2 May 2026, 06:45 2 May 2026, 00:48 1 May 2026, 18:29 30 Apr 2026, 18:36 29 Apr 2026, 12:40 29 Apr 2026, 00:50 25 Apr 2026, 06:37 25 Apr 2026, 00:42 24 Apr 2026, 18:20 24 Apr 2026, 12:28 23 Apr 2026, 18:31 23 Apr 2026, 12:28 23 Apr 2026, 00:46 22 Apr 2026, 18:29 22 Apr 2026, 00:42 21 Apr 2026, 18:29 21 Apr 2026, 12:30 21 Apr 2026, 06:45 20 Apr 2026, 18:26 20 Apr 2026, 06:53 18 Apr 2026, 18:18 17 Apr 2026, 00:44 16 Apr 2026, 18:31 16 Apr 2026, 00:46 15 Apr 2026, 18:31 15 Apr 2026, 06:44 14 Apr 2026, 18:31 14 Apr 2026, 12:29 13 Apr 2026, 18:37 13 Apr 2026, 00:44 12 Apr 2026, 06:38 10 Apr 2026, 18:23 9 Apr 2026, 00:33 8 Apr 2026, 18:32 8 Apr 2026, 00:40 7 Apr 2026, 00:40 2 Apr 2026, 18:23 31 Mar 2026, 06:35 31 Mar 2026, 00:39 28 Mar 2026, 06:26 28 Mar 2026, 00:36 27 Mar 2026, 18:23 27 Mar 2026, 00:39 26 Mar 2026, 18:27 25 Mar 2026, 18:24 23 Mar 2026, 18:22 20 Mar 2026, 00:35 18 Mar 2026, 12:23 18 Mar 2026, 00:36 17 Mar 2026, 18:24 17 Mar 2026, 00:33 16 Mar 2026, 18:25 16 Mar 2026, 12:23 14 Mar 2026, 00:32 13 Mar 2026, 18:15 13 Mar 2026, 00:34 11 Mar 2026, 00:31 9 Mar 2026, 00:34 8 Mar 2026, 18:10 8 Mar 2026, 00:35 7 Mar 2026, 18:10 7 Mar 2026, 06:14 7 Mar 2026, 00:33 6 Mar 2026, 00:38 5 Mar 2026, 18:41 5 Mar 2026, 06:22 5 Mar 2026, 00:34 4 Mar 2026, 18:18 4 Mar 2026, 06:20 3 Mar 2026, 18:20 3 Mar 2026, 00:35 27 Feb 2026, 18:15 24 Feb 2026, 06:27 24 Feb 2026, 00:33 23 Feb 2026, 18:27 21 Feb 2026, 00:33 20 Feb 2026, 12:16 19 Feb 2026, 20:53 19 Feb 2026, 20:37
5 May 2026, 23:00
14 May 2026, 21:00 14 May 2026, 07:00 13 May 2026, 00:57 12 May 2026, 01:59 11 May 2026, 18:00 7 May 2026, 20:02 7 May 2026, 17:08 5 May 2026, 23:00 2 May 2026, 06:45 2 May 2026, 00:48 1 May 2026, 18:29 30 Apr 2026, 18:36 29 Apr 2026, 12:40 29 Apr 2026, 00:50 25 Apr 2026, 06:37 25 Apr 2026, 00:42 24 Apr 2026, 18:20 24 Apr 2026, 12:28 23 Apr 2026, 18:31 23 Apr 2026, 12:28 23 Apr 2026, 00:46 22 Apr 2026, 18:29 22 Apr 2026, 00:42 21 Apr 2026, 18:29 21 Apr 2026, 12:30 21 Apr 2026, 06:45 20 Apr 2026, 18:26 20 Apr 2026, 06:53 18 Apr 2026, 18:18 17 Apr 2026, 00:44 16 Apr 2026, 18:31 16 Apr 2026, 00:46 15 Apr 2026, 18:31 15 Apr 2026, 06:44 14 Apr 2026, 18:31 14 Apr 2026, 12:29 13 Apr 2026, 18:37 13 Apr 2026, 00:44 12 Apr 2026, 06:38 10 Apr 2026, 18:23 9 Apr 2026, 00:33 8 Apr 2026, 18:32 8 Apr 2026, 00:40 7 Apr 2026, 00:40 2 Apr 2026, 18:23 31 Mar 2026, 06:35 31 Mar 2026, 00:39 28 Mar 2026, 06:26 28 Mar 2026, 00:36 27 Mar 2026, 18:23 27 Mar 2026, 00:39 26 Mar 2026, 18:27 25 Mar 2026, 18:24 23 Mar 2026, 18:22 20 Mar 2026, 00:35 18 Mar 2026, 12:23 18 Mar 2026, 00:36 17 Mar 2026, 18:24 17 Mar 2026, 00:33 16 Mar 2026, 18:25 16 Mar 2026, 12:23 14 Mar 2026, 00:32 13 Mar 2026, 18:15 13 Mar 2026, 00:34 11 Mar 2026, 00:31 9 Mar 2026, 00:34 8 Mar 2026, 18:10 8 Mar 2026, 00:35 7 Mar 2026, 18:10 7 Mar 2026, 06:14 7 Mar 2026, 00:33 6 Mar 2026, 00:38 5 Mar 2026, 18:41 5 Mar 2026, 06:22 5 Mar 2026, 00:34 4 Mar 2026, 18:18 4 Mar 2026, 06:20 3 Mar 2026, 18:20 3 Mar 2026, 00:35 27 Feb 2026, 18:15 24 Feb 2026, 06:27 24 Feb 2026, 00:33 23 Feb 2026, 18:27 21 Feb 2026, 00:33 20 Feb 2026, 12:16 19 Feb 2026, 20:53 19 Feb 2026, 20:37
Fri 1 18:29 Sat 2 00:48 Sat 2 06:45 Tue 5 23:00 Thu 7 17:08 Thu 7 20:02 Mon 11 18:00 Tue 12 01:59 Wed 13 00:57 Thu 14 07:00 Thu 14 21:00

After 2026-05-02 06:45 UTC, this monitor no longer uses markdownified HTML/MDX. Comparisons across that boundary can therefore show more extensive diffs.

hooks.md +126 −59

Details

1# Hooks1# Hooks

2 2 

3Experimental. Hooks are under active development. Windows support temporarily

4disabled.

5 

6Hooks are an extensibility framework for Codex. They allow3Hooks are an extensibility framework for Codex. They allow

7you to inject your own scripts into the agentic loop, enabling features such as:4you to inject your own scripts into the agentic loop, enabling features such as:

8 5 


23 20 

24- Matching hooks from multiple files all run.21- Matching hooks from multiple files all run.

25- Multiple matching command hooks for the same event are launched concurrently,22- Multiple matching command hooks for the same event are launched concurrently,

26 so one hook can’t prevent another matching hook from starting.23 so one hook cannot prevent another matching hook from starting.

27- `PreToolUse`, `PermissionRequest`, `PostToolUse`, `UserPromptSubmit`, and24- `PreToolUse`, `PermissionRequest`, `PostToolUse`, `UserPromptSubmit`, and

28 `Stop` run at turn scope.25 `Stop` run at turn scope.

29- Hooks are currently disabled on Windows.

30 26 

31## Where Codex looks for hooks27## Where Codex looks for hooks

32 28 

33Codex discovers `hooks.json` next to active config layers.29Codex discovers hooks next to active config layers in either of these forms:

30 

31- `hooks.json`

32- inline `[hooks]` tables inside `config.toml`

34 33 

35In practice, the two most useful locations are:34Installed plugins can also bundle lifecycle config through their plugin

35manifest or a default `hooks/hooks.json` file. See [Build

36plugins](https://developers.openai.com/codex/plugins/build#bundled-mcp-servers-and-lifecycle-config) for the

37plugin packaging rules.

38 

39In practice, the four most useful locations are:

36 40 

37- `~/.codex/hooks.json`41- `~/.codex/hooks.json`

42- `~/.codex/config.toml`

38- `<repo>/.codex/hooks.json`43- `<repo>/.codex/hooks.json`

44- `<repo>/.codex/config.toml`

39 45 

40If more than one `hooks.json` file exists, Codex loads all matching hooks.46If more than one hook source exists, Codex loads all matching hooks.

41Higher-precedence config layers don’t replace lower-precedence hooks.47Higher-precedence config layers do not replace lower-precedence hooks.

48If a single layer contains both `hooks.json` and inline `[hooks]`, Codex

49merges them and warns at startup. Prefer one representation per layer.

42 50 

43Project-local hooks load only when the project `.codex/` layer is trusted. In51Project-local hooks load only when the project `.codex/` layer is trusted. In

44untrusted projects, Codex still loads user and system hooks from their own52untrusted projects, Codex still loads user and system hooks from their own


131Notes:139Notes:

132 140 

133- `timeout` is in seconds.141- `timeout` is in seconds.

134- `timeoutSec` is also accepted as an alias.

135- If `timeout` is omitted, Codex uses `600` seconds.142- If `timeout` is omitted, Codex uses `600` seconds.

136- `statusMessage` is optional.143- `statusMessage` is optional.

137- Commands run with the session `cwd` as their working directory.144- Commands run with the session `cwd` as their working directory.


139 relative path such as `.codex/hooks/...`. Codex may be started from a146 relative path such as `.codex/hooks/...`. Codex may be started from a

140 subdirectory, and a git-root-based path keeps the hook location stable.147 subdirectory, and a git-root-based path keeps the hook location stable.

141 148 

149Equivalent inline TOML in `config.toml`:

150 

151```toml

152[features]

153codex_hooks = true

154 

155[[hooks.PreToolUse]]

156matcher = "^Bash$"

157 

158[[hooks.PreToolUse.hooks]]

159type = "command"

160command = '/usr/bin/python3 "$(git rev-parse --show-toplevel)/.codex/hooks/pre_tool_use_policy.py"'

161timeout = 30

162statusMessage = "Checking Bash command"

163 

164[[hooks.PostToolUse]]

165matcher = "^Bash$"

166 

167[[hooks.PostToolUse.hooks]]

168type = "command"

169command = '/usr/bin/python3 "$(git rev-parse --show-toplevel)/.codex/hooks/post_tool_use_review.py"'

170timeout = 30

171statusMessage = "Reviewing Bash output"

172```

173 

174## Managed hooks from `requirements.toml`

175 

176Enterprise-managed requirements can also define hooks inline under `[hooks]`.

177This is useful when admins want to enforce the hook configuration while

178delivering the actual scripts through MDM or another device-management system.

179 

180```toml

181[features]

182codex_hooks = true

183 

184[hooks]

185managed_dir = "/enterprise/hooks"

186windows_managed_dir = 'C:\enterprise\hooks'

187 

188[[hooks.PreToolUse]]

189matcher = "^Bash$"

190 

191[[hooks.PreToolUse.hooks]]

192type = "command"

193command = "python3 /enterprise/hooks/pre_tool_use_policy.py"

194timeout = 30

195statusMessage = "Checking managed Bash command"

196```

197 

198Notes for managed hooks:

199 

200- `managed_dir` is used on macOS and Linux.

201- `windows_managed_dir` is used on Windows.

202- Codex does not distribute the scripts in `managed_dir`; your enterprise

203 tooling must install and update them separately.

204- Managed hook commands should use absolute script paths under the configured

205 managed directory.

206 

142## Matcher patterns207## Matcher patterns

143 208 

144The `matcher` field is a regex string that filters when hooks fire. Use `"*"`,209The `matcher` field is a regex string that filters when hooks fire. Use `"*"`,


148Only some current Codex events honor `matcher`:213Only some current Codex events honor `matcher`:

149 214 

150| Event | What `matcher` filters | Notes |215| Event | What `matcher` filters | Notes |

151| --- | --- | --- |216| ------------------- | ---------------------- | ------------------------------------------------------------ |

152| `PermissionRequest` | tool name | Current Codex runtime only emits `Bash`. |217| `PermissionRequest` | tool name | Support includes `Bash`, `apply_patch`\*, and MCP tool names |

153| `PostToolUse` | tool name | Current Codex runtime only emits `Bash`. |218| `PostToolUse` | tool name | Support includes `Bash`, `apply_patch`\*, and MCP tool names |

154| `PreToolUse` | tool name | Current Codex runtime only emits `Bash`. |219| `PreToolUse` | tool name | Support includes `Bash`, `apply_patch`\*, and MCP tool names |

155| `SessionStart` | start source | Current runtime values are `startup` and `resume`. |220| `SessionStart` | start source | Current runtime values are `startup`, `resume`, and `clear` |

156| `UserPromptSubmit` | not supported | Any configured `matcher` is ignored for this event. |221| `UserPromptSubmit` | not supported | Any configured `matcher` is ignored for this event |

157| `Stop` | not supported | Any configured `matcher` is ignored for this event. |222| `Stop` | not supported | Any configured `matcher` is ignored for this event |

223 

224\*For `apply_patch`, matchers can also use `Edit` or `Write`.

158 225 

159Examples:226Examples:

160 227 

161- `Bash`228- `Bash`

162- `startup|resume`229- `^apply_patch$`

163- `Edit|Write`230- `Edit|Write`

164 231- `mcp__filesystem__read_file`

165That last example is still a valid regex, but current Codex `PreToolUse` and232- `mcp__filesystem__.*`

166`PostToolUse` events only emit `Bash`, so it won’t match anything today.233- `startup|resume|clear`

167 234 

168## Common input fields235## Common input fields

169 236 


172These are the shared fields you will usually use:239These are the shared fields you will usually use:

173 240 

174| Field | Type | Meaning |241| Field | Type | Meaning |

175| --- | --- | --- |242| ----------------- | ---------------- | ------------------------------------------- |

176| `session_id` | `string` | Current session or thread id. |243| `session_id` | `string` | Current session or thread id. |

177| `transcript_path` | `string | null` | Path to the session transcript file, if any |244| `transcript_path` | `string \| null` | Path to the session transcript file, if any |

178| `cwd` | `string` | Working directory for the session |245| `cwd` | `string` | Working directory for the session |

179| `hook_event_name` | `string` | Current hook event name |246| `hook_event_name` | `string` | Current hook event name |

180| `model` | `string` | Active model slug |247| `model` | `string` | Active model slug |


221Fields in addition to [Common input fields](#common-input-fields):288Fields in addition to [Common input fields](#common-input-fields):

222 289 

223| Field | Type | Meaning |290| Field | Type | Meaning |

224| --- | --- | --- |291| -------- | -------- | ---------------------------------------------- |

225| `source` | `string` | How the session started: `startup` or `resume` |292| `source` | `string` | How the session started: `startup` or `resume` |

226 293 

227Plain text on `stdout` is added as extra developer context.294Plain text on `stdout` is added as extra developer context.


242 309 

243### PreToolUse310### PreToolUse

244 311 

245Work in progress312`PreToolUse` can intercept Bash, file edits performed through `apply_patch`,

246 313and MCP tool calls. It is still a guardrail rather than a complete enforcement

247Currently `PreToolUse` only supports Bash tool interception. The model can314boundary because Codex can often perform equivalent work through another

248still work around this by writing its own script to disk and then running that315supported tool path.

249script with Bash, so treat this as a useful guardrail rather than a complete

250enforcement boundary

251 316 

252This doesn't intercept all shell calls yet, only the simple ones. The newer317This doesn't intercept all shell calls yet, only the simple ones. The newer

253 `unified_exec` mechanism allows richer streaming stdin/stdout handling of318 `unified_exec` mechanism allows richer streaming stdin/stdout handling of

254shell, but interception is incomplete. Similarly, this doesnt intercept MCP,319 shell, but interception is incomplete. Similarly, this doesn't intercept

255Write, WebSearch, or other non-shell tool calls.320 `WebSearch` or other non-shell, non-MCP tool calls.

256 321 

257`matcher` is applied to `tool_name`, which currently always equals `Bash`.322`matcher` is applied to `tool_name` and matcher aliases. For file edits through

323`apply_patch`, matchers can use `apply_patch`, `Edit`, or `Write`; hook input

324still reports `tool_name: "apply_patch"`.

258 325 

259Fields in addition to [Common input fields](#common-input-fields):326Fields in addition to [Common input fields](#common-input-fields):

260 327 

261| Field | Type | Meaning |328| Field | Type | Meaning |

262| --- | --- | --- |329| ------------- | ------------ | --------------------------------------------------------------------------------------------------------- |

263| `turn_id` | `string` | Codex-specific extension. Active Codex turn id |330| `turn_id` | `string` | Codex-specific extension. Active Codex turn id |

264| `tool_name` | `string` | Currently always `Bash` |331| `tool_name` | `string` | Canonical hook tool name, such as `Bash`, `apply_patch`, or an MCP name like `mcp__fs__read` |

265| `tool_use_id` | `string` | Tool-call id for this invocation |332| `tool_use_id` | `string` | Tool-call id for this invocation |

266| `tool_input.command` | `string` | Shell command Codex is about to run |333| `tool_input` | `JSON value` | Tool-specific input. `Bash` and `apply_patch` use `tool_input.command` while MCP tools send all the args. |

267 334 

268Plain text on `stdout` is ignored.335Plain text on `stdout` is ignored.

269 336 


297 364 

298### PermissionRequest365### PermissionRequest

299 366 

300Work in progress

301 

302`PermissionRequest` runs when Codex is about to ask for approval, such as a367`PermissionRequest` runs when Codex is about to ask for approval, such as a

303shell escalation or managed-network approval. It can allow the request, deny368shell escalation or managed-network approval. It can allow the request, deny

304the request, or decline to decide and let the normal approval prompt continue.369the request, or decline to decide and let the normal approval prompt continue.

305It doesn't run for commands that don't need approval.370It doesn't run for commands that don't need approval.

306 371 

307`matcher` is applied to `tool_name`, which currently always equals `Bash`.372`matcher` is applied to `tool_name` and matcher aliases. Current canonical

373values include `Bash`, `apply_patch`, and MCP tool names such as

374`mcp__server__tool`; `apply_patch` also matches `Edit` and `Write`.

308 375 

309Fields in addition to [Common input fields](#common-input-fields):376Fields in addition to [Common input fields](#common-input-fields):

310 377 

311| Field | Type | Meaning |378| Field | Type | Meaning |

312| --- | --- | --- |379| ------------------------ | ---------------- | --------------------------------------------------------------------------------------------------------- |

313| `turn_id` | `string` | Codex-specific extension. Active Codex turn id |380| `turn_id` | `string` | Codex-specific extension. Active Codex turn id |

314| `tool_name` | `string` | Currently always `Bash` |381| `tool_name` | `string` | Canonical hook tool name, such as `Bash`, `apply_patch`, or an MCP name like `mcp__fs__read` |

315| `tool_input.command` | `string` | Shell command associated with the approval request |382| `tool_input` | `JSON value` | Tool-specific input. `Bash` and `apply_patch` use `tool_input.command` while MCP tools send all the args. |

316| `tool_input.description` | `string | null` | Human-readable approval reason, when Codex has one |383| `tool_input.description` | `string \| null` | Human-readable approval reason, when Codex has one |

317 384 

318Plain text on `stdout` is ignored.385Plain text on `stdout` is ignored.

319 386 


354 421 

355### PostToolUse422### PostToolUse

356 423 

357Work in progress424`PostToolUse` runs after supported tools produce output, including Bash,

358 425`apply_patch`, and MCP tool calls. For Bash, it also runs after commands that

359Currently `PostToolUse` only supports Bash tool results. It’s not limited to426exit with a non-zero status. It can't undo side effects from the tool that

360commands that exit successfully: non-interactive `exec_command` calls can still427already ran.

361trigger `PostToolUse` when Codex emits a Bash post-tool payload. It can’t undo

362side effects from the command that already ran.

363 428 

364This doesn't intercept all shell calls yet, only the simple ones. The newer429This doesn't intercept all shell calls yet, only the simple ones. The newer

365 `unified_exec` mechanism allows richer streaming stdin/stdout handling of430 `unified_exec` mechanism allows richer streaming stdin/stdout handling of

366shell, but interception is incomplete. Similarly, this doesnt intercept MCP,431 shell, but interception is incomplete. Similarly, this doesn't intercept

367Write, WebSearch, or other non-shell tool calls.432 `WebSearch` or other non-shell, non-MCP tool calls.

368 433 

369`matcher` is applied to `tool_name`, which currently always equals `Bash`.434`matcher` is applied to `tool_name` and matcher aliases. For file edits through

435`apply_patch`, matchers can use `apply_patch`, `Edit`, or `Write`; hook input

436still reports `tool_name: "apply_patch"`.

370 437 

371Fields in addition to [Common input fields](#common-input-fields):438Fields in addition to [Common input fields](#common-input-fields):

372 439 

373| Field | Type | Meaning |440| Field | Type | Meaning |

374| --- | --- | --- |441| --------------- | ------------ | --------------------------------------------------------------------------------------------------------- |

375| `turn_id` | `string` | Codex-specific extension. Active Codex turn id |442| `turn_id` | `string` | Codex-specific extension. Active Codex turn id |

376| `tool_name` | `string` | Currently always `Bash` |443| `tool_name` | `string` | Canonical hook tool name, such as `Bash`, `apply_patch`, or an MCP name like `mcp__fs__read` |

377| `tool_use_id` | `string` | Tool-call id for this invocation |444| `tool_use_id` | `string` | Tool-call id for this invocation |

378| `tool_input.command` | `string` | Shell command Codex just ran |445| `tool_input` | `JSON value` | Tool-specific input. `Bash` and `apply_patch` use `tool_input.command` while MCP tools send all the args. |

379| `tool_response` | `JSON value` | Bash tool output payload. Today this is usually a JSON string |446| `tool_response` | `JSON value` | Tool-specific output. For MCP tools, this is the MCP call result. |

380 447 

381Plain text on `stdout` is ignored.448Plain text on `stdout` is ignored.

382 449 


415Fields in addition to [Common input fields](#common-input-fields):482Fields in addition to [Common input fields](#common-input-fields):

416 483 

417| Field | Type | Meaning |484| Field | Type | Meaning |

418| --- | --- | --- |485| --------- | -------- | ---------------------------------------------- |

419| `turn_id` | `string` | Codex-specific extension. Active Codex turn id |486| `turn_id` | `string` | Codex-specific extension. Active Codex turn id |

420| `prompt` | `string` | User prompt that's about to be sent |487| `prompt` | `string` | User prompt that's about to be sent |

421 488 


453Fields in addition to [Common input fields](#common-input-fields):520Fields in addition to [Common input fields](#common-input-fields):

454 521 

455| Field | Type | Meaning |522| Field | Type | Meaning |

456| --- | --- | --- |523| ------------------------ | ---------------- | ------------------------------------------------- |

457| `turn_id` | `string` | Codex-specific extension. Active Codex turn id |524| `turn_id` | `string` | Codex-specific extension. Active Codex turn id |

458| `stop_hook_active` | `boolean` | Whether this turn was already continued by `Stop` |525| `stop_hook_active` | `boolean` | Whether this turn was already continued by `Stop` |

459| `last_assistant_message` | `string | null` | Latest assistant message text, if available |526| `last_assistant_message` | `string \| null` | Latest assistant message text, if available |

460 527 

461`Stop` expects JSON on `stdout` when it exits `0`. Plain text output is invalid528`Stop` expects JSON on `stdout` when it exits `0`. Plain text output is invalid

462for this event.529for this event.