---
title: "Alerts and channels"
description: "Alert rules choose which mentions you hear about and how often. Channels are Slack, Telegram, email or a webhook."
canonical: https://docs.rumoro.dev/alerts
markdown: https://docs.rumoro.dev/alerts.mdx
---

# Alerts and channels

Alert rules choose which mentions you hear about and how often. Channels are Slack, Telegram, email or a webhook.

Every alert has a **rule** and sends to one or more **channels**. Its filter decides which mentions count, by keyword, platform, relevance, sentiment, intent, language, links, authors and more. Its mode decides how often. `instant` sends each mention as soon as it is scored. `hourly`, `daily` and `weekly` send one digest per period, at the local time and weekday you choose.

One channel can serve many rules. Alerts and digests cost nothing.

## Channels

| Kind | How to connect | What it gets |
| --- | --- | --- |
| Slack | Connect Slack and choose a channel ([Slack](/alerts/slack)) | Instant messages with **Interacted** and **Mute** buttons, and every kind of digest |
| Telegram | Start the bot or add it to a group ([Telegram](/alerts/telegram)) | Instant messages and every kind of digest |
| Email | Up to 20 addresses | Instant emails, at most 20 an hour per channel, plus daily and weekly digests |
| Webhook | A URL, with a signing secret you see once ([Webhooks](/webhooks)) | Signed JSON for each mention or digest, with the rule's `event` name |

### Attention alerts

Any channel can also get [attention alerts](/guides/attention), without a rule. These are short messages when a keyword spikes, turns negative or gets noisy, or when a channel keeps failing. Turn them on per channel under Alerts, or with `events` on `PATCH /v1/channels/{id}`.

### Email

A workspace created in the dashboard starts with a **Daily digest** rule that emails you at 09:00 your time. To email more addresses, add them in Alerts or in Settings › Integrations › Email. Team members with a verified email are confirmed right away. Anyone else first has to click a confirmation link, which is valid for 24 hours.

After 20 instant emails in an hour, further mentions wait for the channel's **daily** digest (up to 100). If the channel has no daily digest rule, they only stay in the feed.

## What reaches a rule

- A mention is sent when it passes the filter and its relevance is at least `minRelevance` (40 by default, and 0 sends everything).
- The look-back of a new keyword goes to the feed and digests, never to instant alerts.
- People muted for the whole workspace never reach any channel.
- If you mark a mention not relevant, done or ignored before it is sent, it isn't sent.
- In Slack and Telegram, a post matched by several keywords or rules arrives as one message per channel.

## Mute authors

A rule's muted list keeps certain people out of that rule, such as your team, a bot or a competitor. Their mentions still appear in the feed and still count toward usage. A profile link or a link to one of their posts mutes that account.

| You enter | Rumoro saves |
| --- | --- |
| `https://x.com/name`, `https://twitter.com/name/status/123` | `https://x.com/name` |
| `https://www.linkedin.com/in/name`, `…/company/name/posts` | The profile or company page link |
| `https://www.reddit.com/user/name`, `u/name` | `https://www.reddit.com/user/name` |
| `https://news.ycombinator.com/user?id=name` | The same link |
| `name.bsky.social`, `did:plc:…` | The profile link, or the DID |
| `https://www.youtube.com/channel/UC…` | The same link |
| `https://www.tiktok.com/@name`, `…/@name/video/123` | `https://www.tiktok.com/@name` |
| `https://www.instagram.com/name/` | The same link. A post link doesn't name the author. |
| A GitHub, DEV or Stack Overflow profile link | The profile link |
| Any other site, such as `https://techcrunch.com/…` | `https://techcrunch.com`, which mutes that publication |
| `@name`, `Display Name` | `name` or `Display Name`, which mutes that name or handle on every platform |

Links that don't name a person, such as a subreddit, a thread or a story, are refused, and the error names the entry. A YouTube `@handle` link is saved but never matches, because mentions carry the channel link and name, so use one of those. A rule can mute up to 200 authors.

## Watch a link

A rule can react to what a post **links to**, for example a partner's referral link or a competitor's website. Put up to 20 hosts in `filter.linkHosts` ("Links to" in the dashboard). `["linear.app"]` also matches `docs.linear.app`, and a full URL or a `www.` prefix counts as the same host. Posts without links never match. `GET /v1/mentions` and `GET /v1/people` accept the same `linkHosts` filter.

```ts tab="TypeScript"
await rumoro.createAlert({
  body: { name: 'Links to our docs', mode: 'instant', filter: { linkHosts: ['docs.example.dev'] }, channelIds: ['dest_...'] },
});
```

