Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

576 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Hogsend

Hogsend

CI Discord

Your customer lifecycle belongs in your repo.

Hogsend is lifecycle automation in TypeScript for product-led teams. Written by you—or your coding agent. Reviewed in a PR. Shipped like the rest of your product.

Onboarding. Trial conversion. Payment recovery. Retention. Win-back. Across email, in-app, SMS, Discord, and more.

Your product already emits the signals. Hogsend turns them into durable journeys that wait, branch, send, and stop based on what each customer does—or does not do. Journeys and real-time segments are plain TypeScript functions, not YAML configs or drag-and-drop canvases.

Send first-party events directly with @hogsend/js or @hogsend/client, or bring Stripe, PostHog, Segment, and any webhook source. Fan the results back to analytics, Slack, a CRM, or your warehouse. PostHog works beautifully with Hogsend, but it is optional.

Built for technical founders and product engineers who want to ship the customer lifecycle as fast as they ship the product.

Live demo | Documentation | Getting Started | Integrations | Recipes | CLI Reference | Compare

Everything ships on npm: scaffold an app with pnpm create hogsend@latest --domain mysite.com, self-host with Docker, or one-click on Railway.

Live demodemo.hogsend.com is a fully-seeded Studio you can click around; it runs "Forgeline", a fictional credit-based dev-tool SaaS. Use the one-click Enter the demo button, or sign in with demo@hogsend.com / forgeline-demo-2026.

Quickstart

pnpm create hogsend@latest my-app --domain mysite.com
cd my-app && pnpm hogsend dev

One command boots the lot — infra, migrations, API, worker, and Studio at localhost:3002/studio. Sends are test-mode-safe from the first minute: every email redirects to your own inbox until hogsend domain check verifies your DNS (records printed for your DNS host, auto-applied on Cloudflare/Vercel when a token is set).

Full guide: Getting Started | hogsend dev | hogsend domain | Test mode

A note from Doug — I built Hogsend to do more for my clients, faster. I kept rebuilding the same PostHog + Resend lifecycle plumbing for every team, so I built it properly once and opened it up for everyone. If you'd like a hand getting it running — PostHog setup, journeys, templates, deploy — I can have you live in days. It's source-available and yours to run solo; the offer to help is there if you want it.

About Hogsend & how to get in touchDoug Silkstone


How It Works

Events arrive from your product, Stripe, PostHog, or any webhook. Durable journeys react across your providers, then engagement and lifecycle outcomes flow back onto the same contact and event stream.

Example Hogsend and PostHog lifecycle flow

PostHog is a first-class optional integration: use the events and identities you already capture, then send opens, clicks, answers, and lifecycle outcomes back to it. Without PostHog, Hogsend’s own SDKs and contact model run the same lifecycle loop.

Deep dive: How It Works | Why PostHog? | Why Hatchet? | Philosophy


What You Can Build

  • 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
  • Real-time segments — group users the instant their behavior matches (power users, trials expiring, gone dormant), then trigger a journey off the membership change itself

Each is a single TypeScript file using defineJourney() — or defineBucket() for segments. The repo ships with 10 production-ready journeys and 3 example buckets covering common lifecycle stages.

Buckets — real-time segments

Buckets are the peer of journeys: named, code-defined groups a user joins the moment their data matches and leaves when it stops. Each join and leave fires an event through the same pipeline, so a membership change can trigger a journey — bind one to bucketEntered("went-dormant") and it runs the instant someone goes dormant. You write a defineBucket() with criteria (the same condition engine, or a fluent builder) — no run, just the predicate. Membership recomputes in real time off your own event stream — the sub-hour, code-first complement to PostHog's ~24h batch cohorts — with a reconcile pass for time-based leaves and a maxDwell TTL.

import { days, defineBucket } from "@hogsend/engine";

export const wentDormant = defineBucket({
  meta: {
    id: "went-dormant",
    name: "Went dormant",
    enabled: true,
    timeBased: true,
    criteria: (b) =>
      b.all(
        b.event("app.active").exists(), // was active at some point
        b.event("app.active").within(days(7)).notExists(), // but not lately
      ),
  },
});

Full guide: Buckets


Journey Blueprints

Agents can prototype journeys before they become code. A Journey Blueprint is a lifecycle journey authored as a JSON graph, stored in the database (journey_blueprints), and run by the engine's generic interpreter — it goes live the moment you enable it, no deploy. Blueprints run through the same enrollment guards, durable execution, and terminal states as code journeys; they provide a fast staging path for an agent or the admin API.

