Get alerts in Slack or a webhook
Agent Pulse raises an alert when a budget nears its limit, spend jumps, or an agent starts failing. Every alert appears on the Notifications page, and can also go to Slack or a webhook of your own.
Alerts raised
| Alert | When | Severity |
|---|---|---|
| Budget near limit | Spend reaches 75% of a budget, then 95% | Notice at 75%, urgent at 95%. Both are emailed |
| Budget reached | Spend reaches the limit. An enforced budget says calls are now refused; a warn-only budget says they continue | Urgent |
| Budget running out early | Spend is on pace to pass the limit before the period ends | Notice |
| Allocation drawing on reserve | A workspace has used its allocation and starts drawing on its budget's reserve | Notice |
| Reserve near used up | What has been drawn from a budget's reserve reaches 75% of it, then 95% | Notice at 75%, urgent at 95% |
| Reserve used up | A budget's reserve is used up | Urgent |
| Spend spike | The last 24 hours cost at least twice an ordinary day, and $5 more | Notice; urgent at four times |
| Failure spike | An agent fails far more of its calls in an hour than it usually does | Notice; urgent at half its calls |
An ordinary day is the middle day of the 14 before, once at least seven of them had spend. A spend spike names the agents and customers behind the increase and their share of it. A failure spike needs at least 20 calls and 5 failures in the hour, and a failure rate three times the agent's usual and at least 10 points above it. Calls stopped by a budget are not counted as failures.
Each alert is raised once. A spike stays one alert for 12 hours, and a failure spike for 6. A budget's alerts are raised once a period, and once for the budget as a whole, however many users share it. A budget switched between enforce and warn only while past its limit raises "Budget reached" again, so the alert always says what is happening to calls.
Urgent alerts, and a budget's first warning at 75% of its limit, are also emailed. They go to the people in your organisation who can manage its budgets, those with the organisation administrator or governance administrator role, and only to people who have signed in to Agent Pulse. An invited person who has not signed in yet, and a service account, are not emailed. Each alert is emailed once for the period it belongs to.
Email is switched on for the whole Agent Pulse service, not per organisation. Where it is off, alerts still appear on Notifications and go to your Slack and webhook channels. Every other notice stays on the Notifications page and your channels.
Send alerts to Slack
- In Slack, create an incoming webhook for the channel you want, and copy its URL.
- In Agent Pulse, open Notifications, then Alert channels, and choose Add a channel.
- Choose Slack, paste the URL, and give the channel a name.
- Choose which alerts it takes: all, or urgent only, and optionally only some kinds.
- Send a test alert to check it arrives.
Organisation admins and budget admins can add and change channels.
Send alerts to your own endpoint
Add a channel as above, choose Webhook, and give an HTTPS address. Agent Pulse shows a signing secret once, starting whsec_. Keep it to check that deliveries came from us.
Each alert is a POST with a JSON body:
{
"type": "budget.threshold",
"timestamp": "2026-10-04T09:12:00Z",
"data": { "kind": "BUDGET_THRESHOLD", "severity": "URGENT", "title": "brightline budget at 95%" },
"link": "https://www.agentspulse.ai/notifications"
}
type is one of budget.threshold, budget.exhausted, budget.projected, budget.drawing_reserve, reserve.threshold, reserve.exhausted, spend.spike, failure.spike, model.retiring, model.price_changed, or test. Ignore a type you do not know; more may be added. data is the alert as the Notifications page shows it.
Check a delivery is ours
Deliveries follow the Standard Webhooks signing scheme, so a Standard Webhooks library can check them. Each carries three headers:
| Header | What it is |
|---|---|
webhook-id |
The delivery's ID, the same on every retry. |
webhook-timestamp |
When this attempt was sent, in seconds since 1970. |
webhook-signature |
v1, and the base64 HMAC-SHA256 of {id}.{timestamp}.{body}, keyed with the secret after whsec_, base64-decoded. |
import base64, hashlib, hmac
def is_ours(secret: str, headers, body: bytes) -> bool:
key = base64.b64decode(secret.removeprefix("whsec_"))
signed = f"{headers['webhook-id']}.{headers['webhook-timestamp']}.".encode() + body
expected = "v1," + base64.b64encode(hmac.new(key, signed, hashlib.sha256).digest()).decode()
return any(hmac.compare_digest(expected, s) for s in headers["webhook-signature"].split())
import { createHmac, timingSafeEqual } from "node:crypto";
function isOurs(secret: string, headers: Record<string, string>, body: string): boolean {
const key = Buffer.from(secret.replace(/^whsec_/, ""), "base64");
const signed = `${headers["webhook-id"]}.${headers["webhook-timestamp"]}.${body}`;
const expected = Buffer.from("v1," + createHmac("sha256", key).update(signed).digest("base64"));
return headers["webhook-signature"].split(" ").some((s) => {
const given = Buffer.from(s);
return given.length === expected.length && timingSafeEqual(given, expected);
});
}
When you rotate the secret, deliveries carry a signature under each secret, space separated, until the old one stops being honoured. Reject a timestamp more than a few minutes old.
Retries
Any 2xx response within ten seconds counts as delivered. Anything else, a redirect included, is retried for a day after the alert, with the same webhook-id, so you can tell a retry from a new alert. Pausing or deleting a channel stops its retries.