Codex
Codex hooks: hooks.json, PreToolUse allow/deny, exit code 2 and config.toml [hooks]
How do I run a script before or after Codex uses a tool, and how do I block a command with a hook?
Codex hooks are on by default. Put them in ~/.codex/hooks.json or <repo>/.codex/hooks.json, or as [hooks] tables in config.toml. Each event, such as PreToolUse, takes a list of matcher groups with command handlers. Codex sends the event as JSON on stdin. To block a tool call, exit 2 with the reason on stderr, or print hookSpecificOutput.permissionDecision "deny". Project hooks run only after the project's .codex/ folder is trusted.
Compare: hooks in every AI coding agent →
Codex settings change often
Get one email a week when a setting, flag or permission rule changes, checked against the vendor docs.
Where Codex reads hooks
- ~/.codex/hooks.json or inline [hooks] tables in ~/.codex/config.toml (user).
- <repo>/.codex/hooks.json or <repo>/.codex/config.toml (project). Project hooks load only when that .codex/ layer is trusted.
- Enabled plugins, through hooks/hooks.json or a hooks entry in .codex-plugin/plugin.json.
- Managed hooks for an organization, through [hooks] in requirements.toml.
- Higher-precedence layers do not replace hooks from lower ones; they add to them. Hooks that are not managed must be reviewed and trusted before they run.
- Hooks are enabled by default. Turn them off with hooks = false under [features] in config.toml (codex_hooks is a deprecated alias).
Events
- SessionStart: a session or subagent starts. The matcher filters on the source: startup, resume, clear, compact or fork.
- UserPromptSubmit: before your prompt is sent.
- PreToolUse: before a tool call, covering Bash, apply_patch, MCP tools and other local function tools.
- PermissionRequest: when Codex is about to ask you for approval.
- PostToolUse: after a supported tool produces output.
- PreCompact and PostCompact: before and after the conversation is compacted (matcher: manual or auto).
- SubagentStop, Stop, Interrupt (an active main-thread turn is interrupted) and SessionEnd (the main thread ends).
Block a shell command
.codex/hooks.json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python3 .codex/hooks/check_bash.py",
"timeout": 30,
"statusMessage": "Checking command"
}
]
}
]
}
}The hook gets JSON on stdin with session_id, cwd, hook_event_name, model and, for tool events, tool_name and tool_input. To block, exit 2 and write the reason to stderr, or exit 0 and print a decision:
PreToolUse hook output (stdout)
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "git push is not allowed from Codex"
}
}Matchers, handlers and timeouts
- matcher is a regex. "*", an empty string or no matcher matches everything. It is ignored for UserPromptSubmit, Stop and Interrupt.
- For PreToolUse, PostToolUse and PermissionRequest the matcher is the tool name. For apply_patch you can also match Edit or Write.
- Handler types: command and mcp_tool (server, tool, input). prompt and agent handlers are parsed but skipped.
- commandWindows (command_windows in TOML) overrides the command on Windows. async: true runs the command in the background.
- timeout is in seconds and defaults to 600. SessionEnd and Interrupt default to 1 second and allow at most 3.
- additionalContextLimit caps the context a hook can add (default 2500 tokens; 0 passes the full context).
Exit codes and JSON output
- Exit 0 with no output: success, Codex continues.
- Exit 2: blocks on events that support it, such as PreToolUse, UserPromptSubmit, Stop and SubagentStop, with the reason taken from stderr. On PostToolUse it sends feedback.
- PreToolUse: permissionDecision is allow or deny. ask is not supported yet. updatedInput rewrites the tool input and works only with allow.
- PermissionRequest: hookSpecificOutput.decision with behavior allow or deny (and a message). Deny wins over allow.
- UserPromptSubmit, PostToolUse, SubagentStop and Stop: top-level decision "block" with a reason. On Stop, block does not reject the turn.
- On every event: continue, stopReason and systemMessage. additionalContext in hookSpecificOutput is added as developer context.
Get Codex settings changes by email
When a setting, flag or permission rule in this guide changes, it goes in the weekly AgentAtlas update, checked against the vendor docs. One email a week.
Sources
More on Codex
More Codex guides
- Codex CLI: approval and sandbox modes (and what --yolo does)
- Configure Codex with config.toml
- Codex AGENTS.md: global, project and override instruction files explained
- Codex MCP servers: codex mcp add and [mcp_servers] in config.toml
- Codex sandbox modes: read-only, workspace-write, danger-full-access and network access
- codex exec: run Codex non-interactively in scripts and CI with JSON output
- Codex skills: create SKILL.md, skill folder locations and $skill invocation
Other guides
Reviewed Oct 20, 2026. Settings change often; the linked vendor docs are the source of truth.