Connect a mailbox
Outlook accounts (Microsoft)
With a Microsoft Entra app registration, the sign-in page offers Sign in with Microsoft. It works for personal Microsoft accounts (Outlook.com, Hotmail, Live) and for Microsoft 365 work or school accounts. postkeep then reads and sends mail through Microsoft Graph, and new-mail webhooks use Graph change notifications. No app passwords are involved.
You need about 10 minutes and:
- a public HTTPS address for postkeep (
POSTKEEP_PUBLIC_URL, for examplehttps://mail.example.com); - an account that can open the Microsoft Entra admin center. Any Microsoft 365 business account works; with a personal account, sign up for a free Azure account first, which creates a directory for you.
The steps below use https://mail.example.com; replace it with your own
POSTKEEP_PUBLIC_URL, exactly as you set it (no trailing slash).
1. Register the app
- Open the Microsoft Entra admin center and sign in. You need at least the Application Developer role.
- Go to Entra ID > App registrations and select New registration.
- Name:
postkeep(users see it on the consent screen). - Supported account types: choose Any Entra ID Tenant + Personal
Microsoft accounts (shown as Accounts in any organizational directory
and personal Microsoft accounts in older versions of the page).
postkeep signs in through Microsoft’s
commonendpoint, which needs this option. - Redirect URI: platform Web, URI
https://mail.example.com/oauth/microsoft/callback. It must match exactly:https, your host, this path, no trailing slash, no query. - Select Register. On the app’s Overview page, copy the Application (client) ID.
2. Create a client secret
- In the app, open Certificates & secrets > Client secrets > New client secret.
- Enter a description such as
postkeep, pick an expiry (at most 24 months; Microsoft recommends less than 12, see add credentials), and select Add. - Copy the secret’s Value right away; it is shown only once. (The Secret ID next to it is not the secret.)
- Put a reminder in your calendar a few weeks before it expires. The
expiry date is listed under Client secrets. To rotate: create a new
secret, set it as
POSTKEEP_MICROSOFT_CLIENT_SECRETand restart postkeep, then delete the old secret. When a secret expires, sign-ins and token refreshes fail until you replace it; users don’t need to sign in again once the new secret is in place.
3. API permissions
postkeep asks for its permissions when a user signs in, so personal accounts work without this step. List them anyway if work or school accounts will sign in: an administrator who approves postkeep in the Entra admin center approves the permissions listed here (see Microsoft admin consent).
- Open API permissions > Add a permission > Microsoft Graph > Delegated permissions.
- Add
Mail.ReadWrite,Mail.Sendandoffline_access.User.Readis usually already listed; keep it. - Remove any other permission. postkeep requests exactly these four:
| Permission | Why |
|---|---|
Mail.ReadWrite |
Read, search, flag, move and delete messages; create folders |
Mail.Send |
Send and reply |
offline_access |
A refresh token, so the connection survives without signing in again |
User.Read |
The account’s address and a stable account id |
Do not add application permissions. postkeep only ever acts as the signed-in user.
4. Configure postkeep
POSTKEEP_MICROSOFT_CLIENT_ID=<Application (client) ID>
POSTKEEP_MICROSOFT_CLIENT_SECRET=<client secret Value>Restart postkeep. The log names the redirect URI it uses:
sign-in with Microsoft enabled redirect_uri=https://mail.example.com/oauth/microsoft/callbackThe sign-in and account pages now show Sign in with Microsoft. If you
set POSTKEEP_ALLOWED_EMAILS, list the Outlook addresses there too.
5. New-mail webhooks
Nothing more to set up. When a user turns on the webhook for an Outlook account, postkeep creates a Graph subscription for new Inbox messages. Graph then calls:
| Endpoint | Purpose |
|---|---|
POST https://mail.example.com/push/microsoft |
Change notifications |
POST https://mail.example.com/push/microsoft/lifecycle |
Lifecycle notifications (reauthorization, removed subscription, missed notifications) |
Requirements:
- Microsoft must reach those URLs over HTTPS with a publicly trusted certificate. If a firewall or proxy only allows certain sources, allow Microsoft Graph’s notification traffic or webhooks won’t arrive.
- Graph validates the URL when the subscription is created and expects the answer within 10 seconds; postkeep answers straight away.
Subscriptions for Outlook messages last at most 10,080 minutes (7 days). postkeep renews each one a day before it expires, and recreates it if Graph removes it. When an account is deleted from postkeep its subscription isn’t cancelled at Microsoft; it lapses within 7 days and postkeep ignores its notifications in the meantime.
A lifecycle notification is applied only to the subscription that was
verified. A late subscriptionRemoved or reauthorizationRequired for a
subscription that has been replaced leaves the replacement alone. missed
still schedules a catch-up. If Graph’s delete fails while the webhook is
turned off, other than because the subscription is already gone, postkeep
keeps the local record. The next time the webhook is turned on, postkeep
renews that subscription a day before it expires instead of creating
another. If creating a subscription answers 409 Conflict, postkeep deletes
its own Inbox subscriptions that notify this server and tries the create
once more.
Each notification only tells postkeep to look: it then lists Inbox
messages received since the last event it recorded. A forged, replayed or
duplicated notification can’t produce a false or duplicate event, and the
clientState secret of each subscription is compared in constant time
before anything happens.
Graph does not promise to deliver every notification, so while the webhook is on postkeep also lists the Inbox every 15 minutes and reports mail that no notification announced. Outlook allows an app four requests at a time for one mailbox. Each postkeep server sends at most two at once for a mailbox, its tools and its webhook together, so a second server, such as one starting during a rolling deploy, stays inside that limit.
Publisher verification
Microsoft labels apps without a verified publisher as unverified on the consent screen. What that means depends on who signs in:
- Personal Microsoft accounts (Outlook.com, Hotmail, Live) see the label but can consent.
- Work or school accounts in your own directory are not affected.
- Work or school accounts in other organizations: for multitenant apps registered after November 8, 2020, Microsoft’s default consent policy doesn’t let users consent to an unverified app that asks for more than basic sign-in and profile access. postkeep asks for mail access, so those users see “Need admin approval” and an administrator of their organization has to grant consent (or allow it through an admin consent workflow); Microsoft admin consent shows how. Organizations can also block user consent entirely.
To remove the label, complete publisher verification: you need a Microsoft AI Cloud Partner Program (formerly MPN) account for a business, a work account in the directory that owns the app, and a verified publisher domain. Apps registered with a personal Microsoft account can’t be verified. For a server used by you, your family or your own organization, verification is usually unnecessary.
Disconnecting
Deleting an Outlook account on postkeep’s account page deletes its stored tokens. Microsoft offers apps no endpoint to revoke a grant, so to remove postkeep’s access at Microsoft as well:
- personal accounts: account.live.com/consent/Manage;
- work or school accounts: the My Apps portal or your administrator.
Troubleshooting
| Message | Cause |
|---|---|
AADSTS50011 (redirect URI mismatch) |
The redirect URI in the app registration differs from {POSTKEEP_PUBLIC_URL}/oauth/microsoft/callback (check https, host, trailing slash) |
AADSTS7000215 (invalid client secret) |
The secret’s Secret ID was used instead of its Value, or the value was mistyped |
AADSTS7000222 (expired client secret); every Outlook account fails at once |
Create a new secret and update POSTKEEP_MICROSOFT_CLIENT_SECRET (step 2); users don’t need to sign in again |
AADSTS50194 or AADSTS50020 (“not configured as a multi-tenant application”, “does not exist in tenant”) |
The app was registered for one directory only; recreate it with Any Entra ID Tenant + Personal Microsoft accounts |
| “Need admin approval” | An administrator has to approve postkeep for the organization; see Microsoft admin consent and publisher verification |
| “Your organization has to approve postkeep” | Microsoft answered that an administrator has to approve the app (AADSTS90094, AADSTS65001 or consent_required), or that a reviewer decides on the request (AADSTS90095); see Microsoft admin consent |
| “Not approved”, after the admin consent link | Microsoft recorded no approval, for example because the administrator declined. Try again with one of the roles named in Microsoft admin consent |
| “This Microsoft account has no mailbox postkeep can reach” | The account has no Exchange Online mailbox (for example a work account without an Exchange license, or a mailbox hosted on-premises). postkeep only trusts the address of a real mailbox |
| The account page asks to sign in again | Microsoft rejected the refresh token (revoked consent, an administrator reset the password or revoked sessions, or 90 days without use); sign in with Microsoft again |