# Alerts

> Ownrecord raises an alert when something needs a person, such as a registration deadline or a blocked jurisdiction. Every alert is in the console inbox; high and critical ones are also emailed to the people whose role handles them and posted to Slack.

An **alert** says that something needs someone: a registration deadline is getting close, a jurisdiction was blocked automatically, a webhook endpoint keeps failing, the ledger doesn't balance. Each alert has a code from the catalogue (for example `REG_AUTO_BLOCKED`), a severity, a message in plain words and a link to the page where you act on it.

## Severities and where alerts go

| Severity   | Inbox | Email                                                         | Slack                             |
| ---------- | ----- | ------------------------------------------------------------- | --------------------------------- |
| `info`     | yes   | no                                                            | no                                |
| `high`     | yes   | yes, unless your alert settings keep high alerts in the inbox | channels set to high and critical |
| `critical` | yes   | always                                                        | every channel                     |

Alerts belong to a mode: test-mode alerts go to the test-mode inbox and the test-mode Slack channels, with a test-mode label in emails. Test mode is a complete parallel world, so you can practice the whole compliance workflow there.

## Who sees and who is emailed

Each alert is about one area, such as registrations, the ledger or webhooks. The **inbox** shows an alert to every member who may read that area at the alert's legal entity or brand, in its mode, so it never shows more than the rest of the console does. Owners and admins see every alert. Acknowledging or resolving an alert needs write access to its area (for example a tax manager for registration alerts, finance for ledger alerts).

**Email** goes to the roles that handle the area, at the alert's legal entity or above:

| Area                                                | Emailed roles                    |
| --------------------------------------------------- | -------------------------------- |
| Registrations, thresholds, prerequisites            | Tax managers and owners          |
| Filings                                             | Tax managers, finance and owners |
| Ledger and reconciliation                           | Finance and owners               |
| Invoices                                            | Finance and owners               |
| Payments and disputes                               | Support, finance and owners      |
| Webhooks                                            | Developers, admins and owners    |
| Processors, security, legal entities, the audit log | Admins and owners                |
| Your Ownrecord subscription                         | Owners and billing admins        |
| Approval requests                                   | The people who may approve it    |

A tax manager limited to one legal entity is emailed only about that entity. A member without live access isn't emailed about live alerts.

