Office 365 Management Activity API Integration

Configure HTTP Pull to retrieve audit logs from the Office 365 Management Activity API including Exchange, SharePoint, Azure AD, Teams, and DLP events using OAuth2 client credentials authentication with content blob pagination.

Overview

The Office 365 Management Activity API provides comprehensive audit and activity data from Microsoft 365 services including Exchange, SharePoint, Azure Active Directory, Teams, Power BI, and other workloads. The API uses OAuth2 client credentials flow and a content blob retrieval pattern where you query for available content, then automatically fetch individual content blobs.

Prerequisites & Setup

Before configuring Edge Delta, complete these steps in Microsoft Azure:

1. Enable Unified Audit Logging

Unified audit logging must be enabled before the API returns data:

  1. Navigate to Microsoft Purview compliance portal
  2. Go to Audit > Search
  3. If prompted, click “Start recording user and admin activity”
  4. Wait up to 12 hours for content to become available

PowerShell method:

Connect-ExchangeOnline
Set-AdminAuditLogConfig -UnifiedAuditLogIngestionEnabled $true

2. Register Azure AD Application

  1. Go to Azure Active Directory > App registrations > New registration
  2. Name: “EdgeDelta O365 Management API”
  3. Account type: “Accounts in this organizational directory only”
  4. Click Register (no redirect URI needed)

3. Configure API Permissions

Add Office 365 Management APIs application permissions (not Microsoft Graph):

  1. Go to API permissions > Add a permission
  2. Select APIs my organization uses > Search “Office 365 Management APIs”
  3. Choose Application permissions
  4. Add required permissions:
    • ActivityFeed.Read (required for all content types)
    • ActivityFeed.ReadDlp (required only for DLP.All)
  5. Click “Grant admin consent for [tenant]”

4. Create Client Secret

  1. Navigate to Certificates & secrets > Client secrets > New client secret
  2. Description: “EdgeDelta Integration”
  3. Expiration: 12-24 months (recommended)
  4. Click Add and immediately copy the secret value

5. Collect Configuration Values

From your Azure AD application Overview page:

  • Tenant ID: Directory (tenant) ID
  • Client ID: Application (client) ID
  • Client Secret: Value copied in step 4
  • Publisher Identifier: Use your Tenant ID

6. Start Content Subscriptions

Start subscriptions for each content type you want to monitor (one-time setup):

Bash (Linux/Mac):

# Get OAuth token first
TOKEN=$(curl -X POST "https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/token" \
  -d "client_id={CLIENT_ID}&client_secret={CLIENT_SECRET}&scope=https://manage.office.com/.default&grant_type=client_credentials" \
  | jq -r '.access_token')

# Start subscriptions
for contentType in "Audit.Exchange" "Audit.SharePoint" "Audit.AzureActiveDirectory" "Audit.General" "DLP.All"
do
  curl -X POST "https://manage.office.com/api/v1.0/{TENANT_ID}/activity/feed/subscriptions/start?contentType=${contentType}&PublisherIdentifier={TENANT_ID}" \
    -H "Authorization: Bearer ${TOKEN}"
done

PowerShell (Windows):

# Get OAuth token first
$body = @{
    client_id     = "{CLIENT_ID}"
    client_secret = "{CLIENT_SECRET}"
    scope         = "https://manage.office.com/.default"
    grant_type    = "client_credentials"
}

$tokenResponse = Invoke-RestMethod -Uri "https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/token" `
                                   -Method POST `
                                   -Body $body

$token = $tokenResponse.access_token

# Start subscriptions
$contentTypes = @("Audit.Exchange", "Audit.SharePoint", "Audit.AzureActiveDirectory", "Audit.General", "DLP.All")

foreach ($contentType in $contentTypes) {
    Invoke-RestMethod -Uri "https://manage.office.com/api/v1.0/{TENANT_ID}/activity/feed/subscriptions/start?contentType=$contentType&PublisherIdentifier={TENANT_ID}" `
                     -Method POST `
                     -Headers @{Authorization = "Bearer $token"}

    Write-Host "Started subscription for $contentType"
}

Subscriptions persist indefinitely; you only need to start them once.

