Hogsend
New courseMeasure → Keep → Grow with PostHog

Superpowers for growth engineers.

Hogsend is a TypeScript framework for growth teams that code. Identify and enrich the people on your product, react to their events, message them where they are, and measure what moved.

$ pnpm dlx create-hogsend@latest
Deployed
CLI
Add a win-back journey: trigger when someone enters the went-dormant bucket, check in, wait 7 days, then send the offer. Exit the moment they come back.

Event plugins

  • Stripe
  • Clerk
  • Supabase
  • Segment
  • Intercom & Fin
  • Vapi
  • Twilio
  • Discord
  • Telegram
  • PostHog
  • Resend
  • Crisp
  • Postmark
  • HubSpot
  • Attio
  • HighLevel
  • Meta CAPI
  • Slack
  • Stripe
  • Clerk
  • Supabase
  • Segment
  • Intercom & Fin
  • Vapi
  • Twilio
  • Discord
  • Telegram
  • PostHog
  • Resend
  • Crisp
  • Postmark
  • HubSpot
  • Attio
  • HighLevel
  • Meta CAPI
  • Slack

Code-first

Journeys are TypeScript in your repo. Version, review and ship them like the rest of your product.

Full-funnel

Onboarding, activation, retention, monetization — one engine across every stage.

Event-powered

React to any event from PostHog, your product, your warehouse or any webhook source.

Measure impact

Built-in experiments, holdouts and lift measurement on every journey.

Signal → response

Whenlifecyclelivesinyourcodebase,everyleakisfixable.

Hogsend turns each leak into a response your product can ship. Built for code-first teams that need a best-in-class growth engine.

my-app — hogsend/ written by create-hogsend · web/ is your product
hogsend/src/journeys/product/onboarding.ts
import { days } from "@hogsend/core";
import {
  defineJourney,
  sendConnectorAction,
  sendEmail,
} from "@hogsend/engine/journeys";

// The first week as control flow: welcome now, then branch
// on whether they actually created a project.
export const onboarding = defineJourney({
  meta: {
    id: "onboarding",
    trigger: { event: "user.signed_up" },
    entryLimit: "once",
  },
  run: async (user, ctx) => {
    await sendEmail({ to: user.email, template: "welcome" });

    // Park durably until THIS user creates a project — or 3 days pass.
    const { timedOut } = await ctx.waitForEvent({
      event: "project.created",
      timeout: days(3),
    });

    if (timedOut) {
      // Stalled — nudge them, and tag the team where it works.
      await sendEmail({ to: user.email, template: "activation-nudge" });
      await sendConnectorAction({
        connectorId: "discord",
        action: "sendChannelMessage",
        args: {
          channelId: process.env.GROWTH_CHANNEL_ID,
          content: `${user.email} stalled before their first project.`,
        },
      });
      return;
    }

    await sendEmail({ to: user.email, template: "first-win" });
  },
});
about

Send, wait, branch on what they did

waitForEvent parks the journey durably until THIS user creates a project — or three days pass. The branch afterwards is an if statement, and the stalled path tags the growth team on Discord.

Durable waitEvent or timeoutBranch = if

Using Hogsend means that lifecycle marketing becomes part of the product, not a campaign bolted on beside it.

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.

Durable execution

Journeys run as Hatchet durable tasks. A seven-day wait survives deploys, restarts, and crashes.

Source-availablenpmjs.com/@hogsend →
How it works

Measure everything. React in TypeScript. Prove what moved revenue. Steer it all from Studio. Review and ship marketing like the rest of your product, watched live as every send, wait, and branch runs.

Agent-native

Put your growth machine on autopilot.

Your coding agent works where your lifecycle does: inside your codebase. Ask for an outcome and it can write, modify, and validate the journey without another drag-and-drop builder.

Plain TypeScript surface

Journeys are defineJourney() files. Claude Code, Cursor, or any agent writes and modifies them like any other code.

export const onboarding = defineJourney({

trigger: { event: "user.signed_up" },

run: async (user, ctx) => {},

});

Validated by your type-checker

No drag-and-drop canvas to drift out of date — the compiler rejects a journey that references a template that doesn't exist.

# pre-push

$ pnpm check-types

✓ journeys/welcome.ts

✓ journeys/winback.ts — 0 errors

A CLI agents can drive

hogsend skills plus --json on every command give agents a first-class interface to inspect and operate the running system.

$ hogsend journeys --json

[ { "id": "onboarding", "status": "registered" }]

Or authored at runtime

Journey Blueprints

