Microsoft Outlook Automation for Agencies: One n8n Workflow, All Your Clients
Microsoft Outlook sits at the center of business communication for the majority of enterprise and mid-market companies. Email sequences, meeting scheduling, CRM sync, inbox monitoring - all of it flows through Outlook for a huge portion of the B2B world. If you run an automation agency with clients on Microsoft 365, or if you are building a SaaS that integrates with business email and calendar, Outlook automation is not optional.
The problem is that Microsoft OAuth is one of the more complex authorization systems to configure correctly - and n8n's credential model makes it even harder to scale. This article explains how to set up Microsoft OAuth in n8n (the right way, using Azure), what the common pitfalls are, and how to architect a single n8n workflow that handles Outlook automation for every client - without duplicating the workflow for each Microsoft tenant.
Who This Is For
This article is for:
- Email automation agencies managing outreach, newsletters, or transactional email flows through clients' Outlook accounts
- CRM automation builders syncing contacts, emails, and activities between Outlook and systems like HubSpot, Salesforce, or a custom CRM
- Meeting scheduling services that book appointments, send calendar invites, and manage availability across multiple business clients
- n8n agency setup practitioners who deliver Microsoft 365 automation as a managed service and are currently running one workflow per client
- SaaS developers using n8n as a backend where each user account connects their Microsoft 365 account
- IT consultants building internal automation for organizations with multiple Microsoft tenants (subsidiaries, franchises, departments)
If you are using the Microsoft Graph API in n8n and your client list is growing, this article is directly relevant to your situation.
How Microsoft OAuth Works in n8n
Microsoft's OAuth implementation runs through Azure Active Directory (now called Microsoft Entra ID). To use any Microsoft 365 service - Outlook email, calendar, Teams, OneDrive - you register an application in Azure and use it as the OAuth client.
Azure App Registration: The Starting Point
Before anything works in n8n, you need an Azure app registration. Here is the process:
- Go to the Azure Portal and navigate to Azure Active Directory > App registrations > New registration.
- Give your app a name (e.g., "Acme Automation Bot").
- Under Supported account types, choose:
- "Accounts in this organizational directory only" - for single-tenant apps (just your org)
- "Accounts in any organizational directory" - for multi-tenant apps (clients across different Microsoft tenants) ← this is what agencies and SaaS builders need
- "Accounts in any organizational directory and personal Microsoft accounts" - if you also need consumer Outlook.com support
- Set the Redirect URI to n8n's OAuth callback URL:
https://your-n8n-instance.com/rest/oauth2-credential/callback - Click Register.
After registration, navigate to Certificates & secrets and create a Client Secret. Store this securely - it is only shown once.
Under API Permissions, add the Microsoft Graph permissions your automation needs. Click "Add a permission > Microsoft Graph > Delegated permissions."
Permission Scopes for Outlook Automation
| Permission | What it enables |
|---|---|
Mail.Read |
Read emails in the connected mailbox |
Mail.ReadWrite |
Read and modify emails |
Mail.Send |
Send emails on behalf of the user |
Calendars.Read |
Read calendar events |
Calendars.ReadWrite |
Create, update, delete calendar events |
Contacts.Read |
Read contacts |
Contacts.ReadWrite |
Manage contacts |
User.Read |
Read basic user profile |
offline_access |
Get refresh tokens (essential for long-lived automation) |
Critical: Always include offline_access in your scope request. Without it, Microsoft does not return a refresh token, and your automation will break after the access token expires (typically 60–90 minutes for Microsoft tokens).
Delegated vs. Application Permissions
This is where many Microsoft OAuth setups go wrong in n8n.
Delegated permissions require a signed-in user. The app acts on behalf of a specific user account. This is what you use for "send emails as client@theirdomain.com" or "read John's calendar." The OAuth flow requires the user to sign in and consent. You get a token scoped to that user.
Application permissions do not require a signed-in user. The app authenticates as itself (client credentials flow) and can access any mailbox in the tenant - but requires an admin to grant consent for the entire tenant. This is suitable for server-to-server scenarios where you are processing email for an entire organization.
For most agency and SaaS use cases, delegated permissions are the correct choice. The client user authenticates once, you store their refresh token, and you act on their behalf going forward.
The Microsoft Graph API Endpoints
Microsoft Graph is the unified API for all Microsoft 365 services. For Outlook, the key endpoints are:
List messages in inbox:
GET https://graph.microsoft.com/v1.0/me/mailFolders/inbox/messages
Authorization: Bearer {access_token}
Send an email:
POST https://graph.microsoft.com/v1.0/me/sendMail
Authorization: Bearer {access_token}
Content-Type: application/json
{
"message": {
"subject": "Weekly Report",
"body": {
"contentType": "HTML",
"content": "<p>Your report is attached.</p>"
},
"toRecipients": [
{
"emailAddress": {
"address": "client@example.com"
}
}
]
}
}
Create a calendar event:
POST https://graph.microsoft.com/v1.0/me/events
Authorization: Bearer {access_token}
Content-Type: application/json
{
"subject": "Quarterly Review",
"start": {
"dateTime": "2026-03-15T14:00:00",
"timeZone": "Eastern Standard Time"
},
"end": {
"dateTime": "2026-03-15T15:00:00",
"timeZone": "Eastern Standard Time"
},
"attendees": [
{
"emailAddress": { "address": "stakeholder@client.com" },
"type": "required"
}
]
}
Read calendar events for a date range:
GET https://graph.microsoft.com/v1.0/me/calendarView
?startDateTime=2026-03-01T00:00:00Z
&endDateTime=2026-03-08T00:00:00Z
Authorization: Bearer {access_token}
Common Errors in Microsoft Graph Automation
AADSTS65001: The user or administrator has not consented: The user has not completed the OAuth consent screen, or admin consent is required for the permissions you requested. For sensitive permissions like Mail.Send, some organizations require admin approval.
AADSTS700016: Application not found in the directory: Your Azure app is registered as single-tenant but the client's Microsoft account is in a different tenant. Change the app registration to multi-tenant.
401 InvalidAuthenticationToken: The access token has expired. Microsoft access tokens typically expire after 3600 seconds (1 hour). Ensure your token management layer (CredBridge) refreshes the token using the stored refresh token.
403 Forbidden - AccessDenied: The token does not have the required permission scope. Check the API Permissions in your Azure app registration and ensure the user consented to the correct scopes.
429 Too Many Requests: Microsoft Graph throttles at the tenant level. Back off and retry. For bulk operations, implement exponential backoff.
The Scaling Challenge: Each Client Needs Their Own Microsoft Auth
Here is the fundamental problem with the n8n agency setup for Microsoft clients.
In n8n, a Microsoft credential record stores:
- Your Azure app's Client ID
- Your Azure app's Client Secret
- The access token for a specific Microsoft user account
- The refresh token for that user account
That last point is the issue. Even though you have one Azure app (registered once), each client's credential in n8n contains tokens for that specific user. In the standard n8n setup, you must:
- Create a separate credential record for each client
- Have each client complete the OAuth flow through n8n's credential interface (which means giving them access to your n8n instance, or doing it yourself)
- Create a separate workflow (or sub-workflow variant) for each client that uses their specific credential
For an n8n agency setup with 10 Microsoft clients, this is manageable but annoying. For 30 clients, it becomes a full-time maintenance job. For 50+ clients, it is unworkable.
The other problem: n8n's credential interface is not designed to be shown to end customers. You cannot send a client to your-n8n.com/credentials and ask them to authenticate. The UI exposes your entire automation infrastructure. You need a proper OAuth flow that your clients can complete on your own website or a hosted page - and that stores the resulting tokens somewhere your n8n workflows can access at runtime.
How to Set Up One n8n Workflow for All Microsoft Clients Using CredBridge
CredBridge solves both problems. It hosts the OAuth consent flow for Microsoft (so your clients never see n8n's credential interface), stores the resulting tokens per tenant, handles refresh automatically, and exposes a simple API that your n8n workflow calls at runtime to get a valid token for any client.
Step 1: Configure Your Azure App for Multi-Tenant Access
In your Azure app registration:
- Set "Supported account types" to "Accounts in any organizational directory (Any Azure AD directory - Multitenant)"
- Set the redirect URI to CredBridge's OAuth callback:
https://api.credbridge.io/oauth/callback/microsoft
Add the delegated permissions your workflows need. For a typical email + calendar agency setup:
Mail.Read,Mail.Send,Calendars.ReadWrite,User.Read,offline_access
Copy your Client ID and Client Secret and add them to CredBridge's provider configuration for Microsoft.
Step 2: Client Onboarding - The OAuth Flow
On your client onboarding page, include a "Connect Microsoft 365" button. The link:
https://login.microsoftonline.com/common/oauth2/v2.0/authorize
?client_id=YOUR_AZURE_CLIENT_ID
&response_type=code
&redirect_uri=https://api.credbridge.io/oauth/callback/microsoft
&scope=Mail.Send%20Calendars.ReadWrite%20User.Read%20offline_access
&state=TENANT_ID
&response_mode=query
&prompt=consent
The state parameter carries the tenant_id (your internal client identifier). After the client signs in and consents, Microsoft redirects to CredBridge's callback. CredBridge exchanges the authorization code for access and refresh tokens, stores them against the tenant_id, and redirects the client back to your success page.
The client never sees n8n. They see a standard Microsoft sign-in - the same one they use every day.
Step 3: The n8n Workflow - Token Fetch + Graph API Call
Here is the complete n8n configuration for an email-sending workflow that works for all clients.
Trigger: Webhook receives:
{
"tenant_id": "client_contoso_001",
"recipient_email": "contact@contoso.com",
"email_subject": "Q1 Performance Report",
"email_body_html": "<p>Please find your Q1 report summary below...</p>"
}
Node: Get Microsoft Token (HTTP Request):
{
"method": "GET",
"url": "https://api.credbridge.io/v1/token",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "X-CredBridge-Key",
"value": "={{ $env.CREDBRIDGE_API_KEY }}"
}
]
},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "tenant_id",
"value": "={{ $json.tenant_id }}"
},
{
"name": "provider",
"value": "microsoft"
}
]
}
}
Node: Send Email via Microsoft Graph (HTTP Request):
{
"method": "POST",
"url": "https://graph.microsoft.com/v1.0/me/sendMail",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "=Bearer {{ $('Get Microsoft Token').item.json.access_token }}"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"bodyContentType": "json",
"jsonBody": {
"message": {
"subject": "={{ $('Webhook').item.json.email_subject }}",
"body": {
"contentType": "HTML",
"content": "={{ $('Webhook').item.json.email_body_html }}"
},
"toRecipients": [
{
"emailAddress": {
"address": "={{ $('Webhook').item.json.recipient_email }}"
}
}
]
},
"saveToSentItems": true
}
}
Node: Check Response (IF node):
Microsoft Graph returns 202 Accepted for successful sendMail calls (with no body). Check the HTTP status code:
202→ success path4xx/5xx→ error handling path (log error, notify via your monitoring system)
Step 4: Calendar Event Creation for All Clients
Adding calendar functionality is the same pattern - just swap the endpoint:
Node: Create Calendar Event (HTTP Request):
{
"method": "POST",
"url": "https://graph.microsoft.com/v1.0/me/events",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "=Bearer {{ $('Get Microsoft Token').item.json.access_token }}"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"bodyContentType": "json",
"jsonBody": {
"subject": "={{ $json.meeting_title }}",
"start": {
"dateTime": "={{ $json.start_datetime }}",
"timeZone": "={{ $json.timezone }}"
},
"end": {
"dateTime": "={{ $json.end_datetime }}",
"timeZone": "={{ $json.timezone }}"
},
"attendees": [
{
"emailAddress": {
"address": "={{ $json.attendee_email }}",
"name": "={{ $json.attendee_name }}"
},
"type": "required"
}
],
"isOnlineMeeting": true,
"onlineMeetingProvider": "teamsForBusiness"
}
}
This creates a Teams meeting link automatically for every booked appointment - for any client, using the client's own Microsoft account, with a single workflow.
Architecture Summary: What You Are Building
Your Client (browser)
→ Clicks "Connect Microsoft 365" on your website
→ Microsoft OAuth consent screen
→ CredBridge stores tokens, assigns tenant_id
→ Your app stores tenant_id per client
n8n Workflow (runs on trigger: schedule / webhook / CRM event)
→ Receives: { tenant_id, email_data OR event_data }
→ HTTP Request → CredBridge API → Returns: { access_token }
→ HTTP Request → Microsoft Graph API (using access_token)
→ Success/Error handling
One Azure app. One CredBridge account. One n8n workflow. Unlimited clients (within your CredBridge plan).
Comparing Approaches for Microsoft Automation at Scale
| Approach | Client onboarding | Token refresh | Workflow count | Admin effort |
|---|---|---|---|---|
| n8n native Microsoft credential (1 per client) | Manual (n8n admin UI) | Automatic (inside n8n) | 1 per client | High |
| Hardcoded tokens in HTTP Request nodes | Custom OAuth page needed | Manual or custom code | 1 (fragile) | Very high |
| Environment variables per client | Custom setup needed | Manual | 1 (brittle) | Very high |
| CredBridge token proxy | Hosted OAuth flow, client-facing | Automatic (CredBridge) | 1 for all clients | Low |
Get Started With CredBridge for Microsoft Clients
Microsoft 365 automation at agency scale requires solving three problems at once: a client-facing OAuth flow, secure token storage with automatic refresh, and runtime token injection into your workflows. CredBridge handles all three.
Solo plan - $19/month: Up to 10 Microsoft tenant connections. Right for boutique agencies and independent automation builders.
Agency plan - $49/month: Up to 50 Microsoft tenant connections. Built for established agencies and growing SaaS products.
Start at credbridge.io - connect your Azure app, onboard your first Microsoft client, and run your first Graph API call from n8n in under 15 minutes.
Related: How to automate Google Calendar for 50+ clients with one n8n workflow | How to build a SaaS on n8n with multi-tenant credentials