Available Content Types

Content TypeDescriptionWorkloads
Audit.ExchangeEmail and mailbox activitiesExchange Online, Exchange Admin
Audit.SharePointFile and site operationsSharePoint Online, OneDrive
Audit.AzureActiveDirectoryIdentity and access eventsAzure AD authentication, user management
Audit.GeneralAll other Microsoft 365 servicesTeams, Power BI, Dynamics 365, Planner, Forms, Copilot, Viva
DLP.AllData loss prevention eventsDLP policies across all services

Configuration Examples

Exchange Audit Logs

Complete configuration for Exchange email and mailbox activities:

nodes:
- name: o365_exchange_audits
  type: http_pull_input
  endpoint: https://manage.office.com/api/v1.0/YOUR_TENANT_ID/activity/feed/subscriptions/content
  method: GET
  pull_interval: 5m

  # OAuth2 Client Credentials Authentication
  authorization:
    strategy: oauth_client_credentials
    client_credentials:
      token_url: https://login.microsoftonline.com/YOUR_TENANT_ID/oauth2/v2.0/token
      client_id: YOUR_CLIENT_ID
      client_secret: YOUR_CLIENT_SECRET
      scopes:
        - https://manage.office.com/.default
      header_templates:
        - header: Authorization
          value: Bearer $ACCESS_TOKEN

  # Content type and publisher identifier
  parameters:
    - name: contentType
      value: Audit.Exchange
    - name: PublisherIdentifier
      value: YOUR_TENANT_ID

  # Time window: 6 minutes lookback, ending 1 minute ago
  parameter_expressions:
    - name: startTime
      value_expression: FormatTime(Now() - Duration("6m"), "%Y-%m-%dT%H:%M:%SZ")
    - name: endTime
      value_expression: FormatTime(Now() - Duration("1m"), "%Y-%m-%dT%H:%M:%SZ")

  # Automatic content blob pagination
  pagination:
    url_json_path: contentUri
    response_format: json
    max_parallel: 3
    inherit_auth: true
    error_strategy: continue

Other Content Types

For SharePoint, Azure AD, General, and DLP content types, use the same configuration and change only the contentType parameter:

# SharePoint
parameters:
  - name: contentType
    value: Audit.SharePoint

# Azure Active Directory
parameters:
  - name: contentType
    value: Audit.AzureActiveDirectory

# General (Teams, Power BI, etc.)
parameters:
  - name: contentType
    value: Audit.General

# Data Loss Prevention
parameters:
  - name: contentType
    value: DLP.All

All other configuration remains identical across content types.

Using Environment Variables

For production deployments, use environment variables:

nodes:
- name: o365_exchange_audits
  type: http_pull_input
  endpoint_expression: Concat(["https://manage.office.com/api/v1.0/", EDXEnv("AZURE_TENANT_ID", ""), "/activity/feed/subscriptions/content"], "")
  method: GET
  pull_interval: 5m

  authorization:
    strategy: oauth_client_credentials
    client_credentials:
      token_url: https://login.microsoftonline.com/YOUR_TENANT_ID/oauth2/v2.0/token
      client_id: YOUR_CLIENT_ID
      client_secret: YOUR_CLIENT_SECRET
      scopes:
        - https://manage.office.com/.default
      header_templates:
        - header: Authorization
          value: Bearer $ACCESS_TOKEN

  parameters:
    - name: contentType
      value: Audit.Exchange

  parameter_expressions:
    - name: PublisherIdentifier
      value_expression: EDXEnv("PUBLISHER_IDENTIFIER", "")
    - name: startTime
      value_expression: FormatTime(Now() - Duration("6m"), "%Y-%m-%dT%H:%M:%SZ")
    - name: endTime
      value_expression: FormatTime(Now() - Duration("1m"), "%Y-%m-%dT%H:%M:%SZ")

  pagination:
    url_json_path: contentUri
    response_format: json
    max_parallel: 3
    inherit_auth: true
    error_strategy: continue

How Content Blob Pagination Works

The Office 365 Management Activity API uses a two-step retrieval pattern:

Step 1 - List Content Response:

