Skip to main content

Salesforce Event Monitoring

Fluency collects Salesforce Event Monitoring data — the EventLogFile records covering logins, API calls, report exports, file transfers and Apex execution — through the Salesforce REST API. Salesforce commonly holds the most commercially sensitive data an organization owns, and the ordinary administrative interface does not show who bulk-exported it. Event Monitoring does, which makes it the primary source for insider-risk and departing-employee investigations as well as for spotting API abuse by a compromised integration user.

Authentication is machine-to-machine. You create a dedicated Salesforce user, grant it exactly two permissions, and create an app that uses the OAuth 2.0 client credentials flow with that user as its Run As identity. Fluency then holds a consumer key and secret rather than anyone's password, and every call it makes is attributable to that one user in your own audit trail. No user account signs in interactively, nothing is installed, and nothing is pushed — Fluency polls.

The setup has two halves:

  1. In Salesforce — create the integration user, a permission set, and an app with the client credentials flow enabled. This produces three values: an API URL, a Consumer Key and a Consumer Secret.
  2. In Fluency — install the Salesforce Event Monitoring application template and paste those three values into its form.

What arrives

StreamSalesforce objectPermissionContents
Event log filesEventLogFile, downloaded through its LogFile blobView Event Log Files + API Enabled, on the run-as userOne CSV per event type per interval — roughly 50 event types with the Event Monitoring add-on, 7 without

Salesforce does not stream events. It batches them into log files: one file per event type per day, and — for orgs with the add-on — one or more files per event type per hour. Fluency queries EventLogFile for new records and downloads each file's contents. That batching is the single most important thing to understand about this integration, because it means data is expected to be hours old. See Confirm it is running.

The event types security teams reach for

Salesforce publishes around 50 event types; the EventLogFile Supported Event Types reference lists them all. These are the ones that answer the questions a SOC usually asks:

Event typeAnswersIn the free set?
Login, LogoutWho authenticated, from where, with which client and login typeYes
LoginAsWhich administrator impersonated which userNo
API, RestApi, BulkApiWhat an integration or script called, how much it pulled, and how long it tookNo
ApiTotalUsageTotal API call volume for the orgYes
ReportExportWho exported which report — the departing-employee questionNo
ContentTransferFile and attachment downloadsNo
ApexExecution, ApexCalloutCustom code runs and outbound calls from ApexNo
URI, LightningPageViewPage-level user activity. High volume — enable deliberatelyNo

Everything here is read-only. Fluency never writes back to Salesforce.

Before you begin

The Event Monitoring add-on — this is a hard gate. Sold standalone or as part of Salesforce Shield. Without it, Salesforce generates only the free subset — Apex Unexpected Exception, CORS Violation Record, CSP Violation, Hostname Redirects, Login, Logout and API Total Usage — with 1-day retention, delivered only in the 24-hour files. Report exports, file transfers and API detail are not generated at all. The integration installs and runs perfectly well against an org without the add-on; it just collects very little. Confirm the licence with your Salesforce account executive before spending anyone's time.

With the add-on you get the full event-type set, hourly files, and 30-day retention on-platform — extendable to a year through the opt-in setting Salesforce added in Summer '24.

A Salesforce administrator who can create users, create and assign permission sets, and create apps in Setup.

A spare Salesforce Integration user licence. Enterprise, Unlimited and Performance orgs are provisioned with five at no cost; Developer Edition has them too. Check under Setup → Company Information before you start, because running out mid-way is an unhelpful place to stop.

Whether you can create a Connected App depends on when your org was created

Salesforce is retiring Connected Apps in favour of External Client Apps. From Winter '26, new-org Connected App creation is off by default; from Spring '26, turning it back on requires a request to Salesforce Support. If New Connected App is greyed out in your org, that is why — use the External Client App path in step 4. Existing Connected Apps keep working.

Access to the Fluency portal with permission to install applications.

Part 1 — Salesforce

All of this happens in Salesforce Setup. Work through the four steps in order: the app in step 4 cannot be finished until the user from step 1 exists, because Salesforce will not save a client credentials flow without a run-as identity.

1. Create the integration user

The client credentials flow has no interactive login. Salesforce executes every API call as a designated user, so that user's permissions are the integration's permissions. Give it a dedicated account — not a person's, and emphatically not a System Administrator's.

Go to Setup → Users → Users → New User.

  • Last Name / Alias — something you will recognise in a log, Integration / si here.
  • Email — a mailbox an administrator monitors. Salesforce sends the activation mail there.
  • Username — must be unique across all of Salesforce and formatted like an email address, though it need not be a real mailbox.
  • User LicenseSalesforce Integration.
  • ProfileMinimum Access - API Only Integrations.

New User form with User License set to Salesforce Integration and Profile set to Minimum Access - API Only Integrations

