Dashboard
Audience
Contacts, topics, default opt-in, segments, signup forms, CSV import/export and the preferences page.
Audience holds the contacts of the organization, their properties and their per-topic subscription state. Every domain of the organization shares this data. The page has four tabs: Contacts, Topics, Segments and Forms.
Contacts
A contact is an email address that is unique in the organization. A contact has an optional first name and last name. It has a properties map of scalar values (≤ 100 keys, [A-Za-z0-9_.-]). It has a status: Subscribed, Unsubscribed or Bounced.
The list filters by status and searches by email address. The contact page shows the properties and every topic with the effective state of the contact. It also shows a Preferences link (/p/<token>). You can paste this link into a footer or send it to the person.
An empty Contacts tab shows Add a contact and Import CSV.
You can create contacts here (single add or CSV import), with POST /api/v1/contacts, or with POST /api/v1/events. When you delete a contact, the platform removes its subscription rows. Suppressions are separate and stay.
Topics
A topic is something that a person can opt in to or out of, for example newsletter or product-updates. A topic has a slug, a name, a description and a default opt-in flag. The API uses the slug, and so does kind: broadcast.<slug>. The preferences page shows the name and the description. The default opt-in flag has two settings:
- default opt-in on — every contact without an explicit row counts as subscribed;
- off — only contacts who explicitly subscribed count.
The effective state of a contact for a topic is its explicit row if there is one, otherwise the default. Broadcasts to a topic go to contacts with effective state subscribed and contact status Subscribed.
Segments
A segment is a saved filter with up to 20 rules. Every rule must match (AND):
| Rule | Example |
|---|---|
| Property equals / contains / exists | plan = pro, city contains york, company exists |
| Topic state | newsletter subscribed (effective state) |
| Created after | 2026-01-01T00:00:00Z |
| Contact status | subscribed, unsubscribed, bounced |
The editor shows a live count. Segments narrow broadcasts. They also define the shape of condition steps in automations.
Plan contact limit
Each plan includes a number of contacts. The Free plan includes 1,000. From 80% of the limit, the Audience page shows an Upgrade banner. At the limit, Refresh refuses new contacts from the console, CSV import, the API, events and signup forms. Updates to existing contacts still work. An import stops adding new rows at the limit and reports the first refused line.
Signup forms
A signup form is a public page at /s/<id> that subscribes a person to one topic. The Forms tab lists the forms of the organization. Each row shows the public URL and a Copy embed button. The embed is a plain HTML <form> that posts to the public URL. Paste it into your site; it needs no JavaScript. You can also put the public page in an iframe.
A form has a name, a topic, a verified sending domain and a From address on that domain. It has an optional redirect URL (https:// only). After a submit or a confirm, the person lands on that URL. Without one, the person sees a branded page in the style of the preferences page.
Double opt-in is on by default. With it, a submit sends a confirmation message from the From address of the form. The subject is Confirm your subscription to <topic name>. The link in it is valid for 48 hours. Refresh stores no contact before the person opens the link. The confirmation message itself appears in the message log.
The link opens a page with a Confirm subscription button. Mail scanners open links on their own, so only the button click confirms. Then Refresh creates the contact and writes the subscription with source signup:<id>. It also starts topic.subscribed automations. A second click on the same link changes nothing. With double opt-in off, a submit subscribes a new address at once.
The form takes email (required), first_name and last_name. It refuses disposable domains and role addresses. The error line reads This address cannot be subscribed.
An address that already has the topic gets the same success page as a new address, and no message. A person who unsubscribed still receives the confirmation message, also on a form with double opt-in off. Only their own confirm subscribes them again. Then Refresh removes the unsubscribe suppression. A link sent before the person unsubscribed no longer works. Bounce and complaint suppressions stay, and such an address gets the error line instead.
Refresh rate-limits each form to 10 submits a minute per IP address and 3 confirmation messages a day per address. An organization sends at most 500 confirmation messages a day across all its forms. Disable a form to make its public page return 404. Delete a form to remove it; confirmed contacts stay.
Import and export
Import CSV accepts pasted text (≤ 2 MB, ≤ 5,000 rows) with one contact per line:
email,first,last,key=value,key2=value2
jane@example.org,Jane,Doe,plan=pro,"city=New York"
The import skips an optional header line that starts with email. It honours RFC 4180 quoting. It reports invalid addresses, duplicates within the file and malformed properties per line, and skips them. It also refuses disposable domains, role addresses (noreply@, postmaster@, abuse@, …) and domains without a mail server, and reports each as rejected. The import updates existing addresses (sets the names, merges the properties). It never duplicates them.
Export CSV downloads the current list. You can filter the list by status first.
Preferences page
/p/<token> is a public page for one contact. It shows the topics of the organization as checkboxes, plus Unsubscribe from all. When the contact saves, the platform writes explicit subscription rows. Unsubscribe from all also sets the contact to Unsubscribed and adds an organization-wide suppression. See Unsubscribe & tracking for details.