Alert rules
A rule answers one question per event type: who hears about this, over which channel, in what words.
Nothing is delivered until a rule exists. A fresh organization records every event in the alert history and notifies nobody, so turning Willow on can't page your security team at 3am by accident. Configure rules under Security → Alerts.
How a rule is put together
Rule (one event type, e.g. "Tool call blocked")
└── Delivery (one audience + one wording + one or more channels)
├── Audience → who
├── Channels → how it reaches them
├── Destinations → which Slack/Teams channel or webhook endpoint
└── Template → what it says
The delivery is the piece that does the real work, and a rule can have several. That's what lets one event reach two groups differently: the security team gets the full technical detail on Teams, while the end user who tripped the guard gets a short, friendlier email. Same event, two deliveries.
Deliveries are isolated from each other. A broken webhook endpoint on one delivery can't stop the email copy on another from going out.
Creating a rule
- Go to Security → Alerts → Rules and select Add rule.
- Pick the event the rule reacts to. One rule per event type.
- Optionally override the severity for your organization.
- Add one or more deliveries. For each one:
- Choose the audience.
- Choose the channels it reaches them over.
- For Slack, Teams, or Webhook, pick the destinations — add these first under the Channels tab.
- Optionally write a template to reword the notification.
- Save. Use Test on any delivery to send yourself a sample.
A rule and each of its deliveries have their own enable toggle, so you can silence one route without deleting its configuration.
Audiences
| Audience | Who it resolves to |
|---|---|
| All admins | Every admin in the organization. |
| Specific admins | Only the admins you pick. |
| The user who triggered it | The person the event is about — whoever ran the blocked call, typed the flagged prompt, or made the request. Yields nobody when the event carries no user. |
| Members of groups | Every end user in any of the groups you pick. |
| Specific end users | Only the end users you pick. |
| All end users | Every end user in the organization. Use with care. |
Every audience also accepts extra addresses — a shared security@ inbox or an on-call alias that isn't a Willow account. These are always included on top of the resolved audience, and because there's no user behind them they're never subject to personal mutes.
Webhook and Microsoft Teams ignore the audience entirely — they post to the destinations you pick, not to people. A Teams delivery aimed at "All admins" posts once to the channel, not once per admin.
Event types
Every event Willow can notify on, its default severity, and which in-app inbox its record lands in.
Skill Guard
Build-time guard checks that run when a skill is created, updated, or discovered during a device scan.
| Event | Default severity | Description |
|---|---|---|
| Skill blocked | High | A skill was blocked by a guard during creation, publishing, or a device scan. |
| Skill warning | Medium | A skill triggered a guard warning. It was not blocked but should be reviewed. |
| Skill redacted | Medium | Sensitive content was automatically redacted from a skill before it was saved. |
Runtime Guard
Guards evaluated at runtime against live tool calls passing through the gateway.
| Event | Default severity | Description |
|---|---|---|
| Tool call blocked | High | A tool call was blocked by a runtime guard before it executed. |
| Tool call warning | Medium | A tool call triggered a runtime guard warning. |
| Tool call redacted | Low | Sensitive data was automatically redacted from a tool call. |
Prompt Guard
Guards run by the browser extension on prompts typed into web AI chats (ChatGPT, Claude, Gemini, …) before they are submitted.
| Event | Default severity | Description |
|---|---|---|
| Prompt blocked | High | A prompt was blocked by the browser extension before submission. |
| Prompt warning | Medium | A prompt was flagged by the browser extension. |
| Prompt redacted | Low | Sensitive content was automatically redacted from a prompt. |
AI Discovery
AI discovery policy enforcement for MCP servers, skills, and plugins found during device scans.
| Event | Default severity | Description |
|---|---|---|
| New capability discovered | Medium | A device scan discovered new or updated capabilities. The alert lists each one with the enforcement action applied (blocked, warned, or allowed). |
| Capability blocked by policy | High | A discovered MCP server, skill, or plugin was blocked by an AI discovery policy rule. |
| Capability warned by policy | Medium | A discovered MCP server, skill, or plugin triggered a warning from a policy rule. |
New capability discovered fires once per scan and only for capabilities new to your whole organization. Seeing the same MCP server on a second device later does not raise it again.
MCP Requests
| Event | Default severity | Description |
|---|---|---|
| New integration requested | Low | A user requested a new MCP integration and it's waiting on a decision. |
Slack deliveries render interactive Approve and Reject buttons on this event; any admin holding the Integrations → Create permission (integrations:create) can decide from Slack, and the message updates in place to record who did. Other channels get a link into Willow instead — see Teams and Webhook.
Skill Review
| Event | Default severity | Description |
|---|---|---|
| Skill needs approval | Medium | A skill was held for review because it scored at or above the org AI-risk threshold, or a guard routed it to review. An admin must approve or reject it. |
Marketplace Sync
| Event | Default severity | Description |
|---|---|---|
| Sync needs review | Medium | A marketplace reconcile was skipped because it would remove content from existing plugins. Review the affected plugins and re-run the sync to apply. |
API Keys
| Event | Default severity | Description |
|---|---|---|
| API key reset due | Medium | An integration's shared API key is approaching or past its reset deadline and should be rotated. |
Severity
Each event carries the default severity above unless a rule overrides it. Set the override on the rule when your organization ranks an event differently — one company's "warning" is another's page-the-on-call.
The override applies before anything is recorded or sent, so the alert history and the outbound notification always agree.
Which inbox an event lands in
Most events are recorded on the Alerts page. Events that need someone to do something instead land in the Notifications bell, because they're work items rather than history:
- New integration requested
- Skill needs approval
Outbound delivery works identically either way.
Templates
A template rewords a notification. Leave a field blank and the system-generated text is used, so you can override just the title.
| Field | Purpose |
|---|---|
| Title | Headline of the notification. |
| Message | Body text. |
| Action label | Text on the call-to-action button, e.g. Review in Willow. |
Templates may reference {{token}} placeholders. These are available on every event:
{{title}} {{message}} {{severity}} {{event_type}} {{event_label}} {{org_name}} {{action_url}} {{recipient_name}} {{recipient_email}} {{timestamp}}
{{message}} is the system-generated body, so a template can wrap it rather than replace it — for example Security note: {{message}} — escalate to #soc if this repeats.
Each event also exposes its own metadata tokens; guard events carry {{guards}}, {{actor}}, {{user_email}}, {{source}}, {{device}}, {{integration}} and {{tool_name}}, skill events add {{skill_name}}, prompt events add {{platform}} and {{host}}. The rule editor lists the exact set for the event you picked. Unknown tokens render as empty strings rather than leaking the raw {{…}}.
Testing a delivery
Every delivery has a Test button that sends a sample notification through every channel on it. Tests bypass the rate limit, and deliberately ignore personal mutes — you're checking routing, and your own mute shouldn't make a working delivery look broken.
If a channel fails, the error names the reason: the Willow bot isn't in the Slack channel, no browser subscriptions exist yet, the Teams workflow was deleted. These are configuration problems you can act on, not outages.
Rate limiting
Each delivery is limited to 50 sends per hour, per channel. Beyond that, sends are dropped and recorded as rate_limited rather than queued.
The budget counts sends, not recipients: one send fans out to the whole resolved audience, so a delivery aimed at 200 users still consumes one unit. Webhook and Teams destinations each consume from the delivery's budget separately.
To avoid hitting the limit during a burst, narrow the audience or split noisy events onto their own rules. The Test button is exempt.
Personal preferences
By default, personal notification preferences are off and delivery is mandatory. Turn on Let people manage their own notifications in the Channels tab to add a Notifications section to every admin's and end user's profile, where they can mute individual events or a whole channel.
You can restrict which events are mutable. Saved preferences are ignored while the setting is off, not deleted, so turning it off is reversible.
Mutes apply per channel: muting email does not silence the in-app copy. Webhook and Microsoft Teams can't be muted at all, since neither is aimed at a person.
Troubleshooting
Nothing is delivered anywhere. Confirm a rule exists for the event, that both the rule and the delivery are enabled, and that the guard is actually firing — check Monitor → Logs.
A channel shows "Not configured". The transport isn't available on this deployment. See the channel's page for what it needs. Willow Admin, Webhook, and Teams need no server configuration at all.
The audience resolved to nobody. Recorded as a failed delivery with that reason. Common causes: a group that no longer has members, an affected_user audience on an event that carries no user, or everyone having muted that event on that channel.
Missing notifications during a spike. You're likely hitting the 50-per-hour limit. Check the delivery log for rate_limited entries.