That profile is the point of the exercise: it grants API Enabled and Api Only User and nothing else, and the API-only restriction cannot be edited off. The user can call REST, SOAP and Bulk API; it cannot sign in to the web interface at all.

Click Save.

2. Create the permission set

The Salesforce Integration licence deliberately grants almost nothing. Everything the integration needs beyond bare API access arrives through a permission set, and that permission set needs a permission set licence attached before it is allowed to hand out the permission we want.

Go to Setup → Permission Sets → New.

  • LabelSIEM Integration, or whatever your naming convention gives you.
  • API Name — fills itself in.
  • License — leave at --None--. This is the permission set licence field, and setting it here would lock the set to one user-licence type; the entitlement we need is assigned separately, below.

Permission Set Create form with the label SIEM Integration and License left at None

Click Save.

Enable the Salesforce API Integration permission set licence

On the new permission set, open Permission Set License Assignments — reachable from the permission set overview — and tick Salesforce API Integration.

Permission Set License Assignments with Salesforce API Integration enabled

Salesforce's own wording on that page explains why this exists: "Some permissions can only be granted to users who have a specific permission set license." The Salesforce API Integration licence entitles a Salesforce Integration user to the same permissions a System Administrator could hold — but only entitles. It grants nothing by itself. The permission set below is what actually grants.

Skip this and View Event Log Files either will not appear or will not stick when you save.

Grant the two system permissions

Open System Permissions → Edit on the permission set and tick exactly two:

  • API Enabled"Access any Salesforce.com API."
  • View Event Log Files"Get CSV event log files via the API."

System Permissions showing API Enabled ticked, and View Event Log Files ticked with the description Get CSV event log files via the API

Do not grant View All Data. It also unlocks EventLogFile, which is why it turns up in older integration guides, and it hands the integration read access to every record in the org along the way. Two permissions are enough.

Save, and confirm on the dialog Salesforce raises:

Permission Changes Confirmation dialog listing Api Only User, View Event Log Files and API Enabled under Enabled

3. Assign the permission set to the integration user

On the permission set, click Manage Assignments → Add Assignments, pick the integration user, and Assign. Or from the other direction — the user record's Permission Set Assignments related list, Edit Assignments, move SIEM Integration into Enabled Permission Sets.

Permission Set Assignments with SIEM Integration moved into the Enabled Permission Sets column

The user record is where to check the result. Both related lists should have a row:

User record showing Permission Set Assignments with SIEM Integration and Permission Set License Assignments with Salesforce API Integration

If Permission Set License Assignments is empty, the entitlement from step 2 did not reach the user and the API calls will be denied later with an access error rather than an authentication one.

4. Create the app and enable the client credentials flow

Two app types can do this. External Client Apps are the current model and the one to use for anything new; Connected Apps are the legacy model, still fully functional where one already exists, but increasingly hard to create. The screenshots below are from a Connected App.

Go to Setup → App Manager → New External Client App.

Lightning Experience App Manager with the New External Client App button, and a Connected App listed below

