Home / admin / chatter-notification-tuning
chatter-notification-tuning
Use when admins are reducing Chatter signal-to-noise across an org — covers org-level Chatter Email Settings, per-user Email Settings preferences, group-level email digest frequency (Daily / Weekly / Every post / Never), bell-icon Custom Notifications vs Chatter feed-post tradeoffs, automated-feed suppression on record types via Feed Tracking config, and the Chatter Settings org defaults that control whether new users start with everything-on.
Open the skill folder on GitHub
Chatter Notification Tuning
Activate this skill when an admin needs to reduce Chatter feed noise, lower email volume, or rebalance the notification surface across Chatter feed posts, Custom Notifications (bell), and email. The skill is about governance of the notification channel — not authoring new automation.
Group lifecycle — creation policy, archiving, ownership transfer, unlisted groups — belongs to admin/chatter-group-governance. That skill governs which groups exist; this one governs what is noisy inside them.
Before Starting
Gather this context before proposing changes:
- Where the noise comes from. Do not guess this; the answer is one query and it changes everything
downstream.
SELECT Type, COUNT(Id) FROM FeedItem WHERE CreatedDate = LAST_N_DAYS:30 GROUP BY Type— directFeedItemqueries need the View All Data permission (object_reference.txt L136505–136507). Three sources, three different fixes:- Automated feed posts — Flow
chatterPostaction calls (api_meta.txt L68655), ApexFeedIteminserts. These show as
TextPostfrom a small set ofCreatedByIdvalues. - Feed Tracking — shows as
TrackedChange, "a change or group of changes to a trackedfield" (object_reference.txt L136377–136378).
- Group activity and approvals — human
TextPost, andApprovalPostwhenenableApprovalRequestis on (api_meta.txt L112490–112496).
- Automated feed posts — Flow
- Who can actually make the change. Two permissions gate most of this work.
CollaborationGroupMember.NotificationFrequency"can only be set by the member or users with the 'Modify All Data' permission" (object_reference.txt L67510–67513) — group ownership is not enough. DirectFeedItemqueries need View All Data. Confirm both before planning a bulk change. - What the four frequencies are. The whole design space is
P(on every post),D(Daily),W(Weekly),N(Never) — object_reference.txt L67515–67518. There is noLimited. A plan built on "email me only for @mentions and threads I already joined" is built on a value the platform does not have; seereferences/gotchas.mdgotcha 1 before you promise anyone a volume number. - Do you have Custom Notifications?
CustomNotificationTypeis deployable metadata, available inAPI version 46.0 and later (api_meta.txt L41785), with
desktopandmobileas its only delivery channels (L41802–41812). If the org has never used it, migrating high-volume automated feed posts to the bell is the most-leveraged move available. - Is digital experiences enabled?
FeedItem.Visibilityexists only "in API version 26.0 and later,if digital experiences is enabled for your org" (object_reference.txt L136412–136414). In an org without it, code that sets
Visibilityunconditionally breaks, and the internal/external exposure question does not arise.
Questions to Ask Before Configuring
Ask these before touching Setup, writing XML, or staging a CSV. Each one maps to a gotcha that turns a reasonable-looking tuning change into a change that fails, reverses itself, or overrides someone's deliberate choice.
| Ask | Why it matters | What a good answer adds |
|---|---|---|
| "Which channel is loud — the feed, the inbox, or the bell?" | They are three different data stores with three different fixes; EntitySubscription (follows), CollaborationGroupMember (digests) and Messaging.CustomNotification (bell) share nothing but a complaint | The one surface to work on, instead of a plan that touches all three and cannot be measured |
| "Who is running the bulk change, and do they hold Modify All Data?" | NotificationFrequency can only be set by the member or a Modify All Data holder (L67510–67513); a group owner's load fails per row | Either a named runner, or the decision to change only User.DefaultGroupNotificationFrequency and ask members to change their own |
"Which of P / D / W / N is the intended default, and if it is P, why?" | P self-disables above 10,000 members in a site and silently switches everyone to Daily (L67519–67522) | A written justification the checker's CNT-POLICY-EVERYPOST will stop demanding, or a switch to D before that decision gets made for you |
| "Is the volume automated or human?" | An 800-post Flow is not a digest problem — no frequency setting makes those posts worth reading | The routing decision between migrating a chatterPost action (examples § 3) and tuning frequency (examples § 2) |
| "Are we changing what new joins inherit, or rewriting existing members' choices?" | User.DefaultGroupNotificationFrequency applies "when the user joins groups" (L295160–295163) and is not retroactive; rewriting CollaborationGroupMember rows overrides choices people made | Two separate changes with two separate consent stories, rather than one that quietly does both |
| "Does this org have digital experiences enabled?" | FeedItem.Visibility only exists if it does (L136412–136414), and for record posts it already defaults to InternalUsers (L136430–136431) | Whether the external-exposure audit is real work or a wild goose chase |
| "What does 'quiet enough' look like, and against which number?" | Without a before-measurement the change cannot be defended or reversed on evidence | The 30-day FeedItem-by-Type baseline that step 7 re-runs against |
What a proper configuration adds over just doing it: the org gets a notification policy that is written down, linted against the real group population and the real enum, deployed from source control, and measured against a before-baseline — instead of a one-off Setup change that nobody can explain in six months, built on a frequency value that does not exist, applied by someone whose permissions silently dropped half the rows.
Core Concepts
Concept 1 — Four control surfaces, and only two of them are metadata
| Surface | Where it lives | What it controls | Source |
|---|---|---|---|
ChatterSettings | settings/Chatter.settings-meta.xml | enableChatter, enableApprovalRequest, feed presentation | api_meta.txt L112452–112648 |
ChatterEmailsMDSettings | settings/ChatterEmailsMD.settings-meta.xml | the collaboration-email master switch, digest scheduling mode, email-to/reply-to-Chatter | L112368–112445 |
User fields | data — Data Loader / DML | personal digest frequency, inherited group frequency, every per-event email | object_reference.txt L295156–295207, L296132–296300 |
CollaborationGroupMember.NotificationFrequency | data — Data Loader / DML | one member's digest frequency in one group | L67503–67540 |
Two consequences follow from that table, and both are load-bearing.
There is no org-level default digest frequency. ChatterEmailsMDSettings has ten boolean elements and none of them is a frequency (L112383–112418). What new users inherit is the defaulted-on-create value of User.DefaultGroupNotificationFrequency: N, "but for Professional, Enterprise, Unlimited, and Developer Edition organizations that existed before API version 22.0, the default value remains D" (L295165–295172). In a long-lived Enterprise org, every new hire is inheriting a daily digest that nobody chose.
The only org-wide email lever is binary. enableCollaborationEmail — "whether collaboration email notifications can be sent (true) or not (false)" (L112391–112392). Setting it false makes every preference below it inert. It ends the complaint by ending the channel; treat it as a decision about whether the org uses Chatter email at all, not as a volume dial.
Concept 2 — Feed Tracking is what makes records noisy, and its failure modes are not the folklore ones
Feed Tracking is two elements that must travel together: enableFeeds on the object (api_meta.txt L42035–42039) and trackFeedHistory on each field, where "to set this field to true, the enableFeeds field on the associated CustomObject must also be true" (L43668–43672). Deploying the fields without the object is the most common failure, and the bundled checker raises CNT-TRACK-NO-FEEDS on it.
Two real failure modes, and one that gets blamed but cannot happen:
- Roll-up and formula fields. These recompute on dependent updates, so they generate feed activity
with no human event behind it — the highest-yield removals when they are present. UNVERIFIED (2026-09-05): no supplied guide states whether roll-up summary or formula fields accept
trackFeedHistoryat all; the only documented eligibility restriction covers standard fields (L2164–2167). Read the retrieved*.field-meta.xmlfiles fortrackFeedHistoryrather than assuming this category exists in the org in front of you. - Whole-object tracking where a record type would do.
recordTypeTrackFeedHistory(L42234–42241) scopes feed tracking to one record type, with the same
enableFeedsprerequisite. When one integration-fed record type is the noise source, this is the surgical cut. - Not
LastModifiedDate. System fields "such asCreatedByIdorLastModifiedDate" are noteligible for feed tracking at all (L2164–2167). Anyone who reports finding it tracked is misremembering; sending an admin to look for that checkbox costs credibility.
Expect a smaller volume drop than the field-count drop suggests: a TrackedChange is "a change or group of changes to a tracked field" (object_reference.txt L136377–136378), so several fields changed in one save can collapse into one feed item.
Concept 3 — Custom Notifications (bell) vs Chatter feed posts
| Goal | Use |
|---|---|
| Targeted, transient alert ("your case was reassigned") | Custom Notification (bell). No FeedItem written. Desktop and/or mobile channel. |
| Durable record of an event ("approval rejected on Opp #4567") | Chatter FeedItem. Preserves the audit trail; ApprovalPost and TrackedChange types exist for exactly this. |
| Broadcast announcement ("system maintenance Sunday") | Custom Notification with a GroupId or QueueId recipient, NOT a feed post on user records. |
| Process telemetry ("flow completed successfully") | NEITHER. Email alert or a dashboard. Chatter is not a logging channel. |
The migration is one actionType swap in the Flow — chatterPost (api_meta.txt L68655) to customNotificationAction, "available in API version 46.0 and later" (L68710) — plus a deployed CustomNotificationType. A Flow posting a feed item on every Closed Won produces thousands of FeedItem rows a year; the bell reaches the same people and writes none.
Two limits shape the recipient design. Messaging.CustomNotification.send() takes at most 500 values (apexrefguide.txt L166776–166778) — not 2,000 — but a single GroupId or QueueId counts as one value and expands to every active member (L166766–166776), which collapses most large fan-outs to a set of size one. And send() requires the Send Custom Notifications permission, failing without it (L166591–166594).
Concept 4 — Choosing among four frequencies, with N as the honest default
The design space is P / D / W / N (object_reference.txt L67515–67518). Match it to group purpose rather than reaching for one org-wide answer:
| Group purpose | Frequency | Why |
|---|---|---|
Broadcast / announcement (IsBroadcast = true) | D | Low post rate, high must-read; a daily digest is the whole point |
| Active project group | W | A heartbeat that says "this is still moving" without an inbox per thread |
| Social / interest group | N | People read these when they choose to; pushing them is what generates the complaint |
| Genuinely urgent, small, bounded | P | Only with a written reason — and only if the group cannot grow past 10,000 members, above which P self-disables and everyone is switched to Daily (L67519–67522) |
The org-wide question is a different one: what should a new member inherit? That is User.DefaultGroupNotificationFrequency, and N is both the platform's own current default (L295165–295168) and the defensible choice — it makes every noisier setting a decision somebody made rather than a default nobody noticed.
Recommended Workflow
- Measure before you plan. Run the 30-day
FeedItem-by-Typequery inreferences/metadata-examples.md§ 8 as a View All Data holder. The dominantTyperoutes the rest of this workflow, and the number is the baseline step 7 re-runs against. Record both intemplates/chatter-notification-tuning-template.md§ 1. - Retrieve the metadata baseline and lint it.
sf project retrieve start --metadata Settings:Chatter Settings:ChatterEmailsMD --target-org prod-audit python3 scripts/check_chatter_notification_tuning.py --manifest-dir force-app/main/defaultTwo findings stop everything else.
CNT-SETTINGS-ELEMENTmeans the file in source control contains an element the guide does not document, so nothing in it will deploy.CNT-EMAIL-MASTERmeansenableCollaborationEmailis alreadyfalse— the channel is off, and whatever people are complaining about is not Chatter email.references/metadata-examples.md§§ 1–2 carry the deployable shapes. - Prune Feed Tracking, if step 1 said
TrackedChange. Remove roll-up and formula fields first;consider
recordTypeTrackFeedHistorybefore removing tracking wholesale. Package the.object-meta.xmlwith the field files —CNT-TRACK-NO-FEEDSfires when you forget, and so does the deploy.references/examples.md§ 4 works a twelve-field Account down to four. - Migrate transient automated posts, if step 1 said
TextPostfrom automation ids. The checker'sCNT-FLOW-CHATTERPOSTandCNT-APEX-FEEDITEMfindings are the worklist. SwapchatterPostforcustomNotificationActionand deploy theCustomNotificationType(references/metadata-examples.md§ 3). Keep durable business events on the feed — readreferences/llm-anti-patterns.mdanti-pattern 7 before anyone proposes aFeedItempurge instead. Run the two in parallel for two weeks before deactivating the original. - Write the digest policy down, then lint it against the real group population.
sf data query --result-format csv --target-org prod \ --query "SELECT Id, Name, IsBroadcast, IsArchived FROM CollaborationGroup" > groups.csv python3 scripts/check_chatter_notification_tuning.py \ --manifest-dir force-app/main/default \ --policy notification-policy.json --group-inventory groups.csvCNT-POLICY-GROUPcatches overrides that name a group that no longer exists;CNT-FREQ-VALUErejectsLandLimited;CNT-POLICY-EVERYPOSTmakes you justifyPin writing. The policy shape is inreferences/metadata-examples.md§ 7. - Apply it in two separate changes. First
User.DefaultGroupNotificationFrequencyandUser.DigestFrequency, which only affect what people inherit going forward. Then, as a Modify All Data holder and only if you decided to, theCollaborationGroupMemberrows — which override choices people made. Lint the CSV before each load:python3 scripts/check_chatter_notification_tuning.py --member-csv members_update.csvreferences/examples.md§ 5 covers the per-user email flags and their inverted polarity;references/gotchas.mdgotcha 9 is the one that turns a suppression load into an amplification. - Re-measure at two weeks and diff. Re-run step 1's query and step 2's checker invocation. The
dominant
Typeshould have changed, not just shrunk — ifTrackedChangestill leads after achatterPostmigration, step 3 was the work that mattered and did not happen. CommitChatter.settings,ChatterEmailsMD.settings, the notification types and the policy JSON so the next org inherits the decision rather than rediscovering it.
Reference Files
| File | Read it when |
|---|---|
references/metadata-examples.md | Writing the deployable ChatterEmailsMD.settings, Chatter.settings, CustomNotificationType or Feed Tracking XML, the package.xml and sf project commands — or any of the data-side artefacts: the policy JSON, the member and user CSVs, the DML alternative, and the verification queries |
references/gotchas.md | The change failed, reversed itself, or did the opposite of what you intended — an invalid NotificationFrequency, a settings element that will not deploy, a per-post setting that became Daily on its own, a send() that failed above 500, a suppression load that increased volume |
references/examples.md | Working a scenario end to end before any XML exists — the new-hire default, a bulk group-frequency change, a chatterPost migration, Feed Tracking pruning, per-user preference resets, and the audit query that routes between them |
references/well-architected.md | Justifying bell vs feed, W vs D vs N, inherited-default vs rewrite, or source-suppression vs frequency-tuning — and locating the official source behind any claim in this skill |
references/llm-anti-patterns.md | Reviewing output an AI assistant produced here, especially anything mentioning Limited, an emailOn* settings element, a 2,000-recipient batch, the Tooling API, or a FeedItem purge |
templates/chatter-notification-tuning-template.md | Running workflow steps 1–4 — the diagnosis, the questions, the policy, and the record of what you deliberately left alone |
scripts/check_chatter_notification_tuning.py | Workflow steps 2, 5, 6 and 7 — before any settings deploy, before any policy is agreed, and before every CSV load |
Related Skills
admin/chatter-group-governance— the group population itself: creation policy, public/private/unlisted, archiving, ownership transfer (this skill tunes what is noisy inside a group; that one governs which groups exist)apex/apex-custom-notifications-from-apex— Custom Notification API patterns, recipient targeting, target page navigationapex/apex-connect-api-chatter— programmatic feed manipulation via Connect APIadmin/email-deliverability-strategy— broader email volume management beyond Chatter