Skip to main content

Office365 Audit API

The Office365 template collects the Office 365 unified audit log — mail, file, site, Teams, administrative and DLP activity from every Microsoft 365 workload — through the Office 365 Management Activity API. It is the single highest-value feed most Microsoft 365 tenants have, because it records what an attacker does after they are already inside.

What makes this page different from most integration guides is that you never open the Microsoft portal. There is no application to register, no permission to tick and no client secret to copy. Instead you supply one administrator's email address, and Microsoft asks that administrator to approve a Fluency application that already exists. The credential is a tenant-wide consent grant, and it belongs to your tenant rather than to any person in it.

That has a consequence worth understanding before you start: the person who finishes this setup may not be you. The install puts a decision in someone else's inbox, and until they act on it, the application will sit there reporting healthy and collecting nothing.

The setup has two halves, and they run in the opposite order to every other guide in this manual:

  1. In Fluency — install the Office365 application template with an administrator's email address. This is what sends the approval request.
  2. In Microsoft — that administrator opens the request, reviews the permissions, and accepts.

Which of the four Microsoft 365 pages you want

Four templates collect Microsoft 365 audit data and they overlap heavily. Three of them — this one, Office 365 Resource Watch and Azure Directory Audit — are authorised by administrator consent and are documented on their own pages because each asks for a different permission set and lands in a different place. The fourth, Office 365 Audit, does all three jobs in one application and authenticates with a client secret you create.

Office365
(this page)
Azure Directory AuditOffice365 Resource WatchOffice365-Audit
GuideThis pageAzure AD auditResource WatchOffice 365 Audit
CollectsThe unified audit logEntra directory auditTenant resource inventoryAll three
Default authenticationAdministrator consentAdministrator consentAdministrator consentClient secret only
What you supplyAn admin's email addressAn admin's email addressAn admin's email addressTenant ID, client ID, client secret
Who finishes the setupAn administrator, in their mailboxAn administrator, in their mailboxAn administrator, in their mailboxYou
Lands inIndex Office365Index AzureAuditSIEM → ResourcesBoth indexes and Resources
Entra applicationFluency Office365 Event ReaderFluencyAzureADAuditFluencyOffice365ResourceWatchOne you register yourself
The three consent templates are three separate applications, not one

Each of the three is a distinct application in Microsoft Entra ID with its own name and its own permission set, so consenting to one does not consent to the others. Installing all three means three consent requests, three approvals and three entries under Enterprise applications.

If you want all three feeds and would rather approve once, that is the argument for Office 365 Audit — one application, one credential, one thing to rotate. The argument against it is that it needs an app registration and a client secret that expires. Neither route collects more data than the other.

This template and Office365-Audit cannot both run at their defaults

Both write to a data-lake index named Office365, and two applications writing to the same index name in the same lake will collide — the second one aborts. Either change the index name on whichever you install second, or uninstall the other first. See Troubleshooting.

What arrives

Events land in the data-lake index named on the install form, Office365 by default. The Management Activity API is subscription-based rather than a plain query: Fluency starts a subscription per content type, then polls for content blobs and fetches each one.

Content typeCoversBacked by
Audit.AzureActiveDirectorySign-in and directory activity as it appears in the unified logActivityFeed.Read
Audit.ExchangeExchange Online administrative and mailbox activityActivityFeed.Read
Audit.SharePointSharePoint and OneDrive file and site activityActivityFeed.Read
Audit.GeneralEverything else — Teams, Power Platform, Purview, Viva EngageActivityFeed.Read
DLP.AllDLP events across all workloads, including the detected sensitive dataActivityFeed.ReadDlp
Service messagesService-health and incident messagesServiceHealth.Read
Checking which content types your install subscribes to

The five above are what the API offers and what the consented permissions cover. The subscriptions the Office365 template actually starts are recorded on the install itself: open the application's details panel with the blue eye button and read its Actions list, which lists every object the install created.