Agents author journeys as JSON — stored in your database, enrolling users without a deploy. Review and enable them in Studio. When one earns permanence, hogsend blueprints promote turns it into a code journey in your repo.

MCP server

npx @hogsend/mcp connects Claude Desktop, Cursor, or claude.ai to your running instance. Create blueprints, run health reports, send test emails — your admin key, your infrastructure.

LLMs write and modify journeys like any other code in your repo

Claude CodeCursorCopilotOr any agent you prefer
Get set up

Start with one command. pnpm dlx create-hogsend@latest . Everything below ships with it, in v0.59.0.

pnpm dlx create-hogsend@latest .

Run the scaffolder yourself: pnpm dlx create-hogsend@latest .

Hogsend Video

Every video becomes an event stream. This player is @hogsend/video. Press play — the feed beside it is the events it captures.

Elena Verna, Head of Growth at Lovable, on Lenny's Podcast. Played through our own @hogsend/video; press play and the real watch-depth events replace the loop. All our React components →

@hogsend/videostandby

Watch-depth events from the player, streaming live.

Depth that survives scrubbing

percentWatched is the deepest point reached — monotonic, so skipping back and forth never inflates it.

Milestones fire once

video.progress lands a single event at 25, 50, 75, and 90 percent, alongside started, completed, and replay.

One event shape, any player

YouTube, Vimeo, and native <video> emit the same events, so journeys never care where the video lives.

Emitters, not lock-in

createHogsendEmitter feeds your journeys; the same interface writes to PostHog or GA4, or all of them combined.

Watch depth is a journey trigger like any other event — the retargeting play in the playbook runs on it.

Read the video docs →
Feature flags

Customize the whole journey with feature flags. Flip any part of your marketing on or off, anytime.

Native, DB-backed flags, evaluated against your Hogsend contacts by the same targeting engine that runs your journeys. One flag can swap a headline, a video, an email, or a whole branch of a journey. Flip the arms below and the page reacts, running on the real API on the right.

Lifecycle without the hire

Onboarding, trials, and win-back ship as code your agents extend, with no lifecycle team to staff.

visitor-teammultivariate

One arm per visitor. Your targeting rules choose it in production; flip the arms here to preview.

import { useFlag } from "@hogsend/react";

export function Hero() {
  // One flag decides who's reading. Sticky per visitor, no redeploy.
  const team = useFlag("visitor-team");

  return <TeamHero team={team} />; // swaps the video + the pitch
}

evaluated for you

visitor-team"founder"

One definition, read everywhere. The same evaluation runs in the browser, on the server, and inside a journey. Already run PostHog flags? Keep them and read them right beside these.

Read the flags docs →
Contact groups

Bill, Derek, and Bob all work at Acme. Hogsend can treat them as one account.

Contact groups roll individual activity up to the company, team, or workspace it belongs to. One call associates a contact with a group; every event they fire carries the association, membership accrues automatically, and the account reads back as one entity in Studio.

Acme Inc

pro
Company · acme.com · ID: grp_8f4a72e9
Members
350%

vs last 30 days

MRR
$4.2k8.3%

vs last 30 days

Customer since
Mar '25

32 days

Health score
92

Excellent

6 pts

Usage & Health

Seats used42 / 50
Feature adoption78%
Account health92%
Team engagement68%

Activity (events)

Last 30 days
1.5k1k5000
1,240
Apr 19Apr 26May 3May 10May 17

Properties

Planpro
Seats42
IndustryDeveloper tools
Regionus-east-1

Tags

productionhigh-engagementbeta-access

Members (3)

MemberEmailEvents (30d)Last seenRole
Bill Chen
bill@acme.com1,24018%2h agoadmin
Derek Vaughn
derek@acme.com4306%1d agomember
Bob Portis
bob@acme.com2104%3d agomember
View all members
acme.comcompany

One (type, key) is the account. The browser associates events; the server writes properties and members.

// server — the secret key writes properties
await hogsend.groups.identify({
  groupType: "company",
  groupKey: "acme.com",
  displayName: "Acme Inc",
  properties: { plan: "pro", seats: 42 },
});

await hogsend.groups.addMember({
  groupType: "company",
  groupKey: "acme.com",
  contactId: bill.id,
  role: "admin",
});

secret — writes properties

Group properties and memberships are secret-key writes, enforced at the route.

Journeys are person-scoped today — group-level journeys are a later phase, not a fine-print surprise.

Read the 0.50 write-up →
In-email answers

The email answers back. Send surveys, questions and buttons in your email.

Every link in every email is rewritten to your own domain at send time, so opens and clicks are tracked first-party — no provider pixels. An EmailAction goes further: the click emits a named event with properties, and a waiting journey reads the answer directly.

