One-off sends to your whole audience
A campaign sends one template to every subscribed member of a list — or every active member of a bucket — at an instant you pick. Commit it as a file or queue it with one API call. Scheduled, cancelable until send, deduplicated per recipient.
One file · Ships on deploy · Cancel until it sends
Not everything is triggered. Journeys cover email that reacts to what a user did — sign-up, trial ending, going quiet. A launch, a changelog, a pricing change reacts to nothing: it goes to everyone on a list, once, at a time you choose. In most stacks that send lives in a dashboard, outside the repo that owns the rest of your email. Campaigns put the one-off send in the same place as your journeys — code.
A committed file, or one API call
Both create the same campaign: the same row, the same durable send.
import { defineCampaign } from "@hogsend/engine";
export const productLaunch = defineCampaign({
id: "product-launch",
audience: { list: "product-updates" },
template: "marketing/product-update",
props: {
headline: "Saved views are here",
ctaUrl: "https://example.com/changelog",
},
sendAt: "2026-07-15T16:00:00Z",
});The worker picks this up on deploy. Edit it while it's still scheduled and the next deploy syncs the change; move sendAt and it reschedules. Once sent, it's retired — redeploys no-op.
await hs.campaigns.send({
list: "product-updates",
template: "marketing/product-update",
props: { headline: "Saved views are here" },
sendAt: "2026-07-15T16:00:00Z",
idempotencyKey: "launch-2026-07",
});The same thing at runtime — from the SDK, hogsend campaigns send in the CLI, or a plain POST to /v1/campaigns. Agents can drive this too.
Scheduled, guarded, deduplicated
Campaign sends go down the same path as journey email: render, preference check, first-party tracking, then the provider wire.
Punctual, with a backstop
sendAt becomes a scheduled run in the workflow engine. A reaper cron sweeps every five minutes and promotes anything due that didn't fire.
No duplicate sends
Every recipient send carries an idempotency key. If a crash or retry re-runs the campaign, already-dispatched emails short-circuit and only the unsent tail goes out.
Preferences enforced twice
Unsubscribed and suppressed contacts are excluded when the audience resolves, and every individual send re-checks before the provider is called.
Never a surprise blast
A committed campaign whose sendAt is already stale on first deploy is marked expired, not sent.
Cancelable until it's done
Cancel a scheduled, queued, or in-flight campaign. Mid-send, delivery stops at the next chunk of 100 — dispatched email can't be recalled; the rest is spared.
Tracked like everything else
Opens and clicks are first-party: rewritten links and the pixel land in your database and PostHog, identical to journey sends, whatever provider is on the wire.
A list or a bucket, resolved at send time
{ list: "product-updates" } reaches every subscribed member — opt-in lists reach explicit subscribers, opt-out lists reach everyone who hasn't left. { bucket: "power-users" } reaches whoever is in the behavioral bucket at the moment the campaign runs. No CSV uploads, no exported segments going stale.
Authored in code, watched in Studio
The Campaigns view shows every campaign's status, audience, scheduled time, and live progress — sent, skipped, failed. One button cancels anything still in flight. Studio can't author campaigns; that stays in code, where it can be reviewed.
Broadcasts map one-to-one
An Audience becomes a list. A Broadcast becomes a campaign. A scheduled broadcast is sendAt, cancelable until it fires. Resend stays on as the send wire — same API key, same domains. Hogsend replaces the orchestration, not the delivery.
Questions, answered
Short answers here; the docs go deeper.
Go deeper
A journey is triggered per user and has control flow — waits, branches, event checks. A campaign has none of that: one template, one audience, one instant. It fires once and is retired.
Send your first campaign
Scaffold an app with create-hogsend — the template ships with two example campaigns, disabled, ready to point at your list.
Free to self-host · One scaffold command · No per-contact billing
pnpm dlx create-hogsend@latest my-app