SpyBara
Go Premium Account
2026
27 Mar 2026, 18:23
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
29 Apr 2026, 12:40
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
Thu 2 18:23 Tue 7 00:40 Wed 8 00:40 Wed 8 18:32 Thu 9 00:33 Fri 10 18:23 Sun 12 06:38 Mon 13 00:44 Mon 13 18:37 Tue 14 12:29 Tue 14 18:31 Wed 15 06:44 Wed 15 18:31 Thu 16 00:46 Thu 16 18:31 Fri 17 00:44 Sat 18 18:18 Mon 20 06:53 Mon 20 18:26 Tue 21 06:45 Tue 21 12:30 Tue 21 18:29 Wed 22 00:42 Wed 22 18:29 Thu 23 00:46 Thu 23 12:28 Thu 23 18:31 Fri 24 12:28 Fri 24 18:20 Sat 25 00:42 Sat 25 06:37 Wed 29 00:50 Wed 29 12:40 Thu 30 18:36

hooks.md +192 −42

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 

9- Send the conversation to a custom logging/analytics engine6- Send the conversation to a custom logging/analytics engine

10- Scan your team's prompts to block accidentally pasting API keys7- Scan your team's prompts to block accidentally pasting API keys

11- Summarize conversations to create persistent memories automatically8- Summarize conversations to create persistent memories automatically

12- Run a custom validator when a conversation turn stops, enforcing standards9- Run a custom validation check when a conversation turn stops, enforcing standards

13- Customize prompting when in a certain directory10- Customize prompting when in a certain directory

14 11 

15Hooks are behind a feature flag in `config.toml`:12Hooks are behind a feature flag in `config.toml`:


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 cannot prevent another matching hook from starting.23 so one hook cannot prevent another matching hook from starting.

27- `PreToolUse`, `PostToolUse`, `UserPromptSubmit`, and `Stop` run at turn24- `PreToolUse`, `PermissionRequest`, `PostToolUse`, `UserPromptSubmit`, and

28 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:34In practice, the four most useful locations are:

36 35 

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

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

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

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

39 40 

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

41Higher-precedence config layers do not replace lower-precedence hooks.42Higher-precedence config layers do not replace lower-precedence hooks.

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

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

45 

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

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

48active config layers.

42 49 

43## Config shape50## Config shape

44 51 


75 ]82 ]

76 }83 }

77 ],84 ],

85 "PermissionRequest": [

86 {

87 "matcher": "Bash",

88 "hooks": [

89 {

90 "type": "command",

91 "command": "/usr/bin/python3 \"$(git rev-parse --show-toplevel)/.codex/hooks/permission_request.py\"",

92 "statusMessage": "Checking approval request"

93 }

94 ]

95 }

96 ],