Audit.AzureActiveDirectory is worth a note, because it is why this template and Azure Directory Audit look like they overlap and do not. Both carry directory activity, but from different sources with different shapes: this one is Entra activity as Microsoft 365 records it in the unified log, and the other is the Entra directory audit read directly from Microsoft Graph. They are different records of overlapping events, retained under different rules — so running both is duplication in coverage, not in data.

Duplicates are the specification, not a fault

Microsoft states plainly that this feed repeats events on purpose, and that deduplicating them is the consumer's job:

This duplication of events is an expected and designed behavior intended to prevent the loss of any audit events. When an error is encountered by an audit event, the audit service retries the process, which may result in duplicate audit events being generated and stored in the audit log storage(s). While exporting audit search results using the UI from the Microsoft Purview portal automatically removes duplicates, the Office 365 Management Activity API does not have this de-duplication feature. Consequently, duplicates may appear in the results returned by the Office 365 Management Activity API. It is the responsibility of the SIEM solution to implement logic to remove such duplicated events upon ingestion.

Three consequences follow, and all three produce support tickets from people who think something is broken:

  • Counts will not reconcile against Purview. The portal's audit search deduplicates; this API does not. A higher count in Fluency than in Purview for the same window is the expected result, not evidence of double ingestion.
  • Deduplicate on the record's Id, the GUID that uniquely identifies an audit record, and sort on CreationTime rather than on arrival order — a later content blob can contain earlier events.
  • You cannot cross-check with Microsoft's help. "Microsoft doesn't provide any kind of a log that allows you to cross-check any given application or third-party (ISV) application." If you need assurance that nothing was missed, the check has to be built from the content blobs themselves.

Latency is loose for the same reason — Microsoft trades speed for completeness and declines to commit to a number. There is "no guaranteed maximum latency for notification delivery (in other words, there is no SLA)"; typically "most notifications are sent within one hour of the event", and for "core services (such as Exchange, SharePoint, OneDrive, and Teams) event availability is typically 60 to 90 minutes." Microsoft's stated reason is worth quoting to anyone who asks why this feed is not real-time: "Because audit events are often used for forensic investigations, Microsoft prioritizes data completeness over latency."

Judge this integration's health over hours, not minutes, and never alert on the absence of an event within a short window.

Retention, and why an outage costs you

Two separate clocks apply, and confusing them is the usual way people misjudge how urgent an outage is.

  • Microsoft's retention of the audit log itself. Managed by Microsoft Purview. Microsoft notes that "The default retention period for Audit (Standard) changed from 90 days to 180 days. Audit (Standard) logs generated before October 17, 2023 are retained for 90 days. Audit (Standard) logs generated on or after October 17, 2023 follow the new default retention of 180 days." Audit (Premium) retains Exchange, SharePoint, OneDrive and Entra records for one year.
  • How far back the API will actually serve. Much shorter. Content blobs expire after 7 days, startTime may not be more than 7 days in the past, and a single listing call may not span more than 24 hours.

Microsoft draws the distinction itself, and it is the clearest statement of the risk on this page:

The data that is returned by both methods is the same. The only difference is that with the API, you can get data for the last 7 days only [...] When searching the audit log in the Security & Compliance Center (or by using the corresponding Search-UnifiedAuditLog cmdlet in Exchange Online PowerShell), you can get data for the retention period in effect when the data is generated (for example, 90 days or one year).

So the audit log in Purview is not the thing Fluency reads. If collection stops for longer than a week, the events are still searchable in Purview and are permanently unavailable to the integration. Treat a week as the hard deadline for fixing a broken collection, not a month — and note that the recovery path is a Purview export, not a backfill.

Before you begin

Unified audit logging must be on in the tenant. Nothing reaches the Management Activity API until it is. Microsoft is explicit: "Before you can access data through the Office 365 Management Activity API, you must enable unified audit logging for your Office 365 organization." It is on by default for most Microsoft 365 organizations, but not for Business Basic, Business Standard or Business Premium, and not for unmanaged tenants on an enterprise free trial. Check from Exchange Online PowerShell:

