---
title: "Alerts and channels"
description: "Rules decide what you hear about and when. Channels are where it lands."
canonical: https://docs.mentio.dev/alerts
markdown: https://docs.mentio.dev/alerts.mdx
---

# Alerts and channels

Rules decide what you hear about and when. Channels are where it lands.

An alert is a **rule** times one or more **channels**. A rule says what to watch (a filter over
keywords, platforms, relevance, sentiment, intent, language, the links in a post, muted authors, bots) and how often to hear about it: an
`instant` rule fires per mention as it is classified, an `hourly` rule sends one digest every
hour, a `daily` rule one digest at a local time of day, a `weekly` rule one digest a week on
the weekday you pick. Channels are the places a message lands, and a channel can serve any number of rules.

Alerts and digests are never billed.

When the same author posts the same text again for the same keyword within three days (one announcement pasted into five subreddits), only the first post sends an instant alert, and a digest lists it once. See [Cross-posts](/how-it-works#cross-posts).

## Channels

| Kind     | How it connects                                                                 | What it receives                                                                             |
| -------- | ------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| Slack    | OAuth install, then pick a public channel (see [Slack](/alerts/slack))          | Instant messages with Interacted and Mute author buttons; hourly, daily and weekly digests   |
| Telegram | Press Start on the bot, or add it to a group (see [Telegram](/alerts/telegram)) | Instant messages; hourly, daily and weekly digests                                           |
| Email    | A list of addresses                                                             | Instant messages, capped at 20 per hour per channel; daily and weekly digests (never hourly) |
| Webhook  | A URL; you get a signing secret once (see [Webhooks](/webhooks))                | Signed JSON per mention or digest, with the rule's event name                                |

Any channel can also receive **attention alerts**, no rule involved: one short message when a
keyword spikes, turns negative or goes noisy, or a channel keeps failing. Switch them on per channel
under Alerts, or send `events` on `PATCH /v1/channels/{id}`. See [Needs attention](/guides/attention).

### Email

Every new workspace starts with a "Daily digest" rule at 09:00 local time to the person who created
it. Add more addresses under Settings, Integrations, Email, or under Alerts.

Team members are confirmed on sight. Anyone else receives a confirmation link first and gets
nothing until they click it, so a typo or a stranger's inbox never sees your mentions. Instant email
is capped at 20 messages per hour per channel; past that, the rest are left to the daily digest.

### Webhooks

A webhook channel receives one signed JSON `POST` per mention or digest, carrying the rule's `event`
name so one endpoint can serve several rules. Creating one, verifying the signature, retries,
testing and the payloads are covered in [Webhooks](/webhooks).

## Mute authors

A rule's muted list keeps someone's mentions out of that rule: your own team, a bot, a competitor
posting about themselves. Their mentions stay in the feed and in usage; they just never trigger the
rule. Add them in the dashboard or through the API, in whatever form you have to hand:

| You give                                                                 | Stored as                          | Mutes                                 |
| ------------------------------------------------------------------------ | ---------------------------------- | ------------------------------------- |
| `https://x.com/name`, `https://twitter.com/name/status/123`              | `https://x.com/name`               | that X account                        |
| `https://www.linkedin.com/in/name`, `…/company/name/posts`               | `https://www.linkedin.com/in/name` | that person or company page           |
| `https://www.reddit.com/user/name`, `u/name`                             | `https://www.reddit.com/user/name` | that Reddit account                   |
| `https://news.ycombinator.com/user?id=name`                              | the same link                      | that Hacker News user                 |
| `name.bsky.social`, `did:plc:…`                                          | the profile link, or the DID as is | that Bluesky account                  |
| `https://www.youtube.com/channel/UC…`                                    | the same link                      | that channel                          |
| `https://www.tiktok.com/@name`, `…/@name/video/123`                      | `https://www.tiktok.com/@name`     | that TikTok account                   |
| `https://www.instagram.com/name/` (a post link does not name its author) | `https://www.instagram.com/name/`  | that Instagram account                |
| `@name`, `Display Name`                                                  | `name`, `Display Name`             | that name or handle on every platform |

A link that names no person, such as a subreddit, a thread or a Hacker News story, is rejected with
the entry named. A YouTube `@handle` link is accepted but cannot match, because YouTube mentions carry
the channel link and the channel name: use one of those instead.

## Watch a link

A rule can also watch what a post LINKS to, which is how a partner's affiliate link or a
competitor's domain becomes a message instead of something you find a week later. Put the hosts in
`linkHosts` (Links to, in the dashboard):

```bash
curl -X POST "$MENTIONS_API_URL/v1/alerts" \
  -H "Authorization: Bearer $MENTIONS_API_KEY" -H "Content-Type: application/json" \
  -d '{ "name": "Competitor links", "mode": "instant",
        "filter": { "linkHosts": ["octolens.com"] }, "channelIds": ["dest_…"] }'
```

An entry names a company, not one server: `octolens.com` also matches `blog.octolens.com`, and a
pasted URL or a `www.` prefix is read as the same host. A post that links to nothing never matches.
The same hosts filter the feed (`GET /v1/mentions?linkHosts=octolens.com`) and the audience
(`GET /v1/people?linkHosts=octolens.com`, the people who have shared such a link).

## One rule, several cases

The conditions of a rule all have to hold. For "negative posts on Reddit, or anyone about to buy",
put the cases in `filter.anyOf`: a mention is sent when the rest of the filter holds and at least one
group does. A group takes the conditions of the mentions list, as a [saved view](/guides/views) does
([OR across groups](/conventions#or-across-groups-anyof)):

```bash
curl -X POST "$MENTIONS_API_URL/v1/alerts" \
  -H "Authorization: Bearer $MENTIONS_API_KEY" -H "Content-Type: application/json" \
  -d '{ "name": "Hot", "mode": "instant",
        "filter": { "anyOf": [ { "platforms": ["reddit"], "sentiments": ["negative"] },
                               { "intents": ["buy_intent"] } ] },
        "channelIds": ["dest_…"] }'
```

The rule's relevance floor still applies to every group. The dashboard shows the groups on the rule
and keeps them when you edit the rest.

## Daily and weekly digests

A daily rule collects instead of firing. At the hour you pick, in the time zone you pick, it sends
one digest per channel with the day's counts against the previous day, the split by platform, the
top mentions, anything negative, and buying signals. Days with nothing new are skipped unless the
rule says otherwise. A weekly rule does the same once a week, on `schedule.weekday` (0 Sunday to 6
Saturday), over the week that passed. **Run now** sends the rule's own period (the last hour, day,
or week) immediately without moving the schedule.

## Hourly digests

An `hourly` rule sends one digest every UTC hour, covering the hour before (matched from 09:00 to
10:00, sent at 10:05: the five minutes let the hour's last mentions be scored), with the same two
lists as the daily one and the counts against the previous hour. An hour with nothing to show (no
mention at or over the rule's relevance floor) sends nothing. It takes no `schedule`: a time of day and a
time zone are ignored, and a `weekday` is refused, since the rule cannot be weekly too.

Hourly digests go to Slack, Telegram and webhooks only. Up to 24 a day per rule would bury an inbox,
so an hourly rule with an email channel is refused with `400 hourly_email_unsupported`; use a daily
digest for email. A delivery that fails for a reason that can pass (a rate limit, a 5xx) is retried
until 10:45, then that hour is dropped: by then the next one is due, and a late
digest would only land next to it. Its mentions stay in the feed.

## Via the API

```bash
# Channels
curl -H "Authorization: Bearer $MENTIONS_API_KEY" "$MENTIONS_API_URL/v1/channels"
curl -X POST "$MENTIONS_API_URL/v1/channels" \
  -H "Authorization: Bearer $MENTIONS_API_KEY" -H "Content-Type: application/json" \
  -d '{ "kind": "email", "emails": ["ops@example.com"] }'

# A daily digest at 09:00 Madrid time, a weekly one every Monday for the Spanish posts,
# and an hourly one of the negative posts to Slack
curl -X POST "$MENTIONS_API_URL/v1/alerts" \
  -H "Authorization: Bearer $MENTIONS_API_KEY" -H "Content-Type: application/json" \
  -d '{ "name": "Morning digest", "mode": "daily",
        "schedule": { "hour": 9, "minute": 0, "timezone": "Europe/Madrid" },
        "filter": {}, "channelIds": ["dest_…"] }'
curl -X POST "$MENTIONS_API_URL/v1/alerts" \
  -H "Authorization: Bearer $MENTIONS_API_KEY" -H "Content-Type: application/json" \
  -d '{ "name": "Week in review", "mode": "weekly",
        "schedule": { "hour": 9, "minute": 0, "timezone": "Europe/Madrid", "weekday": 1 },
        "filter": { "languages": ["es"] }, "channelIds": ["dest_…"] }'
curl -X POST "$MENTIONS_API_URL/v1/alerts" \
  -H "Authorization: Bearer $MENTIONS_API_KEY" -H "Content-Type: application/json" \
  -d '{ "name": "Hourly heat", "mode": "hourly",
        "filter": { "sentiments": ["negative"] }, "channelIds": ["dest_slack…"] }'

# Mute and unmute authors without rewriting the rest of the filter.
# Both return the alert and are safe to retry.
curl -X POST "$MENTIONS_API_URL/v1/alerts/feed_…/mute" \
  -H "Authorization: Bearer $MENTIONS_API_KEY" -H "Content-Type: application/json" \
  -d '{ "authors": ["https://x.com/yourcofounder", "u/yourbot", "did:plc:…"] }'
curl -X POST "$MENTIONS_API_URL/v1/alerts/feed_…/unmute" \
  -H "Authorization: Bearer $MENTIONS_API_KEY" -H "Content-Type: application/json" \
  -d '{ "authors": ["https://x.com/yourcofounder"] }'

# Send a hello through every channel of a rule, or the rule's digest (its own period) now
curl -X POST -H "Authorization: Bearer $MENTIONS_API_KEY" "$MENTIONS_API_URL/v1/alerts/feed_…/test"
curl -X POST -H "Authorization: Bearer $MENTIONS_API_KEY" "$MENTIONS_API_URL/v1/alerts/feed_…/run"
```

`PATCH /v1/alerts/{id}` still replaces the whole filter, `excludeAuthors` included; use `mute` and
`unmute` to change one author.

Slack channels need the workspace connected first (`409 slack_not_connected` otherwise). Telegram
chats are not created through `POST /v1/channels`: mint a link with `POST /v1/telegram/links` and
press Start.
