Skip to main content

Microsoft 365

Microsoft 365 Integration

Microsoft 365 Adapter Architecture

Exchange, Outlook Calendar, and OneDrive — unified through Microsoft Graph API via Azure AD OAuth 2.0

Service Layer
Microsoft365Service
service/mail.py
service/calendar.py
Adapter
Microsoft365Adapter
adapter/mail.py
adapter/calendar.py
adapter/users.py
External API
Microsoft Graph API
graph.microsoft.com/v1.0/
MSAL Bearer token

📧 Mail (Exchange / Outlook)

📅 Calendar (Outlook)

📁 OneDrive

👤 Users

📄 SharePoint

Azure AD App Registration·MSAL · tenant_id / client_id / client_secret

What & Why

Microsoft Graph is a single unified REST endpoint for the entire Microsoft 365 surface: Exchange mail, Outlook Calendar, OneDrive, Teams, and user directory data. One OAuth token gives Octopus access to all of them without juggling multiple APIs.

For organizations on the Microsoft stack, this mirrors the Google Workspace integrations (gmail, google-calendar, google-drive) — the adapter → service → auto-generated routes pattern is identical; only the auth layer and downstream API differ.

Key properties of the adapter:

  • Azure AD OAuth 2.0 via MSAL (msal Python library). Supports both delegated (user) and application (tenant-level) permission flows.
  • Microsoft Graph API v1.0 (https://graph.microsoft.com/v1.0/) for all calls.
  • Delta queries for incremental email and calendar sync — only changed items are fetched on subsequent calls.
  • Automatic token refresh — the adapter handles 401 responses by refreshing the access token and retrying transparently.

Azure AD OAuth Flow

Azure AD OAuth 2.0 Flow

5-step authorization from App Registration to Graph API calls

1
Azure AD App Registration
Create an app in Azure Portal. Set MICROSOFT_TENANT_ID, MICROSOFT_CLIENT_ID, MICROSOFT_CLIENT_SECRET. Add redirect URI.
2
Initiate authorization
POST /v1/oauth/microsoft-365/authorize → Constellation returns an authorization_url pointing to the MSAL authorize endpoint.
3
Admin / user consent
User (or tenant admin for delegated scopes) grants consent on the Microsoft consent screen. Scopes: Mail.Read Mail.Send Calendars.ReadWrite Files.ReadWrite User.Read
4
Callback + token exchange
Azure AD calls oauth.py callback. MSAL exchanges the authorization code for an access token + refresh token. Tokens stored in Redis.
5
Graph API calls
Adapter attaches Authorization: Bearer <access_token> to every httpx request. On 401, MSAL refreshes silently and retries.
Tenant-Level (Application)
Admin grants once for the whole org. No per-user consent required. Used for list_users, directory reads.
User-Level (Delegated)
Each user authorizes their own mailbox, calendar, and files. Token is scoped to that user's data.

Methods Reference

Email (Exchange / Outlook)

MethodDescriptionKey params
list_emailsList messages in a folder with optional filtersfolder (inbox/sent/…), top, skip, search, filter
get_emailFetch a single message by IDmessage_id
send_emailSend a new messageto, subject, body, cc, bcc, html (bool)
reply_to_emailReply to an existing messagemessage_id, body, reply_all (bool)
forward_emailForward a message to new recipientsmessage_id, to, comment
delete_emailMove a message to Deleted Itemsmessage_id
move_emailMove a message to a different foldermessage_id, destination_id
list_foldersList all mail folders in the mailbox

Calendar (Outlook)

MethodDescriptionKey params
list_eventsList events in a calendar within a time windowcalendar_id, start_time, end_time
create_eventCreate a new calendar eventsubject, start, end, attendees, body, calendar_id
update_eventUpdate an existing eventevent_id, calendar_id, fields to update
delete_eventDelete an eventevent_id, calendar_id
get_free_busyGet free/busy schedule for one or more usersschedules (list of emails), start_time, end_time, interval_minutes

OneDrive

MethodDescriptionKey params
list_filesList items in a folder (root by default)folder_path, top
get_fileGet metadata for a specific file or folderitem_id or item_path
upload_fileUpload a file to OneDrivefile_path (local), destination_path, conflict_behavior
download_fileDownload a file from OneDriveitem_id, local_path
create_folderCreate a new folderfolder_name, parent_path

Users

MethodDescriptionKey params
get_userGet a specific user's profile from the directoryuser_id or user_principal_name
list_usersList users in the tenant directorytop, filter
get_user_photoGet the profile photo of a useruser_id

Setup

1. Azure AD App Registration

  1. Go to Azure PortalAzure Active DirectoryApp registrationsNew registration.
  2. Set a name (e.g. octopus-constellation). Choose Accounts in this organizational directory only (single-tenant) or Any Azure AD directory (multi-tenant).
  3. Add a redirect URI: http://localhost:3007/oauth/microsoft365/callback (web platform). Use your deployed URL in production.
  4. Under Certificates & secrets → create a Client secret. Copy it immediately.
  5. Under API permissionsAdd a permissionMicrosoft GraphDelegated permissions. Add:
    • Mail.Read, Mail.Send, Mail.ReadWrite
    • Calendars.ReadWrite
    • Files.ReadWrite
    • User.Read, User.ReadBasic.All
  6. Click Grant admin consent for your tenant.
  7. Note your Tenant ID, Application (client) ID, and the client secret value.

2. Environment variables

# Required
MICROSOFT_TENANT_ID=your-tenant-id
MICROSOFT_CLIENT_ID=your-client-id
MICROSOFT_CLIENT_SECRET=your-client-secret

# Optional (defaults shown)
MICROSOFT_REDIRECT_URI=http://localhost:3007/oauth/microsoft365/callback

Add the same vars to docker-compose.yml under the api service environment block.

3. Verify connection

# Check OAuth status for a user
GET /v1/oauth/microsoft-365/status
# → { "status": "needs_oauth", "authorization_url": "https://login.microsoftonline.com/..." }
# → { "status": "authorized", "user_email": "user@company.com" }

CLI Examples

# List the 20 most recent inbox messages
constellation microsoft-365 list-emails --folder inbox --top 20

# Get a specific email by ID
constellation microsoft-365 get-email --message-id AAMkAGI2...

# Send a new email
constellation microsoft-365 send-email \
--to user@company.com \
--subject "Hello from Octopus" \
--body "Sent via Constellation CLI"

# Reply to a message
constellation microsoft-365 reply-to-email \
--message-id AAMkAGI2... \
--body "Thanks!" \
--reply-all

# List calendar events for May 2026
constellation microsoft-365 list-events \
--start 2026-05-01 \
--end 2026-05-31

# Create a calendar event
constellation microsoft-365 create-event \
--subject "Team Sync" \
--start "2026-05-15T10:00:00" \
--end "2026-05-15T11:00:00" \
--attendees "alice@company.com,bob@company.com"

# Check free/busy for two users
constellation microsoft-365 get-free-busy \
--schedules "alice@company.com,bob@company.com" \
--start 2026-05-15 \
--end 2026-05-15

# List OneDrive root folder
constellation microsoft-365 list-files

# Upload a file to OneDrive
constellation microsoft-365 upload-file \
--file-path ./report.pdf \
--destination-path "Documents/report.pdf"

# List tenant users
constellation microsoft-365 list-users --top 50

HTTP API

# List emails via HTTP
curl -X POST https://api.yourdomain.com/v1/microsoft-365/list-emails \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"folder": "inbox", "top": 10}'

# Response
# {
# "messages": [
# {"id": "AAMkAGI2...", "subject": "Hello", "from": "sender@company.com", "receivedDateTime": "2026-05-12T10:00:00Z", "isRead": false}
# ]
# }

# Send email via HTTP
curl -X POST https://api.yourdomain.com/v1/microsoft-365/send-email \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"to": "user@company.com", "subject": "Hello", "body": "From Octopus"}'

# Create calendar event via HTTP
curl -X POST https://api.yourdomain.com/v1/microsoft-365/create-event \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"subject": "Team Sync", "start": "2026-05-15T10:00:00", "end": "2026-05-15T11:00:00"}'

Auth Reference

# Initiate OAuth flow (returns authorization_url)
POST /v1/oauth/microsoft-365/authorize

# Check authorization status
GET /v1/oauth/microsoft-365/status

# Revoke tokens
DELETE /v1/oauth/microsoft-365/revoke

When a token is missing or expired, any integration call returns:

{
"detail": {
"action": "oauth_required",
"message": "User needs to authorize Microsoft 365 access",
"auth_url": "http://localhost:3007/oauth/microsoft365/authorize?..."
}
}

Direct the user to auth_url to complete the consent flow.


See also: SharePoint