social-inbox
Provides an agent-first inbox for Instagram DMs, comments, and mentions. It supports syncing and listing unread items, searching stored content, reading threads, marking items read/unread, adding labels, drafting replies and moderation actions, and sending human-approved drafts via Meta's Instagram APIs.
Social Inbox
Self-hosted, agent-first inbox for Instagram DMs, comments and mentions (YouTube and LinkedIn are planned, X/Twitter is out of scope).
The main interface is an MCP server for AI assistants. A compact REST API and a small CLI (sinbox) sit on the same service layer.
Token-light by design. Responses are columnar, IDs are short (
i7k,t3f,d12), lists are unread-only by default,sincecursors return deltas, limits are capped, you can pick fields, and thread summaries are cached. Overview + a 20-item list is about 800 tokens.Nothing is sent without a human. Replies, hides and deletes start as drafts. A human approves them with the CLI or REST using an
approve-scoped key. Real sending is off until you setSENDING_ENABLED=true. Sends are idempotent, rate-limited (30/h) and audited in an append-only log.Small. One Node 22 container with SQLite (WAL + FTS5). There is no Redis and no Postgres. It uses about 100 MB RSS.
See SPEC.md for the full design and CHANGELOG.md for what is implemented.
Quick start (local, no credentials)
npm ci && npm run build
FAKE_CONNECTOR=true SENDING_ENABLED=true \
SINBOX_AGENT_KEY=sk_agent_dev SINBOX_ADMIN_KEY=sk_admin_dev \
DB_PATH=./data/dev.db npm start
# another terminal
export SINBOX_URL=http://localhost:8787 SINBOX_KEY=sk_agent_dev
node dist/cli/index.js sync
node dist/cli/index.js ls
node dist/cli/index.js draft i1 "Hej! Minikurs kosztuje 99 zł."
node dist/cli/index.js --key sk_admin_dev approve d1 --sendnode scripts/smoke.mjs runs the same flow automatically and checks memory.
Related MCP server: meta-mcp
MCP
Endpoint: POST https://inbox.wozu.co/mcp (Streamable HTTP, stateless, JSON responses), header Authorization: Bearer <agent key>.
Tool | Purpose |
| Unread counts per source, DM windows closing within 6h, head cursor. Call first. |
| Columnar items. |
| Last N messages (≤30), cached or extractive summary, reply window |
| One item; |
| FTS5 over stored text |
| Batch read/unread and labels; |
| Store a ≤300-char summary that later sessions reuse ( |
|
|
|
|
| List drafts |
| Executes only approved drafts; re-calls return the same native ID |
| Forces a poll (rate-limited to once per 30 s) |
| Only when |
Example inbox_list response:
{"cols":["id","src","t","who","age","x","lbl","win"],
"rows":[["i1","ig.dm","t1","@ania_k","2h","Hej! Ile kosztuje minikurs…",["q","lead","needs-wozu"],"21h"]],
"next":"c_k","more":false}Columns that are empty for every row are dropped automatically.
Client config
Claude Code (HTTP):
claude mcp add --transport http social-inbox https://inbox.wozu.co/mcp \
--header "Authorization: Bearer $SINBOX_AGENT_KEY"Claude Desktop / Cursor / any stdio client (through the CLI bridge):
{
"mcpServers": {
"social-inbox": {
"command": "node",
"args": ["/path/to/social-inbox/dist/cli/index.js", "mcp"],
"env": { "SINBOX_URL": "https://inbox.wozu.co", "SINBOX_KEY": "sk_..." }
}
}
}(Run npm ci && npm run build in the checkout first.)
Clients that support remote HTTP servers with headers (Cursor mcp.json):
{ "mcpServers": { "social-inbox": { "url": "https://inbox.wozu.co/mcp", "headers": { "Authorization": "Bearer sk_..." } } } }REST
All routes need Authorization: Bearer <key>. Scopes: read, draft, send, approve.
Method | Path | Scope |
GET |
| read |
GET |
| read |
GET |
| read |
GET |
| read |
PUT |
| draft |
GET |
| read |
POST |
| draft |
GET |
| read |
POST |
| draft |
POST |
| draft |
PATCH |
| draft |
POST |
| approve |
POST |
| send |
POST |
| read |
POST |
| approve |
Public routes: /healthz, /privacy, /data-deletion (Meta callback + status page), /webhooks/meta, /oauth/meta/callback.
CLI (sinbox)
sinbox login <url> <key> # saves ~/.config/sinbox/config.json (0600)
sinbox ov # overview
sinbox ls [--since last] [--all] [--src ig.dm] [--lbl lead]
sinbox thread t3f | item i7k [--full] | search "minikurs"
sinbox mark i1 i2 [--unread] | note t3f "summary"
sinbox draft <i|t> "text" [--kind dm|reply|private_reply] | moderate i7k hide
sinbox drafts [--status pending] | show d12 | edit d12 "new text"
sinbox approve d12 [--send] | reject d12 | send d12
sinbox sync [ig.dm] | connect meta | mcp
sinbox admin key-create --name agent2 --scopes read,draft,send # runs on the server (DB_PATH)
sinbox admin key-list | key-revoke <name> | export <igsid> | forget <igsid> | rotate-key --old K --new KUse the admin key (with approve) only on your own machine. Give agents the agent key.
Instagram setup (Meta)
The app is the existing Meta app with Instagram API via Facebook Login for Business. The Instagram professional account must be linked to a Facebook Page.
Permissions (Use case → Instagram API / Messenger for Instagram):
instagram_basic,instagram_manage_messages,instagram_manage_comments,pages_show_list,pages_read_engagement,pages_manage_metadata.instagram_manage_messagesmust be added to the use case. No publishing or ads scopes.Facebook Login for Business → Settings → Valid OAuth Redirect URIs: add
https://inbox.wozu.co/oauth/meta/callback. Optionally create a Login Configuration with the permissions above and setMETA_LOGIN_CONFIG_ID. Without it, the scopes are passed explicitly.Env: set
META_APP_ID,META_APP_SECRET,TOKEN_ENC_KEY(openssl rand -hex 32) andPUBLIC_URL.Connect:
sinbox --key <admin key> connect metaprints a login URL. Open it, choose the Page linked to@wozu_co, and you'll be redirected back. The server exchanges the code for a long-lived user token and then a non-expiring Page token. It stores both encrypted (AES-256-GCM) and finds the linked IG account.Polling starts on its own: conversations every 2 min, media comments every 5 min (comments are only fetched for posts whose
comments_countchanged), tags every 15 min. The first run backfills 30 days.Webhooks (optional, M1b): need a Live app and Advanced Access. In App Dashboard → Webhooks → Instagram, set the callback to
https://inbox.wozu.co/webhooks/metawith yourWEBHOOK_VERIFY_TOKEN, subscribe tomessages,comments,mentions(and optionallymessaging_seen,message_reactions), then setMETA_WEBHOOKS_ENABLED=true. Every POST is checked againstX-Hub-Signature-256(HMAC-SHA256 of the raw body, timing-safe compare). Unsigned or invalid requests get 401.App Review needs: privacy policy URL
https://inbox.wozu.co/privacyand data-deletion callbackhttps://inbox.wozu.co/data-deletion. Both are served by the app.
Dev-mode limits (Standard Access): you can read your own comments and conversations and reply to or hide comments on your own media. DMs can only be sent to people with a role on the app (testers). Webhooks don't fire until the app is Live with Advanced Access, which is why polling always runs.
Reply rules enforced at draft time, again at approval, and again at send: DMs can only be sent within 24 h of the user's last message. With META_HUMAN_AGENT=true (a separate Meta feature) that becomes 7 days. Messages are limited to 1000 chars. Private replies are limited to one per comment, within 7 days. Comment replies are limited to 2200 chars and always go to the top-level comment.
Configuration
See .env.example. The main settings:
Var | Default | |
|
| Kill switch for all platform writes |
|
|
|
|
| |
| — | Bootstrap keys (stored as SHA-256). More keys: |
| — | 32 bytes hex/base64; required to connect accounts |
| — | See above |
|
| Daily purge job |
Deploy (Coolify)
Create a new resource in the existing project, either Dockerfile or Docker Compose using
coolify.compose.yaml. Set the domain tohttps://inbox.wozu.co; Traefik and Let's Encrypt are handled by Coolify. The container listens on8787.Mount a persistent volume at
/data. Set the env vars in Coolify. Set a memory limit of 192 MB.DNS: an A record for
inbox.wozu.copointing to the VPS.Backups:
sqlite3 /data/inbox.db ".backup /backup/inbox.db"from the nightly job. This takes an online backup that is consistent while the app runs.Create extra keys inside the container:
docker exec -it <ctr> sinbox admin key-create --name grok --scopes read,draft,send.
Development
npm ci
npm run typecheck
npm test # vitest: fake connector + mocked Graph API fixtures (test/fixtures/meta)
npm run build && node scripts/smoke.mjsThe layout follows SPEC §5: src/core (store, drafts/approval, compact formatting, triage, windows), src/mcp, src/api, src/cli, src/connectors/{meta,fake}, src/jobs, src/web, migrations/.
License
MIT
This server cannot be deployed
Maintenance
Related MCP Connectors
Instagram for AI agents: publish, read comments and DMs, insights, and engage from your account.
- PostgaleOAuthcom.postgale
Lets AI agents post and reply on Instagram, TikTok, Threads and more, with a human approval queue.
Agentic Instagram DM automation: comment-to-DM links, follow to unlock, inbox, post scheduling.
Read and search your Instagram, WhatsApp and LinkedIn DMs and prepare follow-ups you approve.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables AI assistants to read, send, and manage Instagram direct messages, including viewing conversations, sending DMs to users, reacting to messages, and searching for users by username.10-
- AlicenseAqualityCmaintenanceEnables AI assistants to manage Instagram and Threads accounts — publish content, handle comments, view insights, search hashtags, and manage DMs through the Meta Graph API.5929 npm13MIT
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to read Instagram accounts, research any Business or Creator account, publish content, and manage comments and DMs across three tiers of access.2MIT
- AlicenseNot gradedqualityCmaintenanceGives AI assistants access to one or more IMAP/SMTP mailboxes and a professional Instagram account through Meta's official API, exposing 20 tools that search, read, send, draft, move and delete e-mail and that read profile metrics, comments and Direct while replying to comments, hiding spam, sending DMs and publishing images, carousels or Reels. Server-side guards enforce rules like the 24-hour Direct window and header-only tokens, credentials never reach the model, and every write is recorded to an audit trail.MIT