Get-AdminAuditLogConfig | Format-List UnifiedAuditLogIngestionEnabled

True means you are covered. If it returns False, turn it on in the Microsoft Purview portal — the Audit solution card shows a Start recording user and admin activity banner — or run:

Set-AdminAuditLogConfig -UnifiedAuditLogIngestionEnabled $true
Turning auditing on is not retroactive

Microsoft notes that "The unified audit log configuration change can take up to 60 minutes to take effect." Nothing that happened before you switched it on is ever recoverable — the audit log only holds what it was recording at the time. Turn it on, then let it run for a day before concluding the integration is broken.

Changing that setting needs the Audit Logs role in Exchange Online; by default the Compliance Management and Organization Management role groups hold it.

An administrator who can consent on the tenant's behalf. This is the prerequisite people get wrong, because "an administrator" is not specific enough. Microsoft's requirement is:

Privileged Role Administrator, for granting consent for apps requesting any permission, for any API.

Cloud Application Administrator, AI Administrator, or Application Administrator, for granting consent for apps requesting any permission for any API, except Microsoft Graph app roles (application permissions).

The permissions this application requests are Office 365 Management API application permissions plus one delegated Graph permission — no Microsoft Graph app roles — so a Cloud Application Administrator or Application Administrator should be able to approve it, and a Global Administrator certainly can. That is not true of the other two consent templates, which do request Graph app roles and therefore need Privileged Role Administrator or Global Administrator.

That reading comes from the consent screen this guide reproduces, which is an older capture than the ones on the other two pages; if the application's permission set has grown a Graph app role since, the stricter rule applies instead. If you are not sure, send it to a Global Administrator and the question does not arise.

The address you supply has to be a working mailbox that the approver reads, because that is where the request lands.

Access to the Fluency portal with permission to install applications.

Part 1 — Fluency

Install the Application

Go to Platform → Applications → Install Application From Template and choose Office365 from the Office Software (SaaS) and IAM category.

ParameterNotes
Enable Admin Consent Mode?Checkbox, ticked by default. Leave it ticked for the route this page documents. Unticking it changes the form — see If your tenant will not allow the consent route.
Admin Email addressThe administrator who will be asked to approve. Must be a mailbox they actually read.
datalakePre-filled managed.
datalake index namePre-filled Office365. Two applications writing to the same index name in the same lake will collide, and the second one aborts.

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

Setup New Office365 Application panel with Enable Admin Consent Mode ticked and an Admin Email address field

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

Running does not mean working — it means waiting

The badge reflects the pipeline, not the consent. Until the administrator approves in Microsoft, this application is a correctly-built pipeline with nothing to read, and it looks identical to one that is collecting normally.

There is no "waiting for approval" state to watch for, so do not use the badge to decide whether the request was received or acted on. The check that actually answers the question is on the Microsoft side, in Confirm it is running.

Some organisations disable user and admin consent to third-party applications outright. If yours does, or if you would simply rather hold your own credential, untick Enable Admin Consent Mode — the form replaces the email field with three others:

ParameterNotes
Azure Tenant IDEntra's Directory (tenant) ID.
Azure ClientIDEntra's Application (client) ID for an app registration you create.
Azure ClientSecretThat registration's client secret Value.

This is the same credential shape Office 365 Audit uses, and the app registration is built the same way — follow Part 1 of that page, but add only the three Office 365 Management API permissions (ActivityFeed.Read, ActivityFeed.ReadDlp, ServiceHealth.Read) if this template is all you are installing.

Before you go that way, consider installing Office365-Audit instead

If you are registering an application and managing a client secret anyway, Office 365 Audit gives you the directory audit and sign-in logs from the same registration and the same secret, for a few more permission checkboxes. Unticking this box is the right answer when you specifically want only the unified audit log; it is the wrong answer if you were going to install the other templates too.

Part 2 — Microsoft

