Documentation

Ledger

Documentation

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.

Getting started

You do not need to know Node to walk the product. After install, the left rail is the whole app. Click top to bottom.

  1. Sign in — use the owner you created on /install, or the demo buttons on the live listing.
  2. Dashboard — three numbers only: reply rate, positive replies, bounce. If bounce is red, stop sending.
  3. ICP — write who you sell to in plain language. Save. This is the brief, not a formality.
  4. Inboxes — Add inbox. Paste SMTP and IMAP from your host. Test send. Wait until SPF, DKIM, and DMARC are teal. Orange means still checking.
  5. Settings — type a street address. Launch will not unlock without it.
  6. Lists — New list, then Import CSV or Find leads. Open a row to edit a person. Verify until the risk chip is under 3%.
  7. Sequences — add an email step. Type {{unsubscribe_url}} in the body or the sequence stays Not ready.
  8. Campaigns — pick the list, sequence, and inbox. If Launch is locked, read the reason on the row and click through. Do not guess.
  9. Inbox — replies land here. e marks interested and opens a deal. u unsubscribes.
  10. Pipeline — drag Interested → Meeting → Won or Lost. Meeting wants a calendar URL.

Demo

Live app: webbuddy-outbound.fly.dev/login. On that box, User and Admin fill the fields in one click.

RoleEmailPasswordLands in
Userdemo@demo.comdemo1234Demo workspace — lists, campaigns, replies
Adminadmin@demo.comadmin1234Same workspace, then Open operator

Demo data lives only on that hosted database. A buyer install starts empty.

Architecture

Two long processes, one database, one queue. The browser never talks to SMTP. Campaigns sit in Redis until the worker sends them from the customer’s inbox.

Browser/docs UIWeb appNext.jsPostgresRedisBullMQWorkersend + IMAPSMTP / IMAP
  • Web — Next.js. Pages, APIs, install, billing. Run with npm run dev or npm start.
  • Workersrc/workers/index.ts. Drains the send queue and polls IMAP. Run npm run worker beside the web process.
  • Postgres — workspaces, lists, campaigns, replies. Encrypted SMTP and API keys.
  • Redis — BullMQ jobs. If Redis is down, nothing sends.

Branding and colors

Change look in one file: src/app/globals.css. The :root block is the brand. Do not add a second green. Teal is healthy and the only accent.

TokenDefaultUse for
--accent#0E7A72Brand, healthy, primary buttons.
--attention#D97706In progress, waiting on DNS.
--danger#B42318Bounce, blocked launch, money-losing.
--canvas#F7F6F3Page background.
--panel#FFFCF8Tables and forms.
--rail#121214Left navigation.
--ink#1A1917Body text.

Wordmark lives in src/components/icons.tsx (SealIcon). Favicon is src/app/icon.png. Account mail chrome is src/emails/ledger.tsx. After you change tokens, open /design and confirm no second palette appeared.

Annotated screens

Each shot is the live product at 1280×720. The numbered notes match the chrome on that screen.

Sign in

Sign in
  • 1 — User / Admin fill the demo fields. Hidden when DEMO_MODE is off.
  • 2 — Email and password. Same form buyers see after /install.
  • 3 — Create workspace / reset stay on this page.

Dashboard

Dashboard
  • 1 — Reply / positive / bounce. No open rate.
  • 2 — Playbook chips. Red means a gate failed.
  • 3 — Campaign table. Locked rows say why.

Lists

Lists
  • 1 — New list, then import, find, or Apollo fetch.
  • 2 — Risk chip. Over 3% and launch stays locked.
  • 3 — Click a lead to edit or delete.

Sequences

Sequences
  • 1 — Email and wait steps on one timeline.
  • 2 — Missing {{unsubscribe_url}} keeps the sequence Not ready.
  • 3 — A/B subject is optional.

Campaigns

Campaigns
  • 1 — Attach list + sequence + inbox.
  • 2 — Launch lock is the next action, not a toast.
  • 3 — Pause, resume, stop stay on the row.

