SSD Nodes Learn Hosting plans →
Guides Matt ConnorBy Matt Connor

Connect Outlook to Self-Hosted n8n

Register a Microsoft Entra app for self-hosted n8n, fix the callback URL, and plan for the client secret expiry that later breaks your Outlook workflows.

How to connect Outlook to self-hosted n8n

To connect Outlook to self-hosted n8n, you register an app in Microsoft Entra ID. Then you give that app the OAuth callback URL your n8n instance prints, and paste the app's client ID and client secret into an n8n credential. n8n Cloud users skip all of this, because Cloud ships a Microsoft app that n8n manages for them. On your own server the app registration belongs to you, so its settings and its expiring secret are yours to manage.

A few terms first. OAuth (Open Authorization) is the sign-in flow that lets n8n act on your mailbox without storing your password. Microsoft Entra ID is Microsoft's identity service, formerly called Azure Active Directory. Microsoft Graph is the API that n8n calls to read and send Outlook mail. A UPN (user principal name) is the sign-in name of a Microsoft 365 account, and it looks like an email address.

Most failures come from two settings: the redirect URI and the account type. A third failure arrives months later, when the client secret expires. This guide covers all of them, then builds a small workflow that answers new mail.

What you need before you start

  • A self-hosted n8n instance reachable over HTTPS on a real domain. Running n8n on a VPS with Docker and HTTPS walks through that setup.
  • A Microsoft account. A personal Outlook.com account works, and so does a work or school account in a Microsoft 365 tenant.
  • For a work tenant: permission to consent to apps yourself, or an administrator who can grant consent for you.

Why the callback URL depends on your public URL

OAuth sign-in ends with Microsoft sending your browser back to n8n with a one-time code. That return address is the callback URL. n8n builds it from what it believes its own public URL is. The path is always /rest/oauth2-credential/callback. The part in front of the path comes from your configuration.

n8n uses N8N_EDITOR_BASE_URL when it is set. Without it, n8n falls back to its webhook URL. Without that, it builds the address from N8N_PROTOCOL, N8N_HOST and N8N_PORT, which default to http, localhost and 5678.

That last fallback is the common trap. Behind a reverse proxy, n8n cannot see the domain the proxy answers on. So an instance with no URL variables prints http://localhost:5678/rest/oauth2-credential/callback. If you register your real domain in Entra but n8n sends localhost, sign-in fails with this error:

AADSTS50011: The redirect URI 'http://localhost:5678/rest/oauth2-credential/callback' specified in the request does not match the redirect URIs configured for the application

Microsoft compares the URI in the request with the registered list character by character. Set the public URL explicitly in your compose file:

services:
  n8n:
    environment:
      - N8N_HOST=n8n.example.com
      - N8N_PROTOCOL=https
      - N8N_EDITOR_BASE_URL=https://n8n.example.com
      - N8N_WEBHOOK_URL=https://n8n.example.com/
      - N8N_PROXY_HOPS=1

The variable name changed recently. From n8n 2.35.0, the webhook variable is N8N_WEBHOOK_URL. The older name WEBHOOK_URL still works as an alias, but n8n logs a deprecation warning at startup (as of October 2026). On an older release, use WEBHOOK_URL. N8N_PROXY_HOPS=1 tells n8n that it runs behind one reverse proxy, so it trusts the X-Forwarded-For, X-Forwarded-Host and X-Forwarded-Proto headers your proxy sends.

docker compose up -d
docker compose exec n8n env | grep -E 'N8N_EDITOR_BASE_URL|WEBHOOK_URL'

up -d recreates the container because its environment changed. The second command should print both variables with your https domain. If your compose service has a different name, replace n8n in the second command. n8n reads these values only at startup, so editing the file changes nothing until the container restarts.

Now do the check that matters. In n8n, create a new credential and choose Microsoft Outlook OAuth2 API. The form shows an OAuth Redirect URL field. It must start with https:// and your domain. If it still shows localhost, fix the variables first, because every later step copies this value.

Register the app in Microsoft Entra ID

Keep the n8n credential form open in one tab. Then follow these steps in another tab.

  1. Open the Application Registration Portal and sign in with the Microsoft account that will own the app.
  2. Select New registration and give the app a name, such as n8n.
  3. Under supported account types, select Accounts in any organizational directory (Any Azure AD directory - Multi-tenant) and personal Microsoft accounts. That is the name n8n's documentation uses. Newer portal versions may say "Microsoft Entra ID tenant" in place of "Azure AD directory". Pick the option that covers any organisation plus personal accounts.
  4. Under Redirect URI, choose Select a platform, then Web, and paste the OAuth Redirect URL from n8n.
  5. Select Register, then copy the Application (client) ID from the overview page into the n8n credential's Client ID field.

