SpyBara
Go Premium Account
2026
23 Feb 2026, 18:27
18 May 2026, 22:01 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
18 May 2026, 22:01
18 May 2026, 22:01 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 Mon 18 22:01

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 +645 −0 added

Details

1# Hooks

2 

3Hooks are an extensibility framework for Codex. They allow

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

5 

6- Send the conversation to a custom logging/analytics engine

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

8- Summarize conversations to create persistent memories automatically

9- Run a custom validation check when a conversation turn stops, enforcing standards

10- Customize prompting when in a certain directory

11 

12Hooks are enabled by default. If you need to turn them off in `config.toml`,

13set:

14 

15```toml

16[features]

17hooks = false

18```

19 

20Use `hooks` as the canonical feature key. `codex_hooks` still works as a

21deprecated alias.

22 

23Admins can force hooks off the same way in `requirements.toml` with

24`[features].hooks = false`.

25 

26Runtime behavior to keep in mind:

27 

28- Matching hooks from multiple files all run.

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

30 so one hook cannot prevent another matching hook from starting.

31- Non-managed command hooks must be reviewed and trusted before they run.

32- `PreToolUse`, `PermissionRequest`, `PostToolUse`, `UserPromptSubmit`, and

33 `Stop` run at turn scope.

34 

35## Where Codex looks for hooks

36 

37Codex discovers hooks next to active config layers in either of these forms:

38 

39- `hooks.json`

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

41 

42Installed plugins can also bundle lifecycle config through their plugin

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

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

45plugin packaging rules.

46 

47In practice, the four most useful locations are:

48 

49- `~/.codex/hooks.json`

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

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

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

53 

54If more than one hook source exists, Codex loads all matching hooks.

55Higher-precedence config layers don't replace lower-precedence hooks.

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

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

58 

59Plugin hooks are off by default in this release. If

60`[features].plugin_hooks = true`, Codex can also discover hooks bundled with

61enabled plugins. Otherwise, enabled plugins won't run bundled hooks.

62 

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

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

65active config layers.

66 

67## Review and manage hooks

68 

69Codex lists configured hooks before deciding which ones can run. Use `/hooks`

70in the CLI to inspect hook sources, review new or changed hooks, trust hooks, or

71disable individual non-managed hooks. If hooks need review at startup, Codex

72prints a warning that tells you to open `/hooks`.

73 

74Managed hooks from system, MDM, cloud, or `requirements.toml` sources are marked

75as managed, trusted by policy, and can't be disabled from the user hook browser.

76 

77## Config shape

78 

79Hooks are organized in three levels:

80 

81- A hook event such as `PreToolUse`, `PostToolUse`, or `Stop`

82- A matcher group that decides when that event matches

83- One or more hook handlers that run when the matcher group matches

84 

85```json

86{

87 "hooks": {

88 "SessionStart": [

89 {

90 "matcher": "startup|resume",

91 "hooks": [

92 {

93 "type": "command",

94 "command": "python3 ~/.codex/hooks/session_start.py",

95 "statusMessage": "Loading session notes"

96 }

97 ]

98 }

99 ],

100 "PreToolUse": [

101 {

102 "matcher": "Bash",

103 "hooks": [

104 {

105 "type": "command",

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

107 "statusMessage": "Checking Bash command"

108 }

109 ]

110 }

111 ],

112 "PermissionRequest": [

113 {

114 "matcher": "Bash",

115 "hooks": [

116 {

117 "type": "command",

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

119 "statusMessage": "Checking approval request"

120 }

121 ]

122 }

123 ],

124 "PostToolUse": [

125 {

126 "matcher": "Bash",

127 "hooks": [

128 {

129 "type": "command",

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

131 "statusMessage": "Reviewing Bash output"

132 }

133 ]

134 }

135 ],

136 "UserPromptSubmit": [

137 {

138 "hooks": [

139 {

140 "type": "command",

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

142 }

143 ]

144 }

145 ],

146 "Stop": [

147 {

148 "hooks": [

149 {

150 "type": "command",

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

152 "timeout": 30

153 }

154 ]

155 }

156 ]

157 }

158}

159```

160 

161Notes:

162 

163- `timeout` is in seconds.

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

165- `statusMessage` is optional.

166- `async` is parsed, but async command hooks aren't supported yet. Codex skips

167 handlers with `async: true`.

168- Only `type: "command"` handlers run today. `prompt` and `agent` handlers are

