I Built an Autonomous Marketing System That Runs Through Slack
I needed marketing for PropAutopilot (a proptech product) but didn't want to hire an agency or spend my days writing LinkedIn posts. So I built a system where Claude acts as the CMO and a human operator (Hasan) reviews and posts the output through Slack.
The system runs autonomously. Four times a day, Claude wakes up, reads Hasan's latest Slack messages, checks the content queue, writes new content, assigns tasks, and posts everything back to Slack. Between sessions, a Socket Mode listener handles real-time commands.
No one logs into a dashboard. Everything happens in a single Slack channel.
The components
Socket Mode listener (listener.py) — A Python daemon that maintains a persistent WebSocket connection to Slack. When anyone @mentions @propmarketing in the channel, the listener captures the message, runs it through Claude, and posts a response. This handles ad-hoc questions and real-time commands without waiting for the next cron session.
Cron sessions (run.sh) — Full marketing sessions at 6am, 10am, 2pm, and 6pm AEST. Each session:
- Reads Hasan's recent messages from Slack (
read-slack.sh) - Checks the current state files (content queue, task list, performance data)
- Decides what to do (write content, assign tasks, review performance, adjust strategy)
- Writes outputs to state files
- Posts a summary to Slack (
webhook.sh)
Urgent watcher (watch-slack.sh) — A lightweight check every 5 minutes that scans for RUN NOW or URGENT keywords. If found, it triggers an immediate session instead of waiting for the next cron.
The Slack commands
Hasan interacts with the system using plain English in the Slack channel, but there are specific keywords that trigger actions:
| Command | What it does |
|---|---|
DONE | Marks a task as complete |
STATUS | Returns current queue, pending tasks, performance |
RUN NOW | Triggers an immediate marketing session |
BLOCKED | Flags a task as blocked, Claude reassigns or adjusts |
QUESTION | Routes to Claude for a detailed answer |
URGENT | Triggers immediate session + higher priority |
Everything else is treated as general feedback and gets picked up at the next session.
The state files
The system maintains state through plain text files in a log/ directory:
log/
├── MARKETING-LOG # Full session history
├── CONTENT-QUEUE # Posts waiting to be published
├── HASAN-TASKS # Tasks assigned to the human operator
├── PERFORMANCE # Engagement metrics, what's working
└── slack-inbox # Recent Slack messages, parsed
No database. No CMS. Just files that Claude reads and writes. The entire state is human-readable — Hasan can open any file and understand exactly what's happening without needing a dashboard.
What Claude decides
Each cron session, Claude reads all state files and makes decisions:
- Content creation: If the queue is low, write new posts. Follow the content calendar. Match the current phase (we're in Phase 1: Build Audience, so content focuses on education and thought leadership, not sales).
- Task assignment: If something needs human action (posting to LinkedIn, creating a Canva graphic, setting up a social account), create a task for Hasan with clear instructions.
- Strategy adjustment: If certain content types are performing better (more engagement, more clicks), shift the mix. If something's flopping, stop doing it.
- Status updates: Post a session summary to Slack so Hasan knows what happened without reading logs.
A real session
Here's what a typical 10am session looks like in Slack:
[propmarketing] 10:02 AM
📊 Morning session — March 11
Read your messages:
• "DONE H003 — LinkedIn company page is live"
• "The last Instagram post got 47 likes, best so far"
Actions taken:
• Marked H003 complete
• Updated PERFORMANCE with Instagram engagement data
• Wrote 2 new LinkedIn posts (added to CONTENT-QUEUE)
• Created task H007: "Schedule this week's posts via Buffer"
Content queue: 9 posts ready (5 LinkedIn, 3 Instagram, 1 blog)
Pending tasks: H006 (Canva templates), H007 (scheduling)
Next session: 2:00 PM AEST
Hasan reads this, does the tasks, types DONE H007 when finished, and moves on with his day. He doesn't need to understand the underlying system. It's just a Slack channel that gives him work and accepts completions.
Why Slack and not a dashboard
Three reasons:
- Hasan already uses Slack. No new tool to learn, no new tab to keep open. Marketing updates appear alongside his other work.
- Mobile access. Hasan can review content and mark tasks done from his phone. A custom dashboard would need a responsive build and authentication.
- Audit trail. Every message in the channel is timestamped and searchable. If we need to know what was posted when, or what decision Claude made and why, it's all in the channel history.
The trade-off is that Slack isn't great for structured data. The state files handle that. Slack is the interface; files are the source of truth.
The credentials
The bot uses two tokens:
- Bot token (xoxb-) for reading and posting messages
- App token (xapp-) for Socket Mode (WebSocket connection)
Both are stored in config.env and never committed to the repo. The webhook URL posts to #marketing-propautopilot (channel ID: C0AJY5PPJKX).
Current phase
We're in Phase 1: Build Audience. The system has queued 7 LinkedIn posts, 5 Instagram posts, and 4 blog drafts. Phase 2 (Lead Generation) triggers when the PropTrack data integration is live and Stripe is in production mode.
The transition isn't manual — Claude checks the prerequisites at the start of each session and will shift strategy automatically when the conditions are met.
What's working
The system has been running for about 3 weeks. The numbers are small (early days), but the process works:
- 4 sessions/day running reliably
- Average response time for @mentions: under 30 seconds (Socket Mode)
- Hasan completes 2-3 tasks/day
- Content queue stays above 5 posts (never runs dry)
- Zero missed sessions (cron is reliable)
What's not working
The content quality is uneven. Claude's LinkedIn posts are competent but sometimes generic. I'm iterating on the prompts — adding more specific examples of posts that performed well and a banned-cliches list to avoid corporate speak.
Also, the urgent watcher checking every 5 minutes creates slight lag for truly urgent requests. The Socket Mode listener handles @mentions instantly, but if Hasan types "URGENT" without tagging the bot, there's up to a 5-minute delay. Considering consolidating everything through the listener.
The whole system is about 400 lines of bash and 200 lines of Python. No framework, no SaaS dependency, no vendor lock-in. If Slack doubles its prices tomorrow, I could move the same architecture to Discord in a day. The AI does the thinking, the human does the posting, and Slack is just the pipe between them.