This half is performed by the administrator whose address you entered — possibly you, possibly someone in a different team who has no idea why they are being asked. Everything below is written for them.

What the administrator receives

Fluency sends a message to that address containing a link to Microsoft's admin consent page for the application. Opening it takes them to login.microsoftonline.com, where they sign in and are shown the permission request.

If the administrator reports nothing arriving

Check spam quarantine first, and treat a missing message as a support question rather than a reason to reinstall — reinstalling changes nothing about how the request is delivered.

Review the request and accept

The consent screen is the only security control in this entire procedure. It is worth reading rather than clicking through.

Microsoft consent prompt headed Permissions requested, Accept for your organization, for the application Fluency Office365 Event Reader from fluencysecurity.com, listing four permissions

Four permissions are requested:

Consent screen wordingPermissionWhat it buys
Read activity data for your organizationActivityFeed.ReadThe unified audit log. This is the one the integration exists for.
Read DLP policy events including detected sensitive dataActivityFeed.ReadDlpDLP events, including the sensitive content that triggered them.
Read service health information for your organizationServiceHealth.ReadService-health and incident messages.
Sign in and read user profileUser.Read (delegated)Identifies the signing-in administrator during the consent itself. It is not used for collection.

Three checks before accepting:

  1. The application is Fluency Office365 Event Reader and the publisher is Fluency — fluencysecurity.com, or Fluency Corp. with a verified-publisher badge. If the name or the domain is anything else, stop.
  2. The list matches the table above. A consent screen asking for more than this is not this integration.
  3. The screen says Accept for your organization or Review for your organization. That is the tenant-wide grant. A screen without it is granting something narrower and the integration will not work.
"This application is not published by Microsoft" is normal

Older captures of this screen carry that line, and it means only that Fluency is a third party — which is true of every SIEM integration. It is not a warning about this specific application. What is worth attention is the publisher name and domain immediately above it, and the permission list below it.

ActivityFeed.ReadDlp is the one that deserves a second thought

The other permissions return metadata about activity. This one returns DLP events including the detected sensitive data — which is to say, excerpts of the content that matched a DLP rule: card numbers, identifiers, whatever your policies look for.

That is genuinely useful for investigating a data-loss alert, and it is also the broadest thing on the list. If your organisation would rather Fluency never held that content, this is the point to decide it — and the answer is to use the client-secret route with only ActivityFeed.Read and ServiceHealth.Read granted, because the consent screen is all-or-nothing. You cannot accept three of four permissions.

Click Accept. Microsoft records the grant against the tenant and redirects back.

If the administrator clicks Cancel, Microsoft returns AADSTS65004: The resource owner or authorization server denied the request. and no grant is created. Nothing on the Fluency side reports this.

What the grant is, and what it is not

Once accepted, the grant lives on the tenant, not on the administrator who approved it. Microsoft's own description of the flow makes the consequence explicit — after the initial consent, the application requests tokens that "include information only about the requesting application and not about the admin that originally granted consent."

In practice:

  • It does not expire. There is no secret to rotate and no renewal date to miss.
  • It survives the approver leaving. Deactivating the administrator who clicked Accept does not affect collection. This is the main practical advantage of the consent route over a client secret, and it is the opposite of how a personal API token behaves.
  • It is revocable by anyone with the role, from Enterprise applications, without telling Fluency. See Maintenance.
  • It does not grant a person anything. No Fluency user gains access to your tenant; the grant is to the application, which reads the audit feed and nothing else.

Confirm it is running

Because Running only reports the pipeline, confirm on the Microsoft side first — that is where the answer to "did the approval actually happen" lives.

In the Microsoft Entra admin center, go to Entra ID → Enterprise apps → All applications and search for Fluency Office365 Event Reader. If consent was granted, the application is there. If it is not, the request was never accepted, and nothing else you check will tell you that.

Enterprise application entry for Fluency Office365 Event Reader showing the Properties page with Name and a Delete button in the command bar

