Skip to content

Hooks reference

Reference for declaring event hooks. A hook is a trigger of type event, so it’s stored in a project’s triggers block and managed through the trigger tools. For the conceptual model and a hands-on walkthrough, see Event hooks and Automating with hooks.

An event hook is one entry in the project’s triggers map, keyed by name. Each entry is when (trigger) + what (run) + enabled:

triggers:
tidy-up:
trigger:
type: event # discriminant: event | schedule | webhook
on: onArchive # for type: event — the lifecycle event
run:
prompt: "A chat was archived — jot a line in housekeeping.md."
# promptFile: tidy-up.md # XOR prompt; a .md under .paddock/triggers/, read fresh each fire
tools: [Read, Write] # the capability (herdctl allowed_tools); [] or omitted = tool-less
permissionMode: acceptEdits # default | acceptEdits | bypassPermissions | plan
model: claude-haiku-4-5-20251001 # optional; omit to inherit the project default
maxTurns: 30 # optional; default 30
maxSpawnDepth: 0 # optional; 0 = may not spawn sub-agents
session: new # new (fresh chat each fire) | resume (accrete into one)
enabled: false # new hooks default to false (disabled)

Field notes:

FieldNotes
trigger.typeevent for a hook. (schedule is a time-driven trigger; webhook is reserved and not yet fireable.)
trigger.onThe lifecycle event. onArchive (a chat is archived) is the event that fires a normal hook agent. The enum also accepts afterTurn (a user turn completed), but an afterTurn event trigger defines/customizes the post-turn curator (the sweeper) rather than a general hook.
run.prompt / run.promptFileExactly one is required. promptFile is a .md path under .paddock/triggers/, read fresh at fire time (git-tracked, agent-editable). Declaring both is a validation error and the whole trigger entry is dropped; via set_trigger, supplying one side clears the other.
run.toolsThe hook agent’s allow-list — its whole capability. Empty/omitted ⇒ a tool-less hook (reasoning only). The picker’s tool names come from Paddock’s grantable-tool catalog (GRANTABLE_TOOLS): Read, Glob, Grep, Edit, Write, NotebookEdit, Bash, WebFetch, WebSearch, Task, and more.
run.permissionModeClaude Code permission mode for the hook’s turns. bypassPermissions runs every tool unprompted — see the caution below.
run.modelModel override; absent inherits the project default.
run.maxTurnsUpper bound on agent turns in one fire. Default 30.
run.maxSpawnDepthBounds the hook’s own spawning of sub-chats.
run.sessionnew starts a fresh chat each fire; resume accretes into one owned session.
enabledA disabled hook is stored but never fired. New hooks default disabled.

A hook fires as its own agent, trigger-<slug>-<name> — so the tools above are what that agent is registered with (see #319 for how far that grant actually goes today), and the chat’s capability banner is projected from that same config.

The Triggers tab drives these; they’re here for completeness.

Method + pathDoes
GET /api/projects/:slug/triggersList the project’s triggers, plus the picker catalog: grantableTools, the available events, and triggerTypes.
GET /api/projects/:slug/triggers/:nameFetch one trigger (404 if undeclared).
PUT /api/projects/:slug/triggers/:nameCreate or replace one. Enable/disable is this same call with enabled flipped.
DELETE /api/projects/:slug/triggers/:nameDelete one.

With the opt-in enabled (see PADDOCK_HOOKS_MCP in Environment variables), Claude gets four mcp__paddock_manage__* tools. They manage all triggers — this reference covers the event (hook) shape. The three below shape a trigger; the fourth, run_trigger, fires one immediately.

Enumerate the current project’s triggers (hooks and schedules) with their capabilities and enabled state.

Create or update a trigger by name (create-or-update — there’s no separate enable/disable verb; flip enabled). A brand-new trigger defaults to enabled: false. Arguments are flat scalars; for an event hook:

ArgumentPurpose
nameThe trigger name (required).
typeevent for a hook.
eventThe lifecycle event, e.g. onArchive.
prompt or prompt_fileThe turn’s prompt inline, or a .md under .paddock/triggers/.
toolsThe capability allow-list. Accepts a JSON array or a comma/newline-separated string (the CLI-runtime MCP transport, which a hook fire uses, can flatten arrays); empty ⇒ tool-less.
permission_modedefault | acceptEdits | bypassPermissions | plan.
modelModel override.
max_turnsTurn bound (default 30).
max_spawn_depthBounds the hook’s own spawning.
sessionnew | resume.
enabledArm/disarm. Omit on an edit to leave the armed state unchanged.

An edit that omits a field leaves it unchanged; supplying prompt clears prompt_file and vice-versa (they’re mutually exclusive).

Delete a trigger by name.