OutboundOS is a self-hosted outbound workspace. Find or import a list, write a sequence, send from the customer’s SMTP, and handle the reply. Campaigns never leave from a shared warmup pool or from outboundos.org.
This is the Envato buyer guide. Same tokens as the rest of the product. Teal is healthy. Orange is in progress. Red blocks send. The visual contract is /design.
Demo
Live app: webbuddy-outbound.fly.dev/login. On that box, User and Admin fill the fields in one click.
| Role | Password | Lands in | |
|---|---|---|---|
| User | demo@demo.com | demo1234 | Demo workspace — lists, campaigns, replies |
| Admin | admin@demo.com | admin1234 | Same workspace, then Open operator |
Demo data lives only on that hosted database. A buyer install starts empty.
Requirements
- Node.js 20 or newer
- Postgres 16
- Redis 7 (BullMQ send queue)
- SMTP + IMAP on the sending domain, or Mailpit for local
- A VPS, Fly, Railway, Docker, or similar. Not Vercel alone — the worker is a long process.
Install
cp .env.example .env npm install npm run infra npm run db:push npm run test npm run dev npm run worker
Open http://localhost:3000. First visit is /install — owner email, password, workspace name. Mailpit UI is http://localhost:8025. Compose Postgres is on :5433 so it does not collide with a local Postgres on 5432.
Full stack in one command: docker compose --profile full up --build
Two processes
npm run dev (or start) is the app. npm run worker sends and polls IMAP. Without the worker, campaigns queue and never leave.
First campaign
- ICP — write who you sell to. Industry, size, titles, geos, pain. This feeds find filters and first-line hints.
- Inboxes — add SMTP + IMAP. Send a test. Wait until SPF, DKIM, and DMARC pass (or use Mailpit locally, which skips DNS).
- Settings — add a postal address. CAN-SPAM requires it before launch.
- Lists — create a list. Import a CSV, find leads, or fetch Apollo. Verify. Clean risk must stay under 3%.
- Sequences — add email steps. Every body must include
{{unsubscribe_url}}. Add wait steps between sends. - Campaigns — attach list + sequence + inbox. If Launch is locked, the row says why. Fix that, then launch.
- Inbox — replies land here. Mark interested to open a deal. A reply stops remaining steps for that lead.
Environment
Copy .env.example. Never commit a real .env. Rotate AUTH_SECRET and KMS_KEY in production.
| Key | Required | Notes |
|---|---|---|
| DATABASE_URL | Yes | Postgres. Local compose uses port 5433. |
| REDIS_URL | Yes | Default redis://localhost:6379. |
| AUTH_SECRET | Yes | Long random string. Session signing. |
| KMS_KEY | Yes | Encrypts SMTP, API, and AI keys at rest. |
| APP_URL | Yes | Public origin. Unsubscribe and invite links. |
| HOSTED | No | false = /install. true = /signup, no Mailpit. |
| SUPER_ADMIN_EMAILS | No | Comma list. Those users see Open operator. |
| DEMO_MODE | No | User / Admin fill on /login. Off for buyers. |
| RESEND_API_KEY | No | Account mail: welcome, invite, reset. |
| RESEND_FROM | No | From header for account mail. |
| OPENAI_API_KEY | No | Optional first lines. Also in Settings. |
| HUNTER_API_KEY | No | Find leads. Workspace Settings override env. |
| PROSPEO_API_KEY | No | Fills Hunter misses. |
| APOLLO_API_KEY | No | Fetch search into a list. BYO key. |
| MILLIONVERIFIER_API_KEY | No | Inbox-exists verify. Else MX only. |
| STRIPE_* | No | Hosted billing only. Not needed to send. |
| MAILPIT_* | No | Local test inbox. Ignored when HOSTED=true. |
Self-host vs hosted
- HOSTED=false First visit is
/install. One owner on the box. Mailpit is allowed for test sends. This is the default buyer path. - HOSTED=true Signup creates a workspace. Shared workers, isolated data. Still their SMTP only. Plan caps can apply. Use this if you resell as SaaS (Extended license).
The loop
Six steps, in order. Skip one and launch stays locked.
- ICP — who you sell to, written down before the list exists.
- Inbox — their SMTP and IMAP. DNS must pass before the first live send.
- List — find, Apollo fetch, or CSV. Suppression survives re-import. Clean gate is 3%.
- Sequence — every email step carries
{{unsubscribe_url}}. - Launch — locked until list, sequence, and inbox are green.
- Replies — stop-on-reply cancels the rest. Interested becomes a deal.
Screens
- Dashboard
- Playbook chips, reply / positive / bounce, campaign table. No open rate.
- ICP
- Industry, size, titles, geos, pain, buying trigger. Feeds first-line hints and find filters.
- Inboxes
- Connect SMTP + IMAP. Test send. SPF, DKIM, DMARC. Ramp starts at 40/day. Bounce over 3% pauses the inbox until you clear it.
- Lists
- Create a list. Find leads (Hunter, then Prospeo), fetch Apollo, or import CSV. Edit or delete a lead. Verify. Generate first lines.
- Sequences
- Email and wait steps. A/B subject. Missing unsubscribe token = not ready. Local spam score is a hint, not a gate.
- Campaigns
- Attach list + sequence + inbox. Launch lock says why. Pause, resume, stop. Sending hours and daily cap live on the campaign.
- Inbox
- Replies from IMAP. Keys: j / k move, e interested, u unsub, r reply, p poll. A reply halts remaining sends for that lead.
- Pipeline
- New, Interested, Meeting, Won, Lost. Meeting wants a URL. Won and Lost are terminal.
- Analytics
- Reply rate, positive replies, bounce. Subject winners. Never opens. There is no tracking pixel.
- Settings
- Workspace name, postal address, AI and finder keys. Export redacts secrets. Invite teammates.
CSV import
First row is headers. One column must be a work email. Apollo, Clay, Instantly, Smartlead, HubSpot, ZoomInfo, and a plain spreadsheet all map. Sales Nav rarely has email — enrich first.
Recognised email headers include email, work email, person email. Names and company fill when present. A one-column list of addresses is also accepted. Suppressed addresses are skipped on re-import.
Email tokens
Use these in sequence bodies and subjects.
| Token | Required | Fills with |
|---|---|---|
| {{unsubscribe_url}} | Yes | One-click unsubscribe. Every email step. |
| {{first_name}} | No | Lead first name. |
| {{last_name}} | No | Lead last name. |
| {{company}} | No | Company. |
| {{title}} | No | Job title. |
| {{first_line}} | No | Generated or written opener. |
Unknown tokens render empty. Do not invent {{name}} — use {{first_name}}.
Launch gates
- DNS must pass (or local skip). Fail = red, launch locked.
- List bounce risk over 3% = not launchable.
- Every email step must include
{{unsubscribe_url}}. - Postal address is required for the compliance footer.
- Inbox paused (bounce > 3%) blocks launch until cleared.
- A reply sets halt. Remaining jobs for that lead are cancelled.
- OOO re-queues. Unsubscribe writes a suppression that survives re-import.
- Sent mail includes
List-Unsubscribe. Analytics never count opens.
Worker
npm run worker runs src/workers/index.ts. It drains the BullMQ send queue and polls IMAP. Fly and Docker start this as a second process. If Redis is down, the worker logs a connection error and nothing sends.
Optional keys
Hunter, Prospeo, Apollo, MillionVerifier, and AI keys go in Settings (encrypted with KMS_KEY) or in env. Usage is billed by those providers. CSV import, sequences, campaigns, inbox, pipeline, and analytics work with no keys.
Find order: Hunter first, Prospeo fills misses. Apollo fetch is a separate button for a BYO Apollo key. Verify without MillionVerifier is MX-only.
Operator
Add emails to SUPER_ADMIN_EMAILS. Those users see Open operator and can manage workspaces, plans, and hosted mode. On a self-hosted box, the first owner can also open the panel.
Production
Set HOSTED=true, APP_URL to the public origin, and strong AUTH_SECRET / KMS_KEY. Run web + worker against managed Postgres and Redis. fly.toml in the repo is one working shape: web process, worker process, drizzle-kit push on release.
Regular license: one end product, typically one company box. Extended license: you may offer this as a hosted service to multiple customers. See Envato’s license FAQ.
Source tree
| Path | What lives there |
|---|---|
| src/app | Routes and API. Next.js App Router. |
| src/components | Shell, marketing, and design-system primitives. |
| src/lib/domain | Launch, verify, ramp, templates. Covered by Vitest. |
| src/lib/services | Workspace-scoped reads and writes. |
| src/workers | Send queue and IMAP poller. |
| src/db | Drizzle schema. |
| src/emails | Account mail (React Email). |
| documentation | This guide, offline HTML. |
Gates live in src/lib/domain. Run npm test after you change them. Do not add a second color palette — see /design.
FAQ
- Signup says use /install
- HOSTED is false. That is correct for a self-hosted box.
- Campaign stays locked
- Read the lock. Fix DNS, list risk, unsubscribe token, or postal address.
- Nothing sends
- Is the worker up? Is Redis reachable? Is the inbox Ready, not paused?
- Find is empty
- Put a Hunter or Prospeo key in Settings. Or import a CSV.
- Verify is only MX
- Add a MillionVerifier key for inbox-exists.
- Can I send from outboundos.org?
- No. Campaigns use the inbox SMTP the workspace connected.
- Does it track opens?
- No. Replies and bounces only. There is no pixel.
- Where is the demo data on my install?
- Nowhere. Seeded data is only on the live listing demo.
Support
Item comments on CodeCanyon, for six months from purchase, as Envato requires. Include Node version, HOSTED, whether the worker is running, and the launch-lock text if send is blocked. Do not paste DATABASE_URL, KMS_KEY, or SMTP passwords.
Credits
Next.js 16, React 19, Drizzle, BullMQ, Tailwind CSS v4, Geist, Source Serif 4, Nodemailer, IMAPFlow, Zod. Optional: Hunter, Prospeo, Apollo, MillionVerifier, Resend, Stripe.
v1.0.0 — CodeCanyon release. Same product as the live demo.