```python tab="Python"
rumoro.alerts.create(
    name="Links to our docs", mode="instant", filter={"linkHosts": ["docs.example.dev"]}, channelIds=["dest_..."]
)
```

```bash tab="curl"
curl -X POST https://api.rumoro.dev/v1/alerts \
  -H "Authorization: Bearer $RUMORO_API_KEY" -H "Content-Type: application/json" \
  -d '{ "name": "Links to our docs", "mode": "instant", "filter": { "linkHosts": ["docs.example.dev"] }, "channelIds": ["dest_..."] }'
```

## One rule, several cases

To catch, say, "negative posts on Hacker News, or any question", list the cases in `filter.anyOf`. A mention is sent when the rest of the filter matches and at least one case does ([Conventions](/conventions#or-across-groups-anyof)).

```ts tab="TypeScript"
await rumoro.createAlert({
  body: {
    name: 'Needs a reply',
    mode: 'instant',
    filter: { anyOf: [{ platforms: ['hackernews'], sentiments: ['negative'] }, { intents: ['question'] }] },
    channelIds: ['dest_...'],
  },
});
```

```python tab="Python"
rumoro.alerts.create(
    name="Needs a reply",
    mode="instant",
    filter={"anyOf": [{"platforms": ["hackernews"], "sentiments": ["negative"]}, {"intents": ["question"]}]},
    channelIds=["dest_..."],
)
```

```bash tab="curl"
curl -X POST https://api.rumoro.dev/v1/alerts \
  -H "Authorization: Bearer $RUMORO_API_KEY" -H "Content-Type: application/json" \
  -d '{ "name": "Needs a reply", "mode": "instant",
        "filter": { "anyOf": [ { "platforms": ["hackernews"], "sentiments": ["negative"] },
                               { "intents": ["question"] } ] },
        "channelIds": ["dest_..."] }'
```

The relevance minimum applies to every case. The dashboard shows the cases on the rule and keeps them when you edit other settings. A rule can also filter by `groupIds`, author `tags`, `minFollowers`, `minConfidence`, star `ratings` and `notRatings`, and engagement minimums.

## Daily and weekly digests

A daily rule sends one digest per channel at the hour and time zone you choose. Mentions count by the time they were scored. A day without a relevant mention (40 or higher) is skipped unless you turn `skipEmpty` off. A weekly rule works the same way on `schedule.weekday` (0 is Sunday, 6 is Saturday).

What a digest contains depends on the channel.

- **Email, Slack and Telegram** show how many mentions came in and how many are relevant, the change against the previous period, the platforms and the keywords. Then come two lists, "Needs you today" and "Also worth a look", with negative mentions and those showing buying intent, questions, bug reports or complaints, most urgent first. Email shows up to 5 and 7, Slack and Telegram up to 5 and 5.
- **Webhooks** get the JSON described in [Digest events](/webhooks/digest-events).

**Run now** sends the rule's period (the last hour, day or week) immediately and doesn't change the schedule.

## Hourly digests

An `hourly` rule sends five minutes past every UTC hour and covers the hour before. An hour with nothing at or above the minimum sends nothing, except with **Run now**. Hourly rules take no `schedule`, and a `weekday` is refused.

Only Slack, Telegram and webhook channels take hourly digests. A rule with an email channel is refused (`400 hourly_email_unsupported`). A delivery that fails on a rate limit or a 5xx is retried until 45 minutes past the hour after it was due, and then skipped, leaving the mentions in the feed.

## Through the API

### Channels

```ts tab="TypeScript"
// List your channels
const { data: channels } = await rumoro.listChannels();

// Create an email channel
await rumoro.createChannel({ body: { kind: 'email', emails: ['support@example.dev'] } });
```

```python tab="Python"
# List your channels
channels = rumoro.channels.list()

# Create an email channel
rumoro.channels.create(kind="email", emails=["support@example.dev"])
```

```bash tab="curl"
# List your channels
curl https://api.rumoro.dev/v1/channels -H "Authorization: Bearer $RUMORO_API_KEY"

# Create an email channel
curl -X POST https://api.rumoro.dev/v1/channels \
  -H "Authorization: Bearer $RUMORO_API_KEY" -H "Content-Type: application/json" \
  -d '{ "kind": "email", "emails": ["support@example.dev"] }'
```

A Slack channel needs Slack connected first (`409 slack_not_connected`). Telegram chats are added with `POST /v1/telegram/links`.

### Rules

```ts tab="TypeScript"
// Daily at 08:30 in New York
await rumoro.createAlert({
  body: { name: 'Morning summary', mode: 'daily', schedule: { hour: 8, minute: 30, timezone: 'America/New_York' }, channelIds: ['dest_...'] },
});

// Every hour, relevant mentions only
await rumoro.createAlert({ body: { name: 'Hourly pulse', mode: 'hourly', channelIds: ['dest_...'] } });

// Every Friday at 16:00 in Paris, French posts only
await rumoro.createAlert({
  body: {
    name: 'Weekly recap',
    mode: 'weekly',
    schedule: { hour: 16, minute: 0, timezone: 'Europe/Paris', weekday: 5 },
    filter: { languages: ['fr'] },
    channelIds: ['dest_...'],
  },
});
```

```python tab="Python"
# Daily at 08:30 in New York
rumoro.alerts.create(
    name="Morning summary", mode="daily",
    schedule={"hour": 8, "minute": 30, "timezone": "America/New_York"}, channelIds=["dest_..."],
)

# Every hour, relevant mentions only
rumoro.alerts.create(name="Hourly pulse", mode="hourly", channelIds=["dest_..."])

# Every Friday at 16:00 in Paris, French posts only
rumoro.alerts.create(
    name="Weekly recap", mode="weekly",
    schedule={"hour": 16, "minute": 0, "timezone": "Europe/Paris", "weekday": 5},
    filter={"languages": ["fr"]}, channelIds=["dest_..."],
)
```

```bash tab="curl"
# Daily at 08:30 in New York
curl -X POST https://api.rumoro.dev/v1/alerts \
  -H "Authorization: Bearer $RUMORO_API_KEY" -H "Content-Type: application/json" \
  -d '{ "name": "Morning summary", "mode": "daily",
        "schedule": { "hour": 8, "minute": 30, "timezone": "America/New_York" }, "channelIds": ["dest_..."] }'

# Every hour, relevant mentions only
curl -X POST https://api.rumoro.dev/v1/alerts \
  -H "Authorization: Bearer $RUMORO_API_KEY" -H "Content-Type: application/json" \
  -d '{ "name": "Hourly pulse", "mode": "hourly", "channelIds": ["dest_..."] }'

# Every Friday at 16:00 in Paris, French posts only
curl -X POST https://api.rumoro.dev/v1/alerts \
  -H "Authorization: Bearer $RUMORO_API_KEY" -H "Content-Type: application/json" \
  -d '{ "name": "Weekly recap", "mode": "weekly",
        "schedule": { "hour": 16, "minute": 0, "timezone": "Europe/Paris", "weekday": 5 },
        "filter": { "languages": ["fr"] }, "channelIds": ["dest_..."] }'
```

`PATCH /v1/alerts/{id}` replaces the whole filter, including `excludeAuthors`. A rule's name is 1 to 80 characters, and a rule can have up to 20 channels.

### Mute and unmute

These change the muted list without rewriting the filter. Both take the same body, are safe to repeat and return the rule.

```ts tab="TypeScript"
await rumoro.muteAlertAuthors({
  path: { id: 'feed_...' },
  body: { authors: ['https://x.com/ourcompany', 'u/release-bot', 'name.bsky.social'] },
});

await rumoro.unmuteAlertAuthors({ path: { id: 'feed_...' }, body: { authors: ['name.bsky.social'] } });
```

```python tab="Python"
rumoro.alerts.mute("feed_...", authors=["https://x.com/ourcompany", "u/release-bot", "name.bsky.social"])

rumoro.alerts.unmute("feed_...", authors=["name.bsky.social"])
```

```bash tab="curl"
curl -X POST https://api.rumoro.dev/v1/alerts/feed_.../mute \
  -H "Authorization: Bearer $RUMORO_API_KEY" -H "Content-Type: application/json" \
  -d '{ "authors": ["https://x.com/ourcompany", "u/release-bot", "name.bsky.social"] }'

curl -X POST https://api.rumoro.dev/v1/alerts/feed_.../unmute \
  -H "Authorization: Bearer $RUMORO_API_KEY" -H "Content-Type: application/json" \
  -d '{ "authors": ["name.bsky.social"] }'
```

### Test and run

```ts tab="TypeScript"
// Send a test through every channel of the rule
await rumoro.testAlert({ path: { id: 'feed_...' } });

// Send the digest for the current period now
await rumoro.runAlertDigest({ path: { id: 'feed_...' } });
```

```python tab="Python"
# Send a test through every channel of the rule
rumoro.alerts.test("feed_...")

# Send the digest for the current period now
rumoro.alerts.run("feed_...")
```

```bash tab="curl"
# Send a test through every channel of the rule
curl -X POST https://api.rumoro.dev/v1/alerts/feed_.../test -H "Authorization: Bearer $RUMORO_API_KEY"

# Send the digest for the current period now
curl -X POST https://api.rumoro.dev/v1/alerts/feed_.../run -H "Authorization: Bearer $RUMORO_API_KEY"
```
