Skip to content

Office 365 / Outlook / Hotmail

Overview

Choose Personal if you send from a consumer Microsoft mailbox such as @outlook.com, @hotmail.com, @live.com, or @msn.com. This path uses a one-time sign-in to obtain a refresh_token, which Apprise then rotates automatically during normal use.

Choose Organizational if you send from a work or school Microsoft 365 tenant backed by Exchange Online. This path uses an Azure app registration plus the client_credentials flow with your tenant_id, client_id, and client_secret.

Personal consumer accounts — @outlook.com, @hotmail.com, @live.com, @msn.com, and regional Hotmail/Live variants — use a refresh token flow instead of the organizational client credentials flow.

This involves two phases:

  • One-time setup: register a public-client app in Azure, then run a short script to sign in and capture your refresh_token.
  • Ongoing: Apprise uses the stored refresh_token to obtain short-lived access tokens automatically. Every successful send silently rotates the refresh token, so it stays alive as long as Apprise is used at least once every 90 days.
  1. Go to App Registrations in the Azure portal and click Register an application.

    • Give it a name (e.g. Apprise Personal).

    • Under Supported account types, select Personal Microsoft accounts only.

    • Under Redirect URI, choose Public client / native (mobile & desktop) and set the URI to:

      https://login.microsoftonline.com/common/oauth2/nativeclient
    • Click Register.

  2. From the Overview panel, copy the Application (client) ID - this is your client_id.

  3. Under Manage -> Authentication, scroll to Advanced settings and set Allow public client flows to Yes, then click Save. This enables the device code flow used to obtain the initial token.

  4. Under Manage -> API permissions:

    • Click Add a permission -> Microsoft Graph -> Delegated Permissions
    • Search for and check Mail.Send, then click Add permissions

This is a one-time step. The scripts below use Microsoft’s Device Code Flow: your script prints a short code and a link; you open that link in any browser and sign in; the script captures your refresh_token automatically.

The whole process typically takes under two minutes.

Only requests is required - already installed if you have Apprise.

Edit the CLIENT_ID line, then copy and paste the entire block into your terminal.

import time, requests
# - Edit this line
CLIENT_ID = "your-client-id-here" # Application (client) ID from Azure
# -
DEVICE_URL = "https://login.microsoftonline.com/consumers/oauth2/v2.0/devicecode"
TOKEN_URL = "https://login.microsoftonline.com/consumers/oauth2/v2.0/token"
SCOPE = "Mail.Send offline_access"
# Step 1 - request a device code
r = requests.post(DEVICE_URL, data={"client_id": CLIENT_ID, "scope": SCOPE})
r.raise_for_status()
d = r.json()
# Step 2 - the script prints a link and a short code; open the link in a
# browser and enter the code to sign in with your Microsoft account
print("\n" + d["message"] + "\n")
# Step 3 - poll quietly until you finish signing in (usually under 60 sec)
interval = d.get("interval", 5)
while True:
time.sleep(interval)
r = requests.post(TOKEN_URL, data={
"client_id": CLIENT_ID,
"grant_type": "urn:ietf:params:oauth:grant-type:device_code",
"device_code": d["device_code"],
})
t = r.json()
if "refresh_token" in t:
print("Your refresh_token (use this in your Apprise URL):\n")
print(t["refresh_token"])
break
if t.get("error") == "slow_down":
interval += 5
elif t.get("error") != "authorization_pending":
print("Error:", t.get("error_description", t))
break

Valid syntax is as follows (both o365:// and azure:// are accepted aliases):

  • o365://{source}/{client_id}/{refresh_token}/
  • o365://{source}/{client_id}/{refresh_token}/{targets}

Apprise automatically detects personal mode when source is a known consumer domain (outlook.com, hotmail.com, live.com, msn.com, and regional variants). Use ?mode=personal to force personal mode on any other domain.

VariableRequiredDescription
source*YesYour consumer email address (e.g. user@outlook.com). Used as the sender and, when no targets are given, also as the recipient.
client_id*YesThe Application (client) ID from your Azure app registration.
refresh_token*YesThe refresh token from the one-time setup above. Apprise rotates this automatically on each successful send.
targetsNoOne or more recipient email addresses. If omitted, email is sent to source.
toNoAlias for targets. Useful in YAML configuration.
ccNoOne or more Carbon Copy addresses. Accepts comma-separated values and named addresses (e.g. Name <email@example.com>).
bccNoOne or more Blind Carbon Copy addresses. Accepts comma-separated values and named addresses (e.g. Name <email@example.com>).
reply_toNoOne or more Reply-To addresses. When a recipient replies, their reply goes here instead of to source. Accepts comma-separated values and named addresses.
modeNoForce personal mode on a non-standard domain: ?mode=personal.
VariableDescription
overflowThis parameter can be set to either split, truncate, or upstream. This determines how Apprise delivers the message you pass it. By default this is set to upstream
👉 upstream: Do nothing at all; pass the message exactly as you received it to the service.
👉 truncate: Ensure that the message will fit within the service’s documented upstream message limit. If more information was passed then the defined limit, the overhead information is truncated.
👉 split: similar to truncate except if the message doesn’t fit within the service’s documented upstream message limit, it is split into smaller chunks and they are all delivered sequentially there-after.
formatThis parameter can be set to either text, html, or markdown. Some services support the ability to post content by several different means. The default of this varies (it can be one of the 3 mentioned at any time depending on which service you choose). You can optionally force this setting to stray from the defaults if you wish. If the service doesn’t support different types of transmission formats, then this field is ignored.
verifyExternal requests made to secure locations (such as through the use of https) will have certificates associated with them. By default, Apprise will verify that these certificates are valid; if they are not then no notification will be sent to the source. In some occasions, a user might not have a certificate authority to verify the key against or they trust the source; in this case you will want to set this flag to no. By default it is set to yes.
ctoThis stands for Socket Connect Timeout. This is the number of seconds Requests will wait for your client to establish a connection to a remote machine (corresponding to the connect()) call on the socket. The default value is 4.0 seconds.
rtoThis stands for Socket Read Timeout. This is the number of seconds the client will wait for the server to send a response. The default value is 4.0 seconds.
emojisEnable Emoji support (such as providing :+1: would translate to 👍). By default this is set to no.
Note: Depending on server side settings, the administrator has the power to disable emoji support at a global level; but default this is not the case.
tzIdentify the IANA Time Zone Database you wish to operate as. By default this is detected based on the configuration the server hosting Apprise is running on. You can set this to things like America/Toronto, or any other properly formated Timezone describing your area.

Send from a personal Outlook account (mode is auto-detected from the domain):

Terminal window
# source: you@outlook.com
# client_id: aa-bb-cc-dd-ee
# refresh_token: (from the script above)
apprise -vv -t "Test Title" -b "Test Body" \
"o365://you@outlook.com/aa-bb-cc-dd-ee/your-refresh-token-here/"

Send to a different recipient from your Hotmail account:

Terminal window
apprise -vv -t "Hello" -b "Message body" \
"o365://me@hotmail.com/aa-bb-cc-dd-ee/your-refresh-token-here/friend@example.com"

Force personal mode on a non-standard domain:

Terminal window
apprise -vv -t "Hello" -b "Message body" \
"o365://me@custom.com/aa-bb-cc-dd-ee/your-refresh-token-here/?mode=personal"
Questions or Feedback?

Documentation

Notice a typo or an error? Report it or contribute a fix .

Technical Issues

Having trouble with the code? Open an issue on GitHub:

Made with love from Canada