to: founder@acme.dev

subject: Quick check-in on your trial

Hey — you're a week into the trial. One click tells us where you are, straight from this email:

Each button is an EmailAction — a link whose click emits trial.check_in with its answer. First click wins.

trial-check-inwaiting

Sent through the tracked mailer: every link rewritten to your domain, opens and clicks first-party.

  • email.senttrial-check-in
  • links rewritten/v1/t/c/9f2c…
  • open pixel/v1/t/o/58d1…
  • ctx.waitForEventtrial.check_in · 3d

the journey hears

ctx.waitForEventwaiting for founder@acme.dev…
Community channel

Your Discord is a lifecycle channel, too. Connect a Discord account to a user on your app.

The bot verifies through their inbox — a one-click emailed confirm, never the Discord-reported address. From then on messages and reactions keep a last-seen on the contact, and a journey can send a DM the same way it sends an email, gated on the member’s channel preference.

generalyour server

jamie11:12 AM

/link

Message #general
GIF
discord-connectlive flow

One slash command folds a Discord account onto the contact — verified through their inbox, never the Discord-reported email.

contact

emailjamie@northwind.io

discord_id

isDiscordLinked

last_seen

on the wire

POST /interactionsed25519 verified

Built for community-led companies — the channel your users actually read, wired into the same journeys as email and SMS.

Read the Discord docs →
Impact experiments

Every journey change is an experiment. So prove it moved the number.

Ship two versions of a journey to a randomised split, hold back a control, and Hogsend measures the goal event against it. You get the incrementality — the welcome series raised activation, and by how much — not a guess.

activation-welcomelive test

Every version ran against a holdout. Select one to read the lift it measured on the goal event.

primary goal

activation

significant

+22%

activation lift vs holdout

96%

confidence

8,412

enrolled users

14 days

experiment window

readout · v3

activation.completed"+22% vs holdout"

Illustrative readout — the report Hogsend generates from your own journeys and goals.

  1. Version the journey

    Every edit is a version. Run the new one against the last — no separate tool.

  2. Split with a holdout

    A randomised share gets the change; a control gets nothing to measure against.

  3. Read the lift

    The goal event, measured against the control, with a confidence you can act on.

Know which edit moved the number

You reworked the welcome series three weeks ago — did it work? Every enrollment carries a fingerprint of the journey code that created it, so the readout splits before-vs-after on its own. No tagging, no spreadsheet archaeology.

meta.version

Split-test without an experiment platform

One call inside the journey assigns each user an arm — sticky across retries and redeploys, no assignment service to run. The readout reports every arm against the same control.

ctx.variant

Proof, not attribution flattery

Opens and clicks can't tell you what would have happened anyway. Hold back 10% as a control and the lift is measured against people who got nothing — the only number allowed to say "caused".

meta.holdout

It won't let you fool yourself

A +40% on twelve users is noise. Under 10 conversions the verdict stays "collecting" — never a percentage; small cohorts ship flagged. You act when the number can carry the decision.

smallSample

Attributed, influenced, and incremental stay separate numbers — only the holdout-backed one may say "caused". Already live? hogsend attribution backfill credits your whole history.

Read the impact docs →
Event plugins

People do more than click emails. Payments, sign-ups, support threads, SMS replies — and more to come.

There’s one plugin system behind all of these — webhook presets in the engine, @hogsend/plugin-* packages beside it, built in the open in the same repo. Signals land as first-class events that trigger, branch, or exit a journey; the CRM and ads legs carry conversions back out.

  • Stripe
  • Clerk
  • Supabase
  • Segment
  • Intercom & Fin
  • Vapisoon
  • Stripe
  • Clerk
  • Supabase
  • Segment
  • Intercom & Fin
  • Vapisoon
  • Twilio
  • Discord
  • Telegram
  • PostHog
  • Resend
  • Crispsoon
  • Twilio
  • Discord
  • Telegram
  • PostHog
  • Resend
  • Crispsoon
  • Postmark
  • HubSpot
  • Attio
  • HighLevel
  • Meta CAPI
  • Slacksoon
  • Postmark
  • HubSpot
  • Attio
  • HighLevel
  • Meta CAPI
  • Slacksoon

Every preset is defineWebhookSource() under the hood — a source we don’t ship is a transform function away.

See all integrations →
Try it live

We use Hogsend to power hogsend.com.

Sign up below. Our own Hogsend install sends the welcome email and powers the feed, bell, survey, and branching that follows.

hogsend.com — live demo
live
Loading your session…

