# Lifecycle email software: count the audience and protect onboarding

Compare lifecycle email billing and test subscription state and onboarding triggers before switching tools.

Canonical page: https://quillwell.com/guides/lifecycle-email-software/
Updated 2026-10-31
Publisher: Quillwell Guides (https://quillwell.com/guides/about/). Editorial standards: https://quillwell.com/guides/editorial/

## The short answer

- Count the whole audience against each vendor’s definition before comparing prices. Customer.io includes people and objects in [profiles](https://customer.io/pricing).
- Keep subscription state separate from the contact record. Loops excludes unsubscribed contacts from its plan count, but they may still receive [transactional email](https://loops.so/docs/contacts/properties).
- If company relationships drive targeting, start with Userlist: it documents [many-to-many relationships](https://userlist.com/docs/company-accounts/). Test your own data mapping before choosing.
- Pause sending during a migration trial and test whether historical users enter onboarding. Resume only after the expected and actual entries match.

## Count the audience before comparing prices

Build the estimate from the records you expect to store, including dormant users as well as account records. A smaller sending list can still leave a larger billable population.

We compared documentation for Customer.io, Loops and Userlist. Their counting rules answer different questions, so use your own audience rather than comparing plan labels alone.

Audience and migration controls

| Vendor | Billing unit | Limit or timing | Control that matters |
| --- | --- | --- | --- |
| Customer.io | Profiles: people plus objects | Essentials includes 5,000 profiles and 1 million monthly emails | CSV preview and empty-value handling |
| Loops | Plan count excludes unsubscribed contacts | Transactional email can still reach unsubscribed contacts | Subscription property and list preferences |
| Userlist | All stored users | Monthly fee uses prior thirty-day user high-water mark | User and company relationships; import matching |

Source: [Customer.io: Customer.io pricing](https://customer.io/pricing), [Loops: Contact properties](https://loops.so/docs/contacts/properties), [Userlist: Userlist pricing](https://userlist.com/pricing/), [Customer.io: Import people or events via CSV](https://docs.customer.io/messaging/people/uploading-people/), [Userlist: Companies and relationship-level data](https://userlist.com/docs/company-accounts/), [Userlist: Importing Data](https://userlist.com/docs/managing-users/importing-data/)

Customer.io [Essentials](https://customer.io/pricing) starts at $100 per month billed monthly with 5,000 profiles. Its allowance also includes two object types. Price overages and channels for your own workload.

Userlist counts all stored users, including inactive ones. It uses the prior thirty-day high-water mark for its [monthly fee](https://userlist.com/pricing/). Removing inactive users may therefore leave this cycle’s bill unchanged. Userlist Basic includes company accounts.

Customer.io recommends removing unnecessary profiles and objects one to two days before the billing date to avoid inclusion in the next cycle, according to its [billing guidance](https://docs.customer.io/accounts/billing/reduce-billing-overages/). Preserve any records you need before deleting them.

For example, take a dormant user who belongs to two companies. Count that user in Userlist’s stored audience; for Customer.io, include the person and any stored objects. In Loops, check the contact’s subscription state before forecasting the plan count.

## Carry identity and subscription state separately

A successful import should preserve who each person is and which messages they should receive. Export the original state before changing records, then compare it with the destination.

Customer.io lets a CSV import ignore empty values or nullify existing attributes. Its [import preview](https://docs.customer.io/messaging/people/uploading-people/) exports mapped data before the irreversible import. Changing an established email, phone or ID requires identifying the person by cio_id.

Userlist matches an imported user by identifier before email, creating a record only if neither matches. Its [import guide](https://userlist.com/docs/managing-users/importing-data/) says imported users are treated as opted in even when double opt-in is enabled. Preserve the original permission records and reconcile preferences before sending.

Loops treats account-wide subscription and mailing-list preferences separately. Its [contact properties guide](https://loops.so/docs/contacts/properties) says sending subscribed:true can resubscribe a contact, while omitting that field avoids an unintended change. Deleting and re-adding a contact can lose its previous subscription state.

Loops also says list unsubscribes cannot be reversed through those contact update methods. An ordinary unsubscribe does not block its transactional path. Compare global subscription with list preferences. Inspect delivery suppression separately rather than treating one field as every sending rule.

For company-based targeting, map the relationship as carefully as the person. Userlist supports [many-to-many relationships](https://userlist.com/docs/company-accounts/) and relationship properties such as a person’s role at a particular company. For complex relationships, its import guide recommends the API or Segment integration.

## Keep old users out of new onboarding

Decide which history makes someone eligible before enabling a campaign. An imported record can match a trigger even when the person signed up long ago.

Customer.io says its new attribute and segment triggers can admit matching backfilled users regardless of data age. Its [trigger guidance](https://docs.customer.io/messaging/send/automations/triggers/) recommends explicit time-based conditions when old users should stay out. Check whether your workspace uses the new or legacy behavior.

Userlist warns that adding marketing leads can change segments built on the assumption that every record is a customer. Its [migration guide](https://userlist.com/docs/managing-users/migrate-email-list/) also calls for reviewing first-seen onboarding triggers. Map the old campaign’s entry conditions to the new system and record the intended outcome for each user.

For a concrete trial, use a synthetic dormant customer and a new signup with the same relevant attributes. The new signup should enter only the intended sequence; the dormant customer should follow the rule you explicitly choose. Inspect workflow entries before enabling live sends.

Email may accompany an in-app guide at the same stage. Use the [product onboarding software guide](https://quillwell.com/guides/product-onboarding-software/) to assess the in-app experience, then test the two paths together so a user’s completed step is reflected in the email decision.

## Test each sending path before cutover

A working contact import does not prove that every message has a working route. Inventory the messages your application sends and assign an owner to each path.

Loops uses published transactional templates and dataVariables rather than raw HTML at send time, according to its [migration guide](https://loops.so/resources/migrate-mailchimp-to-loops). That guide lists dashboard CSV or per-contact upserts for contact import and says there is no public bulk contact import API. Confirm the current endpoint contract before implementation.

One Bubble builder [reported](https://forum.bubble.io/t/sendgrid-loops-email/348420) that Loops contacts were saving and DNS was verified, while asking how to route password resets and confirmations. Use that account as a prompt to test each message path, rather than as evidence of a general defect.

Superconductor says its own Loops welcome messages distinguish direct signups from invited workspace users in its [connector announcement](https://www.superconductor.com/blog/loops-connector). Try both paths with your own fixtures and inspect the resulting content.

A fallback needs its own owner and a current template. PostHog’s [engineering handbook](https://posthog.com/handbook/engineering/developing-locally) describes Customer.io HTTP delivery alongside SMTP backup templates in its own implementation. That example gives you a question for your team, not a universal cutover design.

Loops’ [quickstart](https://loops.so/docs/quickstart) covers marketing and transactional email using events and contact properties. Review the actual event names and template variables together so an imported contact and a live application event reach the intended message.

## Run a migration worksheet

Run this trial with synthetic records and sending paused. Keep the before and after exports alongside expected and actual workflow entries.