To see exactly what was granted, open Security → Permissions. The Admin consent tab lists every permission with a Type column separating Delegated from Application — which matters, because several Microsoft permissions display the same wording for both.

Entra ID Permissions page for an enterprise application, showing the Grant admin consent button, the Admin consent and User consent tabs, and a permissions table with API Name, Claim value, Permission and Type columns

Then, on the Fluency side, check that events are landing in the Office365 index — see Confirm data is arriving.

Check that events are present, not that the count matches anything. As Duplicates are the specification explains, this feed repeats events on purpose and Microsoft's own portal deduplicates where the API does not — so a Fluency count higher than the same Purview search is the expected outcome, and reconciling the two is not a meaningful health check.

Give the first collection a day

The Management Activity API only delivers to an active subscription, and Microsoft warns that on a newly created one it can take up to 12 hours for the first content blobs to become available. An empty Office365 index on the afternoon of the install is expected, not a fault.

Maintenance

There is no credential to rotate

That is the whole point of this route. No secret expires, so nothing here needs a calendar entry. What can still break the integration is someone revoking the consent, so the maintenance task is review, not rotation.

Reviewing and revoking the grant

Anyone with the right role can revoke this at any time, from Entra ID → Enterprise apps → All applications → <the application> → Permissions → Admin consent, using the ... control on a permission row and selecting Revoke permission.

The Admin consent tab of an enterprise application&#39;s Permissions page, with the Revoke Permission control open on a permission row

Two things about revocation are worth knowing in advance, because both produce support tickets:

  • Nothing tells Fluency. Collection stops and the application keeps reporting Running. If a feed goes quiet with no other explanation, check whether the grant is still there before checking anything else.
  • Revoking does not stop the application being re-consented. Microsoft notes that "Revoking the current granted permission doesn't stop users from re-consenting to the application's requested permissions." If the intent is to block it permanently, delete the enterprise application, or restrict consent at the tenant level.

Changing which administrator is named

The address on the form is only used to deliver the approval request. Once consent is granted, it has no further role, and the named administrator has no standing relationship with the integration — so there is nothing to update when they change jobs.

Removing the integration

Uninstall the application in Fluency first, with the red Uninstall button at the foot of its details panel, so the pipeline stops cleanly. Then, in Microsoft, delete the enterprise application (Properties → Delete) or revoke its permissions as above.

Troubleshooting

SymptomLikely causeFix
The application shows Aborted immediately after installThe index name is already taken in that lake — usually Office365 from Office365-AuditThe details panel names the index in red. Uninstall the conflicting application, or reinstall this one with a different index name.
Running, but the index is empty and has always been emptyThe consent was never grantedSearch Enterprise applications for Fluency Office365 Event Reader. If it is absent, the request was not accepted — resend it.
The administrator never received anythingWrong address, or the message was quarantinedCheck the address on the install form, then spam quarantine. Reinstalling does not change how the request is delivered.
The administrator sees Need admin approval rather than a consent screenAADSTS90094 — the signed-in account is not authorised to consentThe account needs Cloud Application Administrator, Application Administrator or Global Administrator. Have someone who holds one open the same link.
Consent fails with AADSTS65004The administrator selected CancelSend the request again. No grant was created.
Running and consent is granted, but the index is still empty after a few hoursUnified audit logging is off, or the subscription is inside its first-content windowRun Get-AdminAuditLogConfig | FL UnifiedAuditLogIngestionEnabled. If True, wait out the 12 hours.
Collection worked for months, then stopped, with no change on our sideThe grant was revoked, or the enterprise application was deletedCheck Enterprise applications → Permissions → Admin consent. Re-consent if it is gone.
Events arrive hours late, or out of orderNormal for this APIMicrosoft gives no latency SLA and does not guarantee ordering. Judge freshness over hours, not minutes, and sort on CreationTime rather than arrival order.
The same event appears more than onceNormal for this APIMicrosoft duplicates deliberately, to avoid losing events, and states that deduplication is the SIEM's job. Deduplicate on the record's Id.
Fluency's event count is higher than the same search in PurviewNormal for this APIThe Purview portal deduplicates its own search results; the API does not. The counts are not meant to match.
Events are missing for a period longer than a weekContent blobs expiredUnrecoverable through the API. The records are still in Purview and can be exported from there, but the integration cannot backfill them.
The stream fails with AF20022No subscription exists for that content typeMicrosoft throttles /subscriptions/start to one call per content type per 15 minutes. Wait, then uninstall and reinstall so the subscriptions are started again.
Microsoft.Office.Compliance.Audit.DataServiceException: Tenant <tenantID> does not existUnified audit logging has never been enabledEnable it, then allow 60 minutes plus several hours before the first events are retrievable.