The account type matters because n8n's Microsoft credential signs in through the login.microsoftonline.com/common endpoint. Microsoft allows that endpoint only for multi-tenant apps. Register a single-tenant app and sign-in fails with:

AADSTS50194: Application '<client id>'(n8n) is not configured as a multi-tenant application. Usage of the /common endpoint is not supported for such applications created after '10/15/2018'.

The platform matters too. n8n exchanges the code for tokens on the server, using the client secret, and that is what the Web platform is for. Register the URI under Single-page application and the exchange fails with AADSTS9002327, because Microsoft only lets that client type redeem tokens from browser JavaScript.

Create the client secret, and plan for its expiry

  1. In your app registration, open Certificates & secrets in the left menu.
  2. Under Client secrets, select New client secret.
  3. Add a description, such as n8n credential, and pick an expiry.
  4. Select Add, then copy the Value column into the n8n credential's Client Secret field.

Copy the Value, not the Secret ID next to it. The portal shows the value only once. After you leave the page it is hidden for good, and you must create a new secret. If you paste the Secret ID by mistake, sign-in fails with AADSTS7000215: Invalid client secret provided.

Read the expiry options carefully. The portal offers preset lifetimes and a Custom option. Microsoft caps every client secret at 24 months and recommends less than 12. No secret lasts forever.

Here is why expiry matters. n8n stores a refresh token for your mailbox. Each time its short-lived access token runs out, n8n sends the client ID, the secret and the refresh token to Microsoft to get a new one. Once the secret expires, that request fails with AADSTS7000222: The provided client secret keys for app '<client id>' are expired. From then on every node that uses the credential fails, including the trigger on every poll. Nothing warns you before that day, so put the expiry date in a calendar when you create the secret.

Rotate the secret before it expires:

  1. Add a new client secret while the old one still works. An app can hold more than one secret at once.
  2. Paste the new value into the credential's Client Secret field and save.
  3. Run one Outlook node by hand and confirm it succeeds. If n8n asks you to reconnect, do so.
  4. Delete the old secret in Certificates & secrets.

Connect the credential, and which scopes it asks for

With the client ID and secret in place, select the connect button in the credential. A Microsoft sign-in window opens and lists the permissions n8n requests. Accept, and the window closes with the credential connected.

The Microsoft Outlook credential requests these scopes (permissions), as listed in n8n's documentation and in the credential's source:

openid offline_access Contacts.Read Contacts.ReadWrite Calendars.Read
Calendars.Read.Shared Calendars.ReadWrite Mail.ReadWrite Mail.ReadWrite.Shared
Mail.Send Mail.Send.Shared MailboxSettings.Read

offline_access is the scope that gives n8n a refresh token, so the connection survives for longer than one hour. The .Shared scopes let n8n use mailboxes that your account can already open. They grant no access by themselves. You do not need to add these permissions in Entra for a normal sign-in, because the credential requests them at sign-in time. Leave the credential's custom scopes option off and request nothing more.

n8n's documentation states the requirement: in an organisation that uses Microsoft Entra, the setting that lets users consent to apps accessing company data on their behalf must be enabled for the user. If it is not, an administrator must grant consent. Many tenants restrict user consent. You will know, because the sign-in window shows Need admin approval in place of the permission list.

The cleanest fix keeps the permissions to exactly what n8n asks for. An administrator opens the app registration, goes to API permissions, and adds the delegated Microsoft Graph permissions from the list above. openid and offline_access sit under OpenId permissions in the picker. Then the administrator selects Grant admin consent for the tenant. After that, the user connects the credential normally.

Personal Outlook.com accounts never see this step, because no tenant administrator stands between you and your own mailbox.

How to read a shared inbox by UPN

To automate a shared mailbox such as support@example.com, enable Use Shared Inbox in the credential and enter the target mailbox's UPN or ID. You still sign in as yourself. n8n then sends its requests to /users/<UPN>/ in Graph in place of /me/.

That has two consequences. First, your own account must already have access to that mailbox, which an Exchange administrator grants as a mailbox permission. Without it, Graph answers with ErrorAccessDenied and the message Access is denied. Check credentials and try again. Second, the UPN lives in the credential, so one credential reads one mailbox. Create a second credential for the shared inbox and keep your personal one as it is.

Build the workflow: answer new mail automatically

