AI Slack Bot That Sends a Daily Trend Report
Build an AI Slack bot that posts a scheduled daily trend report: Slack app setup, cron scheduling, Block Kit, idempotency, read-only agent tools and approval

An AI Slack bot that sends a daily report is a scheduled job that gathers data, has an LLM write a short brief, and posts it into a channel with chat.postMessage. You build it as a Slack app (Bolt for Python or JavaScript, or raw HTTP), run the schedule from a cron job or scheduler outside Slack, and store channel and token details in environment variables.
The one thing that separates a useful daily report from a generic wall of text is change detection: what moved since the last report. This guide shows a complete build where the bot reads ranked trend movers from TrendsAGI, compares them to the prior period, pulls an AI insight for the top mover, and posts a Block Kit summary — with read-only defaults, opt-in writes, and a human-approval step before any campaign change.
What the bot actually does each morning
At a fixed time, the job runs four steps:
- Query trends sorted by velocity (posts per hour measured from stored snapshots) for your category.
- Fetch analytics for the top trend over 7 days and compare it with the previous period.
- Read the AI insight (summary, key themes, sentiment, marketing angles) for context.
- Post a formatted Block Kit message, plus any recommendation that needs human approval.
This is the pattern Slack's docs point you toward, but Slack's own documentation stops at app creation and agent starter templates — it does not ship a scheduled daily report. Raindrop's Briefs run on a schedule and post a write-up to Slack, and Ravenna.ai offers AI Briefs; if you want a product rather than code, those are the buy options. If you want the report built from your own signal source, keep reading.
Step 1: Create and install the Slack app
You need a bot token with the scopes that let it post. Create the app, add a bot user, install it to your workspace, then invite the bot into the target channel with /invite @your-bot. Posting into a private channel fails until the bot is a member.
# .env — never commit this file
SLACK_BOT_TOKEN=xoxb-your-token
TRENDSAGI_API_KEY=your-trendsagi-key
SLACK_CHANNEL_ID=C0123456789In Python, install Bolt and the TrendsAGI SDK:
pip install slack-bolt trendsagiStep 2: Query trend data with the SDK
TrendsAGI measures velocity as a snapshot rate — posts per hour from stored readings — not a forecast or a unique-user count. That is exactly what you want for a daily delta report.
import os
from trendsagi import TrendsAGIClient
client = TrendsAGIClient(api_key=os.environ["TRENDSAGI_API_KEY"])
def top_movers(category="Technology", limit=5):
trends = client.get_trends(
category=category,
sort_by="velocity",
sort_dir="desc",
limit=limit,
period="24h",
)
return trends
def detail(trend_id):
analytics = client.get_trend_analytics(trend_id, period="7d")
insights = client.get_ai_insights(trend_id)
return analytics, insightsIf you prefer JavaScript, call the REST API directly with the same headers: X-API-Key: <key> against https://api.trendsagi.com/api/trends, passing sort_by=velocity and period=24h.
Step 3: Have the LLM write the brief from structured data
Feed the model numbers, not raw channel logs. Pass trend names, velocity values and the 7-day analytics object; ask for a short summary with a headline, two or three bullets, and a one-line takeaway. Keeping the payload structured avoids leaking unrelated conversation content into a prompt.
Every daily summary costs tokens and adds latency. A five-trend payload with a compact instruction set is cheap and fast; pulling dozens of trends and long insight bodies into the prompt is not. Cache the insight text per trend ID for the day so the report and any follow-up questions do not pay twice.
Step 4: Post the daily report with Block Kit
import os
from slack_bolt import App
from trendsagi import TrendsAGIClient
app = App(token=os.environ["SLACK_BOT_TOKEN"])
client = TrendsAGIClient(api_key=os.environ["TRENDSAGI_API_KEY"])
def build_blocks(date_label, movers, insight):
rows = [
{"type": "section", "text": {"type": "mrkdwn", "text": f"*{t['name']}* — velocity {t['volume']} ({t.get('category','')})"}}
for t in movers
]
return [
{"type": "header", "text": {"type": "plain_text", "text": f"Daily trend report — {date_label}"}},
{"type": "section", "text": {"type": "mrkdwn", "text": insight.get("summary", "No summary available.")}},
*rows,
{"type": "context", "elements": [{"type": "mrkdwn", "text": "Velocity is a measured snapshot rate, not a forecast."}]},
]
def post_report(channel):
movers = client.get_trends(category="Technology", sort_by="velocity", limit=5, period="24h")
top_id = movers[0]["id"]
insight = client.get_ai_insights(top_id)
app.client.chat_postMessage(
channel=channel,
blocks=build_blocks("today", movers, insight),
text="Daily trend report",
)Run this on a cron schedule from your own worker or container, not from inside Slack. The scheduler is the only thing that reliably fires the post; Slack's new Slackbot automation (slack.com, October 28, 2026) is event-triggered, and there is no confirmation it fires on a time schedule.
Step 5: Avoid duplicates and handle failures
Give each report a deterministic key (date plus channel). Before posting, check a small store or a Slack search for that key; if it exists, skip. Wrap the post in a retry with exponential backoff, and record failures. Slack's official docs do not describe message-post rate limits in detail, so treat limit errors as retryable rather than fatal, and cap attempts per run.
For deeper rate-limit visibility on the TrendsAGI side, check usage:
usage = client.get_api_usage()
print(usage)Step 6: Read-only by default, writes on approval
If you add agent tooling, the SDK ships TrendsAGITools read-only by default. Only pass allow_writes=True when the bot must change a watchlist or recommendation.
from trendsagi import TrendsAGITools
tools = TrendsAGITools(client) # read-only
tools_ro = tools.openai()
# When human approval is wired in:
tools_rw = TrendsAGITools(client, allow_writes=True)
result = tools.call("list_recommendations", {"priority": "high"})
print(result["result"]["recommendations"][0]["id"])Ad-platform changes are previewed before any explicit execution, so the bot should propose, not act. That gives a human a chance to approve the campaign change in the channel.
Step 7: The same report through the MCP server
If you prefer an agent runtime over a custom cron job, expose the same data through the hosted MCP server and let the agent generate the report:
pip install "trendsagi[mcp]"claude mcp add --transport http trendsagi https://api.trendsagi.com/mcpThe hosted server uses OAuth 2.1 sign-in or an API key as a Bearer token, and tool calls count as API requests. For a local stdio setup:
export TRENDSAGI_API_KEY="your-key"
trendsagi mcpThe same read-only tools — list_trends, get_trend_ai_insights, get_trend_analytics — are available inside an agent, so the daily report can be assembled by the agent and posted to Slack by a thin handler.
End-to-end example: a campaign proposal for human approval
Combine the pieces: find the top mover, check its 7-day analytics, read the insight, then pull a high-priority recommendation and post it with an approve button. The bot never executes the ad change itself; it hands the preview to a person.
def daily_campaign_proposal(channel):
movers = client.get_trends(sort_by="growth", limit=3, period="7d")
top = movers[0]
analytics = client.get_trend_analytics(top["id"], period="7d")
insight = client.get_ai_insights(top["id"])
recs = client.get_recommendations(limit=3, priority="high")
app.client.chat_postMessage(
channel=channel,
text=f"Proposed action for {top['name']}",
blocks=[
{"type": "section", "text": {"type": "mrkdwn", "text": f"*{top['name']}* is accelerating. {insight.get('summary','')}"}},
{"type": "section", "text": {"type": "mrkdwn", "text": "Review the preview and approve before anything is executed."}},
],
)Build or buy: a quick comparison
| Option | Effort | Control | Data source |
|---|---|---|---|
| Custom Bolt bot | Highest — app scopes, token, scheduler, storage | Full | Whatever you connect |
| TrendsAGI MCP agent | Medium | High | Trend velocity and insights |
| Raindrop Briefs | Low | Limited | Connected tools over MCP |
| Apify Morning Brief | Low | Limited | Slack, Gmail, Calendar, Notion |
Pricing for Slack plans, Raindrop, Ravenna and no-code tools is not stated in the pages reviewed here, so verify before committing.
Frequently asked questions
How do I create and install a Slack app so it can post into a channel?
Create the app, add a bot user with chat scopes, install it to the workspace, then invite the bot to the target channel with /invite. Store the bot token in an environment variable and never in source.
Cron vs. Slack-native scheduling: which posts the report reliably?
Run the schedule from a cron job or external scheduler you control, and have it call Slack. Slackbot's October 2026 update is event-triggered per slack.com, and there is no confirmation it fires on a time schedule, so do not rely on it for a daily post.
What data should the report summarize, and how do I avoid leaking channel content?
Summarize structured numbers — trend names, velocity, deltas, analytics — rather than raw messages. Send only those fields to the model, and keep API keys in environment variables.
Which Slack SDK path should I take: Bolt, raw HTTP, or an MCP interface?
Bolt for Python or JavaScript is the fastest route to a posting bot. Use raw HTTP if you already run a web framework, and the MCP server if an agent runtime should generate the report.
How do I format the message and avoid duplicate posts?
Use Block Kit for structure and always pass a plain-text fallback. Use a deterministic key per date and channel, check it before posting, and retry with backoff on failure.
When is a no-code tool better than a custom bot?
If the report is a summary of connected tools and you do not need change detection, a no-code recipe is less work. A custom bot wins when the signal must be measured over time or when you need human-approved write actions.
How we researched this
Prepared by TrendsAGI Research. Facts checked on 31 October 2026 against the sources listed below.
Capability descriptions and code use TrendsAGI's documented REST API, Python SDK and MCP server. External facts come from the sources below.
| Claim | Applies to | Source |
|---|---|---|
| Slackbot now acts on the user's behalf the moment something worth acting on happens, adding event-triggered automation, desktop access and skills. | Slackbot, October 2026 update | slack.com |
| Raindrop Briefs investigate on a schedule with the same tools as the Triage Agent — events, signals, issues, stumbles, users, cost, traces, charts, and anything connected over MCP — and post the write-up to Slack. | Raindrop Platform, Briefs | raindrop.ai |
We re-research and rewrite this page at least every four months; the updated date only changes when the facts are re-checked.
Sources
- Slack developer docs — docs.slack.dev
- Slackbot gets more proactive, in more places — slack.com
- Briefs – Raindrop docs — raindrop.ai