SpyBara
Go Premium Account
2026
13 Mar 2026, 18:15
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
8 Apr 2026, 18:32
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 +398 −0 added

Details

1# Hooks

2 

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

4disabled.

5 

6Hooks are an extensibility framework for Codex. They allow

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

8 

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

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

11- Summarize conversations to create persistent memories automatically

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

13- Customize prompting when in a certain directory

14 

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

16 

17```toml

18[features]

19codex_hooks = true

20```

21 

22Runtime behavior to keep in mind:

23 

24- Matching hooks from multiple files all run.

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

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

27- `PreToolUse`, `PostToolUse`, `UserPromptSubmit`, and `Stop` run at turn

28 scope.

29- Hooks are currently disabled on Windows.

30 

31## Where Codex looks for hooks

32 

33Codex discovers `hooks.json` next to active config layers.

34 

35In practice, the two most useful locations are:

36 

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

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

39 

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

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

42 

43## Config shape

44 

45Hooks are organized in three levels:

46 

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

48- A matcher group that decides when that event matches

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

50 

51```json

52{

53 "hooks": {

54 "SessionStart": [

55 {

56 "matcher": "startup|resume",

57 "hooks": [

58 {

59 "type": "command",

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

61 "statusMessage": "Loading session notes"

62 }

63 ]

64 }

65 ],

66 "PreToolUse": [

67 {

68 "matcher": "Bash",

69 "hooks": [

70 {

71 "type": "command",

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

73 "statusMessage": "Checking Bash command"

74 }

75 ]

76 }

77 ],

78 "PostToolUse": [

79 {

80 "matcher": "Bash",

81 "hooks": [

82 {

83 "type": "command",

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

85 "statusMessage": "Reviewing Bash output"

86 }

87 ]

88 }

89 ],

90 "UserPromptSubmit": [

91 {

92 "hooks": [

93 {

94 "type": "command",

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

96 }

97 ]

98 }

99 ],

100 "Stop": [

101 {

102 "hooks": [

103 {

104 "type": "command",

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

106 "timeout": 30

107 }

108 ]

109 }

110 ]

111 }

112}

113```

114 

115Notes:

116 

117- `timeout` is in seconds.

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

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

120- `statusMessage` is optional.

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

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

123 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.

125 

126## Matcher patterns

127 

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

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

130event.

131 

132Only some current Codex events honor `matcher`:

133 

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

135| --- | --- | --- |

136| `PostToolUse` | tool name | Current Codex runtime only emits `Bash`. |

137| `PreToolUse` | tool name | Current Codex runtime only emits `Bash`. |

138| `SessionStart` | start source | Current runtime values are `startup` and `resume`. |

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

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

141 

142Examples:

143 

144- `Bash`

145- `startup|resume`

146- `Edit|Write`

147 

148That last example is still a valid regex, but current Codex `PreToolUse` and

149`PostToolUse` events only emit `Bash`, so it will not match anything today.

150 

151## Common input fields

152 

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

154 

155These are the shared fields you will usually use:

156 

157| Field | Type | Meaning |

158| --- | --- | --- |

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

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

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

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

163| `model` | `string` | Active model slug |

164 

165Turn-scoped hooks list `turn_id` in their event-specific tables.

166 

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

168 

169## Common output fields

170 

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

172fields:

173 

174```json

175{

176 "continue": true,

177 "stopReason": "optional",

178 "systemMessage": "optional",

179 "suppressOutput": false

180}

181```

182 

183| Field | Effect |

184| ---------------- | ----------------------------------------------- |

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

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

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

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

189 

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

191 

192`PreToolUse` supports `systemMessage`, but `continue`, `stopReason`, and

193`suppressOutput` are not currently supported for that event.

194 

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

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

197 

198## Hooks

199 

200### SessionStart

201 

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

203 

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

205 

206| Field | Type | Meaning |

207| --- | --- | --- |

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

209 

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

211 

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

213hook-specific shape:

214 

215```json

216{

217 "hookSpecificOutput": {

218 "hookEventName": "SessionStart",

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

220 }

221}

222```

223 

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

225 

226### PreToolUse

227 

228Currently `PreToolUse` only supports Bash tool interception. The model can

229still work around this by writing its own script to disk and then running that

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

231enforcement boundary.

232 

233`matcher` is applied to `tool_name`, which currently always equals `Bash`.

234 

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

236 

237| Field | Type | Meaning |

238| --- | --- | --- |

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

240| `tool_name` | `string` | Currently always `Bash` |

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

242| `tool_input.command` | `string` | Shell command Codex is about to run |

243 

244Plain text on `stdout` is ignored.

245 

246JSON on `stdout` can use `systemMessage` and can block a Bash command with this

247hook-specific shape:

248 

249```json

250{

251 "hookSpecificOutput": {

252 "hookEventName": "PreToolUse",

253 "permissionDecision": "deny",

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

255 }

256}

257```

258 

259Codex also accepts this older block shape:

260 

261```json

262{

263 "decision": "block",

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

265}

266```

267 

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

269 

270`permissionDecision: "allow"` and `"ask"`, legacy `decision: "approve"`,

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

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

273 

274### PostToolUse

275 

276Currently `PostToolUse` only supports Bash tool results. It is not limited to

277commands that exit successfully: non-interactive `exec_command` calls can still

278trigger `PostToolUse` when Codex emits a Bash post-tool payload. It cannot undo

279side effects from the command that already ran.

280 

281`matcher` is applied to `tool_name`, which currently always equals `Bash`.

282 

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

284 

285| Field | Type | Meaning |

286| --- | --- | --- |

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

288| `tool_name` | `string` | Currently always `Bash` |

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

290| `tool_input.command` | `string` | Shell command Codex just ran |

291| `tool_response` | `JSON value` | Bash tool output payload. Today this is usually a JSON string |

292 

293Plain text on `stdout` is ignored.

294 

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

296 

297```json

298{

299 "decision": "block",

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

301 "hookSpecificOutput": {

302 "hookEventName": "PostToolUse",

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

304 }

305}

306```

307 

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

309 

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

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

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

313 

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

315 

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

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

318your feedback or stop text and continue from there.

319 

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

321so they fail open.

322 

323### UserPromptSubmit

324 

325`matcher` is not currently used for this event.

326 

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

328 

329| Field | Type | Meaning |

330| --- | --- | --- |

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

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

333 

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

335 

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

337this hook-specific shape:

338 

339```json

340{

341 "hookSpecificOutput": {

342 "hookEventName": "UserPromptSubmit",

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

344 }

345}

346```

347 

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

349 

350To block the prompt, return:

351 

352```json

353{

354 "decision": "block",

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

356}

357```

358 

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

360 

361### Stop

362 

363`matcher` is not currently used for this event.

364 

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

366 

367| Field | Type | Meaning |

368| --- | --- | --- |

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

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

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

372 

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

374for this event.

375 

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

377Codex going, return:

378 

379```json

380{

381 "decision": "block",

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

383}

384```

385 

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

387 

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

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

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

391 

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

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

394 

395## Schemas

396 

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

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