78 "PostToolUse": [97 "PostToolUse": [

79 {98 {

80 "matcher": "Bash",99 "matcher": "Bash",


115Notes:134Notes:

116 135 

117- `timeout` is in seconds.136- `timeout` is in seconds.

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

119- If `timeout` is omitted, Codex uses `600` seconds.137- If `timeout` is omitted, Codex uses `600` seconds.

120- `statusMessage` is optional.138- `statusMessage` is optional.

121- Commands run with the session `cwd` as their working directory.139- Commands run with the session `cwd` as their working directory.


123 relative path such as `.codex/hooks/...`. Codex may be started from a141 relative path such as `.codex/hooks/...`. Codex may be started from a

124 subdirectory, and a git-root-based path keeps the hook location stable.142 subdirectory, and a git-root-based path keeps the hook location stable.

125 143 

144Equivalent inline TOML in `config.toml`:

145 

146```toml

147[features]

148codex_hooks = true

149 

150[[hooks.PreToolUse]]

151matcher = "^Bash$"

152 

153[[hooks.PreToolUse.hooks]]

154type = "command"

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

156timeout = 30

157statusMessage = "Checking Bash command"

158 

159[[hooks.PostToolUse]]

160matcher = "^Bash$"

161 

162[[hooks.PostToolUse.hooks]]

163type = "command"

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

165timeout = 30

166statusMessage = "Reviewing Bash output"

167```

168 

169## Managed hooks from `requirements.toml`

170 

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

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

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

174 

175```toml

176[features]

177codex_hooks = true

178 

179[hooks]

180managed_dir = "/enterprise/hooks"

181windows_managed_dir = 'C:\enterprise\hooks'

182 

183[[hooks.PreToolUse]]

184matcher = "^Bash$"

185 

186[[hooks.PreToolUse.hooks]]

187type = "command"

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

189timeout = 30

190statusMessage = "Checking managed Bash command"

191```

192 

193Notes for managed hooks:

194 

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

196- `windows_managed_dir` is used on Windows.

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

198 tooling must install and update them separately.

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

200 managed directory.

201 

126## Matcher patterns202## Matcher patterns

127 203 

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


133 209 

134| Event | What `matcher` filters | Notes |210| Event | What `matcher` filters | Notes |

135| --- | --- | --- |211| --- | --- | --- |

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

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

138| `SessionStart` | start source | Current runtime values are `startup` and `resume`. |214| `PreToolUse` | tool name | Support includes `Bash`, `apply_patch`\*, and MCP tool names |

139| `UserPromptSubmit` | not supported | Any configured `matcher` is ignored for this event. |215| `SessionStart` | start source | Current runtime values are `startup`, `resume`, and `clear` |

140| `Stop` | not supported | Any configured `matcher` is ignored for this event. |216| `UserPromptSubmit` | not supported | Any configured `matcher` is ignored for this event |

217| `Stop` | not supported | Any configured `matcher` is ignored for this event |

218 

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

141 220 

142Examples:221Examples:

143 222 

144- `Bash`223- `Bash`

145- `startup|resume`224- `^apply_patch$`

146- `Edit|Write`225- `Edit|Write`

147 226- `mcp__filesystem__read_file`

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

149`PostToolUse` events only emit `Bash`, so it will not match anything today.228- `startup|resume|clear`

150 229 

151## Common input fields230## Common input fields

152 231 


189 268 

190Exit `0` with no output is treated as success and Codex continues.269Exit `0` with no output is treated as success and Codex continues.

191 270 

192`PreToolUse` supports `systemMessage`, but `continue`, `stopReason`, and271`PreToolUse` and `PermissionRequest` support `systemMessage`, but `continue`,

193`suppressOutput` are not currently supported for that event.272`stopReason`, and `suppressOutput` aren't currently supported for those events.

194 273 

195`PostToolUse` supports `systemMessage`, `continue: false`, and `stopReason`.274`PostToolUse` supports `systemMessage`, `continue: false`, and `stopReason`.

196`suppressOutput` is parsed but not currently supported for that event.275`suppressOutput` is parsed but not currently supported for that event.


225 304 

226### PreToolUse305### PreToolUse

227 306 

228Currently `PreToolUse` only supports Bash tool interception. The model can307`PreToolUse` can intercept Bash, file edits performed through `apply_patch`,

229still work around this by writing its own script to disk and then running that308and MCP tool calls. It is still a guardrail rather than a complete enforcement

230script with Bash, so treat this as a useful guardrail rather than a complete309boundary because Codex can often perform equivalent work through another

231enforcement boundary.310supported tool path.

311 

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

313 `unified_exec` mechanism allows richer streaming stdin/stdout handling of

314 shell, but interception is incomplete. Similarly, this doesn't intercept

315 `WebSearch` or other non-shell, non-MCP tool calls.

232 316 

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

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

319still reports `tool_name: "apply_patch"`.

234 320 

235Fields in addition to [Common input fields](#common-input-fields):321Fields in addition to [Common input fields](#common-input-fields):

236 322 

237| Field | Type | Meaning |323| Field | Type | Meaning |

238| --- | --- | --- |324| --- | --- | --- |

239| `turn_id` | `string` | Codex-specific extension. Active Codex turn id |325| `turn_id` | `string` | Codex-specific extension. Active Codex turn id |

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

241| `tool_use_id` | `string` | Tool-call id for this invocation |327| `tool_use_id` | `string` | Tool-call id for this invocation |

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

243 329 

244Plain text on `stdout` is ignored.330Plain text on `stdout` is ignored.

245 331 


271`updatedInput`, `additionalContext`, `continue: false`, `stopReason`, and357`updatedInput`, `additionalContext`, `continue: false`, `stopReason`, and

272`suppressOutput` are parsed but not supported yet, so they fail open.358`suppressOutput` are parsed but not supported yet, so they fail open.

273 359 

360### PermissionRequest

361 

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

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

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

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

366 

367`matcher` is applied to `tool_name` and matcher aliases. Current canonical

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

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

370 

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

372 

373| Field | Type | Meaning |

374| --- | --- | --- |

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

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

377| `tool_input` | `JSON value` | Tool-specific input. `Bash` and `apply_patch` use `tool_input.command` while MCP tools send all the args. |

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

379 

380Plain text on `stdout` is ignored.

381 

382To approve the request, return:

383 

384```json

385{

386 "hookSpecificOutput": {

387 "hookEventName": "PermissionRequest",

388 "decision": {

389 "behavior": "allow"

390 }

391 }

392}

393```

394 

395To deny the request, return:

396 

397```json

398{

399 "hookSpecificOutput": {

400 "hookEventName": "PermissionRequest",

401 "decision": {

402 "behavior": "deny",

403 "message": "Blocked by repository policy."

404 }

405 }

406}

407```

408 

409If multiple matching hooks return decisions, any `deny` wins. Otherwise, an

410`allow` lets the request proceed without surfacing the approval prompt. If no

411matching hook decides, Codex uses the normal approval flow.

412 

413Don't return `updatedInput`, `updatedPermissions`, or `interrupt` for

414`PermissionRequest`; those fields are reserved for future behavior and fail

415closed today.

416 

274### PostToolUse417### PostToolUse

275 418 

276Currently `PostToolUse` only supports Bash tool results. It is not limited to419`PostToolUse` runs after supported tools produce output, including Bash,

277commands that exit successfully: non-interactive `exec_command` calls can still420`apply_patch`, and MCP tool calls. For Bash, it also runs after commands that

278trigger `PostToolUse` when Codex emits a Bash post-tool payload. It cannot undo421exit with a non-zero status. It can't undo side effects from the tool that

279side effects from the command that already ran.422already ran.

423 

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

425 `unified_exec` mechanism allows richer streaming stdin/stdout handling of

426 shell, but interception is incomplete. Similarly, this doesn't intercept

427 `WebSearch` or other non-shell, non-MCP tool calls.

280 428 

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

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

431still reports `tool_name: "apply_patch"`.

282 432 

283Fields in addition to [Common input fields](#common-input-fields):433Fields in addition to [Common input fields](#common-input-fields):

284 434 

285| Field | Type | Meaning |435| Field | Type | Meaning |

286| --- | --- | --- |436| --- | --- | --- |

287| `turn_id` | `string` | Codex-specific extension. Active Codex turn id |437| `turn_id` | `string` | Codex-specific extension. Active Codex turn id |

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

289| `tool_use_id` | `string` | Tool-call id for this invocation |439| `tool_use_id` | `string` | Tool-call id for this invocation |

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

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

292 442 

293Plain text on `stdout` is ignored.443Plain text on `stdout` is ignored.

294 444 


307 457 

308That `additionalContext` text is added as extra developer context.458That `additionalContext` text is added as extra developer context.

309 459 

310For this event, `decision: "block"` does not undo the completed Bash command.460For this event, `decision: "block"` doesn't undo the completed Bash command.

311Instead, Codex records the feedback, replaces the tool result with that461Instead, Codex records the feedback, replaces the tool result with that

312feedback, and continues the model from the hook-provided message.462feedback, and continues the model from the hook-provided message.

313 463 


322 472 

323### UserPromptSubmit473### UserPromptSubmit

324 474 

325`matcher` is not currently used for this event.475`matcher` isn't currently used for this event.

326 476 

327Fields in addition to [Common input fields](#common-input-fields):477Fields in addition to [Common input fields](#common-input-fields):

328 478 

329| Field | Type | Meaning |479| Field | Type | Meaning |

330| --- | --- | --- |480| --- | --- | --- |

331| `turn_id` | `string` | Codex-specific extension. Active Codex turn id |481| `turn_id` | `string` | Codex-specific extension. Active Codex turn id |

332| `prompt` | `string` | User prompt that is about to be sent |482| `prompt` | `string` | User prompt that's about to be sent |

333 483 

334Plain text on `stdout` is added as extra developer context.484Plain text on `stdout` is added as extra developer context.

335 485 


360 510 

361### Stop511### Stop

362 512 

363`matcher` is not currently used for this event.513`matcher` isn't currently used for this event.

364 514 

365Fields in addition to [Common input fields](#common-input-fields):515Fields in addition to [Common input fields](#common-input-fields):

366 516 


385 535 

386You can also use exit code `2` and write the continuation reason to `stderr`.536You can also use exit code `2` and write the continuation reason to `stderr`.

387 537 

388For this event, `decision: "block"` does not reject the turn. Instead, it tells538For this event, `decision: "block"` doesn't reject the turn. Instead, it tells

389Codex to continue and automatically creates a new continuation prompt that acts539Codex to continue and automatically creates a new continuation prompt that acts

390as a new user prompt, using your `reason` as that prompt text.540as a new user prompt, using your `reason` as that prompt text.

391 541