Send AI-Summarized Cloud Cost Alerts to Slack Without the Spreadsheet
Turn raw cloud billing data into plain-language cost alerts in Slack with AI. Bolt, Block Kit, signed webhooks, and a human check before anyone panics.
- #slack
- #chatops
- #ai
- #finops
The monthly cloud bill used to arrive as a surprise. By the time finance noticed a spike, the spend was already locked in. I wanted the spike to show up in Slack the day it started, explained in language a sleepy on-call engineer could parse at 7am. So I built a bot that reads the cost data, asks a model to explain what changed and why it probably changed, and posts a short, actionable summary. The hard part was making sure the explanation was grounded in real numbers and reviewed before it caused a fire drill.
Why summarize cost data at all
Cloud billing exports are precise and unreadable. A daily cost-and-usage report has hundreds of line items by service, region, and tag. A human scanning it sees noise. The signal, “EC2 in us-east-1 jumped 40 percent yesterday, driven by a new tag we have never seen,” is buried. Turning that into one sentence is a translation task, and translation is what a model is good at, provided you give it the actual numbers and ask it not to speculate beyond them.
Pulling the numbers first
I compute the deltas in code, not in the model. The model should never do arithmetic on a budget; it should explain arithmetic I already trust.
async function computeCostDeltas() {
const today = await getCostByService("yesterday");
const baseline = await getCostByService("7d-trailing-avg");
return Object.entries(today)
.map(([service, cost]) => ({
service,
cost,
delta: cost - (baseline[service] || 0),
pct: baseline[service] ? (cost / baseline[service] - 1) * 100 : null,
}))
.filter((d) => Math.abs(d.delta) > 25) // ignore noise under $25
.sort((a, b) => b.delta - a.delta);
}
Filtering tiny deltas keeps the channel from crying wolf. Only material changes earn a message.
Letting the model explain, not calculate
I hand the computed deltas to the model and ask for a plain-language summary plus a likely cause based only on the service names and tags it can see.
const prompt = `These are yesterday's cloud cost changes vs a 7-day baseline.
Write a 2-3 sentence Slack summary for an on-call engineer.
Name the biggest driver and a plausible cause from the service/tag names.
Do NOT invent dollar amounts; use only the numbers given.
${JSON.stringify(deltas, null, 2)}`;
The “do not invent dollar amounts” instruction is non-negotiable. The model is a fast junior analyst: great at phrasing, not to be trusted with the books. Every figure in the final message comes from my code, not its imagination.
Pro Tip: Include the previous baseline in the prompt so the model can say “up from $X to $Y” using your verified numbers. If you only pass the delta, it will sometimes back-calculate and get it subtly wrong.
Rendering an alert people will act on
A cost alert needs to be scannable and lead with the action. Block Kit handles the structure:
function buildCostBlocks(summary, topDriver) {
return [
{ type: "header", text: { type: "plain_text", text: "Cost Alert 💸" } },
{ type: "section", text: { type: "mrkdwn", text: summary } },
{
type: "section",
fields: [
{ type: "mrkdwn", text: `*Top driver*\n${topDriver.service}` },
{ type: "mrkdwn", text: `*Change*\n+$${topDriver.delta.toFixed(0)} (${topDriver.pct?.toFixed(0)}%)` },
],
},
{
type: "actions",
elements: [
{ type: "button", text: { type: "plain_text", text: "Open cost dashboard" }, url: process.env.COST_DASH_URL },
{ type: "button", text: { type: "plain_text", text: "Acknowledge" }, action_id: "ack_cost" },
],
},
];
}
Posting it with Bolt
const { App } = require("@slack/bolt");
const app = new App({
token: process.env.SLACK_BOT_TOKEN,
signingSecret: process.env.SLACK_SIGNING_SECRET,
});
async function postCostAlert(blocks) {
await app.client.chat.postMessage({
channel: "#finops",
blocks,
text: "Daily cloud cost alert", // notification fallback
});
}
The acknowledge loop and human judgment
The “Acknowledge” button is the human seam. A cost spike is not automatically a problem; sometimes it is a planned load test. When someone acks, the bot records who looked and stops re-alerting on the same driver for the day.
app.action("ack_cost", async ({ ack, body, client }) => {
await ack();
await client.chat.update({
channel: body.channel.id,
ts: body.message.ts,
text: `Acknowledged by <@${body.user.id}>`,
blocks: appendAckNote(body.message.blocks, body.user.id),
});
});
This keeps a person in the decision. The AI flags and explains; a human decides whether it matters. I never let the bot trigger an automated scale-down or budget action on its own. The cost of a wrong autonomous action here is real money or a real outage.
Verify the inbound interactions
The acknowledge button posts an interaction payload to my endpoint, which is internet-facing. Bolt verifies the Slack signing secret on every request, checking the signature against the raw body and rejecting old timestamps. If you wire a raw handler, do that HMAC check yourself. And the billing data the model sees has account IDs and access keys stripped before it ever leaves my process; the model gets service names and dollar deltas, never credentials.
Tuning the prompt
I built the summary prompt iteratively in the prompt workspace using a week of real cost exports, then promoted the stable version into prompts. The FinOps and alerting templates in the prompt packs are a faster on-ramp if you do not want to start blank. For the alerting side of the house more broadly, this sits next to monitoring alerts.
For building the bot, I leaned on Claude for the summary prompt and Cursor for the Bolt and Block Kit scaffolding, reviewing each change. More patterns live in the Slack category.
Conclusion
A cost alert that lands in Slack the morning a spike starts is worth more than a perfect report nobody reads. Compute the numbers in code, let the model translate them into plain language, render an actionable Block Kit message, and keep a human on the acknowledge button. Verify every inbound signature and keep credentials out of the prompt. The model explains; you decide. That division of labor is what makes the alert trustworthy.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.