This workflow replies to each new message in the inbox, then marks it read. Use three nodes.

  1. Microsoft Outlook Trigger. Select your credential. Set Trigger On to Message Received and leave Output on Simplified. Under Filters, set Folders to Include to Inbox and leave Read Status on unread messages, which is the default. While you test, also set Sender to an address you control.
  2. Microsoft Outlook, with Resource Message and Operation Reply. Set Message ID to {{ $json.id }}. Write your text in Message. Turn on Reply to Sender Only, because it defaults to off, which means a reply to every recipient of the original mail.
  3. Microsoft Outlook, with Resource Message and Operation Update. Set Message ID to {{ $('Microsoft Outlook Trigger').item.json.id }}. Under Update Fields, add Is Read and turn it on.

The third node points back at the trigger by name because, after the reply node, $json holds the reply's output and no longer holds the original message. If you renamed the trigger node, use your name in the expression.

To test, send a mail from your test address, then use Fetch Test Event on the trigger. In manual mode the trigger fetches the newest matching message, so you can step through the reply without waiting. Check your test inbox for the reply and confirm the original now shows as read in Outlook. Then activate the workflow so the trigger runs on its schedule. Remove the Sender filter only when you are sure the reply text is right. An automatic reply sent to another automatic responder, such as an out-of-office message, can produce a loop of mail.

How does the Outlook trigger check for new mail?

The trigger polls. Microsoft does not push mail to it. Its Poll Times setting defaults to every minute. On each poll, n8n asks Graph for messages whose receivedDateTime is at or after the time of the last check and before now. It then saves now as the start of the next window. On the first activation the window starts at the moment you activate, so mail already in the mailbox never fires the trigger.

Without Folders to Include, the trigger queries Graph's /messages endpoint, which spans every folder, Sent Items included. That is why the workflow above limits it to Inbox. The trigger also keeps no list of message IDs it has handled. n8n's own node notes say each email must be processed exactly once, and suggest marking it read, moving it to a folder, or recording its ID. The Update node does the first of those, and the unread filter skips anything already handled.

If every minute is too often, pick an hourly or daily poll time. Those schedules run on the instance's timezone, and how n8n resolves timezones for schedules explains which setting wins when the server and the workflow disagree.

Failure modes, with the strings you will see

  • AADSTS50011, redirect URI mismatch: the OAuth Redirect URL in n8n and the Web URI in Entra differ. Fix the URL variables, restart, and copy the URL again.
  • AADSTS50194: the app is single-tenant. Change the supported account types under Authentication, or register a new app.
  • AADSTS7000215: the Secret ID was pasted in place of the secret Value.
  • AADSTS7000222: the secret expired. Create a new one and paste its value.
  • Need admin approval: the tenant blocks user consent. An administrator must grant it.
  • ErrorAccessDenied on a shared inbox: your account lacks access to that mailbox in Exchange.

The same OAuth pattern applies to Google. Connecting Gmail to self-hosted n8n uses a Google Cloud OAuth client in place of the Entra app, with the same callback URL from the same variables. Nothing in this guide needs a paid plan, and the differences between n8n Community and Enterprise concern team features, not which nodes you can use.

FAQ

What redirect URI do I register in Microsoft Entra for self-hosted n8n?

Register the exact OAuth Redirect URL that the n8n credential form shows, under the Web platform. It ends in /rest/oauth2-credential/callback. If it starts with http://localhost:5678, n8n does not know its public address. Set N8N_EDITOR_BASE_URL and the webhook URL variable to your HTTPS domain, restart the container, and copy the URL again.

Why did my n8n Outlook credential stop working after some months?

The most likely cause is an expired client secret. Microsoft caps client secrets at 24 months, and once the secret expires n8n can no longer refresh its access token. The error reads AADSTS7000222: The provided client secret keys for app ... are expired. Create a new secret in Certificates & secrets, paste its value into the credential, and delete the old one.

Can n8n read a shared mailbox in Microsoft 365?

Yes. Enable Use Shared Inbox in the Microsoft Outlook credential and enter the shared mailbox's UPN. You sign in as yourself, so your account must already have access to that mailbox in Exchange, or Graph returns ErrorAccessDenied. Use a separate credential for each mailbox, because the UPN is stored in the credential.

How often does the n8n Outlook trigger check for new email?

It polls on the schedule set in Poll Times, which defaults to every minute. Each poll asks Microsoft Graph for messages received since the previous check. Mail that arrived before you activated the workflow does not trigger it. The trigger does not track which messages it already handled, so mark each message read or move it after processing.

#n8n#outlook#microsoft-365#oauth#self-hosting