How to Send Email to a Public Group in Salesforce: Apex & Flow

Blog / Salesforce · July 4, 2015 · Updated June 10, 2026 · 8 min read
How to Send Email to a Public Group in Salesforce: Apex & Flow

Salesforce has no native "email this Public Group" button — a Public Group is a sharing-and-access construct, not a mailing list. To email every member you pick one of two routes:

  • Apex (code): query the group's GroupMember records, resolve them to user email addresses, then send a Messaging.SingleEmailMessage with setToAddresses(...) (or one message per user with setTargetObjectId when you want open tracking).
  • Declarative (low-code): point an Email Alert or a Flow "Send Email" action at the Public Group as the recipient type, or send a list email to its members.

Use Apex when membership is dynamic or nested; use the declarative path when an admin should own it without code.

Key takeaways

  • A Public Group can bundle Users, Roles, Roles & Subordinates, and even other Public Groups — so "all members" can mean walking nested groups and roles, not just reading one table.
  • Apex path: get the group with SELECT Id FROM Group WHERE DeveloperName = 'Testing_Group' AND Type = 'Regular', expand GroupMember, collect User emails, then call Messaging.sendEmail.
  • Email alerts (recipient type Public Group) and Flow Send Email actions reach a group without code — the most maintainable choice for admins.
  • Apex caps: 10 sendEmail calls per transaction and 5,000 external addresses per org per 24 hours; emails sent with setTargetObjectId to internal Users do not count toward that cap, while setToAddresses always does.
  • Public Groups are built for sharing rules and record access, not campaigns — for true bulk/marketing email use Account Engagement (Pardot) or Marketing Cloud.
  • Need it wired up properly? Our Salesforce consulting team builds this end to end.

What is a Public Group, and why is there no "email group" button?

A Public Group is a reusable collection of users you reference elsewhere in Salesforce. Create one under Setup → Users → Public Groups → New (older orgs: Setup → Administer → Manage Users → Public Groups), give it a Label and Group Name (the DeveloperName), and add members.

What can a group contain? More than just users:

  • individual Users
  • Roles and Roles & Subordinates (plus the Portal variants)
  • other Public Groups (nesting)

Groups exist to drive sharing rules, record access, list views, and reports — not to send mail. There is no "Email this group" button on the group page, which is exactly why you reach for Apex or a declarative action. (The same gap exists on the inbound side — see how Salesforce turns mail into records with the inbound email service.)

How do you email a Public Group with Apex?

The Apex flow is three steps: (1) find the group, (2) expand its membership into user email addresses, and (3) build and send the email. Because a group can contain Users, Roles, and nested groups, the cleanest approach is to collect User Ids first, then query their email in bulk.

Start by querying the group and its direct GroupMember rows, sorting each reference into "is a user" vs "is a nested group/role to expand":

// 1) Get the Public Group by its API name (DeveloperName).
//    Type = 'Regular' is what marks a Group record as a Public Group.
Group g = [
    SELECT Id, DeveloperName
    FROM Group
    WHERE DeveloperName = 'Testing_Group'
      AND Type = 'Regular'
    LIMIT 1
];

// 2) Read its direct members. UserOrGroupId can be a User, a Role group,
//    or ANOTHER Public Group, so bucket them as we go.
Set<Id> userIds  = new Set<Id>();
Set<Id> groupIds = new Set<Id>();   // nested groups / role groups still to expand

for (GroupMember gm : [SELECT UserOrGroupId FROM GroupMember WHERE GroupId = :g.Id]) {
    Id ref = gm.UserOrGroupId;
    if (ref.getSObjectType() == User.SObjectType) {
        userIds.add(ref);
    } else {
        groupIds.add(ref);          // a Group/Role-group: expand it next
    }
}

How do you resolve nested members and choose a send method?

Nesting is the tricky part. A GroupMember reference can point to another Group, so you loop until only Users remain. One honest caveat: a Group whose Type is 'Role' or 'RoleAndSubordinates' represents a role hierarchy, and the most reliable way to resolve those people is to query User by UserRoleId rather than reading GroupMember — handle that case explicitly for role-heavy orgs.

Picking a send method matters for limits and tracking:

  • setToAddresses(List<String>) — literal addresses, up to 100 recipients per message, great for a single bulk send. It has no open tracking and always counts toward the daily external-email cap. Chunk groups larger than 100 into multiple messages.
  • setTargetObjectId(Id) — one Salesforce record per message; unlocks email templates, merge fields, open tracking, and setSaveAsActivity. For internal Users it does not count toward the daily external cap, but it is one recipient per message.
  • Governor limits to respect: 10 sendEmail calls per Apex transaction and 5,000 external addresses per org per 24 hours (GMT). Note setSaveAsActivity must be false when you use setToAddresses.

The code below finishes the expansion, resolves active users to emails, and sends one bulk message:

