Describe the problem/error/question
I am unable to use Microsoft Outlook OAuth2 credentials on my self-hosted n8n instance. The same workflow and credentials work perfectly on n8n Cloud, but on my self-hosted setup, I always get a 401 Unauthorized error from Microsoft when trying to use the Outlook node. The OAuth2 flow completes successfully, and the credential is created and refreshed, but API calls fail.
What is the error message (if any)?
401 - ""
Authorization failed - please check your credentials
n8n logs also show:
OAuth2 token for "microsoftOutlookOAuth2Api" used by node "Microsoft Outlook" expired. Should revalidate.
OAuth2 token for "microsoftOutlookOAuth2Api" used by node "Microsoft Outlook" has been renewed.
OAuth2 token for "microsoftOutlookOAuth2Api" used by node "Microsoft Outlook" has been saved to database successfully.
Request proxied to Axios failed {"status":401}
401 - ""
Please share your workflow
{
"nodes": [
{
"parameters": {
"pollTimes": {
"item": [
{
"mode": "everyMinute"
}
]
},
"output": "raw",
"filters": {},
"options": {}
},
"type": "n8n-nodes-base.microsoftOutlookTrigger",
"typeVersion": 1,
"position": [
-2960,
500
],
"id": "e2e77a25-0c4b-4ce9-9fc8-0d689145cdc5",
"name": "Microsoft Outlook Trigger"
},
{
"parameters": {
"modelId": {
"__rl": true,
"value": "gpt-4.1-mini",
"mode": "list",
"cachedResultName": "GPT-4.1-MINI"
},
"messages": {
"values": [
{
"content": "=here is the incoming email\n{{ $json.body.content }}\n \n{{ $json.sender }}"
},
{
"content": "this is the email content coming from an outlook n8n node. your job is clean it and make it more readable , get rid of any html tagging but do not remove any email content, structure it subject, body , summary and from (email and name) ",
"role": "system"
}
]
},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.openAi",
"typeVersion": 1.8,
"position": [
-2740,
500
],
"id": "1d816f03-d030-483f-8bc1-34378b8394f9",
"name": "clean email"
},
{
"parameters": {
"inputText": "={{ $json.message.content }}",
"categories": {
"categories": [
{
"category": "Quote Request",
"description": "
},
{
"category": "Non-Quote Request (Everything else)",
"description": "Emails that do not relate to requesting quotes or hiring builders/construction services. This can include a wide range of topics such as newsletters, spam, personal messages, or any other non-relevant content."
}
]
},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.textClassifier",
"typeVersion": 1,
"position": [
-2320,
500
],
"id": "4b88499a-396c-49d8-a458-40e72eb04b71",
"name": "Text Classifier"
},
{
"parameters": {
"modelName": "models/gemini-2.0-flash",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"typeVersion": 1,
"position": [
-2320,
720
],
"id": "09230e74-1398-41e1-8d39-9b18338ba07b",
"name": "Google Gemini Chat Model"
},
{
"parameters": {
"promptType": "define",
"text": "=here is the email {{ $json.message.content }}",
"hasOutputParser": true,
"options": {
"systemMessage": "
}
},
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 1.9,
"position": [
-1760,
500
],
"id": "7b6b6e20-0906-4492-9150-57b1c7189ce4",
"name": "AI Agent"
},
{
"parameters": {
"modelName": "models/gemini-2.0-flash",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"typeVersion": 1,
"position": [
-1800,
740
],
"id": "98ae8cd0-04e4-4759-846b-f6da699e87a7",
"name": "Google Gemini Chat Model1"
},
{
"parameters": {
"jsCode": "const output = $json[\"output\"];\n\nconst jsonString = output.replace(/```json|```/g, '').trim();\n\ntry {\n const jsonData = JSON.parse(jsonString);\n return { json: jsonData };\n} catch (error) {\n return { error: 'Failed to parse JSON' };\n}\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-1420,
340
],
"id": "7edeabc9-7a1f-478c-8fd0-bcb650472396",
"name": "Code"
},
{
"parameters": {
"operation": "reply",
"messageId": {
"__rl": true,
"value": "={{ $('Microsoft Outlook Trigger').item.json.id }}",
"mode": "id"
},
"replyToSenderOnly": true,
"message": "={{ $json.body }}",
"additionalFields": {
"subject": "="
},
"options": {
"saveAsDraft": true
}
},
"type": "n8n-nodes-base.microsoftOutlook",
"typeVersion": 2,
"position": [
-1220,
460
],
"id": "98c383c0-38a0-4466-9d32-a19bf156f666",
"name": "Microsoft Outlook1",
"webhookId": "faa934f5-e837-4541-8d7c-03ab9546f01b"
},
{
"parameters": {},
"type": "n8n-nodes-base.aiTransform",
"typeVersion": 1,
"position": [
-2460,
580
],
"id": "7d35ccc1-7aac-4ea3-8b35-e8e25e6fb115",
"name": "AI Transform"
}
],
"connections": {
"Microsoft Outlook Trigger": {
"main": [
[
{
"node": "clean email",
"type": "main",
"index": 0
}
]
]
},
"clean email": {
"main": [
[
{
"node": "Text Classifier",
"type": "main",
"index": 0
}
]
]
},
"Text Classifier": {
"main": [
[
{
"node": "AI Agent",
"type": "main",
"index": 0
}
]
]
},
"Google Gemini Chat Model": {
"ai_languageModel": [
[
{
"node": "Text Classifier",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"AI Agent": {
"main": [
[
{
"node": "Code",
"type": "main",
"index": 0
}
]
]
},
"Google Gemini Chat Model1": {
"ai_languageModel": [
[
{
"node": "AI Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Code": {
"main": [
[
{
"node": "Microsoft Outlook1",
"type": "main",
"index": 0
}
]
]
}
},
"pinData": {},
"meta": {
"templateCredsSetupCompleted": true,
"instanceId": "0f325321e97fd63d23202f36d164e90bdbf2c8bd14544ff630de405566e0595d"
}
}
## Share the output returned by the last node
{
“errorMessage”: “Authorization failed - please check your credentials”,
“errorDescription”: “401 - ""”,
“errorDetails”: {
“rawErrorMessage”: [
“401 - ""”
],
“httpCode”: “401”
}
}
## Information on your n8n setup
- **n8n version:** 1.97.1
- **Database (default: SQLite):** SQLite (also tested with Postgres)
- **n8n EXECUTIONS_PROCESS setting (default: own, main):** (default)
- **Running n8n via (Docker, npm, n8n cloud, desktop app):** Docker (self-hosted, with Traefik as reverse proxy)
- **Operating system:** Ubuntu 24.04
---
**Additional context:**
- I have created a new Azure app registration with all required permissions (`Mail.Read`, `Mail.Send`, `offline_access`, `openid`, etc.), and granted admin consent.
- The redirect URI in Azure matches my self-hosted n8n exactly (including protocol and path).
- I use the same Microsoft account and mailbox as on n8n Cloud.
- I have set `N8N_TRUST_PROXY=true` in my environment variables.
- The credential is created and tokens are being refreshed (logs show token renewal and saving).
- The same workflow and credential work on n8n Cloud.
**Questions:**
- Is there anything different about how n8n Cloud and self-hosted n8n handle Microsoft OAuth2 tokens?
- Is there a way to debug or view the actual access token being used in a node?
- Are there any known issues with Traefik, proxies, or environment variables that could cause this?
- Any other troubleshooting steps I should try?
---
Let me know if you want to add or remove any details before posting!