Unitpost
Contacts

Your audience, in one place.

Store every contact with custom fields, slice them into static and dynamic segments, personalize with variables, and honor consent automatically. Unlimited contacts on every paid plan — you pay for sends, never per contact.

Every contact

Everything about a contact, on one screen.

Identity, custom fields, topic subscriptions, deliverability, and a full activity history — the whole picture without leaving the page. Add contacts by hand, over the API, or by importing a CSV.

  • Import in seconds

    Upload a CSV and map columns to email, name, opt-out, and any custom field — existing contacts are skipped, not overwritten.

  • Full activity trail

    Created, imported, subscribed, added to a segment, opted out of a topic — every change is logged per contact.

  • Deliverability at a glance

    See whether a contact is mailable or suppressed, with the reason, right in the header.

ada@example.comMailable

Ada Lovelace

ada@example.com

Subscribed

Fields

companyAcme Corp
planpro
seats12
signup_date2026-01-14

Topics

Product updatesNewsletterPromotions · off

Activity

  1. Subscribed to Product updates2d
  2. Added to segment “Beta”5d
  3. Imported from CSV3w

Custom fields

Store any attribute, your way.

Define text, number, boolean, and date fields once for your workspace. Set a default, reference them anywhere as {{ key }}, and rename a key later without breaking your contacts.

  • Four typed field kinds

    Text, Number, Boolean, and Date — with an optional workspace default used when a contact has no value.

  • Safe to evolve

    Rename a field key and it migrates across every contact; delete one and it's cleanly stripped everywhere.

Custom fieldsUse as {{ key }}
LabelKeyDefault

Company

company

Plan

plan

free

Seats

seats

1

Signup date

signup_date

Beta access

beta_access

false

Segmentation

Static lists and dynamic segments.

Curate a fixed list by hand, or save a filter that resolves live on every send. Match all or any of up to 25 conditions across built-in and custom fields — the count updates itself as your data changes.

  • Dynamic, always current

    Rules like plan is “pro” or signup_date is on or after a date recalculate membership automatically — no re-tagging.

  • Rich operators

    is / is not, contains, starts or ends with, greater/less than, and is-set checks — narrowed to each field's type.

  • “Everyone subscribed” built in

    A managed segment of every still-subscribed contact is always ready — no setup, never stale.

Active Pro usersDynamic
MatchAllAnyof the following
planispro
signup_dateis greater than or equal to2026-01-01
last_nameis set
Resolved live on every send312 contacts

Personalization

Personalize with every field.

Drop {{first_name}}, {{email}}, or any custom field into a subject line or body. Values resolve per recipient at send — and a missing required value is caught before the send, never delivered as a blank.

  • Built-in + custom fields

    Reference name and email out of the box, plus any custom field by its key — in the subject and the body.

  • Caught before it sends

    A referenced variable with no value returns a 422 up front, so a broken merge tag never reaches an inbox.

VariablesResolved at send
{{first_name}}{{email}}{{amount}}
Hi Ada, your receipt for $24.00 is ready.

Fallbacks keep sends clean when a value is missing.

Suppressions

A reputation backstop, on by default.

Every hard bounce and spam complaint suppresses that address automatically. Add addresses manually or over the API, and Unitpost skips them on every send — even if they were never a contact.

  • Automatic on bounce & complaint

    Hard bounces and complaints are suppressed for you, protecting your sender reputation.

  • Reasons you can see

    Bounce, Complaint, Unsubscribe, Manual, Import, or API — every suppression carries its reason, and you can un-suppress.

SuppressionsAuto-skipped on send
bounced@example.com
Bounce1h
spam@example.com
Complaint3h
left@example.com
Unsubscribe1d
old@example.com
Manual2d
Hard bounces & complaints suppress automatically to protect your reputation.

Developer-first

Manage your audience with code.

Sync contacts straight from your product. Create, update, unsubscribe, and remove over one REST API — the same audience powering your campaigns and transactional sends.

  • Kept in sync

    Create or update contacts, push custom fields, and fetch their details; email is the unique key.

Create a contact
import { Unitpost } from "unitpost";

const unitpost = new Unitpost(process.env.UNITPOST_API_KEY);

// Send a contact from your product — email is the key.
await unitpost.contacts.create({
  email: "ada@example.com",
  firstName: "Ada",
  lastName: "Lovelace",
  unsubscribed: false,
  properties: {
    company: "Acme Corp",
    plan: "pro",
    seats: 12,
  },
});

Reach the right people

A segment for every send

From a curated newsletter list to a self-updating cohort of paid users — build the audience once and let it stay current.

Newsletter list

A static list you curate by hand or on signup — add or remove contacts one at a time or from a CSV.

Active paid users

A dynamic segment on plan is “pro” — membership recalculates itself as contacts upgrade or churn.

Recent signups

Filter signup_date is on or after a date, and it always reflects the last cohort with no manual upkeep.

Region targeting

Match a custom country or city field to send the right message to the right place.

Everyone subscribed

A built-in, always-fresh segment of every contact still opted in to marketing — ready to send to.

Topic subscribers

Let contacts opt in to “Product updates” or “Promotions” and send only to the ones who want it.

Beta cohort

Flip a beta_access boolean and build a segment around it to gate early features behind consent.

Win-back

Combine field filters to reach dormant accounts — then let opt-outs and suppressions keep it clean.

Questions

Common questions

How do I add or update a contact from my app?

POST /api/v1/contacts with an email plus optional first_name, last_name, unsubscribed, and a custom_fields map; PATCH /api/v1/contacts/{id} updates one. Email is the unique key per workspace, and the path accepts either the con_ id or the email address, so you can sync from your own database without storing our ids. Writes need the contacts:write scope, reads contacts:read.

What's the difference between a static and a dynamic segment?

A static segment is a list you curate — you add and remove contacts explicitly. A dynamic segment is a saved filter that is re-evaluated live every time it's read or sent to, so membership follows your data with no re-tagging. Because dynamic membership is computed, the add-member and remove-member endpoints return 409 for one; you change who's in it by editing the filter.

Is there a limit on how many contacts I can store?

Every paid plan has no contact cap and you're billed on emails sent, not list size — so an unengaged list costs you nothing to keep. The free plan stores up to 1,000 contacts. A segment filter is capped at 25 conditions in total, which is a query-complexity limit rather than a limit on your audience.

Can I store custom attributes on a contact?

Yes. Register a field once with POST /api/v1/contact-fields as text, number, boolean, or date with an optional workspace default, then set per-contact values in the contact's custom_fields map. Values must be scalars so they're safe to interpolate as {{ key }} in a subject or body and to use as segment-filter inputs. Renaming a field key migrates the value on every contact.

Does Unitpost handle unsubscribes and bounces for me?

Yes, at three levels. Every contact has a global marketing opt-out, per-topic preferences they manage on a hosted page, and per-list opt-out in the data model. Hard bounces and spam complaints suppress an address automatically, with the reason recorded. Suppressed addresses are skipped on every future send — even addresses that were never contacts, which you can add yourself over the API.

Are segment filters case-sensitive?

Yes — eq, contains, starts_with, and ends_with all compare exactly, so "Premium" does not match "premium". This is deliberate: custom fields live in a JSON column whose string filters can't express case-insensitive matching, and making built-in columns behave differently would mean the builder preview, the dashboard count, and the actual send could disagree. Normalize values on write if you need loose matching.

Bring your list and start sending

Free includes contacts, custom fields, segments, and topic-level opt-outs — with 3,000 emails a month and up to 1,000 contacts. Import a CSV and send. No credit card.