An approval request (`APPROVAL_REQUESTED`) is emailed to the people who may decide on it: owners, admins and organization-wide finance and tax managers who may run the requested operation themselves, except the person who asked. It is shown in the inbox to everyone who may read approvals, and resolves itself when the request is approved, rejected or expires ([approvals](https://ownrecord.io/docs/concepts/approvals.md)).

## Acknowledge and resolve

- **Acknowledge** (`alerts.acknowledge`): someone is on it. The alert stays unresolved; if the same problem is raised again, its occurrence count goes up instead of a new alert appearing.
- **Resolve** (`alerts.resolve`): it's dealt with. If it happens again, a new alert is raised and delivered again. Many alerts also resolve themselves when the condition clears, for example when the registration a `REG_BLOCKED_EXISTING_CUSTOMERS` alert is about is registered.

## Delivery

Ownrecord delivers an alert after it is saved, never as part of the change that raised it. A critical ledger alert (`LEDGER_INVARIANT_BROKEN`) also notifies Ownrecord's on-call engineers, with only its code, your organization's ID and a link: never amounts, accounts or customers. Each email recipient and each Slack channel is reached once per alert. When one fails (Slack down, a mail outage), only that one is tried again: after 1 minute, then 5 and 15 minutes, 1, 3, 6 and 12 hours. The inbox shows how an alert was delivered.

## Slack

Owners and admins add Slack channels in **Settings → Alerts** (or with [`alert_settings.slack_channels.create`](https://ownrecord.io/docs/reference/alert-settings-slack-channels-create.md)): a name, the channel's [incoming webhook](https://api.slack.com/messaging/webhooks) URL, and whether it gets high and critical alerts or critical ones only. Up to 10 channels per mode.

- The webhook URL is stored encrypted and never shown again; the console shows its host and last characters.
- URLs must be https on a public host. Any Slack-compatible incoming webhook works.
- Every message is **signed** like a [webhook](https://ownrecord.io/docs/concepts/webhooks.md): an `Ownrecord-Signature: t=…,v1=…` header with the channel's own signing secret (`whsec_…`, shown once when you add the channel). Slack ignores it; a relay in front of Slack can verify it.
- **Send test message** posts a test message now and shows what the webhook answered.

## Alert codes

| Code                                            | Severity   | Meaning                                                                                    |
| ----------------------------------------------- | ---------- | ------------------------------------------------------------------------------------------ |
| `REG_TRIGGERED`                                 | high       | Grace started; the deadline is set                                                         |
| `REG_DEADLINE_50`, `REG_DEADLINE_80`            | high       | Half and 80% of the grace period are gone                                                  |
| `REG_DEADLINE_FINAL`                            | critical   | Daily in the last three days before the automatic block                                    |
| `REG_AUTO_BLOCKED`                              | critical   | The jurisdiction was blocked automatically after a missed deadline                         |
| `REG_BLOCKED_EXISTING_CUSTOMERS`                | critical   | An existing customer's renewal, top-up or refund went through under a blocked registration |
| `THRESHOLD_50`, `THRESHOLD_80`, `THRESHOLD_100` | info, high | Threshold counter progress                                                                 |
| `PREREQ_OVERDUE`                                | high       | A registration prerequisite is past its due date                                           |
| `COUNTER_FX_MISSING`                            | high       | A sale couldn't be added to a threshold counter without exchange rates                     |
| `THRESHOLD_COUNTER_DRIFT`                       | high, info | The daily recount found a threshold counter that differs from the sales on record          |
| `VIES_UNAVAILABLE_SPIKE`                        | high       | Several business customers' tax IDs couldn't be checked: a tax register is down            |
| `LEDGER_INVARIANT_BROKEN`                       | critical   | The nightly ledger check failed                                                            |
| `RECON_UNMATCHED`                               | high       | A processor payment or refund doesn't match what was expected                              |
| `REFUND_FAILED`                                 | high       | A refund failed at the processor; the amount stays owed in refunds payable                 |
| `WEBHOOK_FAILING`                               | high       | A webhook endpoint keeps failing                                                           |
| `APPROVAL_REQUESTED`                            | high       | An action is waiting for your approval; resolved once it is decided or expires             |

## Reference

- Operations: [`alerts.list`](https://ownrecord.io/docs/reference/alerts-list.md), [`alerts.get`](https://ownrecord.io/docs/reference/alerts-get.md), [`alerts.acknowledge`](https://ownrecord.io/docs/reference/alerts-acknowledge.md), [`alerts.resolve`](https://ownrecord.io/docs/reference/alerts-resolve.md), [`alert_settings.get`](https://ownrecord.io/docs/reference/alert-settings-get.md), [`alert_settings.update`](https://ownrecord.io/docs/reference/alert-settings-update.md), [`alert_settings.slack_channels.create`](https://ownrecord.io/docs/reference/alert-settings-slack-channels-create.md), [`alert_settings.slack_channels.delete`](https://ownrecord.io/docs/reference/alert-settings-slack-channels-delete.md), [`alert_settings.slack_channels.test`](https://ownrecord.io/docs/reference/alert-settings-slack-channels-test.md).
- Errors: `alert_resolved`, `slack_webhook_url_invalid`, `slack_channel_limit_reached` ([errors](https://ownrecord.io/docs/reference/errors.md)).
- Related: [compliance tasks](https://ownrecord.io/docs/concepts/tasks.md), [registrations](https://ownrecord.io/docs/concepts/registrations.md).