Inbox

Inbox
  • 1 — Thread list. j / k move.
  • 2 — Reply body from IMAP.
  • 3 — e interested, u unsub, r reply, p poll.

Pipeline

Pipeline
  • 1 — New, Interested, Meeting, Won, Lost.
  • 2 — Meeting wants a URL. Won and Lost are terminal.

Sending inboxes

Sending inboxes
  • 1 — SMTP + IMAP on the customer domain.
  • 2 — DNS chips. All three must be teal before a live send.
  • 3 — Bounce over 3% pauses the inbox.

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

  1. ICP — write who you sell to. Industry, size, titles, geos, pain. This feeds find filters and first-line hints.
  2. Inboxes — add SMTP + IMAP. Send a test. Wait until SPF, DKIM, and DMARC pass (or use Mailpit locally, which skips DNS).
  3. Settings — add a postal address. CAN-SPAM requires it before launch.
  4. Lists — create a list. Import a CSV, find leads, or fetch Apollo. Verify. Clean risk must stay under 3%.
  5. Sequences — add email steps. Every body must include {{unsubscribe_url}}. Add wait steps between sends.
  6. Campaigns — attach list + sequence + inbox. If Launch is locked, the row says why. Fix that, then launch.
  7. 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.

KeyRequiredNotes
DATABASE_URLYesPostgres. Local compose uses port 5433.
REDIS_URLYesDefault redis://localhost:6379.
AUTH_SECRETYesLong random string. Session signing.
KMS_KEYYesEncrypts SMTP, API, and AI keys at rest.
APP_URLYesPublic origin. Unsubscribe and invite links.
HOSTEDNofalse = /install. true = /signup, no Mailpit.
SUPER_ADMIN_EMAILSNoComma list. Those users see Open operator.
DEMO_MODENoUser / Admin fill on /login. Off for buyers.
RESEND_API_KEYNoAccount mail: welcome, invite, reset.
RESEND_FROMNoFrom header for account mail.
OPENAI_API_KEYNoOptional first lines. Also in Settings.
HUNTER_API_KEYNoFind leads. Workspace Settings override env.
PROSPEO_API_KEYNoFills Hunter misses.
APOLLO_API_KEYNoFetch search into a list. BYO key.
MILLIONVERIFIER_API_KEYNoInbox-exists verify. Else MX only.
STRIPE_*NoHosted billing only. Not needed to send.
MAILPIT_*NoLocal 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.

  1. ICP — who you sell to, written down before the list exists.
  2. Inbox — their SMTP and IMAP. DNS must pass before the first live send.
  3. List — find, Apollo fetch, or CSV. Suppression survives re-import. Clean gate is 3%.
  4. Sequence — every email step carries {{unsubscribe_url}}.
  5. Launch — locked until list, sequence, and inbox are green.
  6. 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.

TokenRequiredFills with
{{unsubscribe_url}}YesOne-click unsubscribe. Every email step.
{{first_name}}NoLead first name.
{{last_name}}NoLead last name.
{{company}}NoCompany.
{{title}}NoJob title.
{{first_line}}NoGenerated 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

PathWhat lives there
src/appRoutes and API. Next.js App Router.
src/componentsShell, marketing, and design-system primitives.
src/lib/domainLaunch, verify, ramp, templates. Covered by Vitest.
src/lib/servicesWorkspace-scoped reads and writes.
src/workersSend queue and IMAP poller.
src/dbDrizzle schema.
src/emailsAccount mail (React Email).
documentationThis 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.

Listing tags

Stay at fifteen. Prefer these over vendor names (Hunter, Apollo, Stripe, Resend). Paste them as-is on the Envato form.

email marketing, cold outreach, smtp, campaign manager, crm, saas, self hosted, inbox, email sequence, lead management, sales pipeline, multi tenant, dashboard, transactional email, unsubscribe

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.