Create and Manage Hooks via Portal
Two governance hooks using the portal UI: a Stop hook that enforces data formatting (markdown tables with bold headers), and a PostToolUse hook that blocks dangerous shell commands. You'll learn hooks at both the agent level (applies to everything) and the custom agent level (applies to one specific custom agent). Time: ~10 minutes.
Prerequisites
- An Azure SRE Agent in Running state
- Contributor role or higher on the SRE Agent resource
Hooks you previously created via the REST API tutorial appear automatically in the portal UI. You can manage them visually without reconfiguring anything.
Where hooks live in the portal
Hooks operate at two levels. This is a key architectural concept:
| Level | Location in portal | Scope | Use when |
|---|---|---|---|
| Agent level | Builder → Hooks | Applies to the entire agent — all threads and all custom agents | You want agent-wide policies like "audit every tool call" or "block dangerous commands everywhere" |
| Custom agent level | Agent Canvas → Custom agent → Manage Hooks | Applies only when that specific custom agent runs | You want hooks tailored to one custom agent, like "validate this custom agent's output format" |
If an agent-level hook and a custom-agent-level hook both match the same event, both run. Agent-level hooks fire first, then custom-agent-level hooks.
Part 1: Agent-level hooks (Builder → Hooks)
Agent-level hooks apply to the entire agent — every thread, every custom agent. They have activation modes that control when they're active.
Step 1: Open the Hooks page
- Navigate to sre.azure.com and select your agent
- In the sidebar, expand Builder
- Click Hooks
Checkpoint: You see the "Hooks" heading with a description, a Create hook button, and an empty data grid (or a list of existing hooks).
Step 2: Create a Stop hook
A Stop hook fires when the agent is about to return a final response. Use it to validate response quality and enforce formatting rules.
- Click Create hook
- Fill in the form fields:
| Field | Value |
|---|---|
| Name | require-table-format |
| Event type | Stop |
| Activation mode | Always |
| Description | Ensures responses present structured data as markdown tables with bold headers |
- Under Hook Definition, keep Hook type set to Prompt
- Keep Model set to Reasoning Fast (default)
- In the Prompt editor on the right, enter:
Check the agent response below.
$ARGUMENTS
Does the response present any structured data (lists of items, comparisons, metrics) as a markdown table with **bold** column headers?
If no structured data is present, approve.
If structured data IS present as a table with bold headers: {"ok": true}
If structured data is present but NOT formatted as a table: {"ok": false, "reason": "Reformat the structured data as a markdown table with **bold** column headers."}
- Leave Timeout (sec) at
30, Fail mode atAllow, and Max rejections at3 - Click Save
Checkpoint: The dialog closes with a success notification. The hook appears in the data grid with Event type "Stop" and Activation "Always."
The $ARGUMENTS placeholder injects the hook context (including the agent's final response) into the prompt. The LLM evaluates whether the response meets your criteria and returns {"ok": true} to approve or {"ok": false, "reason": "..."} to reject. After 3 rejections (the default), the agent is forced to stop.
Step 3: Test the Stop hook
- Navigate to Chat in the sidebar
- Type "Compare the pros and cons of Python vs Go for building microservices" and press Send
- Watch the agent's response:
- The agent initially responds with a plain text comparison
- The Stop hook evaluates and rejects because the data isn't in a table
- The agent reformats its response as a markdown table with bold headers
Checkpoint: The final response presents the comparison as a formatted table like:
| Language | Pros | Cons |
|---|---|---|
| Python | Rapid development, rich ecosystem | Slower execution, GIL limitations |
| Go | Fast compilation, built-in concurrency | Smaller ecosystem, verbose error handling |
Step 4: Create a PostToolUse hook
A PostToolUse hook fires after a tool completes execution. Use it to audit tool usage, block dangerous commands, or inject additional context.
- Go back to Builder → Hooks
- Click Create hook
- Fill in the form:
| Field | Value |
|---|---|
| Name | block-dangerous-commands |
| Event type | Post Tool Use |
| Activation mode | Always |
| Description | Blocks rm -rf, sudo, and chmod 777 in shell commands |
| Hook type | Command |
| Tool matcher | Bash|ExecuteShellCommand |
- Select Python as the script language
- In the Script editor, enter:
#!/usr/bin/env python3
import sys, json, re
context = json.load(sys.stdin)
command = context.get('tool_input', {}).get('command', '')
dangerous = [r'\brm\s+-rf\b', r'\bsudo\b', r'\bchmod\s+777\b']
for pattern in dangerous:
if re.search(pattern, command):
print(json.dumps({"decision": "block", "reason": f"Blocked: {pattern}"}))
sys.exit(0)
print(json.dumps({"decision": "allow"}))
- Set Fail mode to Block (if the script crashes, the tool result is blocked)
- Click Save
Checkpoint: Both hooks appear in the Hooks data grid.
The Tool matcher field uses regex. Bash|ExecuteShellCommand matches tools named exactly "Bash" or "ExecuteShellCommand" (the pattern is anchored as ^(Bash|ExecuteShellCommand)$). Use * to match all tools.
Step 5: Test the PostToolUse hook
- Go to Chat
- Ask the agent to run a safe command: "Run echo hello" — the hook allows this
- Ask the agent to run a dangerous command: "Run rm -rf /tmp/test" — the hook blocks this
Checkpoint: The safe command executes normally. The dangerous command is blocked, and the agent receives a message explaining why.
Step 6: Edit and delete agent-level hooks
Edit: Click the edit icon on any hook row in the data grid, modify the fields, and click Save.
Delete: Select the checkbox next to hooks you want to remove, click Delete in the toolbar, and confirm.
Checkpoint: Changes are reflected immediately in the data grid.
Part 2: Custom-agent-level hooks (Agent Canvas)
Custom-agent-level hooks are configured directly in a custom agent's definition. They apply only when that specific custom agent runs — not to the main agent or other custom agents.
Step 7: Open the custom agent hooks panel
- In the sidebar, expand Builder and click Agent Canvas
- Click on an existing custom agent to edit it — or click Create custom agent to start a new one
- In the custom agent form, scroll down to the Hooks section
- Click Manage Hooks
Checkpoint: A side panel opens with Stop and Post Tool Use sections. If no hooks are configured, you see empty states with guidance text.
Step 8: Add a hook to a custom agent
Let's add a Stop hook that ensures this custom agent always responds in a structured format:
- In the Manage Hooks panel, click the Add hook button at the bottom of the panel
- In the dialog that opens, fill in the hook form:
| Field | Value |
|---|---|
| Event type | Stop |
| Hook type | Prompt |
| Prompt | Check the response below. $ARGUMENTS Does it include a clear summary section at the end? If yes: {"ok": true} If no: {"ok": false, "reason": "Add a Summary section at the end of your response."} |
| Timeout (sec) | 30 |
| Fail mode | Allow |
| Max rejections | 3 |
- Click Save on the hook
- Click Create (or Save) on the custom agent to save the full configuration
Checkpoint: The hook appears in the Manage Hooks panel under the Stop section. The custom agent form shows "Manage Hooks (1)" on the button.
To test this hook, go to Agent Canvas → select the Test playground view → choose your custom agent from the dropdown → type a question. The hook only runs when this specific custom agent is invoked.
Part 3: Manage hooks per thread
Agent-level hooks with Always activation are active in every conversation by default. Hooks with On Demand activation must be manually activated per thread.
Step 9: Toggle hooks in a conversation
- Open a Chat thread
- Click the + button in the chat footer
- Select Manage Hooks
- Toggle hooks on or off for the current thread
Always hooks can be temporarily deactivated. On Demand hooks can be activated when needed. Required system hooks are locked and cannot be toggled.
Checkpoint: Hook changes take effect immediately in the current thread.
What you learned
- Hooks operate at two levels: Builder → Hooks (agent level) and Agent Canvas → custom agent → Manage Hooks (custom agent level)
- How to create Stop hooks that validate responses before delivery
- How to create PostToolUse hooks that audit and control tool usage
- The difference between Prompt hooks (LLM evaluation) and Command hooks (scripts)
- How activation modes (Always vs On Demand) control global hook behavior per thread
- How to edit, delete, and manage hooks across both surfaces
Troubleshooting
| Issue | Solution |
|---|---|
| Hooks page not visible in sidebar | The Hooks page appears under Builder. Verify your agent is in Running state. If the option still doesn't appear, contact support. |
| "Hook name is required" | Enter a name using only letters, numbers, hyphens, and underscores. |
| "Name must contain only letters, numbers, hyphens, and underscores" | Remove special characters from the hook name. |
| "Hook name cannot start with system__" | The system__ prefix is reserved for system hooks. Choose a different name. |
| "Tool matcher is required for PostToolUse hooks" | PostToolUse hooks need a regex matcher. Use * to match all tools. |
| Hook doesn't fire | For agent-level hooks, check the activation mode — On Demand hooks must be activated per thread. For custom-agent-level hooks, verify the custom agent is being invoked. |
| Stop hook approves everything | Ensure the prompt returns {"ok": false, "reason": "..."} when rejecting. A rejection without a reason is treated as approval. |
| Script errors blocking actions | Set Fail mode to Allow for graceful degradation during development. Switch to Block in production. |
Related
| Resource | What you'll learn |
|---|---|
| Agent Hooks → | Full hook reference, context schema, response formats, and limits |
| Configure Hooks via API → | Create hooks using the REST API v2 and YAML |
| Run Modes → | How hooks complement run mode safety controls |