Skip to main content
Glama

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, since cursors 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 set SENDING_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 --send

node 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

inbox_overview

Unread counts per source, DM windows closing within 6h, head cursor. Call first.

inbox_list

Columnar items. since, src, lbl, unread (default true), q, on, t, limit (≤50), maxChars (≤2000), fields, fmt: "lines"

thread_get

Last N messages (≤30), cached or extractive summary, reply window

item_get

One item; full adds untruncated text, native IDs, permalink, raw

search

FTS5 over stored text

mark

Batch read/unread and labels; upTo: <cursor>

thread_note

Store a ≤300-char summary that later sessions reuse (new_since_sum shows staleness)

draft_reply

kind: reply | dm | private_reply; checks reply window, length and the one-private-reply rule; key = idempotency key

draft_moderation

hide | unhide | delete

drafts

List drafts

send_approved

Executes only approved drafts; re-calls return the same native ID

sync_now

Forces a poll (rate-limited to once per 30 s)

approve

Only when APPROVAL=agent-confirmed. Needs confirmedBy + chat ref, both audited

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

/api/overview

read

GET

/api/items?since=&src=ig.dm,ig.cm&lbl=&unread=0&q=&on=&t=&limit=&maxChars=&fields=&fmt=lines

read

GET

/api/items/:id?full=1

read

GET

/api/threads/:t?last=

read

PUT

/api/threads/:t/note {summary,tags}

draft

GET

/api/search?q=

read

POST

/api/mark {ids,upTo,read,lbl_add,lbl_rm}

draft

GET

/api/drafts?status= · /api/drafts/:d

read

POST

/api/drafts {to,text,kind,key}

draft

POST

/api/drafts/moderation {id,action,key}

draft

PATCH

/api/drafts/:d {text} (pending only)

draft

POST

/api/drafts/:d/approve[?send=1] · /api/drafts/:d/reject

approve

POST

/api/drafts/:d/send

send

POST

/api/sync {src}

read

POST

/api/oauth/meta/start → {url}

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 K

Use 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.

  1. 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_messages must be added to the use case. No publishing or ads scopes.

  2. 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 set META_LOGIN_CONFIG_ID. Without it, the scopes are passed explicitly.

  3. Env: set META_APP_ID, META_APP_SECRET, TOKEN_ENC_KEY (openssl rand -hex 32) and PUBLIC_URL.

  4. Connect: sinbox --key <admin key> connect meta prints 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.

  5. Polling starts on its own: conversations every 2 min, media comments every 5 min (comments are only fetched for posts whose comments_count changed), tags every 15 min. The first run backfills 30 days.

  6. Webhooks (optional, M1b): need a Live app and Advanced Access. In App Dashboard → Webhooks → Instagram, set the callback to https://inbox.wozu.co/webhooks/meta with your WEBHOOK_VERIFY_TOKEN, subscribe to messages, comments, mentions (and optionally messaging_seen, message_reactions), then set META_WEBHOOKS_ENABLED=true. Every POST is checked against X-Hub-Signature-256 (HMAC-SHA256 of the raw body, timing-safe compare). Unsigned or invalid requests get 401.

  7. App Review needs: privacy policy URL https://inbox.wozu.co/privacy and data-deletion callback https://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

SENDING_ENABLED

false

Kill switch for all platform writes

APPROVAL

human

agent-confirmed exposes MCP approve (needs confirmedBy + ref)

MAX_SENDS_PER_HOUR

30

SINBOX_AGENT_KEY / SINBOX_ADMIN_KEY

—

Bootstrap keys (stored as SHA-256). More keys: sinbox admin key-create

TOKEN_ENC_KEY

—

32 bytes hex/base64; required to connect accounts

META_*

—

See above

RETENTION_RAW_DAYS / RETENTION_TEXT_DAYS

30 / 365

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 to https://inbox.wozu.co; Traefik and Let's Encrypt are handled by Coolify. The container listens on 8787.

  • 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.co pointing 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.mjs

The 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

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables 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
    -
  • A
    license
    A
    quality
    C
    maintenance
    Enables AI assistants to manage Instagram and Threads accounts — publish content, handle comments, view insights, search hashtags, and manage DMs through the Meta Graph API.
    59
    29 npm
    13
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI agents to read Instagram accounts, research any Business or Creator account, publish content, and manage comments and DMs across three tiers of access.
    2
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Gives 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