# Alerts, saved views and reports

## What you're told about

| Alert | When |
|---|---|
| Budget thresholds | A budget reaches each threshold you set, once per period |
| Budget pace | A monthly budget is on course to run out before the month ends, once a month per value |
| Spikes | A new [anomaly](/docs/insights) on all spend, a customer, user, feature, project or model |
| Weekly digest | Monday: last week against the week before, the reasons spend moved, and calls on models with no price |
| Expiring credits | Provider credits about to expire unused |
| Failing connections | A provider cost import stops working |
| Restated after the close | A provider changes a day of a month whose close you've approved |
| API keys to rotate | Keys over 90 days old or about to expire |

Most alerts go to owners and admins. Credit-expiry warnings also go to finance members, and restatements after a close go to owners and finance members.

## Where they go

Under **Settings › Alerts**:

- **Email**, on every plan.
- **Slack**: paste an incoming webhook URL. Pro, Team and Enterprise.
- **Webhook** (shown as **Your own system**): an HTTPS endpoint that receives a JSON POST for each alert: `{"type", "org_id", "subject", "text", "data", "url", "delivery_id"}`. Pro, Team and Enterprise. Use `delivery_id` to ignore a retried delivery you've already handled.

Each webhook POST carries `Veqen-Signature: t=<unix seconds>,v1=<hex>`, an HMAC-SHA256 of `t.body` with the signing secret shown when you save the URL. Check the signature and that `t` is recent before trusting a payload:

```python
import hashlib, hmac, time

def verified(body: bytes, header: str, secret: str) -> bool:
    parts = dict(item.split("=", 1) for item in header.split(","))
    expected = hmac.new(secret.encode(), f"{parts['t']}.".encode() + body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts["v1"]) and abs(time.time() - int(parts["t"])) < 300
```

**Send a test alert** checks every channel you've set.

With a full-access key, the same settings are in the API: `GET /v1/alerts` (where alerts go, and what was sent), `PUT /v1/alerts/slack` and `PUT /v1/alerts/webhook` with `{"url": "..."}` (an empty URL turns it off; a new webhook URL returns its `signing_secret` once), and `POST /v1/alerts/test`, up to five times an hour.

## Delivery

Every alert is queued for each destination and sent from there, so an alert isn't lost when Slack or your endpoint is down: failed sends are retried with growing gaps (1, 5, 15, 60, 180, 360 and 720 minutes), for about a day. Errors that won't fix themselves are marked failed at once: a 4xx reply other than 401, 403, 408, 425 or 429, an address that isn't public HTTPS, or a recipient the mail server refuses. **Alerts sent**, on the same page, shows every alert, where it went, its status, how many tries it took and the last problem.

## Saved views

In **Breakdown**, set the dates, filters and grouping you want, then **Save this view**. A view whose dates end today is kept as "the last N days", so it moves with time; one with fixed dates stays on them. Saved views are yours: each member keeps up to 50. With a full-access key, `GET`, `POST /v1/views` and `DELETE /v1/views/{id}` manage the views of the key's creator.

## Scheduled reports

Choose **Email it to me** when you save a view and it's emailed to you:

- **Weekly**: every Monday, the last 7 days.
- **Monthly**: on the 1st, the previous month.

The email has the period's total and calls, the period before for comparison, the view's top 10 rows with their share, and a link back to it in Breakdown. Reports stop if you leave the organization.