Basic Information — give it a name (Fluency Integration is the name Fluency's own onboarding tooling uses) and a contact email.

API (Enable OAuth Settings)

  • Tick Enable OAuth.
  • Callback URL — required by the form even though this flow never redirects a browser anywhere. https://login.salesforce.com is a safe placeholder.
  • Selected OAuth Scopes — add Manage user data via APIs (api), and nothing else. See the warning below.

Flow Enablement — tick Enable Client Credentials Flow and accept the security warning.

Create.

Now set the run-as identity, which lives on a different tab. Go to Setup → External Client App Manager, open the app, and on the Policies tab click Edit. Under OAuth Policies → OAuth Flows and External Client App Enhancements, tick Enable Client Credentials Flow and set Run As (Username) to the integration user from step 1. Save.

Yes, that is the same checkbox name in two places. The one on the app enables the flow; the one on the policy, together with Run As, authorises it.

Select the api scope — and only the api scope

Since Winter '26 (production rollout from 19 September 2025) Salesforce validates scopes on the client credentials flow instead of silently issuing a token. A token request whose app carries no supported scope now fails:

{"error":"invalid_grant","error_description":"no valid scopes defined"}

Full access (full), Manage user data via Web browsers (web) and Perform requests at any time (refresh_token, offline_access) are not supported for this flow — the last two because it never issues a refresh token and never involves a browser. An app configured years ago with Full access (full) and nothing else looks correct in the interface and no longer works. Add Manage user data via APIs (api) and remove the rest; api alone is also the least privilege that works, so this is a change worth making on its own merits.

The consumer key and secret are the whole credential

Salesforce's own warning for this flow: anyone holding the app's consumer key and secret can obtain an access token as the run-as user. There is no second factor and no user approval step. The only control is how little that user can do — which is why step 1 creates a dedicated API-only account with two permissions rather than reusing an administrator's.

5. Collect the consumer key and secret

External Client AppSetup → External Client App Manager, open the app, Settings → OAuth Settings, and reveal the consumer key and secret there.

Connected AppSetup → App Manager, the row's dropdown → View, then API (Enable OAuth Settings) → Manage Consumer Details. Salesforce re-verifies your identity with a code emailed to you before it shows them.

Manage Connected Apps Consumer Details page with the consumer key and secret redacted, and the Staged Consumer Details section below

The Staged Consumer Details section on the same page is the rotation mechanism — see Rotating the consumer secret.

Unlike most OAuth secrets, this one is re-readable: you can come back to this page later rather than losing it forever. Treat it as a credential anyway.

6. Find the API URL

Fluency's API URL is the org's My Domain login URL. Read it from Setup → My Domain → Current My Domain URL:

https://<MyDomainName>.my.salesforce.com

Sandboxes take the form https://<MyDomainName>--<SandboxName>.sandbox.my.salesforce.com.

Not the URL in your address bar

The https://<MyDomainName>.lightning.force.com address you are looking at while you read this is the application URL, for the Lightning interface. It is not a supported API host, and pasting it into Fluency produces a connector that authenticates against nothing. The value you want ends in .my.salesforce.com.

You should now be holding all three values:

ValueWhere it came from
API URLSetup → My Domain → Current My Domain URL
Consumer KeyThe app's OAuth settings / Manage Consumer Details
Consumer SecretThe same page, next to the key

Verify before you leave Salesforce

One request tells you whether all four of the previous steps landed, and it is much easier to read an error here than to infer one from an application that installs and quietly collects nothing:

curl -s -X POST "https://<MyDomainName>.my.salesforce.com/services/oauth2/token" \
-d "grant_type=client_credentials" \
-d "client_id=<consumer key>" \
-d "client_secret=<consumer secret>"

A working setup returns an access_token and an instance_url. The failures map cleanly onto the steps above:

ResponseWhat it means
invalid_grant / no valid scopes definedThe app has no supported scope — step 4, add Manage user data via APIs (api)
invalid_grant / no client credentials user enabledNo Run As user on the flow — step 4, the policies half
invalid_clientThe consumer key or secret is wrong, or the client credentials flow is not enabled on the app
unsupported_grant_typeThe request reached a .lightning.force.com host — step 6

Part 2 — Fluency

Install the Application

Go to Platform → Applications → Install Application From Template and choose Salesforce Event Monitoring from the Cloud-based Business Softwares category.

ParameterNotes
API URLThe org's My Domain login URL, ending .my.salesforce.com.
Consumer KeyThe app's consumer key.
Consumer SecretThe app's consumer secret. Masked once the application is installed.
datalakePre-filled managed.
datalake index namePre-filled Salesforce. Two applications writing to the same index name in the same lake will collide, and the second one aborts.

The last two sit in the collapsed Advanced Configurations row below the parameters. The defaults are usually right; the reason to change the index name is a second Salesforce org, which is the same case that calls for a distinct Name and Display Name.

Leave Name and Display Name at default / Default unless you are installing a second instance — collecting from a sandbox alongside production, for example — in which case give each one a name you will recognise on the applications list.

Setup New Salesforce Event Monitoring Application panel

Press Install. The application then appears in the Installed Applications view, where its badge reads Running once the pipeline is up.

Confirm it is running

The application appears under Installed Applications as Salesforce Event Monitoring: Default. Its badge should read Running in green within a few seconds; Aborted in red means the pipeline stopped on an error, and the details panel will carry the message. Every field in that panel is read-only — changing a credential means uninstalling and installing again.

Give the first collection hours, not minutes

An empty index shortly after install is the expected result, not a fault. Salesforce's own figure for hourly files is that an event takes three to six hours from occurring to appearing in a log file, "however, it can take longer". An org without the Event Monitoring add-on gets 24-hour files only, so the wait is up to a day. Do not start debugging inside that window — there is nothing to see yet, and every symptom looks the same.

Once the window has passed, see Confirm data is arriving for how to check that events are landing. Login events are the sensible thing to search for first: every org has them, and they are in the free set, so they arrive whether or not the add-on is in place.

Maintenance

Rotating the consumer secret

The consumer secret does not expire on a schedule, so rotation is your choice rather than a deadline — but rotate immediately if it has ever been exposed, because it is a complete credential on its own.

Salesforce supports staged rotation, which is what makes this possible without an outage:

  1. On Manage Consumer Details (Connected App) or the app's OAuth settings (External Client App), click Generate under Staged Consumer Details. The current key and secret keep working.
  2. Click Apply. The staged values replace the originals; the old secret stops working at this point.
  3. In Fluency, uninstall the Salesforce Event Monitoring application and install it again with the new values. Template parameters are read-only after install, so reinstalling is the supported way to change a credential.

Collection resumes from wherever the new instance starts. Because Salesforce keeps log files for 30 days with the add-on — one day without it — a short gap during a planned rotation is recoverable in the first case and not in the second.

Removing the integration

Uninstall the application in Fluency first, using the red Uninstall button at the foot of its details panel, so the pipeline stops cleanly before its credentials do.

On the Salesforce side, delete the app — that revokes the key and secret. Then deactivate or freeze the integration user, which both stops it being usable and frees the Salesforce Integration licence. Deleting the permission set is optional; leaving it costs nothing and makes the integration easy to rebuild.

Troubleshooting

SymptomLikely causeFix
Nothing arrives in the first few hoursNormal. Salesforce batches event logs; hourly files lag activity by 3–6 hours, daily files by up to 24Wait out the window before investigating anything else
Token request returns no valid scopes definedThe app carries no scope supported by the client credentials flow — typically Full access (full) and nothing elseAdd Manage user data via APIs (api) to Selected OAuth Scopes, remove full, web and refresh_token (step 4)
Token request returns no client credentials user enabledNo Run As user is set on the flowSet it on the app's policies (step 4)
Token request returns invalid_clientConsumer key or secret mistyped, or Enable Client Credentials Flow was never ticked on the appRe-copy both values (step 5); check the flow is enabled
Authentication succeeds but queries are denied or return nothingThe run-as user is missing View Event Log Files or API Enabled, or never received the Salesforce API Integration permission set licenceCheck both related lists on the user record (step 3)
The connector cannot reach the org at allA .lightning.force.com address was used as the API URLUse the My Domain login URL ending .my.salesforce.com (step 6)
Logins and logouts arrive but report exports never doNo Event Monitoring add-on — the org only generates the free subsetConfirm the licence with your Salesforce account executive
It worked, then stopped after an app changeSalesforce takes up to ten minutes to apply Connected App changes, and applying staged consumer details invalidates the old secret immediatelyWait ten minutes; if a rotation was applied, reinstall with the new values
Volume jumped sharplyURI or LightningPageView were enabled — these record page-level activity for every userExpected. Decide whether you want them before the next invoice

Under the hood

Engineering reference — how EventLogFile collection works

This describes the Salesforce API mechanics any EventLogFile collector has to follow. Administrators connecting Salesforce do not need it.

Authentication

POST https://<MyDomainName>.my.salesforce.com/services/oauth2/token
grant_type=client_credentials
client_id=<consumer key>
client_secret=<consumer secret>

The response carries access_token and instance_url; subsequent calls use Authorization: Bearer <access_token> against instance_url. There is no refresh token — the flow is cheap enough to simply repeat when the token expires, which is why refresh_token/offline_access are not valid scopes for it.

Discovery and download — two calls per file

EventLogFile records are metadata. Querying them tells you which files exist; the contents live behind a separate blob fetch.

GET /services/data/v<version>/query?q=SELECT+Id,EventType,LogDate,Interval,Sequence,LogFileLength+FROM+EventLogFile+WHERE+LogDate+>+<watermark>
GET /services/data/v<version>/sobjects/EventLogFile/<Id>/LogFile

The second call returns CSV, with the column names also available on the record's LogFileFieldNames. Every event type has its own column set — a Login file and a ReportExport file share almost nothing beyond TIMESTAMP, USER_ID and EVENT_TYPE — so parsing is per-type, not generic.

Hourly versus daily, and why both exist

Two fields disambiguate:

Hourly24-hour
IntervalHourlyDaily
Sequence1 and upward0
DeliveryOne or more files per hour of activityOne file per day of activity
Incremental behaviourA new file is created and Sequence incrementsA new file replaces the original for that date

Hourly files require the Event Monitoring add-on. The two sets overlap — the same event appears in an hourly file and, later, in that day's daily file — so a collector that ingests both without filtering on Interval will duplicate everything.

Retention and latency

  • Free subset (no add-on): 1 day retention, daily files only.
  • With the add-on: 30 days on-platform, extendable to one year through the Event Monitoring settings opt-in Salesforce added in Summer '24.
  • Latency: Salesforce states three to six hours for hourly files, with an explicit "can take longer"; daily files up to 24 hours.

Retention is the real constraint on outage tolerance. A collector that is down for a week recovers everything on an org with the add-on and nothing on an org without it.

API consumption

Both calls count against the org's daily API request limit — one query plus one download per file, and the file count scales with the number of enabled event types. ApiTotalUsage is where that consumption shows up in the org's own telemetry, which makes this integration self-observing: you can watch its cost in the data it collects.

References

Salesforce

Fluency