ManyChat charges you a monthly subscription for one core trick: someone comments “LINK” on your reel, and a DM with your link lands in their inbox a second later.
That trick is a webhook, a keyword match, and one API call. It doesn’t need to cost anything.
OpenReply is an open-source (MIT) version of exactly that — Instagram comment-to-DM automation you self-host with your own API keys. No seat limits, no plan caps, no monthly bill. It uses Meta’s official Instagram API: no scraping, no browser automation, no handing over your password. Your account stays inside Meta’s rules, which matters if you’d like to keep it.
In this post I’ll walk you through the full setup: hosting, environment, the Meta app (the part that actually takes time), and testing it end to end.
What you get
- Keyword → DM. One or many keywords per post, whole-word or partial match, with an optional public comment reply on top.
- Tracked links. Your link becomes a tracked redirect with clicks and CTR per campaign — up to two tappable buttons per DM, each tracked separately.
- Follow gate. Optionally require a follow before the link is released, verified against Meta’s actual follow status. It fails open, so a real follower never gets stuck.
- Rate limiting built in. Stays under Meta’s 750-private-replies-per-hour cap and queues the overflow instead of dropping it.
- Multi-account, workspaces, roles. Owner/admin/member with invite links — useful if you run this for clients.
- Inbox and logs. Read and reply to DMs from the dashboard (inside Meta’s 24-hour window), and every send, skip, and failure is logged with a reason.
How it’s built
Two processes, two datastores. A Next.js web app serves the dashboard, the OAuth callback, and the incoming webhook. A separate long-running Node worker consumes the send queue and actually fires the DMs. They share a Postgres database (campaigns, logs, accounts) and Redis (the BullMQ queue and rate limiter).
The split matters for hosting: the web app runs happily on Vercel, but the worker can’t — serverless functions die, and a queue consumer has to stay up. So the recommended free-tier stack is Vercel for the web app and Railway for the worker plus Postgres and Redis.
One rule you cannot break: the web app and worker must share the same DATABASE_URL, REDIS_URL, and ENCRYPTION_KEY. The web app encrypts your Instagram token; the worker decrypts it to send. Different keys and every single send fails.
What you need before you start
- A Facebook account — Meta developer registration is built on it, there’s no Instagram-only path.
- An Instagram Business or Creator account. Personal accounts can’t connect — switch in the Instagram app under Settings → Account type.
- A free Resend account with a verified sender domain. Login is email magic links only — no Resend, no login.
- Free Vercel and Railway accounts. You don’t need to buy a domain — the free
your-app.vercel.appURL is what everything points at.
Set expectations now: the code deploys in minutes. The Meta app setup is where you’ll spend an afternoon the first time. That’s not OpenReply’s fault — it’s just Meta.
Option 1: Run it locally first
If you just want to poke around before deploying:
git clone https://github.com/diwenne/openreply.git
cd openreply
npm install
cp .env.example .env # fill in the values per docs/setup.md
docker-compose up -d # starts Postgres and Redis
npm run db:migrate
npm run dev # web app on http://localhost:3000
npm run worker # second terminal — this sends the DMs
Two processes, always. npm run dev receives the webhooks; npm run worker sends the messages. If comments come in and no DM ever goes out, the worker is the first thing to check. For Meta to reach your local machine you’ll need a tunnel like ngrok http 3000, with your tunnel URL used everywhere a public URL is required.
Option 2: The production setup
Step 1 — Railway: Postgres, Redis, worker
Do Railway first, because Vercel needs the database URLs from it. Create a project, add PostgreSQL, add Redis, then add the worker as a GitHub Repo service pointing at your fork. Override the worker’s build and start commands:
Build Command: npm run db:generate
Start Command: npm run worker
Don’t leave the default npm run build — the worker doesn’t need a Next.js build, and any build step that touches the database will fail because the worker can’t reach Postgres at build time.
On connection strings: Railway gives you two URLs per datastore. The internal ones (*.railway.internal) are for the worker — faster and free of egress inside Railway’s network. The public ones (*.proxy.rlwy.net) are for Vercel and for running migrations from your machine. Give Vercel an internal URL and it will hang and time out — this is one of the classic mistakes.
Step 2 — Migrate the production database
DATABASE_URL="postgresql://...proxy.rlwy.net.../railway" npm run db:migrate
Run once from your machine using the public Postgres URL.
Step 3 — Vercel: the web app
Import your fork as a new Vercel project (it auto-detects Next.js), add every environment variable, and deploy. Key values: NEXTAUTH_URL is your Vercel domain, DATABASE_URL and REDIS_URL are the public Railway URLs, and ENCRYPTION_KEY is the exact same value as on the worker.
Generate the secrets like this:
openssl rand -base64 32 # NEXTAUTH_SECRET
openssl rand -hex 32 # ENCRYPTION_KEY (must be exactly 64 hex chars)
Plus a CRON_SECRET and a WEBHOOK_VERIFY_TOKEN (any random string — you’ll paste the same value into Meta’s webhook config later). The full variable table is in the repo’s docs/setup.md.
The Meta app: where the afternoon goes
This is the slow part, and the setup guide is refreshingly honest about it. The short version, with the traps that catch everyone:
- Create a Business-type app at developers.facebook.com and pick the “Manage messaging and content on Instagram” use case. Trap: don’t pick Facebook Login or the Marketing API use case — the first breaks the OAuth flow with a mismatched-client error, the second drags in heavy review requirements.
- Collect three secrets.
INSTAGRAM_APP_IDandINSTAGRAM_APP_SECRETfrom the Instagram product page,FACEBOOK_APP_SECRETfrom Settings → Basic. Trap: the Instagram app ID is a different number from the Facebook App ID. Use the one under the Instagram product. - Add your Instagram account as a tester — then accept the invite inside Instagram. This is the step everyone misses, and it produces the “Insufficient Developer Role” error. Sending the invite from the Meta dashboard is only half; you must open Instagram → Settings and activity → Apps and websites → Tester invites and accept. Until you do, login keeps failing.
- Register the OAuth redirect in Business login settings:
https://your-app.vercel.app/api/instagram/callback— exact, no trailing slash. - Configure the webhook. Callback URL
https://your-app.vercel.app/api/webhook, verify token = yourWEBHOOK_VERIFY_TOKEN, subscribe to thecommentsfield. Use the console’s Test → Send to My Server button to confirm delivery — it’s a two-step control, and only the second click actually POSTs. - Publish the app. Real comment webhooks only arrive when the app is Live. In Development mode, nothing happens outside the console Test button — this is the number one cause of “I set everything up and nothing works.” OpenReply ships the privacy, terms, and data-deletion pages Meta demands before it lets you publish, on your own domain.
Test it end to end
Connect your account in the app (Settings → Connect Instagram), create a campaign on one of your posts with a keyword like TEST, then comment that keyword from a different account — OpenReply deliberately ignores your own comments. Watch for the DM.
If nothing arrives, /api/health is your friend: it reports the database, Redis, queue, and worker heartbeat. If worker.healthy is false, no DM will ever send no matter how perfect your Meta config is. Beyond that, the Postgres tables tell the whole story — WebhookEvent for delivery, DmLog for send status, OperationalEvent for worker errors.
Or: let an AI assistant drive it
This is my favourite detail in the repo. The setup guide includes a ready-made prompt for Claude Code or Cursor — you open a clone of the repo in your assistant, paste the prompt, and it walks the entire setup with you: generates the secrets, deploys both processes, then steps you through the Meta dashboard one screen at a time, asking for values only you can provide. It even includes rules telling the assistant to diagnose failures by querying the Postgres tables directly instead of guessing.
One caution the docs make and I’ll repeat: you’ll be pasting real secrets into a chat. Only do that in tooling you trust, and rotate anything you pasted once you’re live.
This is exactly the pattern I teach inside my community — self-hosting the tools everyone else rents, with AI doing the heavy lifting on setup. If you want to go deeper on builds like this, that’s what AI Business Mentors is for.
One honest limitation
Everything above covers running OpenReply for your own accounts, or a handful you add as testers — no App Review needed. If you want strangers to connect their own Instagram to your hosted instance, Meta requires Advanced Access review: a screencast of the full flow, written permission justifications (drafts are included in the repo), and business verification with real legal documents. Meta scrutinises automated-DM apps and often rejects the first submission. Most self-hosters skip this entirely and just run their own instance for their own account — which is the whole point anyway.
The bottom line
An afternoon of Meta dashboard pain buys you a ManyChat replacement that runs free on Vercel and Railway’s free tiers, keeps your account inside Meta’s official API, and tracks clicks per campaign. If comment-to-DM is part of how you distribute content — and if you’re gating lead magnets behind comments, it should be — this is one subscription you no longer need.
Build with me
Stop renting your stack
Inside SeanNoCode I show you how to self-host tools like this, wire them into a real client-getting system, and build a one-person AI business around them — live Q&A, course library, and a community of people actually shipping.
