A contact group is a saved audience: a named rule over your contacts that you build once and address again and again. In Engage ▸ Emails a send can go to any mix of individual contacts, accounts, contact groups and Contact Filters segments, unioned and deduplicated; in the legacy composer at /emails the Recipient Group field lists groups and nothing else. Everything else you can slice your contacts by — a filter you typed into All Contacts, the results of a CSV import — is a view. A group is the only one with an id, a description and filters you can edit and reuse.
What a group stores
A group holds four things: a name, an optional description, a list of filters, and a list of upload batches. That is all. It does not hold contacts.
Membership is resolved live, on every read. When the composer previews recipients, when a send fans out, when Engage shows you the group as a segment — each of those runs the same query against your contacts using the group’s stored criteria. The practical consequence is the useful one: a group is a rule, not a snapshot. A contact captured tomorrow who matches the filters is in tomorrow’s send, with no action from you. It also cuts the other way — a contact whose title changes out of range silently leaves the group.
The groups themselves are stored under a contact-group: key per workspace, which is why they survive independently of the contacts they describe: deleting every contact leaves the group intact and empty.
Building a group
From Engage
Open Engage → Contact Filters and choose New contact group. The inline form asks for a Name (required) and a Description, then gives you the full filter builder under Membership filters. The right rail previews members live as you edit, showing the matching count and a sample of the people in it.
The preview is the same resolution the saved group will use, including the case the form’s own copy would otherwise hide: leave every filter blank and the group matches every contact. That is occasionally what you want and is more often an accident, so read the count before you save.
From the legacy Email Marketing screen
The older surface still works and is where most existing groups were built. Go to /emails and pick the Groups tab for the list plus a New Contact Group control, or filter the All Contacts tab and save your current filters as a group in one step. /emails is reachable only by typing the URL — it is not in the tab rail — and it is listed alongside the other survivors in legacy URLs that still work.
Both surfaces drive the same filter builder and write the same object. Use whichever you are already in.
Filters
Nine fields can be filtered on. Six of them are offered by the visual builder; three exist in the schema and resolve correctly but can only be set through the API.
| Filter | Stored field | Type | Operators | Matches | In the filter builder? |
|---|---|---|---|---|---|
| Emails | email | String | LIKE %value% | Any part of the email address — .com, @acme, a full address | Yes |
| Titles | title | String | LIKE %value% | Any part of the job title | Yes |
| Companies | company_name | String | LIKE %value% | Any part of the company name | Yes |
| Competitive Products | competitive_products | String | LIKE %value% | Any part of the competitive-products field on the contact’s company | Yes |
| Company Size | company_size | Number | = > >= < <= | Headcount recorded on the company | Yes |
| Company Revenue | company_revenue | Number | = > >= < <= | Revenue recorded on the company | Yes |
| — | company_sic_codes | String | LIKE %value% | Any part of the SIC codes on the company | API only |
| — | company_industry | String | LIKE %value% | Any part of the industry on the company | API only |
| — | current_customer | Boolean | exact match | Whether the company is flagged as a current customer | API only |
| Uploads | (not a filter) | Batch list | intersection | Membership of a named CSV import — see below | Yes |
String matching is always a substring match and is case-insensitive in the way SQLite’s LIKE is; there is no “starts with”, no “equals” and no wildcard of your own. A number filter left at zero is treated as unset and dropped, so you cannot filter for exactly zero employees. An empty string filter is likewise ignored, which is why the builder can emit all six filter objects at once without them doing anything.
Every filter is ANDed
Put a single value in each filter. When you genuinely need “CTO or VP”, make two groups and add both as recipients of one email in Engage ▸ Emails — recipients are unioned and deduplicated, so someone in both groups is mailed once. Or find a single substring that covers both cases.
Upload batches narrow, they do not widen
The Uploads selector is not a filter. Each entry restricts membership to contacts that appear in that named import — and selecting two entries restricts membership to contacts that appear in both, which for two separate imports is normally nobody.
To mail two imports together, make one group per batch and add both groups to a single email in Engage ▸ Emails; in the legacy composer, send to one and then the other. Or drop the Uploads selector entirely and build the group on a field the two imports share. A CSV import becomes a selectable batch automatically; importing contacts covers column mapping, duplicates and what a re-import does to existing values.
Groups versus Engage segments
Engage’s Contact Filters section offers a row of segment tokens. Every one of them can be picked as recipients in Engage ▸ Emails, and the section’s New email menu starts a draft with the current one preloaded. Only a contact group is a saved, editable object, and only a group can be picked in the legacy composer.
| Thing | Where it appears | Saved? | Can receive an email? | Member limit |
|---|---|---|---|---|
| All contacts | Engage segment token | No — a built-in view | Yes, from Engage ▸ Emails | The screen paginates |
| Hot leads | Engage segment token | No — computed from scores | Yes, from Engage ▸ Emails | 1,000 shown |
| Stalled | Engage segment token | No — computed from scores | Yes, from Engage ▸ Emails | 1,000 shown |
| A persona | Engage segment token | The persona is saved; its members are the contacts tagged with it | Yes, from Engage ▸ Emails | 1,000 shown |
| A committee role | Engage segment token | No — its members are the contacts whose roles were set by hand | Yes, from Engage ▸ Emails | 1,000 shown |
| An account | Engage ▸ Accounts | The account is saved | Yes, from Engage ▸ Emails | none on a send |
| A contact group | Engage segment token, Engage ▸ Emails, and the legacy composer’s Recipient Group | Yes | Yes, from either composer | 1,000 shown in Engage; no limit on a send |
| An upload batch | A selectable input to a group | Yes, as a batch | Only through a group that selects it | none |
The 1,000 is worth reading carefully, because it applies to the view and not to the group. When Engage renders a contact group as a segment it resolves at most 1,000 members, so a group of 4,000 shows you a thousand of them. A send addressed to that same group resolves all 4,000. If a group looks capped in Engage and uncapped in the composer, both numbers are correct.
Where Hot and Stalled come from, and what a persona is scored on, is in engage scores. The rest of the Contact Filters surface is described in Engage workspace.
Who is actually in a group
Two different subtractions apply, at two different moments, and confusing them is the most common source of “my audience is smaller than I expected”.
Locked contacts are excluded from membership. Your plan sets how many contacts your workspace can see; the visible window is the oldest contacts you captured, up to that number. Anything past it is locked — not deleted, not hidden from your bill, just unreadable and unmailable through every route. Locked contacts are excluded from the group’s members and from its count. The API returns both numbers — the visible count and the true match count — which is how the CMS can tell you how many are locked rather than silently shrinking your audience. Usage and limits explains the window and why the oldest survive rather than the newest.
Opted-out and hard-bounced contacts stay in the group. They match the filters, so they are members, and they are counted in the recipient count you see while composing. They are removed at send time instead: in Engage ▸ Emails the Recipients tab already splits them out as opted out or bounced and Send leaves them out; the legacy composer removes them when the batch is created; and every send checks again per recipient. This is why the Recipients number in Sent Emails can be lower than the number you previewed, and it is working as intended: the group describes an audience, and consent is applied to the send.
flowchart TD A["Every contact you have captured"] --> B["Visible window<br/>oldest N, where N is your plan's contact quota"] B --> C["Filters, ANDed<br/>including every value inside one filter"] C --> D["Upload batches, intersected<br/>a contact must be in all selected batches"] D --> E[["Group members<br/>this is the number you preview"]] E --> F["At Send: Engage ▸ Emails drops everyone not opted in or hard-bounced;<br/>the legacy composer drops do-not-contact — 400 if nothing survives"] F --> G["Drop opted-out<br/>at fan-out"] G --> H["Drop hard-bounced<br/>at fan-out"] H --> I[["Recipients<br/>this is the Sent Emails number"]] A -. "locked contacts stop here" .-> Z(["Counted as locked,<br/>never listed, never mailed"])
Six independent narrowings, in that order. When an audience comes out the wrong size, work down the chain rather than re-reading the filters — the answer is usually stage two or stage five.
Editing and deleting
Open a group to preview its current members, adjust its filters, or delete it. Edits take effect immediately and retroactively: because membership resolves live, changing a filter changes who the next send goes to, with no republish step. Sends that already went out are unaffected — they recorded their recipients at fan-out.
Deleting is a soft delete. The group is marked deleted, the deletion is written to the audit trail, and the record is tracked so the change propagates; the contacts themselves are untouched. A group that a past send used stays linked from that row in Sent Emails.
| Action | Privilege | Minimum role |
|---|---|---|
| List and preview groups | contact:read | Read Only |
| Create or edit a group | contact:write | Content Publisher |
| Delete a group | contact:delete | Content Publisher |
That threshold catches people out: a Content Writer who can build a form cannot build an audience. Contact privileges sit one rung higher than page-authoring privileges, and the only way to grant them without promoting someone is the API-only contact override. The full matrix is in the privilege matrix.
Once a group exists, it is an input to a send — sending a marketing email picks up at the composer’s Recipients field. The contacts inside it — where they came from, what a record holds, and how to work with one person rather than a list — are covered in contacts, and what happens to somebody who opts out is in unsubscribes and opt-outs.