169 parsed but skipped.

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

171- For repo-local hooks, prefer resolving from the git root instead of using a

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

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

174 

175Equivalent inline TOML in `config.toml`:

176 

177```toml

178[[hooks.PreToolUse]]

179matcher = "^Bash$"

180 

181[[hooks.PreToolUse.hooks]]

182type = "command"

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

184timeout = 30

185statusMessage = "Checking Bash command"

186 

187[[hooks.PostToolUse]]

188matcher = "^Bash$"

189 

190[[hooks.PostToolUse.hooks]]

191type = "command"

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

193timeout = 30

194statusMessage = "Reviewing Bash output"

195```

196 

197## Managed hooks from `requirements.toml`

198 

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

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

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

202To enforce managed hooks even for users who disabled hooks locally, pin

203`[features].hooks = true` in `requirements.toml` alongside `[hooks]`.

204 

205```toml

206[features]

207hooks = true

208 

209[hooks]

210managed_dir = "/enterprise/hooks"

211windows_managed_dir = 'C:\enterprise\hooks'

212 

213[[hooks.PreToolUse]]

214matcher = "^Bash$"

215 

216[[hooks.PreToolUse.hooks]]

217type = "command"

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

219timeout = 30

220statusMessage = "Checking managed Bash command"

221```

222 

223Notes for managed hooks:

224 

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

226- `windows_managed_dir` is used on Windows.

227- Codex doesn't distribute the scripts in `managed_dir`; your enterprise

228 tooling must install and update them separately.

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

230 managed directory.

231 

232## Plugin-bundled hooks

233 

234Plugin-bundled hooks are opt-in for this release. When

235`[features].plugin_hooks = true` and a plugin is enabled, Codex can load

236lifecycle hooks from that plugin alongside user, project, and managed hooks.

237 

238```toml

239[features]

240plugin_hooks = true

241```

242 

243By default, Codex looks for `hooks/hooks.json` inside the plugin root. A plugin

244manifest can override that default with a `hooks` entry in

245`.codex-plugin/plugin.json`. The manifest entry can be a `./`-prefixed path, an

246array of `./`-prefixed paths, an inline hooks object, or an array of inline

247hooks objects.

248 

249```json

250{

251 "name": "repo-policy",

252 "hooks": "./hooks/hooks.json"

253}

254```

255 

256Manifest hook paths are resolved relative to the plugin root and must stay

257inside that root. If a manifest defines `hooks`, Codex uses those manifest

258entries instead of the default `hooks/hooks.json`.

259 

260Plugin hook commands receive these environment variables:

261 

262- `PLUGIN_ROOT` is a Codex-specific extension that points to the installed

263 plugin root.

264- `PLUGIN_DATA` is a Codex-specific extension that points to the plugin's

265 writable data directory.

266- Codex also sets `CLAUDE_PLUGIN_ROOT` and `CLAUDE_PLUGIN_DATA` for

267 compatibility with existing plugin hooks.

268 

269Plugin hooks use the same event schema as other hooks. They are non-managed

270hooks, so they require trust review before they run.

271 

272## Matcher patterns

273 

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

275`""`, or omit `matcher` entirely to match every occurrence of a supported

276event.

277 

278Only some current Codex events honor `matcher`:

279 

280| Event | What `matcher` filters | Notes |

281| ------------------- | ---------------------- | ------------------------------------------------------------ |

282| `PermissionRequest` | tool name | Support includes `Bash`, `apply_patch`\*, and MCP tool names |

283| `PostToolUse` | tool name | Support includes `Bash`, `apply_patch`\*, and MCP tool names |

284| `PreToolUse` | tool name | Support includes `Bash`, `apply_patch`\*, and MCP tool names |

285| `SessionStart` | start source | Current runtime values are `startup`, `resume`, and `clear` |

286| `UserPromptSubmit` | not supported | Any configured `matcher` is ignored for this event |

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

288 

289\*For `apply_patch`, `matcher` values can also use `Edit` or `Write`.

290 

291Examples:

292 

293- `Bash`

294- `^apply_patch$`

295- `Edit|Write`

296- `mcp__filesystem__read_file`

297- `mcp__filesystem__.*`

298- `startup|resume|clear`

299 

300## Common input fields

301 

302Every command hook receives one JSON object on `stdin`.

303 

304These are the shared fields you will usually use:

305 

306| Field | Type | Meaning |

307| ----------------- | ---------------- | ------------------------------------------- |