This isn’t a mock — it’s our own install, a stock create-hogsend app in production. The welcome email arrives from hello@hogsend.com in seconds; the feed, bell, and survey card are real @hogsend/react components. See the full set →

The elephant in the room

Why not just use PostHog Workflows? Sometimes you should.

Workflows is genuinely good. It lives in the PostHog UI your team already has open, consumes the events you already capture, and needs nothing deployed. And it isn't either/or — both run off the same PostHog events. Hogsend amplifies everything PostHog does.

What Workflows does well

Zero extra infrastructure. A tab in the PostHog UI your team already has open, consuming the events you already capture — nothing to deploy, nothing to operate.

Multi-channel out of the box. Email, SMS, push, Slack, and webhooks without writing a line.

Non-engineers can ship. The no-code canvas means whoever owns lifecycle can see and edit the flow — no developer in the loop.

A genuinely generous free tier. Light volume fits inside it; after that it's per-send pricing on top of your PostHog bill.

Why you'd reach for Hogsend

The flow is code you own. Versioned completely — git log knows who changed the discount email and why. PR review, tests, and rollback apply like everywhere else.

Your domain, your reputation. Sends go through your own Resend or Postmark account, not a managed sender.

Past the canvas ceiling. Park until this user acts, send in their timezone, trigger journeys from journeys, cap re-entries — control flow boxes and arrows can't express.

Integrate any service. A journey is an async TypeScript function — call any API you can import, or an LLM mid-run, and branch on the answer.

They scratch different itches. If you want a drag-and-drop editor, Workflows has you covered — you're here because you'd rather build marketing in, as a developer or a product-first engineering team. If you're a PostHog team, or about to become one, use each where it fits.

Read the full comparison
Powered by Hatchet

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. 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.

Economics

What it costs. Contact count appears in neither bill.

There is no paid tier. You pay for hosting — the Railway template provisions Postgres, Redis, Hatchet, the API, and the worker — and for your own Resend or Postmark account.

HogsendSelf-hosted · $0 software
Software$0, ELv2
Hostingyour infrastructure
Sendingyour Resend / Postmark
Per contactnothing
LoopsSubscribed contacts
5,000 contacts$49/mo*
50,000 contacts$249/mo*
Sendingtheir infrastructure
Grows withyour list
Customer.ioProfiles + emails + credits
Billingper profile + volume
At scalecustom pricing
Sendingtheir infrastructure
Grows withyour list
PostHog WorkflowsPer message sent
Free tier10k msgs/mo*
After that$0.003/send*
Sendingmanaged sender
Grows withsend volume

*Published pricing, checked July 2026.

FAQ

Find what you need.

Is Hogsend open source?

Hogsend is source-available under the Elastic License 2.0 (ELv2). You can read, modify, and self-host all of it for free; the only restriction is offering Hogsend itself as a managed service.

What does Hogsend cost?

The software is free — there is no paid tier. You self-host and pay only your own infrastructure plus your Resend or Postmark account. No per-contact, per-profile, or per-send fees.

How is Hogsend different from PostHog Workflows?

Workflows is PostHog's built-in no-code canvas with a managed sender. Hogsend is typed TypeScript in your repo with durable waits, behavioral branching, and your own email provider — for when lifecycle logic outgrows boxes and arrows.

Does Hogsend replace Resend or use it?

It uses it. Hogsend is the orchestration layer — journeys, segments, suppression, tracking — and sends through your own Resend account by default, with Postmark as a one-env-var swap. Resend's own Automations cover simple dashboard-built sequences; Hogsend is for when the logic belongs in your repo — event-triggered, type-checked, and portable across providers.

Do I need PostHog to use Hogsend?

No. Send first-party events directly with @hogsend/js or the Data API, use signed webhook presets for Stripe, Clerk, Supabase, or Segment, or define any custom source. PostHog is a first-class optional integration.

Will my emails survive a deploy mid-journey?

Yes. Journeys run as Hatchet durable tasks: a user three days into a seven-day wait keeps waiting through deploys, restarts, and crashes, and resumes exactly where they were.

Can AI agents write Hogsend journeys?

Yes — journeys are plain TypeScript files, so coding agents can read your product events and types, write or modify a journey, and leave you a reviewable diff. Your type-checker validates it before it ships.

Get started

First send in minutes.

One prompt scaffolds the app, Docker, env, and ten journeys — the welcome series included. Your agent reads the docs and does the rest.

your agent, one prompt

Visit hogsend.com/docs and implement lifecycle marketing for our product — start with the welcome series.

Paste into your coding agent