The @Claude Slack Demo Is A Postgres Table In Disguise

Anthropic's @Claude-in-Slack demo pulled 77 million views. Enterprise buyers loved it, founders retweeted it, and most small business owners walked away thinking they now need Slack Enterprise Grid to get context-aware AI at work. That's the wrong lesson. Strip the Slack UI off the demo and what remains is four boxes and a database table — and both work equally well over Gmail, WhatsApp, or Telegram.
What the demo actually is: four boxes, not a platform
The @Claude Slack integration is four components in a trench coat. Trigger (someone mentions the bot), context fetch (pull the last N messages plus relevant history for that channel and user), LLM call (stuff the context into a prompt), reply (post back into the same surface). That is the entire product. The part everyone points at as magic — "oh look, it remembers what we discussed last week in the launch channel" — is not a Slack feature. It's a SELECT query on a messages table filtered by channel ID and a time window.
Context is not a platform. Context is a database.
That distinction matters because the pricing and packaging story pretends otherwise. To get @Claude working properly across a workspace you're realistically on Slack's higher paid tiers (Business+ or Enterprise Grid, roughly $15/seat/month at the upper end, before you pay Anthropic per token). A ten-person team is $150/month for the surface alone. What you're actually paying for is a UI wrapper around a schema you can write in an afternoon.
The schema that replaces the moat
Here are the four tables. That's it — this is the "context store" that the enterprise pitch is built on.
CREATE TABLE threads (
id BIGSERIAL PRIMARY KEY,
channel_type TEXT NOT NULL, -- 'gmail' | 'whatsapp' | 'telegram' | 'sms'
external_id TEXT NOT NULL, -- gmail thread_id, wa chat_id, tg chat_id
title TEXT,
created_at TIMESTAMPTZ DEFAULT now(),
UNIQUE (channel_type, external_id)
);
CREATE TABLE messages (
id BIGSERIAL PRIMARY KEY,
thread_id BIGINT REFERENCES threads(id) ON DELETE CASCADE,
sender TEXT NOT NULL,
body TEXT NOT NULL,
sent_at TIMESTAMPTZ NOT NULL,
direction TEXT CHECK (direction IN ('in','out'))
);
CREATE INDEX ON messages (thread_id, sent_at DESC);
CREATE TABLE entities (
id BIGSERIAL PRIMARY KEY,
kind TEXT NOT NULL, -- 'person' | 'company' | 'invoice' | 'deal'
name TEXT NOT NULL,
metadata JSONB DEFAULT '{}'::jsonb
);
CREATE TABLE embeddings (
message_id BIGINT PRIMARY KEY REFERENCES messages(id) ON DELETE CASCADE,
vector VECTOR(1536) -- pgvector
);
CREATE INDEX ON embeddings USING ivfflat (vector vector_cosine_ops);
Four tables. threads is agnostic — it doesn't care whether a "conversation" is a Gmail thread with a client or a WhatsApp chat with a subcontractor. messages is a firehose. entities is what lets the bot connect the dots when a client says "follow up on the Peterson quote" — it links the Peterson entity across threads. embeddings is what lets retrieval find semantically relevant history, not just the last ten lines.
The join between entities and messages is what people mistake for intelligence. It's a foreign key.
The retrieval function is about 40 lines
Here's the "memory" the demo shows off, in Python. Given a thread ID, return the last 20 messages plus the top 5 semantically similar messages from other threads that share an entity.
import psycopg
from openai import OpenAI
client = OpenAI()
def get_context(thread_id: int, query: str, db) -> list[dict]:
q_vec = client.embeddings.create(
model="text-embedding-3-small",
input=query,
).data[0].embedding
with db.cursor() as cur:
cur.execute("""
SELECT sender, body, sent_at
FROM messages
WHERE thread_id = %s
ORDER BY sent_at DESC
LIMIT 20
""", (thread_id,))
recent = cur.fetchall()
cur.execute("""
SELECT m.sender, m.body, m.sent_at
FROM messages m
JOIN embeddings e ON e.message_id = m.id
WHERE m.thread_id != %s
ORDER BY e.vector <=> %s::vector
LIMIT 5
""", (thread_id, q_vec))
similar = cur.fetchall()
return {
"recent": [dict(zip(["sender","body","at"], r)) for r in recent],
"similar": [dict(zip(["sender","body","at"], r)) for r in similar],
}
That function, plus a system prompt describing your business, plus one API call to Claude or GPT — that's the whole context-aware bot. On my home server it returns in under 300ms for a database holding roughly six months of history across a handful of channels.
The infra bill: about $14/month. Postgres with pgvector on a $6 VPS (or free on a home box), a small worker for ingestion, and per-token model costs on top. Compare with $150/month for a ten-seat Slack tier before any model tokens.
Your work does not live in Slack
This is the part the AI-at-work narrative keeps skipping. The people writing that narrative work at companies whose entire universe is Slack. That's not most businesses.
If you run a 3-person agency, a plumbing outfit, a Shopify store, a bookkeeping practice — your conversations live in:
- Gmail threads with clients
- WhatsApp chats (roughly 2B monthly active users globally)
- SMS threads with your accountant
- A Telegram group with two contractors
- iMessage with a supplier who won't switch
The pattern is identical across all of these. Only the trigger and the reply surface change:
| Surface | Trigger | Reply mechanism |
|---|---|---|
| Gmail | Push notification via Pub/Sub | users.messages.send in the same thread |
| WhatsApp Business API webhook | Business API messages endpoint |
|
| Telegram | Bot webhook on @mention |
sendMessage with reply_to_message_id |
| SMS | Twilio inbound webhook | Twilio messages.create |
| Slack | Events API app_mention |
chat.postMessage in thread |
Same four boxes. Same schema. Different channel_type value in the threads table.
What actually changes between surfaces
- Auth model — OAuth for Gmail, a permanent access token for Telegram, a Meta Business verification dance for WhatsApp.
- Rate limits — Gmail is generous per user, WhatsApp Business tiers by phone number quality rating, Telegram is 30 msg/sec per bot.
- Attachment handling — PDFs and images arrive differently; normalize to a
body+attachments[]shape at ingest time.
Everything else — retrieval, model call, entity linking, reply — is shared code.
A build plan you can actually finish this week
This isn't a platform migration. It's a weekend project if you focus.
Day 1 — Ingest. Stand up Postgres with the pgvector extension. Create the four tables. Write one ingest worker for your primary channel. For Gmail, that's a Pub/Sub topic subscribed to users.watch, decoding the history ID and pulling new messages. For WhatsApp Business API, it's a single webhook endpoint that inserts into messages. Backfill the last 90 days so the bot has something to remember.
Day 2 — Retrieval. Write the 40-line function above. Add an embeddings worker that runs on every insert into messages and populates embeddings. Add a simple entity extractor — you can start with a regex-based pass for invoice numbers and client names, then upgrade to an LLM extraction step later.
Day 3 — Reply loop. Wire the trigger. For Gmail: a filter on new inbound messages to a specific alias, or an explicit @bot string in the body. On trigger, run retrieval, call the model with a system prompt that describes your business, and post the reply back into the same thread using the same channel API you ingested from.
def handle_incoming(msg, db):
thread_id = upsert_thread(msg, db)
insert_message(msg, thread_id, db)
if not is_bot_triggered(msg):
return # ingest-only, no reply
ctx = get_context(thread_id, msg["body"], db)
reply = client.messages.create(
model="claude-sonnet-4-5",
system=SYSTEM_PROMPT,
max_tokens=800,
messages=[{
"role": "user",
"content": format_context(ctx, msg["body"])
}]
).content[0].text
send_reply(msg["channel_type"], msg["thread_external_id"], reply)
That is a working context-aware bot on your infrastructure. No per-seat tax. The only recurring costs are the VPS and model tokens — and model tokens are the same whether you pay via Slack or directly.
Where the demo pattern breaks down (be honest about it)
I'm not going to sell you a fairy tale. There are things Slack's integration gives you that a rolled-your-own version does not:
- Identity and permissions out of the box. Slack knows who's in what channel. If you build on WhatsApp, you write the ACL yourself.
- Threading UX. Slack threads are a first-class UI concept. WhatsApp and SMS aren't threaded — you'll fake it with metadata.
- Compliance stories. For regulated industries (HIPAA, SOC 2 boundaries), an enterprise-grade platform has audit logs and admin controls you'd have to build.
- Discoverability inside a big org. In a 500-person company, "just @ the bot in any channel" beats "email this address."
For a solo operator or a 10-person team, none of those are dealbreakers. For a 500-person enterprise, they might be. Pick your tier honestly.
Why bizflowai.io helps with this
The context-store pattern above — four tables, a retrieval function, a webhook per channel — is exactly the substrate we run for clients at bizflowai.io. Most engagements start with Gmail plus one messaging channel, a Postgres + pgvector store on a small VPS, and an entity layer wired to whatever the business already tracks (clients, invoices, deals, tickets). The bot lives where the customer conversations already happen instead of asking the team to migrate into a new chat tool. Same pattern Anthropic productized inside Slack, just pointed at the surfaces small businesses actually use.
The takeaway
The demo is not the product. The table behind the demo is the product.
Once you see the four boxes — trigger, context fetch, LLM call, reply — every "ambient AI" pitch in your feed decomposes into the same shape. The vendor's moat is UI and distribution, not intelligence. And if your business runs on Gmail and WhatsApp instead of Slack, you don't need to change where you work to get context-aware AI. You need four tables and a webhook.
Want more like this?
I publish practical AI automation, GenAI engineering, and faceless content workflows on YouTube every week.
Subscribe to bizflowai.io on YouTube — never miss a new tutorial.
Planning an AI automation project or need a second opinion on your architecture?
Connect with me on LinkedIn — Lazar Milicevic, GenAI Engineer & bizflowai.io Founder.
Visit bizflowai.io for our services, case studies, and AI consulting.
Frequently asked questions
What is Anthropic's @Claude in Slack actually doing under the hood?
@Claude in Slack is a four-step pattern: a trigger (someone mentions the bot), a context fetch (pulling recent messages and relevant history for that channel and user), an LLM call with that context stuffed into the prompt, and a reply back into the same surface. The 'memory' isn't a Slack feature, it's a database query filtered by channel ID and time window.
How do I build a Claude-style ambient AI assistant without Slack?
Stand up a Postgres database with four tables: threads, messages, entities, and embeddings. Write an ingest job that copies incoming messages from Gmail, WhatsApp, or Telegram into the messages table. Add a retrieval function that returns the last twenty messages in a thread plus five semantically similar ones. Then wire a webhook that triggers retrieval, calls the model, and replies back into the same channel.
Why does the Slack @Claude demo matter for small businesses that don't use Slack?
The demo itself is worthless as a product for small teams because their work lives in Gmail, WhatsApp, Telegram, SMS, and shared spreadsheets, not Slack. But the four-box pattern (trigger, context fetch, LLM call, reply) works on any surface. The pattern is transferable; the platform is not. Context is a database, not a proprietary Slack capability.
How much does a self-hosted ambient AI setup cost versus Slack Enterprise Grid?
A self-hosted setup on a small VPS or home server, storing roughly six months of message history across four Postgres tables, runs about fourteen dollars a month in infrastructure with retrieval latency under 300ms. Slack Enterprise Grid, the minimum tier where @Claude works properly, starts around fifteen dollars per seat per month, so a ten-person team pays 150 dollars monthly before Anthropic model call costs.
What database schema do I need to give an AI assistant context across conversations?
Four tables. Threads: one row per conversation (Gmail thread, WhatsApp chat, Telegram group). Messages: one row per message with a foreign key to the thread, plus sender, timestamp, and body. Entities: people, companies, invoices, and deals mentioned across threads so the bot can connect references. Embeddings: vector representations of past messages so retrieval surfaces semantically relevant history rather than just the most recent lines.