# Send Email Report & Slack, Discord, Teams, Telegram Alerts (`charlesmarkette/send-email-report-slack-discord-teams-telegram-alerts`) Actor

Send a dataset report when any scraper finishes: email (your own SMTP or Resend) with a preview table and CSV/JSON attachment, plus Slack, Discord, Microsoft Teams, Telegram or webhook alerts. Sends only when there are items, retries each channel on its own. $0.0009 per notification.

- **URL**: https://apify.com/charlesmarkette/send-email-report-slack-discord-teams-telegram-alerts.md
- **Developed by:** [Charles Markette](https://apify.com/charlesmarkette) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.90 / 1,000 notification sents

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

**Send Email Report & Slack, Discord, Teams, Telegram Alerts** (Notify Step) is the last step after any scraper. When a run finishes, it sends a short report of the run's dataset (the item count, a preview table and links to the dataset and the run) as an **email** and as **Slack, Discord, Microsoft Teams, Telegram or webhook notifications**, all through **your own accounts**. It's for anyone who schedules scrapers or monitors on Apify and wants to be told when there's something new, without opening Apify Console or building a Zapier flow.

- **Email** through your own SMTP server (Gmail, Outlook, SendGrid, Amazon SES, Mailgun, your company server …) or a **Resend** API key, with an HTML table, a plain-text version, and an optional **CSV or JSON attachment** of the data.
- **Slack**, **Discord** and **Microsoft Teams** through their incoming webhooks (in Teams, the new Workflows webhooks).
- **Telegram** through your own bot.
- **Any webhook** (Zapier, Make, n8n, your own API): a JSON POST with the count, links and the first items.

Connect it once on your scraper's **Integrations** tab, and every run reports to you.

### Why use this notification Actor?

- **Reliable.** Each channel is sent and retried on its own: rate limits (HTTP 429) and server errors (5xx) are retried with backoff, honouring the service's `Retry-After`. One channel failing never stops the others, and the run fails only when every channel failed, so a broken webhook gets noticed without hiding the messages that did arrive. If the platform restarts a run, channels already recorded as sent are not sent again (see Limitations for the small gap).
- **Your own providers.** Messages go out from your SMTP server, your Resend account, your Slack/Discord/Teams webhooks and your Telegram bot. Nothing is relayed through a third-party mail service, and you can use your own domain and sender name.
- **Many channels in one step.** Fill in any combination; each gets a message formatted for it (Slack blocks, Discord embeds, a Teams Adaptive Card, Telegram HTML, a real HTML table in email).
- **Only when it matters.** By default it sends nothing when the dataset is empty, or set a minimum number of items. Skipped runs send nothing and aren't charged for notifications.
- **Safe with scraped data.** Every value is escaped for its format (HTML in email, Telegram HTML, Slack's `&`, `<`, `>`, Discord markdown), so scraped text can't break the message, inject links, or ping `@everyone`. Long values are shortened, and messages are kept within each service's size limits.
- **Your secrets stay secret.** Passwords, API keys, bot tokens and webhook URLs (which are secrets too) are stored encrypted, and they're masked in every log line, error message and output.

### Use cases

- **New-item alerts:** a Slack or Telegram message when a monitor finds new listings, jobs, leads or price drops.
- **Daily email report:** an HTML table of the day's results with the full dataset attached as CSV, sent to you or a client.
- **Team channels:** post results to a Microsoft Teams or Discord channel your team already watches.
- **Hand off to automation:** trigger Zapier, Make, n8n or your own API with the item count, links and first items.
- **Run health:** with **When to send: Always**, get a message after every run, including empty ones, so you notice when a scraper stops finding data.

### How to use it

#### Setup (about 2 minutes per channel)

Fill in only the channels you want. To check your setup, run the Actor once **without a dataset**: it sends a real test message with 3 sample items to every configured channel (charged like any other notification). Or turn on **Dry run** (free) to see the rendered messages in the output without sending anything.

**Slack**

1. Go to [api.slack.com/apps](https://api.slack.com/apps), **Create New App → From scratch**, and pick your workspace.
2. Open **Incoming Webhooks**, turn on **Activate Incoming Webhooks**, click **Add New Webhook to Workspace**, pick a channel and **Allow**.
3. Copy the `https://hooks.slack.com/services/…` URL into **Slack incoming webhook URL**. ([Slack's guide](https://docs.slack.dev/messaging/sending-messages-using-incoming-webhooks/))

**Discord**

1. In your server, open **Server Settings → Integrations** and click **Create Webhook**.
2. Pick the channel, then **Copy Webhook URL** into **Discord webhook URL**. ([Discord's guide](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks))

**Microsoft Teams**

1. In Teams, open the channel's **More options (…) → Workflows**, and choose the template **Send webhook alerts to a channel**.
2. Confirm the account, pick the team and channel, select **Add workflow**, and copy the URL it shows into **Microsoft Teams webhook URL**. ([Microsoft's guide](https://support.microsoft.com/en-us/office/create-incoming-webhooks-with-workflows-for-microsoft-teams-8ae491c7-0394-4861-ba59-055e33f75498))

Old Office 365 connector webhooks also work while Microsoft still accepts them, but Microsoft is retiring them, so new setups should use Workflows.

**Telegram**

1. In Telegram, message [@BotFather](https://t.me/BotFather), send `/newbot` and follow the steps. Copy the token (`123456789:AA…`) into **Telegram bot token**. ([Telegram's guide](https://core.telegram.org/bots/tutorial))
2. Send your bot any message (or add it to a group, or make it an admin of your channel). Then open `https://api.telegram.org/bot<your token>/getUpdates` in a browser and copy the `"chat":{"id": …}` number into **Telegram chat ID**. For a public channel you can use `@channelname` instead.

**Email with Resend**

1. Sign up at [resend.com](https://resend.com), add and verify your sending domain ([Resend: domains](https://resend.com/docs/dashboard/domains/introduction)).
2. Create an API key with sending access ([Resend: API keys](https://resend.com/docs/dashboard/api-keys/introduction)) and paste it into **Resend API key**.
3. Set **From** (an address on your verified domain) and **To**.

**Email with SMTP**

1. Fill in **SMTP server**, **port** and **security** from your provider: usually STARTTLS on port 587, or SSL on port 465.
2. Fill in **username** and **password**. Gmail, Outlook and iCloud need an **app password** instead of your normal password (Gmail: [Sign in with app passwords](https://support.google.com/accounts/answer/185833); it needs 2-Step Verification).
3. Set **From** and **To**.

**Any webhook**: paste the URL, and optionally headers such as `{"Authorization": "Bearer …"}`.

#### Use it after a scraper (integration): a notification after every run

1. Open your scraper's Actor or saved task, go to the **Integrations** tab and click **Add integration**.
2. Search for this Actor, choose to start it when the run **succeeded**, and set the input. Apify fills in `{{resource.defaultDatasetId}}` with that run's dataset:

```json
{
  "datasetId": "{{resource.defaultDatasetId}}",
  "actorName": "Amazon price tracker",
  "condition": "onlyIfItems",
  "subject": "{actorName}: {itemCount} new deals",
  "messageTemplate": "{actorName} found {itemCount} items on {date}.",
  "previewFields": ["title", "price", "url"],
  "previewRows": 10,
  "slackWebhookUrl": "https://hooks.slack.com/services/…",
  "smtpHost": "smtp.gmail.com",
  "smtpPort": 587,
  "smtpSecurity": "starttls",
  "smtpUsername": "you@gmail.com",
  "smtpPassword": "your app password",
  "emailTo": ["you@example.com"],
  "attachment": "csv"
}
```

3. Save, and test the integration on a past run.

The message placeholders use **single** braces (`{itemCount}`), because Apify fills in every `{{…}}` in an integration's input itself and rejects variables it doesn't know. The older `{{itemCount}}` form still works in manual runs and saved tasks.

**Only new items:** put [Merge & Deduplicate Datasets](https://apify.com/charlesmarkette/merge-deduplicate-datasets-only-new-items) in "only new" mode between the scraper and this Actor (connect this Actor to the dedupe Actor's runs), and you're told only about items you haven't seen before.

### Input example

A Telegram and Discord alert with a minimum of 5 items:

```json
{
  "datasetId": "aBcDeFgHiJkLmNoPq",
  "actorName": "Job board monitor",
  "condition": "minItems",
  "minItems": 5,
  "previewFields": ["title", "company", "url"],
  "telegramBotToken": "123456789:AA…",
  "telegramChatId": "-1001234567890",
  "discordWebhookUrl": "https://discord.com/api/webhooks/…"
}
```

| Field | Notes |
|---|---|
| Dataset | The dataset to summarize. In integrations use `{{resource.defaultDatasetId}}`. Empty = a test message with 3 sample items (sent and charged like a normal notification). |
| Run URL / Name shown in messages | Optional, shown in messages. |
| When to send | `onlyIfItems` (default), `always`, or `minItems` with **Minimum items**. Runs that don't qualify send nothing. |
| Dry run | Renders every configured message into `OUTPUT` and the dataset without sending (free). With no channel configured, it previews all of them. |
| Subject / Message | Placeholders: `{itemCount}`, `{datasetId}`, `{datasetUrl}`, `{runUrl}`, `{actorName}`, `{date}` (UTC). The old `{{itemCount}}` form is still accepted, but don't use it in integrations. Links to the dataset and the run are added automatically. |
| Preview fields | Fields to show (dots for nested ones, e.g. `seller.name`). Empty = the first 8 fields. |
| Preview rows | Default 10, max 100. Chat apps may show fewer to stay within their limits (see below). |
| SMTP server, port, security, username, password | Your SMTP account. Security: `starttls`, `ssl` or `none`. With `none`, a username or password is refused (credentials are never sent unencrypted), so `none` only suits a trusted relay without login. |
| Resend API key | Instead of SMTP. Fill in one or the other, not both. |
| From, To, Cc, Bcc, Reply-To | Email addresses. Each entry may hold several addresses separated by `,` or `;`. Bcc recipients are never shown in the email headers. |
| Attach the dataset | Email only: `none`, `csv` or `json`. |
| Slack / Discord / Teams webhook URL | One URL per channel. Discord can also post under a custom name; Discord doesn't allow names containing "discord" or "clyde", so such a name is ignored (with a warning) and the webhook's own name is used. |
| Telegram bot token + chat ID | Both are needed. |
| Webhook URL + headers | A JSON POST: `itemCount`, `datasetId`, `datasetUrl`, `runUrl`, `actorName`, `date`, `subject`, `message`, `fields` and `items` (the first *Preview rows* items, unchanged). |

All passwords, keys, tokens, webhook URLs and webhook headers are secret inputs, stored encrypted.

### Output example

The run's **dataset** has one row per configured channel:

```json
[
  {
    "channel": "email",
    "status": "sent",
    "error": null,
    "message": "accepted by smtp.gmail.com",
    "target": "smtp.gmail.com:587 (1 recipient)",
    "attempts": 1,
    "itemCount": 42,
    "datasetId": "aBcDeFgHiJkLmNoPq",
    "sentAt": "2026-10-13T08:30:05+00:00"
  },
  {
    "channel": "slack",
    "status": "sent",
    "error": null,
    "message": "posted",
    "target": "hooks.slack.com",
    "attempts": 1,
    "itemCount": 42,
    "datasetId": "aBcDeFgHiJkLmNoPq",
    "sentAt": "2026-10-13T08:30:04+00:00"
  }
]
```

- `channel` is `email`, `slack`, `discord`, `teams`, `telegram` or `webhook`; `status` is `sent`, `failed`, `skipped` or `dry run`; `error` says why a channel failed (with secrets masked); `target` shows just the host, chat ID or number of recipients.
- The key-value store **`OUTPUT`** holds the same per-channel results plus the item count, whether the condition was met, and the attachment note. In a dry run, each channel also holds the exact message that would be sent (`preview`).
- Without any channel configured, the run returns a single row with setup instructions and sends nothing.

### How much does it cost to send notifications?

This Actor uses pay-per-event pricing: **$0.0009 per notification sent**, plus Apify's $0.00005 run start fee. One notification is one channel message that the service accepted; an email to several recipients is one notification. Test messages (a run without a dataset) are real notifications and are charged. Skipped runs (empty dataset, or fewer than your minimum), dry runs, channels that failed, and the result rows in the dataset aren't charged as notifications; only the start fee applies.

*Examples use the Free-plan rate. Where an example doesn't add it, each run also has its run-start charge, counted per GB of Actor memory (minimum one; one at the default memory); Apify Proxy (where used) is billed separately.*

**Worked example:** a scraper that runs every day and reports to Slack and email sends 2 notifications per run: 2 × $0.0009 + $0.00005 = **$0.00185 per run**, about **$0.06 per month**.

If you set a maximum cost per run, the Actor sends only as many channels as that allows and marks the rest as skipped.

### FAQ

#### Gmail or Outlook says the SMTP login was rejected.

Use an **app password**, not your normal password. Gmail needs 2-Step Verification turned on before you can create one.

#### Slack answers 404 or 410.

The webhook or its channel no longer exists, or the channel was archived. Create a new incoming webhook and paste its URL.

#### My Telegram bot doesn't post.

Send the bot a message first (or add it to the group, or make it an admin of the channel), then get the chat ID from `getUpdates`. Group IDs start with `-`.

#### The placeholders show up as text, or the integration refuses my input.

In integrations use single braces, like `{itemCount}`. Apify fills in double-brace `{{…}}` variables itself and rejects the ones it doesn't know.

#### "The dataset ID is still a template".

`{{resource.defaultDatasetId}}` is filled in only when the Actor runs from an integration. For a manual run, pick the dataset.

#### Nothing was sent, and the run succeeded.

The dataset was empty (or had fewer items than **Minimum items**). Choose **When to send: Always** if you want a message after every run.

#### Can I send to several Slack channels or email lists?

Email takes several To, Cc and Bcc addresses. For several Slack, Discord or Teams channels, connect one integration per channel.

### Limitations

- **Slack:** 3,000 characters per text block and 50 blocks per message; the preview is kept to about 12,000 characters.
- **Discord:** message text up to 2,000 characters, up to 10 embeds and 6,000 characters across all embeds, so Discord shows up to 10 items.
- **Telegram:** 4,096 characters per message, so it shows as many items as fit. If Telegram ever rejects the formatting, the message is resent as plain text.
- **Teams:** 28 KB per message, so the card shows as many items as fit.
- **Email:** the table shows up to 100 items. Values are cut at 300 characters in email and 120 in chat apps.
- **Attachment:** about 10 MB. A bigger dataset attaches its first items, and the email says how many. If even one item is over 10 MB, the attachment is skipped (and the email says so). Your provider's own size limit still applies; many SMTP servers allow 10 to 25 MB.
- **Retries:** Slack, Discord, Teams, Telegram, Resend and the webhook get up to 4 attempts (on HTTP 429, 5xx and network errors; Resend also on a concurrent request with the same idempotency key), waiting at most 60 seconds at a time and 90 seconds in total per channel. A service that asks for a longer wait counts as failed for this run. SMTP gets up to 3 attempts (on connection errors and temporary 4xx replies). Channels are sent in parallel.
- **Restarts:** after each message goes out, the channel is recorded in the run's key-value store, and a restarted run (for example after a platform migration) skips recorded channels. A restart in the moment between a message going out and that record being saved can still send that channel twice. Charges carry a per-run, per-channel idempotency key, so such a repeat isn't charged twice.
- **Duplicates:** if a service accepts a message but its answer is lost (for example a timeout), the retry can deliver it twice. Resend emails are sent with a per-run idempotency key, so Resend won't send the same email twice within 24 hours; if a restarted run's email differs slightly (for example the time in the subject), Resend refuses it and the channel is reported as "probably already sent".
- Webhook URLs must use `https` (the generic webhook may also use `http`). Redirects aren't followed.
- The dataset is read once: without an attachment, only the preview rows are read and the item count comes from the dataset's metadata; with an attachment, items are read in pages of 100 until the 10 MB cap.
- **Formatting:** Telegram and Discord limits are counted the way those services count (UTF-16 code units, so an emoji can count as 2). If Telegram still says a message is too long, it's retried once with a shorter preview. Markdown characters in your data are escaped in Teams cards. In the CSV attachment, any value starting with `=`, `+`, `-`, `@` (or their full-width forms), a tab or a carriage return gets a leading `'` so spreadsheet apps don't run it as a formula; plain numbers such as `-5` or `1.5e3` are left as they are.
- Values of custom webhook headers are masked in logs when they're longer than 8 characters or the header name looks like a credential (`Authorization`, `X-Api-Key`, `…Token`, `…Secret` …).
- Not affiliated with Slack, Discord, Microsoft, Telegram or Resend.

### Related Actors

This notification Actor is part of a small suite of Actors that work together after any scraper:

| Actor | Use it to |
|---|---|
| [Merge & Deduplicate Datasets](https://apify.com/charlesmarkette/merge-deduplicate-datasets-only-new-items) | Keep only items not seen in earlier runs, so you're alerted only about new ones. |
| [Google Sheets Sync](https://apify.com/charlesmarkette/google-sheets-sync) | Keep the full results in a Google Sheet and link it in your message. |
| [Dataset Delivery](https://apify.com/charlesmarkette/dataset-delivery-export-to-sftp-ftp-dropbox-google-drive) | Deliver the full dataset as a file to SFTP, FTP, Dropbox or Google Drive. |
| [SEC Funding Rounds Tracker](https://apify.com/charlesmarkette/sec-form-d-funding-tracker) | Get a daily Slack or email digest of new startup funding rounds. |
| [Vector DB Sync](https://apify.com/charlesmarkette/vector-db-sync-pinecone-qdrant-rag-loader-delta-updates) | Keep a Pinecone or Qdrant index in sync with your crawls. |
| [Phone Number Validator & Formatter](https://apify.com/charlesmarkette/phone-number-validator-formatter-bulk-e164) | Check phone numbers in new leads before you are alerted about them. |
| [RSS & Atom Feed Reader](https://apify.com/charlesmarkette/rss-atom-feed-reader-only-new-items) | Get an email or chat message with new feed items after each run. |
| [Dataset Filter & Transform](https://apify.com/charlesmarkette/dataset-filter-transform-rename-flatten-fields) | Filter the rows first, so you are alerted only about the ones that matter. |
| [Bulk URL Status Checker](https://apify.com/charlesmarkette/bulk-url-status-checker-redirects-ssl) | Get an alert when any of your URLs breaks or its SSL certificate is about to expire. |
| [Broken Link Checker](https://apify.com/charlesmarkette/broken-link-checker-crawl-website-404) | Crawl your site on a schedule and get an alert when broken links appear. |
| [Website Contact Info Scraper](https://apify.com/charlesmarkette/website-contact-info-scraper-emails-phones-socials) | Scan company websites for emails, phones and social profiles. |
| [Career Page Jobs Scraper](https://apify.com/charlesmarkette/career-page-jobs-scraper-greenhouse-lever-ashby) | Get an email or Slack message when tracked companies post new jobs. |

# Actor input Schema

## `datasetId` (type: `string`):

The dataset to summarize, usually the scraper run's output. In an integration (Integrations → Connect Actor or task), use {{resource.defaultDatasetId}}. Leave empty to send a test message with 3 sample items.

## `runUrl` (type: `string`):

Optional link to the run, shown in the message.

## `actorName` (type: `string`):

Optional, e.g. 'Amazon price tracker'. Default: 'Your Apify run'.

## `condition` (type: `string`):

Runs that don't meet the condition send nothing and are free.

## `minItems` (type: `integer`):

Used with 'Only if the dataset has at least Minimum items'.

## `dryRun` (type: `boolean`):

Renders every configured message into OUTPUT and the dataset without sending anything (free). With no channel configured, it previews all channels.

## `subject` (type: `string`):

Email subject and chat message title. Default: '{actorName}: {itemCount} items'.

## `messageTemplate` (type: `string`):

Text above the preview. Default: '{actorName} finished with {itemCount} items.' Links to the dataset and run are added automatically.

## `previewFields` (type: `array`):

Fields to show for each item, e.g. 'title', 'price', 'url'. Dots for nested fields ('seller.name'). Empty = the first 8 fields.

## `previewRows` (type: `integer`):

How many items to show (max 100). Chat apps may show fewer to stay within their message limits.

## `smtpHost` (type: `string`):

e.g. smtp.gmail.com, smtp.office365.com, smtp.sendgrid.net, email-smtp.us-east-1.amazonaws.com

## `smtpPort` (type: `integer`):

Default: 587 for STARTTLS, 465 for SSL, 25 for none.

## `smtpSecurity` (type: `string`):

How to encrypt the connection to the SMTP server.

## `smtpUsername` (type: `string`):

Usually your email address or the provider's SMTP user name. Leave empty for servers without login.

## `smtpPassword` (type: `string`):

Stored encrypted. Gmail, Outlook and iCloud need an app password.

## `resendApiKey` (type: `string`):

Instead of SMTP: a Resend API key (re\_…) with sending access. Stored encrypted.

## `emailFrom` (type: `string`):

e.g. 'Scraper alerts <alerts@yourdomain.com>'. Must be an address your provider lets you send from (with Resend, a verified domain).

## `emailTo` (type: `array`):

One or more recipients.

## `emailCc` (type: `array`):

Optional.

## `emailBcc` (type: `array`):

Optional. Never shown to other recipients.

## `emailReplyTo` (type: `string`):

Optional.

## `attachment` (type: `string`):

Email only. Attachments are limited to about 10 MB: bigger datasets attach the first items and the email says so.

## `slackWebhookUrl` (type: `string`):

https://hooks.slack.com/services/… Stored encrypted.

## `discordWebhookUrl` (type: `string`):

https://discord.com/api/webhooks/… Stored encrypted.

## `discordUsername` (type: `string`):

Optional name to post as (overrides the webhook's name).

## `teamsWebhookUrl` (type: `string`):

The URL of a Teams Workflows 'Send webhook alerts to a channel' flow (or an incoming webhook). Stored encrypted.

## `telegramBotToken` (type: `string`):

From @BotFather, like 123456789:AA… Stored encrypted.

## `telegramChatId` (type: `string`):

Your user ID, a group ID (starts with -) or @channelusername. The bot must be in the group, or an admin of the channel.

## `genericWebhookUrl` (type: `string`):

Receives a JSON POST with itemCount, datasetId, datasetUrl, runUrl, actorName and the first items. Stored encrypted.

## `genericWebhookHeaders` (type: `object`):

Optional JSON object, e.g. {"Authorization": "Bearer …"}. Stored encrypted.

## Actor input object example

```json
{
  "condition": "onlyIfItems",
  "minItems": 1,
  "dryRun": false,
  "previewRows": 10,
  "smtpSecurity": "starttls",
  "attachment": "none"
}
```

# Actor output Schema

## `channels` (type: `string`):

Channel, status, error, target and attempts for each configured channel.

## `summary` (type: `string`):

Item count, condition, attachment note and per-channel results (with rendered messages in a dry run).

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("charlesmarkette/send-email-report-slack-discord-teams-telegram-alerts").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {}

# Run the Actor and wait for it to finish
run = client.actor("charlesmarkette/send-email-report-slack-discord-teams-telegram-alerts").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call charlesmarkette/send-email-report-slack-discord-teams-telegram-alerts --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,charlesmarkette/send-email-report-slack-discord-teams-telegram-alerts"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/r6U7hbqzkizvxjrKr/builds/LTMkkjnOICz1biPK8/openapi.json
