Lifecycle email is a feature.
Ship it like one.
Welcome series, trial nudges, win-backs, payment saves — every product needs them. Hogsend turns PostHog and product events into journeys that live in your repo and send through your own Resend or Postmark account, working by this afternoon.
Put an email in.
The product does the rest.
This form feeds a stock create-hogsend app running in production. It ingests the event, runs its welcome journey, and the email arrives from hello@hogsend.com a few seconds later. A nudge follows in two days unless you deploy first.
Same engine, same journey code you scaffold · unsubscribe is one click
Journeys are TypeScript files in your repo. Everything else follows from that.
Built by a growth engineer after 15+ years of client work — and one too many hand-rolled lifecycle stacks. The story →
Mostpeoplewhosignupnevercomeback.Theproductwasfine;nothingaskedthemtoreturn.Thewelcome,thenudge,thewin-backhavesatatthebottomofthebacklogformonths.Hogsendshipsthelotinanafternoon,fromyourrepo.
The emails every product should send
The flows behind every good lifecycle programme. Ten of them ship in the scaffold, ready to edit.
Journeys and buckets, working together
Journeys are emails that play out over time — sleep, wait for what the user does next, and track opens and clicks as you go. Buckets are live groups of people. And every email and lifecycle event fans out to your destinations — PostHog, Segment, Slack, or any signed webhook.
Emails that play out over time
Trigger on an event, send, sleep, then branch on what happened while you waited. The control flow is plain TypeScript.
export const welcome = defineJourney({
meta: {
id: "activation-welcome",
trigger: { event: "user_signed_up" },
entryLimit: "once",
},
run: async (user, ctx) => {
await sendEmail({ to: user.email, template: "welcome" });
await ctx.sleep({ duration: days(2) });
const { found } = await ctx.history.hasEvent({
userId: user.id,
event: "feature_used",
});
if (!found) {
await sendEmail({ to: user.email, template: "nudge" });
}
},
});Agents can write all of it
Everyone bolted an MCP server onto their UI this year. Hogsend skipped the step. The whole surface is typed code in a repo, which is what agents are already best at.
export const winback = defineJourney({ meta: { id: "winback", trigger: { event: wentDormant.entered }, exitOn: [{ event: wentDormant.left }], }, run: async (user, ctx) => { await sendEmail({ /* check-in */ }); await ctx.sleep({ duration: days(7) }); await sendEmail({ /* final nudge */ }); },});Journeys as code
Journeys are .ts files. Agents read them, write them, and open PRs against them. You review the diff like any other change.
- Add a win-back journey — when someone enters the went-dormant bucket, send a check-in, wait 7 days, then a final nudge. Stop if they come back.
- Created src/journeys/winback.ts — triggers on wentDormant.entered, exits on wentDormant.left, sends reactivation-checkin → ctx.sleep(days(7)) → reactivation-final-nudge. Ran `hogsend journeys --json` to verify — registered. Review the diff.

