Coding agents ask Paidwen before each tool, through their hook.
Codex, Claude Code and other coding agents run a hook before each tool. The hook runs the Paidwen command, paidwen check, which asks Paidwen and then allows or blocks the tool. Your rules, permissions, approvals, budgets, quarantine and journal apply to coding agents as they apply to any agent.
You install the command and sign in once.
curl -fsSL https://paidwen.com/install.sh | shpowershell -ExecutionPolicy ByPass -c "irm https://paidwen.com/install.ps1 | iex"The script downloads the archive of your system from the latest release, checks its SHA-256, and installs paidwen. Then sign in:
paidwen loginThe command shows a code and opens the dashboard. You check the code, choose your organization and authorize. Paidwen creates your agent, named by your email, and the command keeps its key on this machine, readable by you only. No key is copied. paidwen status shows the agent this machine acts as.
The hook reads the answer of Paidwen as JSON.
Codex and Claude Code read the same JSON answer from a hook. Save this template as ~/.paidwen/hook-answer.tmpl:
{{ if not .Allowed }}{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":{{ json .Reason }}}}{{ end }}-output-file reads it, prints the refusal and its reason when Paidwen blocks the tool, and prints nothing when the tool may run. The reason goes to the model, for example Paidwen blocked Bash: the rule "No force push" refuses it. A file keeps the quotes of the template: on Windows, the shell that runs a hook removes the quotes of a template written in the line itself.
Codex runs the command before and after each tool.
Add these lines to ~/.codex/hooks.json, then trust them once with /hooks in Codex:
{
"hooks": {
"PreToolUse": [
{
"matcher": ".*",
"hooks": [
{
"type": "command",
"command": "paidwen check -tool /tool_name -params /tool_input -param cwd=/cwd -session /session_id -call /tool_use_id -wait 0s -output-file ~/.paidwen/hook-answer.tmpl",
"timeout": 60
}
]
}
],
"PostToolUse": [
{
"matcher": ".*",
"hooks": [
{
"type": "command",
"command": "paidwen record -tool /tool_name -params /tool_input -session /session_id -call /tool_use_id -result /tool_response",
"timeout": 60
}
]
}
]
}
}-tool,-params,-param,-sessionand-callread the JSON that Codex gives the hook, by JSON pointers. The command knows no coding agent: the pointers say where each field is.- While a hook waits, Codex tells the model that the command still runs, and the model moves on.
-wait 0sanswers at once: when a rule asks for an approval, the model reads that the approval waits in Paidwen, and the same command runs once someone approves it. - A company puts the same hooks in the
requirements.tomlit manages, withallow_managed_hooks_only = true. Codex trusts managed hooks without/hooks, and no developer removes them.
Claude Code runs the command before and after each tool.
Add these lines to ~/.claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"hooks": [
{
"type": "command",
"command": "MSYS_NO_PATHCONV=1 paidwen check -tool /tool_name -params /tool_input -param cwd=/cwd -session /session_id -call /tool_use_id -wait 580s -output-file '~/.paidwen/hook-answer.tmpl'",
"timeout": 600,
"onFailure": "block"
}
]
}
],
"PostToolUse": [
{
"hooks": [
{
"type": "command",
"command": "MSYS_NO_PATHCONV=1 paidwen record -tool /tool_name -params /tool_input -session /session_id -call /tool_use_id -result /tool_response"
}
]
}
]
},
"otelHeadersHelper": "paidwen headers",
"env": {
"CLAUDE_CODE_ENABLE_TELEMETRY": "1",
"OTEL_METRICS_EXPORTER": "otlp",
"OTEL_EXPORTER_OTLP_METRICS_PROTOCOL": "http/protobuf",
"OTEL_EXPORTER_OTLP_METRICS_ENDPOINT": "https://api.paidwen.com/v1/metrics",
"OTEL_METRIC_EXPORT_INTERVAL": "10000"
}
}- Claude Code gives the hook the same fields as Codex, so the line reads them with the same pointers.
- Claude Code runs its hooks in a Bash shell, Git Bash on Windows. Git Bash turns an argument that starts with
/into a Windows path, andMSYS_NO_PATHCONV=1keeps the pointers as they are. The setting changes nothing on macOS and Linux. - The path of the template stays in single quotes. Without them, Git Bash turns
~into/c/Users/..., a path that the command cannot open on Windows. The command reads~itself, on every system. - Claude Code waits for its hooks, 600 seconds by default.
-wait 580skeeps a wait for a human approval under this timeout. "onFailure": "block"blocks the tool when the command fails or does not answer in time. Without it, Claude Code lets the tool run.paidwen recordsends the result of the tool to the journal. The firewall reads it.- The
envblock sends the cost of each model call to Paidwen every 10 seconds, so that budgets count it.paidwen headersgives Claude Code the key, which never appears in the settings.
A company sets the line once for every developer.
Claude Code reads managed settings that a developer cannot change:
/Library/Application Support/ClaudeCode/managed-settings.jsonon macOS;/etc/claude-code/managed-settings.jsonon Linux and WSL;C:\Program Files\ClaudeCode\managed-settings.jsonon Windows.
Put the same hooks, otelHeadersHelper and env there, with "allowManagedHooksOnly": true, so that no developer removes the hook. Each developer then runs paidwen login once. Until a developer signs in, the command blocks every tool and asks for paidwen login. One line covers 1 developer as well as 10,000.
Rules read the tool and its parameters.
Paidwen knows no tool. Codex and Claude Code name their shell tool Bash, Claude Code names its other tools Edit, Write or Read, and you write rules on the fields they send. On Windows, Codex runs its commands in PowerShell. These rules are examples.
Block a force push:
tool.name == "Bash" && params.command.matches("git\\s+push\\b.*\\s(-f|-{2}force)(\\s|=|$)")Ask for an approval before an MCP tool sends, posts, comments, replies, reviews, merges or approves. Claude Code names an MCP tool mcp__ followed by its server and its tool, and this rule reads the name of the tool only, so a server named postgres or sendgrid does not match:
tool.name.matches("^mcp__.+__.*(send|post|comment|reply|review|merge|approve)")Ask for an approval before the GitHub command comments, reviews or merges a pull request or an issue:
tool.name == "Bash" && params.command.matches("\\bgh\\s+(pr|issue)\\s+(comment|review|merge)\\b")Paidwen only sees the calls of tools. A script that the agent writes to send an email by itself needs its own rule.
Ask for an approval before a file or a folder is deleted, by rm, rmdir, del or PowerShell:
tool.name == "Bash" && params.command.matches("(?i)\\b(rm|rmdir|del|remove-item)\\b")Block a change to an environment file:
(tool.name == "Write" || tool.name == "Edit") && params.file_path.endsWith(".env")Block commands outside the folder of your projects:
tool.name == "Bash" && !params.cwd.startsWith("/home/lea/projects")Put a new rule in observation first: it records what it would have done, and blocks nothing.
An approval waits in Paidwen until someone answers.
When a rule asks for an approval, the command asks the approvers in the dashboard, Slack, Teams or email. Past -wait, it blocks the tool and tells the model that the approval waits in Paidwen, with its deadline. When the agent asks for the same tool with the same parameters in the same session, the command takes up the same approval: the tool runs at once if someone approved it, and no second approval opens.
A budget stops the coding agent.
Claude Code counts the cost of its model calls in the metric claude_code.cost.usage, in US dollars. Paidwen counts every monotonic sum in US dollars that it receives, whatever its name, in the budget of the agent and in Costs. Give the agent a daily or monthly cap on the Agents screen: once the cap is spent, the command blocks the next tool.
A git hook and a CI job use the same command.
A pre-push hook builds a small JSON and asks Paidwen before each push:
#!/bin/sh
while read local_ref local_sha remote_ref remote_sha; do
printf '{"remote":"%s","local_ref":"%s","remote_ref":"%s"}' "$1" "$local_ref" "$remote_ref" | paidwen check -tool-name git.push -params "" || exit 1
doneWithout a template, paidwen check exits with 0 to let the action run, and with 2 to block it, the reason on its error output. The JSON holds only the name of the remote and the references, which never hold a quote nor a backslash. -tool-name gives a fixed name, and -params "" takes the whole JSON as the parameters. A rule such as tool.name == "git.push" && params.remote_ref == "refs/heads/main" then blocks a push to main.
In a CI job, the coding agent reads the same hooks, and the command reads the key of the CI agent from the PAIDWEN_API_KEY environment variable, set from a secret. With workload identity federation, PAIDWEN_IDENTITY_TOKEN_FILE points to the token your platform signs, and no key is stored.
The template reads the answer of Paidwen.
A hook that reads its answer as JSON gets it from a Go template, given by -output-file or, on a shell that keeps quotes, by -output. The command then exits with 0, and prints nothing when the template gives only spaces. The template reads .Allowed, .Reason, .Decision and .Approval, and json writes a value as JSON.
The command has limits.
- A hook is a guardrail, not a security barrier, because an agent can hide a command in a script.
- The command sees the tools that the hook shows it. A tool that runs at the provider of the model, such as a web search, escapes it.
- A rule on a command reads its text:
git push -fand a long force flag are written differently, and a rule must cover both. - Codex refuses a recursive deletion by itself in
codex exec, whatever Paidwen answers. - Codex lets the tool run when its hook fails, for example when
paidwenis missing, because it has no setting that blocks the tool instead. - Codex signed in with ChatGPT exports no cost in US dollars: the budget of a Codex agent does not see its model calls.
- Each tool waits for the round trip to Paidwen, over a new connection, to its servers in California: 0.7 s in median from Madagascar, where Paidwen measured it, and less from closer places.
- A request whose parameters exceed 1 MiB is refused, and the command blocks the tool.
- The budget sees the cost of Claude Code after the export of the metric, 10 seconds with the line above.
paidwen logoutremoves the key from the machine without revoking it. Revoke it on the Agents screen. An agent holds two active keys at most.- Paidwen has run this page with Codex on Windows, a git hook and a CI configuration. The Claude Code lines follow its documentation, with the same fields and the same JSON answer. Cursor needs a paid plan for its hooks, and Paidwen has not run it.