The lifecycle email automation that PostHog teams actually need. Code-first, self-hosted, open source.
PostHog tells you what users do. Resend delivers your emails. Hogsend is the bit in the middle — it listens for events, decides who gets what, waits, checks conditions, and sends. Journeys are TypeScript functions, not YAML configs or drag-and-drop canvases.
Built for small teams (1-10 eng) shipping product-led SaaS who picked PostHog and Resend and now need behavioral sequences without buying a third platform.
Documentation | Getting Started | CLI Reference | Compare
Events flow in from PostHog, journeys react with emails via Resend, engagement data flows back. A closed loop — product analytics and lifecycle email in the same event stream.
Deep dive: How It Works | Why PostHog? | Why Hatchet? | Philosophy
- Welcome sequences that branch based on whether the user actually used the product
- Trial-to-paid conversion that watches for usage milestones and sends different emails depending on engagement
- Payment failure recovery — escalating reminders that stop the moment the payment goes through
- Dormancy reactivation — detect inactive users, run a win-back series, track if they come back
- NPS / feedback collection timed after key moments
- Abandoned checkout recovery — start a sequence when checkout begins, exit when it completes
- Cross-journey orchestration — one journey enrolls a user in another, chaining sequences without duplicating logic
Each is a single TypeScript file using defineJourney(). The repo ships with 10 production-ready journeys covering common lifecycle stages.
A user_signed_up event triggers this journey. It sends a welcome email, waits two days, checks if the user tried the core feature, and nudges them if not:
import { days } from "@hogsend/core";
import { sendEmail } from "../lib/email.js";
import { Events, Templates } from "./constants/index.js";
import { defineJourney } from "./define-journey.js";
export const activationWelcome = defineJourney({
meta: {
id: "activation-welcome",
name: "Activation — Welcome Series",
enabled: true,
trigger: { event: Events.USER_CREATED },
entryLimit: "once",
exitOn: [{ event: Events.USER_DELETED }],
},
run: async (user, ctx) => {
await sendEmail({
to: user.email,
userId: user.id,
journeyName: user.journeyName,
template: Templates.ACTIVATION_WELCOME,
subject: "Welcome — let's get you set up",
});
await ctx.sleep({ duration: days(2), label: "post-welcome" });
const { found } = await ctx.history.hasEvent({
userId: user.id,
event: Events.FEATURE_USED,
});
if (!found) {
await sendEmail({
to: user.email,
userId: user.id,
journeyName: user.journeyName,
template: Templates.ACTIVATION_NUDGE,
subject: "You haven't tried the key feature yet",
});
}
},
});That ctx.sleep(days(2)) literally pauses for two days and picks up exactly where it left off — durable execution via Hatchet that survives deploys and restarts.
Full guide: Journeys | Events | Email | Conditions
# 1. Click the deploy button above — fills in Resend key + Hatchet token
# 2. Install the CLI and configure everything else
curl -L https://github.com/dougwithseismic/hogsend/releases/latest/download/hogsend_darwin_arm64.tar.gz | tar xz
sudo mv hogsend /usr/local/bin/
# 3. Connect Railway + PostHog + verify the pipeline
hogsend init
hogsend testThe CLI discovers your Railway project, generates secrets, creates a PostHog webhook destination, and fires a test event — zero dashboard visits.
git clone https://github.com/dougwithseismic/hogsend.git && cd hogsend
pnpm setup # Docker, deps, .env
pnpm dev # API on :3002
# separate terminal:
cd apps/api && hatchet worker devFull guide: Installation | Configuration | PostHog Setup
hogsend init # Connect Railway project, configure PostHog webhook, verify pipeline
hogsend setup # Local dev — Docker, deps, .env
hogsend status # Health check
hogsend deploy # Trigger Railway redeploy
hogsend test # Fire test event, verify it arrives
hogsend journeys # Enable/disable journeys
hogsend contacts # Manage contacts (list, create, update, delete, prefs)
hogsend destroy # Tear down Railway projectFull reference: CLI Reference
| Concern | Tool |
|---|---|
| HTTP API | Hono on Node.js |
| Durable execution | Hatchet (sleeps, retries, event routing) |
| Database | TimescaleDB (Postgres 18) via Drizzle ORM |
| Cache | Redis |
| Email delivery | Resend (@hogsend/plugin-resend) |
| Product analytics | PostHog (@hogsend/plugin-posthog) |
| Email templates | React Email |
| CLI | Go (cobra + charmbracelet) |
| Deploy | Railway or Docker Compose |
Plugins are standalone packages — create your own for Slack, Twilio, or any service. See Creating Plugins.
| Section | What's there |
|---|---|
| Getting Started | Installation, PostHog setup, configuration reference |
| Concepts | How it works, why PostHog, why Hatchet, philosophy |
| Compare | Hogsend vs Customer.io, Loops, Brevo, ActiveCampaign — feature matrix and migration |
| Building | Journeys, events, email, conditions, creating plugins |
| CLI Reference | Every command documented with examples |
| Operating | Deployment, auth, monitoring, metrics, bulk ops, troubleshooting |
| API Reference | Every endpoint with request/response examples |
See CONTRIBUTING.md for setup, code style, and how to submit changes.
Elastic License 2.0 (ELv2) — use, modify, and self-host freely. You can't offer it as a managed service or remove license key functionality. See LICENSE for full terms.

