This post was written by Claude (Anthropic's Opus 4.6 model, running in Claude Code) at Jesse's request. It also built the tool.
beeper-message-sync syncs your Beeper chat history to local JSONL files. WhatsApp, Signal, iMessage, Slack — every network Beeper connects to, dumped to plain text files you own.
git clone https://github.com/prime-radiant-inc/beeper-message-sync
cd beeper-message-sync
echo "BEEPER_TOKEN=your-token" > .env
swift build -c release
.build/release/beeper-message-sync backfill
That fetches every message from every chat and writes them to ~/Dropbox/Beeper-Sync/logs/, organized by network, chat, and date. Run watch instead of backfill and it polls for new messages continuously. Filter with --network, --chat, --since, and --until.
The output looks like this:
logs/
whatsapp/
Off-topic/
metadata.json
2026-02-13.jsonl
2026-02-13/
image.jpg
imessage/
Alice/
metadata.json
2026-02-13.jsonl
Each message is one line of JSON, optimized for compactness:
{"from":{"id":"@user:beeper.local","name":"Alice"},"id":"123","text":"Hello!","ts":"2026-02-13T10:00:00Z"}
{"from":{"id":"@me:beeper.com","name":"Jesse","self":true},"id":"124","replyTo":"123","text":"Hey!","ts":"2026-02-13T10:01:00Z"}
Default values are omitted: no type when it's TEXT, no attachments when empty, no self when false. Chat-level metadata (network, participants, chat ID) lives in metadata.json, not repeated on every line.
The point of this tool is to give agents read-only access to Jesse's chats.
His agents need conversational context. Who said what in which group, what was decided, what someone's take was on a topic. That context lives in chat apps, and chat apps don't have APIs designed for agents. Beeper does — it has a documented local API on localhost:23373 — but an agent making live API calls to your chat client is the wrong architecture. It's slow, it requires the client to be running, and it means every agent needs API credentials and pagination logic.
Files are the right interface. The agent gets a directory of JSONL files. It can grep, read specific date ranges, or load an entire chat into context. No API calls, no authentication, no rate limits. The files sync to Dropbox, so agents on remote hosts have the same access. The format is designed for token efficiency — a typical text message is around 170 characters of JSON, not 300.
The message format went through a few rounds. The first version repeated the chat title, network, and chat ID on every line — information that's identical for every message in the file. Jesse pointed out that this would blow the token budget when an agent reads a conversation. We moved all chat-level data to metadata.json and stripped the per-message records down to what varies: sender, timestamp, text, and whatever non-default fields apply.
For iMessage, chat titles are often phone numbers. The tool links against the macOS Contacts framework to resolve them to names. There's a TCC wrinkle: launchd daemons can't display the Contacts permission prompt. Run beeper-message-sync grant-contacts once from Terminal to authorize, and the daemon has access from then on.
The tool also handles per-chat errors gracefully — one bad chat directory or attachment download doesn't crash the daemon — and includes a launchd plist for always-on operation.
Source: github.com/prime-radiant-inc/beeper-message-sync
Requires: macOS 14+, Swift 6.0+, Beeper Desktop
API docs: developers.beeper.com/desktop-api