Author them with the MCP server (@hogsend/mcp, the manage_blueprint tool) or the /v1/admin/blueprints API; Studio observes them and toggles enable/disable (it doesn't author). When one has proven itself, hogsend blueprints promote turns it into a real defineJourney() file and freezes the blueprint. The committed code becomes the source of truth.

Full guide: Journey Blueprints | MCP server


Sources & destinations

Hogsend sits between whatever emits events and wherever you want them to land. Journeys and buckets are the reactive middle.

In — events arrive from:

  • Your own app via @hogsend/js, the @hogsend/client SDK, or a raw POST /v1/events
  • Signed presets for Stripe, Clerk, Supabase, and Segment — auto-enabled the moment you set that preset's webhook secret
  • PostHog webhooks, when PostHog is part of your stack
  • Any custom source you write with defineWebhookSource()

Out — engagement and lifecycle events fan to:

  • PostHog, Segment, Slack, a CRM, a warehouse, or any signed webhook via defineDestination()
  • The outbound catalog is 13 events — contact.*, email.* (including email.complained), journey.completed, and bucket.*

Web, email, server, and Discord activity for one human resolve to one Hogsend contact. When PostHog is configured, Hogsend also stitches those identities forward onto the same PostHog person.

The public data-plane API (POST /v1/events, contacts, lists, transactional email) is the front door for all of it — drive it from the typed @hogsend/client SDK or plain HTTP.

Full guides: Integrations | Recipes


Example Emails

Hogsend dogfoods itself. The example templates in apps/api/src/emails/ are real lifecycle emails about Hogsend — built with React Email + Tailwind, sent through journeys defined in code. They're yours to edit, rebrand, or delete. Here's the set as it ships:

Setup guide
activation-quickstart
Setup guide email — get your first journey live
No events yet
activation-nudge
Activation nudge email — we haven't seen any events yet
Journeys as code
activation-feature-highlight
Feature highlight email — journeys are just TypeScript
What others build
activation-community
Community email — see what other teams ship
Usage milestone
conversion-usage-milestone
Usage milestone email — 100 emails sent
Trial ending
conversion-trial-expiring
Trial expiring email — your trial ends in 3 days
Win-back offer
conversion-winback-offer
Win-back offer email — 20% off
Milestone unlocked
retention-achievement
Achievement email — 10,000 emails delivered
Weekly digest
retention-weekly-digest
Weekly digest email — your Hogsend week
Dormancy check-in
reactivation-checkin
Reactivation check-in email — your project's gone quiet
Final nudge
reactivation-final-nudge
Final nudge email — we'll leave it here
NPS survey
feedback-nps-survey
NPS survey email — how are we doing
Payment failed
churn-payment-failed
Payment failed email — we couldn't process your payment

Quick Example

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, defineJourney, sendEmail } from "@hogsend/engine";
import { Events, Templates } from "./constants/index.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. Need to wait on behavior instead of the clock? ctx.waitForEvent({ event: Events.FEATURE_USED, timeout: days(7) }) parks the journey until that user fires the event (or the timeout wins), then resumes — and an exitOn match cancels the wait mid-flight.

Full guide: Journeys | Buckets | Events | Email | Conditions


Get Started

Scaffold a fresh app with create-hogsend. It generates a thin app that pins @hogsend/engine and holds your content — journeys, email templates, webhook sources — then installs everything from npm:

pnpm create hogsend@latest my-app --domain mysite.com
cd my-app

pnpm bootstrap              # Docker, .env, Hatchet token, data-plane key, migrations
pnpm hogsend dev            # API + worker + Studio, one terminal (Ctrl+C stops it all)

--domain wires EMAIL_FROM + EMAIL_DOMAIN into the scaffold, and the create flow offers to run bootstrap for you. Prefer it by hand? pnpm dev + pnpm worker:dev in two terminals is the manual equivalent of hogsend dev's run phase. Fire a user.created event (pnpm hogsend dev --fire user.created --email you@example.com) and watch the journey run. Upgrade the framework with pnpm up "@hogsend/*" — never a fork or a merge.

Full guide: Installation | Configuration | PostHog Setup

The Hatchet token

Journeys are durable, which is what lets ctx.sleep(days(2)) pause for two days and survive deploys. That durability is backed by Hatchet, so you need a HATCHET_CLIENT_TOKEN. The local docker compose runs hatchet-lite for you — mint a token from its dashboard at localhost:8888, or use Hatchet Cloud if you'd rather not run it yourself.

Full guide: Hatchet setup

Deploy

Same app, deployed your way. One-click on Railway, or self-host the full stack anywhere that runs Node.js + Postgres:

Deploy on Railway

Full guide: Deploy on Railway | Deploy with Docker


CLI

@hogsend/cli is the agent-native companion — install it with pnpm add -g @hogsend/cli, or run any command through pnpm dlx @hogsend/cli. It talks to a running instance's admin API; pass --json for machine-readable output.

hogsend dev         # Run the full local stack: infra, API + worker, health, URLs
hogsend domain      # Set up + verify the sending domain (DNS records, auto-apply)
hogsend doctor      # Probe a running instance's health
hogsend journeys    # List, inspect, enable, and disable journeys
hogsend contacts    # List, inspect, and trace contact activity
hogsend stats       # System-wide overview metrics
hogsend events      # Stream a single user's event history
hogsend setup       # Local onboarding — docker compose up, gen secret, db:migrate
hogsend skills      # Install bundled Claude Code skills into .claude/skills
hogsend eject       # Vendor a @hogsend/* package into vendor/<name>
hogsend patch       # Patch a package via pnpm's native patch flow

