Loops

loops.socontributed by Samuele Ongaro

ALMOST

A weekend of work, and real gaps remain.

Email for software companies: one place for marketing campaigns, product loops and transactional mail, with events sent from your app driving who receives what.

Promptfree, for everyone, and the only version there is
Build me email for a software product, replacing Loops — and know what makes this ALMOST.

The idea worth copying is **one contact record shared by campaigns, sequences and transactional mail**, so a person is never emailed twice by two systems that do not know about each other. That is a schema decision and it is free. What is not free is deliverability on a warmed sending domain, and the discipline that stops a product mailing somebody four times in an hour.

STACK
- Node 20+ with Fastify, server-rendered HTML
- SQLite through better-sqlite3, WAL mode
- A sending provider through its API
- A worker for sending, separate from the web process
- Caddy in front

THE DATA MODEL, AND THE ONE THING THAT MATTERS
- contacts: id, email, external_id, name, properties_json, subscribed_marketing, unsubscribed_at, bounced_at, complained_at, created_at, last_seen_at
- **One contacts table.** The person who receives a campaign, a lifecycle sequence and a password reset is the same row. That single identity is why this product exists, and every other design here follows from it
- events: id, contact_id, name, properties_json, at — sent from your application, and what drives everything
- campaigns, sequences, transactional_templates — three kinds of message over one contact
- sends: id, contact_id, kind, source_id, message_id, sent_at, opened_at, clicked_at, bounced_at, complained_at — one table for every message, whatever sent it. That is what makes the frequency rules possible
- enrolments: id, sequence_id, contact_id, current_step, next_send_at, status, stop_reason
- suppression: email_hash, reason, at — global and permanent

FREQUENCY, WHICH IS THE FEATURE NOBODY BUILDS
- Because every send is in one table, you can ask 'how many messages has this person had today' before sending. Do it
- A cap per contact per day and per week for anything that is not transactional, configurable, enforced in the send path rather than in each campaign
- Transactional mail always goes: a receipt or a password reset is never suppressed by a marketing cap. That distinction is a column, and it must be right
- A quiet period after a sequence step, so a campaign does not land an hour later
- This costs an afternoon and it is the difference between a product that communicates and one people mute

EVENTS DRIVING MESSAGES
- Your application sends named events with properties. A sequence is triggered by an event, filtered by properties, and stopped by another event
- 'Signed up' starts onboarding; 'created first project' stops it. That stop condition is what makes lifecycle mail feel intelligent rather than mechanical
- Idempotency by event id, so a retried call does not enrol twice
- Every enrolment records why it started and why it stopped

SENDING
- One delivery row per contact per message, written before sending, which makes a resume exact and a duplicate impossible
- The worker claims a batch with a lease and continues after a crash exactly where it stopped
- Rate limited to what the provider accepts
- A test send required before any campaign

DELIVERABILITY
- Separate subdomains for marketing and transactional, each with SPF, DKIM and DMARC. A password reset that fails because a campaign got complaints is a support disaster, and sharing a domain is how that happens
- Warm slowly; watch the complaint rate; suppress hard bounces immediately and complaints permanently
- One-click unsubscribe with no login, plus the List-Unsubscribe headers — and it must unsubscribe from marketing without breaking transactional

THE MESSAGE
- Markdown or a small block editor producing tables and inline styles, with the plain-text alternative generated from the same source
- Personalisation from contact properties with a default for a missing value
- Preview at phone and desktop width, and in the client that renders with the Word engine

OPERATIONS
- .env: DATABASE_PATH, BASE_URL, PROVIDER credentials, FROM_ADDRESS, SESSION_SECRET, HASH_SALT
- Migrations on boot, each once; nightly backup off the machine
- Health endpoint reporting bounce rate, complaint rate and any contact near the frequency cap

WHAT MATTERS MOST
One contacts table, one sends table, and the frequency cap enforced in the send path. That is the whole idea, and it is a schema rather than a feature.

What you lose

  • One contact record shared by campaigns, sequences and transactional mail, so a person is never emailed twice by two systems
  • Deliverability on a managed sending domain, warmed for you
  • A visual sequence builder with branching on events your app sends

If you would rather not build

  • Customer.io, for the same shape as a service

What it costs

as published on their pricing page

PlanBilled monthlyBilled yearlyLast read
—$49/mo——

Their pricing page is where these came from. Seeing a different price? Tell us.

The escape hatch

open source · no votes, no paid placement

Listmonk

$0

Self-hosted campaigns with a subscriber database and an API.

knadh/listmonkfree · open source

React Email

$0

Build email templates as components; renders to Outlook-safe HTML.

resend/react-emailfree · open source

Why this verdict

our own opinion · changed only by a person

54/100

Verdict kinda at 54. The single contact record is the good idea here and is easy to copy; the branching builder and deliverability are the weekend.

History

tracked since 10 Aug 2026 · nothing is ever overwritten

Interest · last 30 dayspeak 2/day
views01230 Aug4 Sept9 Sept14 Sept19 Sept24 Sept28 Sept
— views— prompt copies none yet— votes none yet

Questions about Loops

answered from the record above

Is Loops free?

No — the plan we track is $49 a month. Pro from around $49/month billed monthly for 5,000 contacts, rising with the contact count.

Can you replace Loops by building your own?

ALMOST. A weekend of work, and real gaps remain. Replacement score 54 out of 100, build time a weekend. Read what you lose before you decide.

How much does Loops cost?

$49 a month on Pro — $588 a year. Recorded 9 Aug 2026.

What do you lose by replacing Loops?

One contact record shared by campaigns, sequences and transactional mail, so a person is never emailed twice by two systems; Deliverability on a managed sending domain, warmed for you; A visual sequence builder with branching on events your app sends. If any of those carry weight for you, keep paying.

Is there an open-source alternative to Loops?

Yes: Listmonk, React Email. The prompt on this page is for when you want it your way instead.

Related entries

same category first, most replaced first

All 33 in Email & newsletters

Not sending yet

Every week, something stops being worth paying for.

New verdicts, prices that moved, entries added. One email a week. Unsubscribe in one click. Nothing is being sent yet — your address is kept here, and the first issue is the first thing it is used for.

free forever · no tracking pixel · stored here, never passed to anyone

Esc