# Alert delivery and Slack routing

> How Voice alert rules are evaluated centrally and delivered to your org's Slack workspace, and how that differs from webhook event delivery.

Alert *rules* for Voice are not something you author per org. TeleQuick
evaluates them centrally in SigNoz against the platform's own telemetry. The
only tenant-facing knob is **delivery**: where this org's Voice alerts get
posted. You supply a Slack incoming-webhook URL in the portal, and the
control-plane API mirrors it into a per-org SigNoz notification channel named
`cc-<org>-voice` that the Voice alert rules route to.

That means there is exactly one delivery target per org per vertical. The
Alerts screen in the portal is pinned to the `voice` vertical.

## What the platform evaluates for you

The alert rules live in SigNoz, on the platform side. You do not create,
edit, or list them from the portal or from the tRPC API. When you save a
Slack webhook, the control-plane API provisions the notification channel for
your org and points the relevant Voice rules at it. The success message on
save reports both halves of that work:

| Response field     | Meaning                                                                    |
| ------------------ | -------------------------------------------------------------------------- |
| `signozProvisioned` | The notification channel `cc-<org>-voice` was created or updated in SigNoz. |
| `rulesProvisioned`  | How many alert rules were configured to route to that channel.              |

If `signozProvisioned` comes back false, the portal reports
*"Saved · webhook stored."* — the URL is persisted against your org, but the
notification channel was not provisioned on this attempt, so nothing will be
delivered yet. Save again to retry the provisioning step.

Because evaluation is central, the signals behind these alerts are the same
ones described in [Telemetry](/platform/telemetry) and
[Telephony Metrics](/glossary/metrics). Use those pages when you want to
reproduce or investigate an alert in your own dashboards.

## Configure Slack delivery for a vertical

1. In Slack, create an incoming webhook (**Apps → Incoming Webhooks**) bound
   to the channel you want alerts in. Slack's own
   [webhook documentation](https://api.slack.com/messaging/webhooks) covers
   app install and scopes.
2. Open **Alerts** in the portal with the target org selected.
3. Paste the webhook URL into **Slack incoming-webhook URL**. The field is
   required and must be `https`. The portal surfaces the validation error when
   you press **Save**, not before.
4. Optionally set a **Channel label** such as `#ops-alerts`.
5. Press **Save**.

Under the hood this is a single upsert, keyed on org plus vertical:

```ts
await alertChannels.upsert.mutate({
  orgId,
  vertical: "voice",
  slackWebhookUrl: "https://hooks.slack.com/services/T000/B000/xxxx",
  slackChannel: "#ops-alerts", // or null
});
```

Saving again with a different URL replaces the stored value for that
org/vertical pair — there is no list of multiple destinations. **Save** stays
disabled until the form differs from the stored record.

The current configuration is read back with
`alertChannels.get.query({ orgId, vertical: "voice" })`, which returns the
stored record (`slack_webhook_url`, `slack_channel`) or nothing when routing
has never been configured. The portal's **Configured** badge is exactly this
distinction.

### Channel label is metadata

The **Channel label** field does not choose the Slack channel. A Slack
incoming webhook is already bound to one channel at creation time, and that
binding decides where messages land. The label is stored alongside the URL so
that the portal can show which channel an org expects its alerts in. To move
alerts to a different Slack channel, create a new webhook in Slack for that
channel and save its URL here.

### Treat the webhook URL as a credential

Anyone holding the URL can post into your channel. It is stored against your
org and mirrored into the SigNoz notification channel. To invalidate it,
delete the webhook in Slack; removing it here stops TeleQuick from using
it, but only Slack can revoke the URL itself.

## What a delivered alert looks like

Delivery is a plain Slack incoming-webhook POST from the SigNoz notification
channel, so alerts appear as messages in the channel the webhook is bound to,
posted by the Slack app that owns the webhook. The content is whatever the
firing platform rule emits — rule name and the notification body configured on
the rule — not a TeleQuick-specific envelope you can template from the
portal.

Two consequences worth planning around:

- Messages are **per firing rule for your org**, not per call. There is no
  `call_sid`-level fan-out here; for per-call data use CDRs or webhooks.
- Formatting is controlled centrally. If you need a machine-readable feed,
  consume the streams in [Telemetry](/platform/telemetry) rather than parsing
  Slack messages.

## Testing and troubleshooting delivery

Once a webhook is saved, **Send test** posts a test message straight to the
stored URL:

```ts
const r = await alertChannels.test.mutate({ orgId, vertical: "voice" });
// r.ok === true  -> Slack accepted the post
// r.ok === false -> r.status / r.message carry Slack's rejection
```

The button is disabled until a configuration exists, because the test uses the
**stored** webhook — not the text currently in the input. Save first, then
test.

| Symptom | What it means | What to do |
| ------- | ------------- | ---------- |
| *"Test message sent — check your Slack channel."* but nothing appears | Slack accepted the POST; the webhook is bound to a channel you are not looking at. | Check the channel the webhook was created for, then update the channel label so the portal matches reality. |
| *"Slack rejected the test: …"* with a status or message | Slack refused the POST — typically a deleted, revoked, or mistyped webhook. | Recreate the webhook in Slack and save the new URL. |
| Save reports *"webhook stored"* with no channel provisioned | The URL persisted but the SigNoz notification channel was not created on that attempt. | Press **Save** again. Until provisioning succeeds, no rules route to your channel. |
| Test succeeds, real alerts never arrive | Delivery works; no rule has fired for your org, or provisioning of rules did not complete. | Confirm the save message reported alert rules configured, then compare against the signals in [Telemetry](/platform/telemetry). |
| The URL is rejected on save | The field is required and must use `https`. | Paste the full `https://hooks.slack.com/services/...` URL. |

## Removing routing and what stops arriving

**Remove** deletes the stored routing for that org and vertical after a
confirmation prompt:

```ts
await alertChannels.remove.mutate({ orgId, vertical: "voice" });
```

After removal:

- The portal clears both fields and the **Configured** badge.
- **Send test** is disabled again, because there is no stored webhook.
- Your org's Voice alerts have no Slack destination. Central evaluation
  continues — you simply stop being notified.

Removal does not touch anything in Slack. The incoming webhook keeps existing
in your workspace until you delete it there.

## Alerts vs webhooks

These are two different delivery systems and they are easy to confuse:

| | Alert delivery (this page) | [Webhooks](/platform/webhooks) |
| - | - | - |
| What is delivered | Platform alert rules firing for your org | Your application's events |
| Who defines the trigger | TeleQuick, centrally in SigNoz | You, when you subscribe |
| Destination | One Slack incoming webhook per org per vertical | Your HTTPS endpoint(s) |
| Granularity | Operational, org-level | Per event / per call |
| Configured with | `alertChannels.get` / `upsert` / `remove` / `test` | The webhooks procedures |

Rule of thumb: alerts go to humans in Slack so someone notices a Voice
problem; webhooks go to your backend so your code reacts to a call. Routing
alerts into Slack is not a substitute for consuming call events, and
subscribing to webhooks does not give you the platform's alert rules.

## Related

- [Telemetry](/platform/telemetry) — the metrics, traces, and CDRs the alert rules are evaluated against
- [Telephony Metrics](/glossary/metrics) — what the underlying numbers mean
- [Authentication](/concepts/authentication) — the API key your server uses to call these procedures