Full reference: CLI Reference


Studio

Hogsend ships with Studio — a read-and-operate admin UI for your instance. It's built to observe, not author: journeys and templates stay code-first, while the Studio shows you what's happening and gives you a few targeted actions (resend a failed send, enable/disable a journey, un-suppress a contact, send a test, manage API keys). It's a static SPA over the same /v1/admin/* API the CLI drives, so everything you can see is also scriptable.

Sends
Every email — filter, sort, and drill into the delivery + engagement timeline
Hogsend Studio — email send log with delivery and engagement status
Journeys
Enrollment and completion rates per journey, with an enable/disable toggle
Hogsend Studio — per-journey completion rates and funnels
Templates
The template catalog with a live preview, per-template stats, and send-test
Hogsend Studio — template catalog with live preview and stats
Overview
Delivery and engagement metrics for the whole instance at a glance
Hogsend Studio — overview metrics dashboard

The engine serves the built Studio at /studio on your API — same origin, so it uses your session cookie and needs no extra config (in the dogfood monorepo, build it once with pnpm --filter @hogsend/studio build). Or drive any instance from your machine with the CLI:

hogsend studio --open                                   # serve locally, open browser
hogsend studio --base-url https://api.example.com --open

Full guide: Studio | hogsend studio

Desktop app

Hogsend Desktop is a native macOS menubar companion. It polls your instance's /v1/health for a live status glyph (🟢/🟡/🔴), raises native notifications on send/journey failures or a worker going offline, and opens the real Studio in a native window — it embeds Studio, it's not a fork. Add one or more instances by URL and switch between them; optionally store credentials in the macOS Keychain for auto-login, and let the built-in updater keep it current.

Download the universal (Apple Silicon + Intel) .dmg from GitHub Releases: Hogsend.dmg. It isn't notarized yet, so on first launch clear the quarantine flag once with xattr -dr com.apple.quarantine /Applications/Hogsend.app (or right-click → Open); after that, auto-updates are seamless.

Full guide: Desktop app | All releases


Reporting

Every send is recorded — template, recipient, journey, and the full engagement trail (delivered, opened, clicked, bounced, complained) — and it's all queryable over the admin API. No external analytics, no ETL; the numbers are SQL aggregates computed on demand.

# What was sent to one user, oldest first
curl -H "Authorization: Bearer $ADMIN_API_KEY" \
  "$API/v1/admin/emails?userId=user_abc123&sort=createdAt&order=asc"

# Opened emails from a specific journey
curl -H "Authorization: Bearer $ADMIN_API_KEY" \
  "$API/v1/admin/emails?journeyId=activation-welcome&engagement=opened"

# One send's full delivery timeline (queued → sent → delivered → opened → clicked)
curl -H "Authorization: Bearer $ADMIN_API_KEY" "$API/v1/admin/emails/$ID"

# Per-template performance over a window
curl -H "Authorization: Bearer $ADMIN_API_KEY" \
  "$API/v1/admin/metrics/emails?from=2026-05-01T00:00:00Z"
  • GET /v1/admin/emails — filter by templateKey, category, status, journeyId, userId, engagement (opened/clicked/bounced/complained), and a date window; sort by any lifecycle timestamp. Each row resolves who it went to and from which journey.
  • GET /v1/admin/emails/{id} — a single send with a chronological events[] timeline and every tracked-link click (URL, IP, user agent).
  • GET /v1/admin/metrics/emails — per-template sent/delivered/opened/clicked/bounced with delivery, open, click, and click-to-delivery rates over an optional window.

Full guide: Email Operations | Metrics & Analytics | API Reference


Stack

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 by default (@hogsend/plugin-resend) — swappable: Postmark via @hogsend/plugin-postmark, or any provider implementing the contract
Product analytics PostHog (@hogsend/plugin-posthog)
Email templates React Email
CLI TypeScript on Node (@hogsend/cli)
MCP server @hogsend/mcp — stdio (npx @hogsend/mcp) + hosted POST /v1/mcp, for Claude Desktop / Cursor / claude.ai
Deploy Railway (one-click), Docker Compose, or bring-your-own

Plugins are standalone packages — create your own for Slack, Twilio, or any service. See Creating Plugins.

The email provider is the one swappable wire. Resend is the default; Postmark ships as an opt-in (pnpm add @hogsend/plugin-postmark@latest, then set EMAIL_PROVIDER=postmark), and SES-style providers slot in behind the same EmailProvider contract. Tracking, preferences, and delivery records stay engine-owned, so first-party open/click tracking works no matter which provider you wire in.


Explore the Docs

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, buckets, 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

Contributing

See CONTRIBUTING.md for setup, code style, and how to submit changes.

License

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.