Skip to content

Contacts and audience ​

Contacts is one page with four tabs — the audience and everything that describes it. The tab is in the URL as ?tab=, so a filtered view is linkable.

Contacts ​

The roster. Columns are Email, Segments (the segments the contact was added to by hand) and Status; above them sit a search box (by email address) and a Subscription state filter — all contacts, those opted out of all email, or the rest.

New contact (/contacts/new) asks for an email address and lets you add the contact to segments by hand. Note what is not there: a contact has no name field. A contact is an address plus whatever custom properties you have declared — see Properties below, and put a name there if you want one.

Edit (/contacts/<id>/edit) is the same form on an existing row, plus the values of every declared property.

The row menu also carries Unsubscribe / Resubscribe, for the opt-out from all email. Status is derived from the suppression list, not stored on the contact, which has two consequences worth knowing: deleting a contact does not resubscribe the address, and an address can be suppressed without ever having been a contact.

Deleting a contact is permanent and takes its stored properties with it.

Segments ​

A segment is everyone matching its filter, plus the contacts added to it by hand. Membership is recalculated on every read, so a contact that gains a matching property joins immediately. There is no rebuild that can fall behind.

Each segment has a name, a URL-safe slug (lowercase letters, digits and underscores, starting with a letter), an optional description, and the filter.

Two settings decide how the segment appears on the preference page. Show on the preference page lets members opt out of it there. Public name is what recipients read instead of the internal name.

The filter builder ​

A filter is a tree. At each level you choose how conditions combine — match all of (AND) or match any of (OR) — then add conditions and nested groups.

No conditions means hand-added members only

A filter with no conditions matches nobody, so the segment holds only the contacts added to it by hand. A nested group with no conditions still matches every contact. The builder says which applies inline.

A condition is a field, a condition (operator) and usually a value. The operators offered depend on the field, so the builder can only produce legal combinations — changing the field resets both the operator and the value.

FieldOperators offered
Email addressis, is not, contains, starts with, ends with
Unsubscribedis (Yes / No)
A text propertyis, is not, contains, starts with, ends with, is set, is not set
A number propertyis, is not, is greater than, is at least, is less than, is at most, is set, is not set
A boolean propertyis, is set, is not set
A date propertyis, is before, is after, is set, is not set

View members shows who is in the segment right now, with a count. A badge marks contacts added by hand, which you can remove there, and contacts who opted out of this segment's broadcasts. Because membership is live, two looks a minute apart can legitimately differ.

Deleting a segment removes the definition, its hand-added members and its opt-outs — the contacts themselves are untouched. A segment a broadcast points at cannot be deleted while that broadcast exists.

Properties ​

Typed custom fields available on every contact: a label, a key (lowercase letters, digits and underscores, starting with a letter), a type — text, number, boolean or date — and an optional default value.

Declaring a property is what lets a segment offer the right operators for it. You can store arbitrary keys on a contact through the API without declaring them; what you cannot do is filter on an undeclared key.

Retyping a property

If you change a declared type after data has been written, contacts whose stored value no longer matches simply drop out of a filter rather than breaking the whole segment.

Unsubscribes ​

The suppression list itself: every opt-out this organization holds. An opt-out from all email blocks every send; an opt-out from a segment blocks only that segment's broadcasts. It is a separate tab rather than a filtered contact view because most entries were never contacts — /v1/send takes free-form recipients.

Columns are the address, the scope (all email or a segment), the source (hosted page, API, or one-click) and when it happened. A scope filter sits next to the search box. Remove from list lifts a suppression; the confirmation is blunt about it, because the person asked you to stop.

Lifting an entry here also refreshes the contact list, since the badge there is derived from the same address.

Importing a CSV ​

Import CSV (/contacts/import) on the Contacts tab.

  1. Choose a file. Up to 10 MB and 50,000 rows, and the first line must be a header. The dialog reads the header immediately and tells you how many rows it found.
  2. Map the columns. Exactly one column must supply the email address; the rest map to declared properties or are ignored. Two columns cannot map to the same field.
  3. Choose what happens on a conflict — leave the existing contact untouched, or update it with the values from the file.
  4. Start import.

The file is parsed and validated before the import is accepted, so a malformed row is reported to you now rather than discovered later. A broken row is dropped, counted and listed under Rows that could not be read; a broken file — an unknown column, no email mapping — rejects the whole upload.

Only the write is queued. Progress shows as Created, Updated, Skipped and Rejected. "Skipped" means the contact already existed and you chose to leave it, or your plan's contact limit was reached partway through — running out of quota is not a failure: the contacts that fit are real and the import completes.

Released under the Apache 2.0 License.