Under the hood

Engineering reference — the consent flow and the Management Activity API

The application Fluency operates is registered as multi-tenant in Fluency's own directory. Microsoft requires this for the pattern: "This property must be set to YES to allow tenant admins to grant consent to your app to access their data by using the Office 365 Management APIs."

When the administrator approves, they are sent to Microsoft's admin consent endpoint:

https://login.microsoftonline.com/{tenant}/v2.0/adminconsent
?client_id={fluency-client-id}
&scope=https://manage.office.com/.default
&redirect_uri={fluency-callback}
&state={opaque}

Approval produces a redirect carrying admin_consent=True and the tenant GUID. Microsoft's guidance explains why the form never asks for a tenant ID: "The tenant ID must be extracted from the access token and stored for future use." Older versions of this integration did ask for it, on a Cloud Integrations page that no longer exists — that requirement is gone, not overlooked.

From then on, collection uses the client credentials grant, which "allows your application to request subsequent access tokens as old ones expire, without requiring the tenant admin to sign in and explicitly grant consent."

Two artefacts result, and they behave differently:

  • Application permissions produce an appRoleAssignment on the service principal.
  • Delegated permissions consented tenant-wide produce an oauth2PermissionGrant with consentType: AllPrincipals.

Neither references the approving user's account, which is why the grant outlives them.

Office 365 Management Activity API

Base URL for a commercial tenant:

https://manage.office.com/api/v1.0/{tenantId}/activity/feed/{operation}

Government tenants use manage-gcc.office.com (GCC), manage.office365.us (GCC High) or manage.protection.apps.mil (DoD).

Collection is a two-step poll: GET /subscriptions/content?contentType=...&startTime=...&endTime=... lists available content blobs, and each blob is fetched from its own contentUri. The constraints that shape it:

  • A new subscription can take up to 12 hours to produce its first content blob.
  • startTime and endTime must be no more than 24 hours apart, and startTime no more than 7 days in the past.
  • Content blobs expire after 7 daysAF20051 is returned for anything older.
  • Listing pages through a NextPageUri response header, not a body property.
  • Events are aggregated across datacenters and are not guaranteed to be in order; a later blob can contain earlier events.
  • Duplicates are by design — the audit service retries on error, and this API does no deduplication. Deduplicate on Id, sort on CreationTime.
  • contentCreated is the date the notification was created, not the date of the event. Microsoft is explicit that "you can never query the API directly for events that occurred within any given period."
  • Throttling is per tenant, with a baseline of 2,000 requests per minute, and E5 organizations "will get approximately twice as much bandwidth as non-E5 organizations."
  • Blob retrieval is throttled separately, returning 403 AF429"Too many requests. Method=GetBlob" — when the PublisherIdentifier parameter is omitted, because callers without it share one general pool.
  • Repeated /subscriptions/start calls are throttled to one per 15 minutes per content type.

Auth is OAuth2 client credentials against https://manage.office.com/.default. The tenant ID in the URL must match the tid in the token, or the call fails with AF20010.

What the install creates

The Actions list on the application's details panel records one line per object created. addDatalakeIndex entries create the index in the managed lake as a side effect of the install, which is why a name that already exists aborts the whole application rather than just one stream.

References

Microsoft

Fluency