308| `session_id` | `string` | Current session or thread id. |

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

310| `cwd` | `string` | Working directory for the session |

311| `hook_event_name` | `string` | Current hook event name |

312| `model` | `string` | Codex-specific extension. Active model slug |

313 

314Turn-scoped hooks list `turn_id` as a Codex-specific extension in their

315event-specific tables.

316 

317`SessionStart`, `PreToolUse`, `PermissionRequest`, `PostToolUse`,

318`UserPromptSubmit`, and `Stop` also include `permission_mode`, which describes

319the current permission mode as `default`, `acceptEdits`, `plan`, `dontAsk`, or

320`bypassPermissions`.

321 

322`transcript_path` points to a conversation transcript for convenience, but the

323transcript format is not a stable interface for hooks and may change over time.

324 

325If you need the full wire format, see [Schemas](#schemas).

326 

327## Common output fields

328 

329`SessionStart`, `UserPromptSubmit`, and `Stop` support these shared JSON

330fields:

331 

332```json

333{

334 "continue": true,

335 "stopReason": "optional",

336 "systemMessage": "optional",

337 "suppressOutput": false

338}

339```

340 

341| Field | Effect |

342| ---------------- | ----------------------------------------------- |

343| `continue` | If `false`, marks that hook run as stopped |

344| `stopReason` | Recorded as the reason for stopping |

345| `systemMessage` | Surfaced as a warning in the UI or event stream |

346| `suppressOutput` | Parsed today but not yet implemented |

347 

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

349 

350`PreToolUse` and `PermissionRequest` support `systemMessage`, but `continue`,

351`stopReason`, and `suppressOutput` aren't currently supported for those events.

352 

353`PostToolUse` supports `systemMessage`, `continue: false`, and `stopReason`.

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

355 

356## Hooks

357 

358### SessionStart

359 

360`matcher` is applied to `source` for this event.

361 

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

363 

364| Field | Type | Meaning |

365| -------- | -------- | -------------------------------------------------------- |

366| `source` | `string` | How the session started: `startup`, `resume`, or `clear` |

367 

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

369 

370JSON on `stdout` supports [Common output fields](#common-output-fields) and this

371hook-specific shape:

372 

373```json

374{

375 "hookSpecificOutput": {

376 "hookEventName": "SessionStart",

377 "additionalContext": "Load the workspace conventions before editing."

378 }

379}

380```

381 

382That `additionalContext` text is added as extra developer context.

383 

384### PreToolUse

385 

386`PreToolUse` can intercept Bash, file edits performed through `apply_patch`,

387and MCP tool calls. It's still a guardrail rather than a complete enforcement

388boundary because Codex can often perform equivalent work through another

389supported tool path.

390 

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

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

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

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

395 

396`matcher` is applied to `tool_name` and matcher aliases. For file edits through

397`apply_patch`, `matcher` values can use `apply_patch`, `Edit`, or `Write`; hook input

398still reports `tool_name: "apply_patch"`.

399 

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

401 

402| Field | Type | Meaning |

403| ------------- | ------------ | ---------------------------------------------------------------------------------------------------------- |

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

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

406| `tool_use_id` | `string` | Tool-call id for this invocation |

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

408 

409Plain text on `stdout` is ignored.

410 

411JSON on `stdout` can use `systemMessage`. To deny a supported tool call, return

412this hook-specific shape:

413 

414```json

415{

416 "hookSpecificOutput": {

417 "hookEventName": "PreToolUse",

418 "permissionDecision": "deny",

419 "permissionDecisionReason": "Destructive command blocked by hook."

420 }

421}

422```

423 

424Codex also accepts this older block shape:

425 

426```json

427{

428 "decision": "block",

429 "reason": "Destructive command blocked by hook."

430}

431```

432 

433You can also use exit code `2` and write the blocking reason to `stderr`.

434 

435To add model-visible context without blocking, return

436`hookSpecificOutput.additionalContext`:

437 

438```json

439{

440 "hookSpecificOutput": {

441 "hookEventName": "PreToolUse",

442 "additionalContext": "The pending command touches generated files."

443 }

444}

445```

446 

447`permissionDecision: "ask"`, legacy `decision: "approve"`, `updatedInput`,

448`continue: false`, `stopReason`, and `suppressOutput` are parsed but not

449supported yet, so they fail open.

450 

451### PermissionRequest

452 

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

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

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

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

457 

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

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

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

461 

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

463 

464| Field | Type | Meaning |

465| ------------------------ | ---------------- | --------------------------------------------------------------------------------------------------------- |

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

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

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

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

470 

471Plain text on `stdout` is ignored.

472 

473Some tool inputs may include a human-readable description, but don't rely on a

474`tool_input.description` field for every tool.

475 

476To approve the request, return:

477 

478```json

479{

480 "hookSpecificOutput": {

481 "hookEventName": "PermissionRequest",

482 "decision": {

483 "behavior": "allow"

484 }

485 }

486}

487```

488 

489To deny the request, return:

490 

491```json

492{

493 "hookSpecificOutput": {

494 "hookEventName": "PermissionRequest",

495 "decision": {

496 "behavior": "deny",

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

498 }

499 }

500}

501```

502 

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

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

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

506 

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

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

509closed today.

510 

511### PostToolUse

512 

513`PostToolUse` runs after supported tools produce output, including Bash,

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

515exit with a non-zero status. It can't undo side effects from the tool that

516already ran.

517 

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

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

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

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

522 

523`matcher` is applied to `tool_name` and matcher aliases. For file edits through

524`apply_patch`, `matcher` values can use `apply_patch`, `Edit`, or `Write`; hook input

525still reports `tool_name: "apply_patch"`.

526 

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

528 

529| Field | Type | Meaning |

530| --------------- | ------------ | ---------------------------------------------------------------------------------------------------------- |

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

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

533| `tool_use_id` | `string` | Tool-call id for this invocation |

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

535| `tool_response` | `JSON value` | Tool-specific output. For MCP tools, this is the MCP call result. |

536 

537Plain text on `stdout` is ignored.

538 

539JSON on `stdout` can use `systemMessage` and this hook-specific shape:

540 

541```json

542{

543 "decision": "block",

544 "reason": "The Bash output needs review before continuing.",

545 "hookSpecificOutput": {

546 "hookEventName": "PostToolUse",

547 "additionalContext": "The command updated generated files."

548 }

549}

550```

551 

552That `additionalContext` text is added as extra developer context.

553 

554For this event, `decision: "block"` doesn't undo the completed Bash command.

555Instead, Codex records the feedback, replaces the tool result with that

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

557 

558You can also use exit code `2` and write the feedback reason to `stderr`.

559 

560To stop normal processing of the original tool result after the command has

561already run, return `continue: false`. Codex will replace the tool result with

562your feedback or stop text and continue from there.

563 

564`updatedMCPToolOutput` and `suppressOutput` are parsed but not supported yet,

565so they fail open.

566 

567### UserPromptSubmit

568 

569`matcher` isn't currently used for this event.

570 

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

572 

573| Field | Type | Meaning |

574| --------- | -------- | ---------------------------------------------- |

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

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

577 

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

579 

580JSON on `stdout` supports [Common output fields](#common-output-fields) and

581this hook-specific shape:

582 

583```json

584{

585 "hookSpecificOutput": {

586 "hookEventName": "UserPromptSubmit",

587 "additionalContext": "Ask for a clearer reproduction before editing files."

588 }

589}

590```

591 

592That `additionalContext` text is added as extra developer context.

593 

594To block the prompt, return:

595 

596```json

597{

598 "decision": "block",

599 "reason": "Ask for confirmation before doing that."

600}

601```

602 

603You can also use exit code `2` and write the blocking reason to `stderr`.

604 

605### Stop

606 

607`matcher` isn't currently used for this event.

608 

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

610 

611| Field | Type | Meaning |

612| ------------------------ | ---------------- | ------------------------------------------------- |

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

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

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

616 

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

618for this event.

619 

620JSON on `stdout` supports [Common output fields](#common-output-fields). To keep

621Codex going, return:

622 

623```json

624{

625 "decision": "block",

626 "reason": "Run one more pass over the failing tests."

627}

628```

629 

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

631 

632For this event, `decision: "block"` doesn't reject the turn. Instead, it tells

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

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

635 

636If any matching `Stop` hook returns `continue: false`, that takes precedence

637over continuation decisions from other matching `Stop` hooks.

638 

639## Schemas

640 

641The linked `main` branch schemas may include hook fields that are not in the

642 current release. Use this page as the release behavior reference.

643 

644If you need the exact current wire format, see the generated schemas in the

645[Codex GitHub repository](https://github.com/openai/codex/tree/main/codex-rs/hooks/schema/generated).