// 3) Expand nested groups/roles until only Users remain.
while (!groupIds.isEmpty()) {
    Set<Id> nextGroupIds = new Set<Id>();
    for (GroupMember gm : [SELECT UserOrGroupId FROM GroupMember WHERE GroupId IN :groupIds]) {
        Id ref = gm.UserOrGroupId;
        if (ref.getSObjectType() == User.SObjectType) {
            userIds.add(ref);
        } else {
            nextGroupIds.add(ref);
        }
    }
    groupIds = nextGroupIds;
}

// 4) Resolve active users to email addresses (bulk SOQL, no queries in loops).
List<String> toAddresses = new List<String>();
for (User u : [SELECT Email FROM User WHERE Id IN :userIds AND IsActive = true AND Email != null]) {
    toAddresses.add(u.Email);
}

// 5) Build and send one email to the whole group.
if (!toAddresses.isEmpty()) {
    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
    mail.setToAddresses(toAddresses);     // up to 100 recipients per message
    mail.setSubject('Project update for the Testing Group');
    mail.setPlainTextBody('Hi team, here is the latest project update.');
    mail.setSaveAsActivity(false);        // required: cannot be true with setToAddresses

    Messaging.sendEmail(new List<Messaging.SingleEmailMessage>{ mail });
}

How do you email a Public Group without code?

If an admin should own this without Apex, Salesforce gives you three low-code options:

  1. Email Alert (fired from Flow): Create an Email Alert under Setup → Email → Email Alerts, pick an email template, set Recipient Type = Public Group, and add your group. Trigger the alert from a Flow (Workflow Rules and Process Builder are retired for new automation). This is the classic "when an Account in Hyderabad is created, email the Testing Group" pattern.
  2. Flow "Send Email" action: A record-triggered or scheduled Flow can loop the group's members and send, or simply invoke the email alert above. Flows replace the legacy Workflow Rule approach for all new builds.
  3. List Email: From a list view or report of the members, use Send List Email for a one-off, lightly tracked blast (Lightning Experience, with its own daily caps).

Email alerts are also how Salesforce routes approval requests over email, so the recipient-type mechanics will feel familiar.

Apex vs Flow vs List Email: which should you choose?

Approach Best for Tracking Key limits
Apex (Messaging.SingleEmailMessage) Dynamic/nested membership, custom logic, scheduled jobs Open tracking only via setTargetObjectId (per user) 10 sendEmail calls/transaction; up to 100 recipients/message; 5,000 external addresses/org/24h
Flow + Email Alert Admin-owned, event-driven sends to the group Limited (template send is logged) Counts toward org email allocation; one template per alert
List Email Quick one-off blast to a list/report of members Activity logged per recipient Lightning only; per-user daily list-email cap; not for true campaigns

Rule of thumb: Flow/email alert for admin-owned automation, Apex when membership or logic is complex, and List Email for a fast one-time send.

When should you use Marketing Cloud or Account Engagement instead?

Be honest about scope: Public Groups are an access-control tool. If your real need is bulk or marketing email — newsletters, drip campaigns, A/B tests, unsubscribe handling, deliverability at scale — neither Apex nor a list email is the right home. Move that to Account Engagement (Pardot) for B2B or Marketing Cloud for high-volume sends, and keep Public Groups for sharing and internal notifications.

Not sure which lever fits your org? Our Salesforce team can map the group model, automation, and email strategy together.

Frequently Asked Questions

Is there a button to email a Public Group in Salesforce?

No. A Public Group is a sharing and record-access construct, not a mailing list, so there is no "Email this group" button. You reach its members with Apex (query GroupMember and send Messaging.SingleEmailMessage), with a Flow-triggered Email Alert whose Recipient Type is the Public Group, or with a list email.

How do I get all members of a Public Group in Apex?

Query the group with SELECT Id FROM Group WHERE DeveloperName = 'Your_Group' AND Type = 'Regular', then read its GroupMember rows. Because UserOrGroupId can point to a User, a role, or another group, bucket User Ids directly and expand nested groups in a loop until only Users remain. Role-based members are most reliably resolved by querying User by UserRoleId.

Does sending email from Apex count against Salesforce limits?

Yes. Apex allows up to 10 sendEmail calls per transaction, and an org can send single emails to at most 5,000 external addresses per 24 hours (GMT). Emails sent with setToAddresses always count toward that cap, but emails sent with setTargetObjectId to internal Users do not.

What is the difference between setToAddresses and setTargetObjectId?

setToAddresses takes literal email strings and can reach up to 100 recipients in one message, but offers no open tracking and always counts toward the external cap. setTargetObjectId sends to one Salesforce record (User, Contact, or Lead), supports templates, merge fields, open tracking, and setSaveAsActivity — and for internal Users it does not consume the external daily cap.

Can I email a Public Group without writing code?

Yes. Create an Email Alert with Recipient Type set to your Public Group and fire it from a Flow (Workflow Rules and Process Builder are retired for new builds), or use Send List Email from a list view or report of the members. These are the admin-friendly, no-code options.

Should I use a Public Group for marketing emails?

No. Public Groups are for sharing rules and record access, and Apex or list email are capped and untracked at scale. For newsletters, drip campaigns, and bulk sends with unsubscribe and deliverability handling, use Account Engagement (Pardot) or Marketing Cloud instead.

Share this article