[
  {
    "contentUri": "https://manage.office.com/.../content-blob-1",
    "contentId": "id-1",
    "contentType": "Audit.Exchange",
    "contentCreated": "2024-01-01T10:00:00Z"
  },
  {
    "contentUri": "https://manage.office.com/.../content-blob-2",
    "contentId": "id-2",
    "contentType": "Audit.Exchange",
    "contentCreated": "2024-01-01T10:05:00Z"
  }
]

Step 2 - Automatic Blob Retrieval:

  • url_json_path: contentUri extracts all content URLs from the array
  • max_parallel: 3 fetches up to 3 blobs concurrently
  • inherit_auth: true uses the same OAuth token for blob requests
  • error_strategy: continue processes remaining blobs if one fails

Time Windows and Rate Limiting

Recommended Configuration:

  • Pull interval: 5 minutes
  • Lookback window: 6 minutes (ending 1 minute ago)
  • Parallel blobs: 3

Why end 1 minute ago? The API may have processing delays. The 1-minute lag ensures better data completeness.

Rate Limits:

  • Per-application and per-tenant limits vary by license tier
  • 5-minute intervals respect typical rate limits
  • If you receive HTTP 429 responses, increase pull_interval to 10-15 minutes

Troubleshooting

ErrorCauseSolution
401 UnauthorizedInvalid credentials or tokenVerify client ID, secret, and tenant ID are correct
403 ForbiddenMissing permissionsEnsure ActivityFeed.Read permission granted with admin consent
400 Bad RequestInvalid parametersCheck time format (ISO 8601) and contentType value
429 Too Many RequestsRate limit exceededIncrease pull_interval or reduce max_parallel
Empty resultsNo data availableCheck unified audit logging is enabled; wait up to 12 hours for new tenants
Subscription not foundContent subscription not startedRun subscription start commands from Prerequisites section

Common Issues:

  • No content blobs: Ensure subscriptions are started (step 6 in Prerequisites)
  • Missing permissions: Verify you added permissions to “Office 365 Management APIs”, not “Microsoft Graph”
  • Expired secrets: Rotate client secrets every 12-24 months
  • Time range errors: Maximum window is 24 hours; content older than 7 days may be unavailable

API Reference

Official Documentation:

Sample Audit Logs

Exchange Audit Log:

{
  "CreationTime": "2024-01-01T10:00:00",
  "Operation": "SendAs",
  "Workload": "Exchange",
  "UserId": "user@contoso.com",
  "ClientIP": "192.168.1.100",
  "ResultStatus": "Success",
  "MailboxOwnerUPN": "mailbox@contoso.com",
  "Item": {
    "Subject": "Monthly Report",
    "InternetMessageId": "<message-id@contoso.com>"
  }
}

SharePoint Audit Log:

{
  "CreationTime": "2024-01-01T10:05:00",
  "Operation": "FileAccessed",
  "Workload": "SharePoint",
  "UserId": "user@contoso.com",
  "ObjectId": "https://contoso.sharepoint.com/sites/team/Documents/report.docx",
  "SourceFileName": "report.docx",
  "ItemType": "File"
}

Azure AD Audit Log:

{
  "CreationTime": "2024-01-01T10:10:00",
  "Operation": "UserLoggedIn",
  "Workload": "AzureActiveDirectory",
  "UserId": "user@contoso.com",
  "ResultStatus": "Success",
  "AzureActiveDirectoryEventType": 1,
  "ExtendedProperties": [
    {
      "Name": "UserAgent",
      "Value": "Mozilla/5.0 (Windows NT 10.0) Chrome/120.0"
    }
  ]
}

Comparison with Microsoft Graph API

FeatureOffice 365 Management APIMicrosoft Graph API
PurposeComprehensive audit logsReal-time directory queries
AuthenticationActivityFeed.Read permissionAuditLog.Read.All permission
PaginationContent blob pattern (contentUri)OData @odata/.nextLink
WorkloadsAll M365: Exchange, SharePoint, Teams, Power BI, DLPPrimarily Azure AD, Security, Compliance
Best ForCompliance monitoring, SIEM integrationReal-time security alerts, directory management

Many organizations use both APIs for comprehensive Microsoft 365 monitoring.