Studio sees everything and changes nothing
Every send, journey run, and contact in one dashboard — preview templates with live props, resend a failed message, pause a sequence. Your editor stays the author.
The whole job is one loop
Activity comes in from PostHog or any webhook, the right emails go out through your provider, and what people do with them fans back out to your tools — PostHog, Segment, Slack, or anywhere. Nothing new to buy or keep in sync.
Scaffold your app
pnpm create hogsend@latest emits a thin app that pins @hogsend/engine and holds your content. Pass --domain to wire your sending domain from the start — sends redirect to your own inbox until the domain verifies.
terminal# Scaffold a thin app that pins the engine$ pnpm create hogsend@latest my-app --domain mysite.com $ cd my-app$ hogsend dev # infra, .env, migrate, API + worker → API on :3002 · Studio at /studioDefine journeys & buckets
TypeScript functions that trigger on events, send emails, wait, branch, and adapt.
journeys/welcome.tsexport const welcome = defineJourney({ meta: { id: "activation-welcome", trigger: { event: "user_signed_up" }, entryLimit: "once", }, run: async (user, ctx) => { await sendEmail({ to: user.email, template: "welcome" }); await ctx.sleep({ duration: days(2) }); const { found } = await ctx.history.hasEvent({ userId: user.id, event: "feature_used", }); if (!found) await sendEmail({ template: "nudge" }); },});Deploy & watch it run
Host with Docker or one-click Railway. Watch every send in Studio.
deploy# One-click Railway template, or your own host$ git push origin main → building hogsend-api …→ building hogsend-worker …→ migrations applied · health check /v1/health ✓ # Watch every send in Studio
What growth can learn from engineering
Email tools never picked up the habits that make software dependable. Bring lifecycle email into the repo and it inherits all of them at once.
Every change has a history
Edit a template in a dashboard and the old version is gone. Nobody can say what the welcome email said in March, who changed it, or why. In a repo every subject line has a diff and a way back.
A second pair of eyes before it sends
Your welcome email gets read more often than your homepage. In most tools it goes live the moment someone clicks Save. In a repo it goes out through the same pull request as everything else you ship.
Tests you can still read next quarter
Most A/B tests survive as a memory of which variant won. The losing copy gets deleted and the reasoning lives in someone's head. When variants are code, the whole experiment stays on the record.
Why click what you could type?
A canvas flow is forty drag-and-drops that nobody can review, reuse, or hand to an agent. The same logic is a dozen lines of TypeScript, and agents are already very good at writing those.
Working by this afternoon
Most platforms want weeks of template building and flow clicking before the first send. The scaffold puts 10 journeys and 13 templates in your repo with one command, so the work starts at editing.
Growth shouldn't be a billing event
Rented platforms meter contacts, which makes your growth their revenue. Self-hosted software costs the same at 50,000 contacts as it did at 500. Postgres has never charged anyone per row.
Journeys as code
Lifecycle logic is TypeScript in your repo — reviewed, type-checked, and versioned like the rest of your product.
Your provider, your reputation
Sends go through your own Resend or Postmark account — or any provider behind the EmailProvider contract.
First-party tracking
The engine rewrites links and tracks opens and clicks itself, whichever provider you plug in. The data lands in your own Postgres.
Durable execution
Journeys run as Hatchet durable tasks — a seven-day wait survives deploys, restarts, and crashes.
Durable execution, by Hatchet
Every journey runs on Hatchet, the durable execution engine underneath Hogsend. It's what lets a long ctx.sleep survive a deploy and resume two days later exactly where it left off, with retries and timeouts handled for you. Hogsend builds on Hatchet rather than rolling its own durability.
Survives deploys & restarts
A long ctx.sleep keeps running across a deploy and resumes days later, exactly where it left off.
Automatic retries & timeouts
Failed steps retry and waits expire on their own — durability you don't have to hand-roll.
Self-host it, or use Hatchet Cloud
Run Hatchet-Lite next to your app, or point at Hatchet Cloud. Same engine either way.
And nobody meters your contacts
Rent models are fine prices for software they host. Hogsend takes a different view: lifecycle email is a feature of your product, and features belong in your repo.
*List prices at the time of writing — all pricing last checked June 2026.
Hogsend is free to self-host — run it commercially, deploy it for clients. It's your Postgres, your event log, your templates, your provider account — pg_dump is the exit interview. And if you'd rather have it installed for you, that's a week of work, done properly.
See for yourself
The engine on npm, the source on GitHub, a one-click deploy, and the engineer who built it — all a click away.
"Over 15+ years of client work, every engagement hit the same wall: PostHog, Resend, and a folder of webhook handlers pretending to be a lifecycle email system. I rebuilt it enough times to know exactly what it should be — so I built it once, properly, and versioned it."
Questions, answered
Still curious? The docs go deeper — including a side-by-side with PostHog Workflows.
Hogsend is source-available under the Elastic License 2.0 (ELv2), not an OSI-approved open-source license. You can read, modify, and self-host all of it for free; the only restriction is offering Hogsend itself as a managed service. All 11 packages are on npm and the full source is on GitHub.
One loop. Your repo.
Tonight.
One scaffold command sets up the app, Docker, env, and ten production journeys. Or deploy the Railway template in a click.
pnpm dlx create-hogsend@latest my-appFree to self-host · PostHog + your provider · no contact tax
Get the changelog
Ships when something ships. No drip nonsense.

