Slack Is Where You Spend the Time You Don't Have
You're deep in code. A teammate asks a question in #engineering. You alt-tab to Slack, type a response, get distracted by three other channels, and five minutes later you're still in Slack.
Or: you're leaving a meeting and need to update your team. You open Slack, find the channel, type the update. 60 seconds for a message you could have spoken in 10.
Every context switch to Slack costs 20–30 seconds of recovery time to get back into flow. Multiply by 30 messages a day. That's 15 minutes of lost flow state — just from switching apps.
"Found the WebSocket memory leak. Fix incoming in about 20 minutes. No need to restart prod, the leak is slow enough. ENG-847 has the details."
Release. Your message arrives in #engineering via webhook. Properly punctuated, natural tone. You never left your editor.
One hotkey per channel. Three channels, three tones, three hotkeys. 15 seconds total. Zero context switching.
How It Works: Voice → Transform → Slack Webhook
Your voice ──▶ SpeechButton STT ──▶ Transform script ──▶ Slack Webhook
(7ms) (Parakeet V3, (adjusts tone, (posts to
100% offline) fixes grammar) channel)
- SpeechButton captures your voice and transcribes it locally (Apple Neural Engine, 100% offline)
- Transform script cleans up the text — removes filler words, adjusts tone for the channel context
- Slack Incoming Webhook receives the formatted message and posts it to the channel
The connection between SpeechButton and Slack is a Python script that calls the Slack Incoming Webhook API. No custom app, no OAuth dance, no middleware. Just a webhook URL and a script.
Setup: 3 Steps, 5 Minutes
A Slack webhook, a config file, and prompt files for each channel tone. That's the entire integration.
Step 1: Create a Slack Incoming Webhook
Each webhook is tied to one channel. Create one per channel you want to post to.
- Go to https://api.slack.com/apps → Create New App → From scratch
- Name it "Voice Messages", select your workspace
- Click Features → Incoming Webhooks → toggle Activate Incoming Webhooks to On
- Click Add New Webhook to Workspace → select the channel → Allow
- Copy the webhook URL:
https://hooks.slack.com/services/T.../B.../xxx
The webhook URL is the only credential you need. Pass it as SLACK_WEBHOOK_URL inline in the exec line — no API key required.
Step 2: Add integrations/send_slack.py
A small Python script that reads the transformed text from stdin and posts it to Slack via webhook. No external dependencies — stdlib only.
#!/usr/bin/env python3 """Send a message to Slack via Incoming Webhook.""" import json, os, sys, urllib.request def main(): text = sys.stdin.read().strip() if not text: sys.exit(0) url = os.environ.get("SLACK_WEBHOOK_URL") if not url: print("SLACK_WEBHOOK_URL not set", file=sys.stderr) sys.exit(1) req = urllib.request.Request(url, json.dumps({"text": text}).encode(), {"Content-Type": "application/json"}) resp = urllib.request.urlopen(req, timeout=10) print(f"Slack ({resp.status}): {text[:60]}...") if __name__ == "__main__": main()
Step 3: SpeechButton config.toml
One hotkey per channel. The transform field points to a prompt file — SpeechButton runs the local AI model against it. The exec field sends the result to Slack.
# ~/.config/speechbutton/config.toml [global] model = "parakeet-tdt-0.6b-v3-int8" [parakeet-tdt-0.6b-v3-int8] language = "en" # Slack — local AI transform (recommended) [[hotkey]] key = "RightCommand" channel = "5" name = "slack" transform = "prompts/slack_message.md" exec = "SLACK_WEBHOOK_URL=https://hooks.slack.com/services/T.../B.../xxx integrations/send_slack.py"
Two options for the transform field
transform = "prompts/slack_message.md"
Uses SpeechButton's built-in local model to rewrite your transcript using the prompt in the file. Free, 100% offline. Nothing leaves your Mac except the final message to Slack.
transform = "transforms/transform_claude.py prompts/slack_message.md"
Sends the transcript to Claude API for higher-quality rewrites. Requires ANTHROPIC_API_KEY. Costs ~$0.001 per message.
Start with Option A — it's free and works offline. Switch to Option B only if you need higher quality.
Step 4: Prompt files — tone per channel
Each channel gets its own prompt file in the prompts/ folder. Same voice, different audience, different output.
You say:
"hey so I basically found the um the memory leak it's in the websocket handler the connection pool uh doesn't clean up on disconnect I've got a fix coming in about twenty minutes"
Clean up this spoken message for a Slack dev channel.
Keep it casual and concise. Fix grammar, remove filler
words (um, uh, like, basically), keep technical terms.
Output ONLY the cleaned message, no quotes.
Slack receives:
Found the memory leak — it's in the WebSocket handler. Connection pool doesn't clean up on disconnect. Fix coming in ~20 min.
You say:
"Quick update the launch is moving to April 15th because the onboarding isn't ready yet the pricing stays the same and Maria is running an AB test on the student discount"
Rewrite this spoken message for a professional Slack
channel. Make it clear, well-structured, and polished.
Use bullet points if there are multiple items.
Output ONLY the message.
Slack receives:
Quick update on the launch:
- Launch date moved to April 15 (onboarding flow not ready)
- Pricing unchanged at $7.99/mo
- Maria is running an A/B test on the student discount
You say:
"Yesterday I fixed the CSV export bug and submitted the PR. Today I'm picking up the auth middleware rewrite. No blockers so far but I might need help with the token refresh logic later."
Format this spoken standup update into three sections:
*Yesterday*, *Today*, *Blockers*. Use bullet points.
If no blockers mentioned, write "None".
Output ONLY the formatted standup.
Slack receives:
*Yesterday*
• Fixed CSV export bug, PR submitted
*Today*
• Auth middleware rewrite
*Blockers*
• None (may need help with token refresh logic later)
Real Workflows
Incident response — update without leaving the terminal
You're debugging a production incident. Every minute counts. Your team needs updates.
"Update: identified the cause. The Redis connection pool is exhausted because the new batch job isn't releasing connections. Temporary fix: increase pool size from 50 to 200. Permanent fix: add connection timeout, PR in progress."
Your team gets a clear, structured update. You didn't leave the terminal. The debugger is still open.
Quick answers — respond without context-switching
Someone asks a question in #engineering. You know the answer.
"The config file is at slash etc slash app slash config dot toml. The retry count is on line 42, change max retries from 3 to 5. Don't forget to restart the service after."
Slack receives: "The config file is at /etc/app/config.toml. The retry count is on line 42 — change max_retries from 3 to 5. Don't forget to restart the service after." The transform converted spoken paths and code to proper formatting. You answered in 8 seconds.
Multi-channel — same info, different audience, different prompt
Assign different channels to different hotkeys with different prompt files.
# Casual dev channel [[hotkey]] key = "RightCommand" channel = "5" name = "slack-eng" transform = "prompts/slack_casual.md" exec = "SLACK_WEBHOOK_URL=https://hooks.slack.com/...engineering integrations/send_slack.py" # Professional general channel [[hotkey]] key = "RightCommand" channel = "6" name = "slack-general" transform = "prompts/slack_professional.md" exec = "SLACK_WEBHOOK_URL=https://hooks.slack.com/...general integrations/send_slack.py"
"WebSocket fix deployed to staging. Running load tests now. If they pass, production deploy at 4pm."
"Heads up: we're deploying a fix for the connection stability issue at 4pm today. No downtime expected. Will update in this channel when done."
Same information, different tone, different audience. 15 seconds total.
Without Transform: Raw Voice to Slack
Don't need AI cleanup? Omit the transform field entirely:
[[hotkey]] key = "RightCommand" channel = "5" name = "slack-raw" exec = "SLACK_WEBHOOK_URL=https://hooks.slack.com/services/T.../B.../xxx integrations/send_slack.py"
Your speech goes directly to Slack — punctuated by SpeechButton's STT engine but otherwise unmodified. Useful for quick casual messages where tone doesn't matter.
Privacy
Here's exactly what stays on your Mac and what goes to the cloud:
| Component | Where it runs | Data sent externally |
|---|---|---|
| Voice capture | Your Mac | ✓ Nothing |
| Speech-to-text (Parakeet V3 int8) | Apple Neural Engine | ✓ Nothing |
| Local AI transform (Option A) | Your Mac | ✓ Nothing |
| Claude API transform (Option B) | Your Mac | Message text → Claude API |
| send_slack.py | Your Mac | Message text → Slack (where it needs to go anyway) |
With Local AI (Option A), everything stays on your Mac except the final webhook POST to Slack. Voice capture, transcription, and the AI rewrite all run locally. Only the finished message leaves your machine — directly to the Slack channel it was meant for.
Get Started
Prerequisites
- macOS 15+ (Sequoia), Apple Silicon
- A Slack workspace where you can create apps
- Python 3 (pre-installed on macOS)
Quick Start
- 1 Download SpeechButton — free 15 minutes/day, no account needed
-
2
Create a Slack webhook at
api.slack.com/apps— Features → Incoming Webhooks → Add New Webhook to Workspace -
3
Save
send_slack.pyto~/.config/speechbutton/integrations/ -
4
Write a prompt file in
~/.config/speechbutton/prompts/slack_message.mdwith the tone instructions for your channel -
5
Add a
[[hotkey]]block toconfig.tomlwith your webhook URL in theexecline - 6 Hold RightCommand, speak, release. Your first voice message arrives in Slack in under 5 minutes.
Start sending Slack messages by voice today
Free 15 min/day · No account needed · macOS 15+ · Apple Silicon
Download for macOS — FreePro ($7.99/mo) removes the daily limit. Requires macOS 15+ and Apple Silicon.