Getting Started
Scaffold a fresh Hogsend app, run it locally against Docker, and watch your first journey fire — from zero to a sending pipeline in a few minutes.
Hogsend is a code-first lifecycle orchestration engine: an event comes in, your code reacts, engagement flows back out. You define journeys in TypeScript, connect them to events (PostHog is the standard source, but any system that can send an HTTP request works), and Hogsend handles the durable execution — email sequences, timing, branching, and exit conditions. Email sends through a swappable provider, Resend by default.
The framework ships as an npm package, @hogsend/engine. You scaffold a fresh app that owns content only — journeys, email templates, webhook sources, custom routes, and your own database migrations — and pins @hogsend/engine. The engine never imports your content; upgrading the framework is pnpm up. See How It Works for the full mental model.
The spine
Two commands take you from nothing to a running, sending app:
pnpm create hogsend@latest my-app --domain mysite.com # scaffold + wire your sending domain
cd my-app
pnpm hogsend dev # infra + .env + migrate + API + worker, one command--domain writes EMAIL_FROM=hello@mysite.com + EMAIL_DOMAIN=mysite.com into the scaffold's env, and the create flow offers to run local setup (Docker, Hatchet token, data-plane key, migrations) for you. hogsend dev then runs everything from one terminal — and prints the URLs when the API is healthy, including Studio at http://localhost:3002/studio. (hogsend is the bundled @hogsend/cli; pnpm add -g @hogsend/cli if you'd rather drop the pnpm prefix.)
Fire an event and a journey runs:
pnpm hogsend dev --fire test.signup --email you@example.com # from a second terminalThen watch it move through Studio. Installation walks the whole loop end to end, including your first journey.
Sends are safe by default. With HOGSEND_TEST_MODE=auto (the default), every email is redirected to your own inbox — subject prefixed [TEST → real@recipient] — until your sending domain verifies. You can exercise real journeys and real sends from minute one without a single message reaching a customer. See Test mode and hogsend domain.
Prefer the pieces one at a time? pnpm bootstrap (Docker + .env + Hatchet token + migrate) followed by pnpm dev and pnpm worker:dev in two terminals is the manual equivalent — both paths are covered below.
Step 1 — scaffold
create-hogsend copies a fresh content-only app and pins the current engine version:
pnpm create hogsend@latest my-app --domain mysite.com--domain wires your sending domain at scaffold time — EMAIL_FROM=hello@mysite.com + EMAIL_DOMAIN=mysite.com land in env.example (and the bootstrap-copied .env inherits them). Skip it and the env keeps a commented placeholder block to fill in later. With --domain and no app name, the name defaults to the first domain label (mysite).
Use . to scaffold into the current folder, and pass --pm npm|yarn|bun to switch package managers (default pnpm). Run it with no app name in a terminal for an interactive setup, or pass --yes to accept every default — scaffold, install, and bootstrap — with no prompts:
pnpm create hogsend@latest my-app --domain mysite.com --yesThe scaffold installs dependencies for you and initializes a git repo. It also drops a .claude/skills/ tree and a tailored CLAUDE.md so Claude Code can author journeys, emails, and buckets with you (skip with --no-skills).
Step 2 — bootstrap
From the app directory, one command sets up everything you need to run locally:
pnpm bootstrapbootstrap is idempotent and safe to re-run. It runs seven steps and does the setup work for you:
- Checks Docker is installed and the daemon is running.
- Creates
.envfrom.env.exampleand generates a freshBETTER_AUTH_SECRET. - Resolves ports — if
5434,6380,8888, or7077are taken, it remaps to free ports and writes them back to.env. - Starts containers — TimescaleDB (Postgres 18), Redis, and Hatchet-Lite via
docker compose up -d --wait. - Auto-mints the Hatchet token — it mints a
HATCHET_CLIENT_TOKENagainst your local Hatchet-Lite and writes it to.env. You do not bring your own token for local dev. - Runs migrations — both the engine track and your client track.
- Mints the data-plane key — an ingest-scoped
HOGSEND_API_KEY(hsk_…) is generated, stored as a hash, and written to.env. This is the key the@hogsend/clientSDK and thehogsendCLI use to call your data plane.
Locally, the Hatchet token is auto-minted by bootstrap. The bring-your-own-token contract applies only to production — Hatchet Cloud or a self-hosted engine. See Get a Hatchet token for the production path.
When it finishes, your stack is up and the Hatchet dashboard is at http://localhost:8888 (login admin@example.com / Admin123!!). On every day after the first, hogsend dev reuses all of this — it detects the running containers and the minted credentials, and just runs the app.
Step 3 — create the first Studio admin
Studio is login-only — public sign-up is disabled, and there is no web create-admin form. The first admin is minted from your server, in one of two ways:
- The bootstrap prompt. After migrations,
pnpm bootstrapoffers an interactive "create your first Studio admin" step (it's skippable, and a no-op in CI / non-TTY or whenSTUDIO_ADMIN_EMAILis already set). - By hand, any time. Run the scaffold's script, which loads
.envand calls the CLI:
pnpm studio:admin # → hogsend studio admin createThe CLI writes directly to the database (it needs DATABASE_URL + BETTER_AUTH_SECRET from the environment — no HTTP, no running API). Prefer the masked password prompt over --password, which can leak into shell history. On a deploy, set STUDIO_ADMIN_EMAIL (+ optional STUDIO_ADMIN_PASSWORD) instead and the API mints the admin on boot into an empty user table. Either way, the password goes only through Better Auth's hasher — never plaintext, never logged. Locked out later? hogsend studio admin reset. See Operating → Studio.
Step 4 — run
One command runs the whole local stack:
pnpm hogsend devhogsend dev detects the infra bootstrap brought up (skipping docker compose up when it's already running), checks .env, runs migrations, spawns the API and the worker as prefixed child processes, waits for GET /v1/health, and prints the local URLs — API, Studio, Hatchet dashboard. Ctrl+C stops everything.
The api and worker are two separate processes that share the same codebase: the api serves HTTP and pushes events to Hatchet; the worker executes durable tasks (email sends, journey orchestration, background jobs). Prefer to run them by hand? Each in its own terminal:
pnpm dev # HTTP API on http://localhost:3002
pnpm worker:dev # Hatchet worker (second terminal)In development the api serves interactive docs at http://localhost:3002/docs (Scalar UI) and the OpenAPI spec at /openapi.json. Health is at GET /v1/health. Both are disabled when NODE_ENV=production.
The first journey to edit is src/journeys/welcome.ts. Fire an event at it and watch the worker pick it up — hogsend dev --fire from a second terminal, or the raw curl:
pnpm hogsend dev --fire user.created --email you@example.com
# equivalent:
curl -X POST http://localhost:3002/v1/events \
-H "Authorization: Bearer $HOGSEND_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "user.created", "email": "you@example.com" }'Installation covers this end to end, including authoring the journey itself.
Step 5 — verify your domain
Until your sending domain verifies, test mode redirects every email to your own inbox — so nothing above could reach a real customer. When you're ready to send live, verify the domain from the CLI (it talks to your running instance's admin API; you'll need an admin key — see connecting):
pnpm hogsend domain status # where am I? (state, records, test-mode banner)
pnpm hogsend domain add mysite.com # register with the provider, print DNS records
pnpm hogsend domain check # poll every 15s until verifiedadd detects where your DNS lives (Cloudflare, Vercel, Route 53, GoDaddy, Namecheap, Porkbun, Google Domains) via NS lookup, prints the records formatted for that host's panel with a deep link — and on Cloudflare or Vercel it offers to apply the records for you when a CLOUDFLARE_API_TOKEN / VERCEL_TOKEN is set. check polls until verified; test mode auto-exits within 60 seconds of DNS verifying, no restart needed. Full reference: hogsend domain.
Prerequisites
- Node.js 22 — pinned via the scaffold's
.node-version. - pnpm (recommended) — or npm/yarn/bun via the
--pmflag. - Docker installed and running — for Timescale, Redis, and Hatchet-Lite.
bootstrapprovisions all three. - An email provider (needed to verify a domain and send real mail — test mode works before that) — Hogsend sends through a swappable provider, Resend by default: sign up free, grab your API key (starts with
re_), and set a realRESEND_API_KEYin.env. Postmark ships as an opt-in alternative, and any other works behind theEmailProvidercontract. The dependency-free smoke test in Installation needs no provider at all. - PostHog account (optional for first boot) — with a project API key (
phc_) if you want PostHog to drive your events and person properties.
PostHog is the standard event source, but it's not required to boot. You can drive Hogsend entirely through the data plane — POST /v1/events and the @hogsend/client SDK — from your own app code, webhook sources, or any of the built-in integrations. Lifecycle events fan back out to PostHog, Segment, or Slack via outbound destinations.
Next steps
Introduction
The lifecycle automation layer PostHog doesn't have yet. Scaffold an app, define journeys in TypeScript, and ship your first lifecycle emails in minutes.
Get a Hatchet token
Hogsend's one prerequisite. Local dev auto-mints it for you in bootstrap; for production, get a token from the self-hosted hatchet-lite dashboard, Hatchet Cloud, or your own instance and set three env vars.