Guide

Send Outlook Email from the CLI

Sending email through Outlook programmatically normally means Azure AD app registration, Graph API permissions, and MSAL token management. Nylas CLI sends Outlook and Microsoft 365 email in one command with automatic OAuth2. It works the same way across Gmail, Outlook, Exchange, Yahoo, iCloud, and IMAP — no provider-specific code needed.

Written by Caleb Geene Director, Site Reliability Engineering

Reviewed by Hazik

VerifiedCLI 3.1.1 · Outlook, Microsoft 365 · last tested April 11, 2026

The problem with sending Outlook email programmatically

Microsoft's recommended way to send email from Outlook is the Graph API /sendMail endpoint. That means registering an app in Azure AD, configuring Mail.Send permissions, getting admin consent, and handling MSAL token acquisition and refresh. According to Microsoft's OAuth 2.0 documentation, access tokens expire after 60-90 minutes and require refresh token rotation.

For a quick send from your terminal or a shell script, that's too much setup. SMTP via smtp.office365.com on port 587 still works, but Microsoft retired Basic Auth for Exchange Online in October 2022. You can't use username/password anymore.

Nylas CLI bypasses all of this. It talks to the Nylas API, which handles OAuth2 token refresh, provider routing, and connection management. Authenticate once, then send with one command.

1. Install

Run brew install nylas/nylas-cli/nylas (other methods). On Windows, use irm https://cli.nylas.com/install.ps1 | iex since most Outlook users are on Windows.

2. Authenticate your Outlook account

Go to dashboard-v3.nylas.com, create an application, and connect your Outlook or M365 mailbox. Then grab your API key:

nylas auth config
# Paste your API key when prompted

# Verify the connection
nylas auth whoami
# => Authenticated as you@company.com (Microsoft)

Credentials are stored in your system keyring. You won't need to re-authenticate unless you revoke the grant.

3. Send from a user mailbox, alias, or shared mailbox

This is the first place Outlook differs from the generic terminal guide. Microsoft 365 teams frequently send from role accounts, shared mailboxes, and department identities. The key question is not just "can I send a message?" It is "which mailbox identity should this message come from?"

# Send from your own Outlook mailbox
nylas email send \
  --to "colleague@company.com" \
  --subject "Q2 planning doc" \
  --body "Hi — the planning doc is ready for review." \
  --yes

# Send from a shared mailbox grant
nylas email send <shared-mailbox-grant-id> \
  --to "vendor@partner.com" \
  --subject "PO #4521" \
  --body "Purchase order attached." \
  --attach ./po-4521.pdf \
  --yes

If send-as or shared mailbox rights are wrong, Microsoft 365 rejects the send even though the command syntax is fine. That is an Exchange Online policy issue, not a shell issue.

4. Distribution lists, aliases, and team queues

Outlook distribution lists, M365 groups, recruiting inboxes, support queues, and finance aliases are common enterprise workflows. The generic terminal page does not need to spend much time there; the Outlook page does.

# Send to a distribution list
nylas email send \
  --to "engineering-all@company.com" \
  --subject "Deployment window tonight" \
  --body "Production deploy starts at 22:00 UTC. Freeze all PRs by 21:00." \
  --yes

# Include compliance or archive recipients
nylas email send \
  --to "contracts@company.com" \
  --cc "legal@company.com" \
  --bcc "compliance@company.com" \
  --subject "Contract update" \
  --body "Updated terms attached." \
  --attach ./contract-v3.pdf \
  --yes

5. HTML, attachments, and business workflow email

Outlook mail often carries invoices, approvals, statements of work, or procurement documents. That is different from the shell-centric here-doc examples in the generic command-line guide.

nylas email send \
  --to "client@example.com" \
  --subject "Invoice #1042" \
  --html "<h1>Invoice #1042</h1><p>Amount due: <strong>$2,400.00</strong></p><p>Payment link: <a href='https://pay.example.com/1042'>pay.example.com/1042</a></p>" \
  --attach ./invoice-1042.pdf \
  --yes

Outlook and M365 accounts support attachments up to 25 MB per message. The CLI auto-detects MIME type, while tenant DLP or transport rules still apply after the send.

6. Schedule delivery and respect Microsoft 365 limits

Outlook automation is also more likely to run into tenant sending limits than the consumer-provider pages. Microsoft 365 enforces per-minute and per-day sending caps, so scheduled or bulk workflows should pace themselves deliberately.

# Schedule a follow-up
nylas email send \
  --to "client@example.com" \
  --subject "Follow-up" \
  --body "Checking in on the proposal." \
  --schedule "tomorrow 9am" \
  --yes

# Rate-limit a mailbox automation loop
while read -r recipient; do
  nylas email send --to "$recipient" --subject "Status update" --body "See attached notes." --yes
  sleep 2
done < recipients.txt

7. JSON output for scripting and audit trails

Add --json to capture the exact message object, message ID, and mailbox context for logs, queue processors, or compliance-sensitive workflows:

nylas email send \
  --to "user@example.com" \
  --subject "Test" \
  --body "Hello." \
  --json --yes
{
  "id": "a1b2c3d4e5f6g7h8",
  "grant_id": "d3f4a5b6-c7d8-9e0f-a1b2-c3d4e5f6g7h8",
  "thread_id": "x9y8z7w6v5u4t3s2",
  "subject": "Test",
  "from": [{"name": "Dev User", "email": "dev@company.com"}],
  "to": [{"name": "", "email": "user@example.com"}],
  "date": "2026-03-28T10:30:00-04:00",
  "object": "message"
}

Use this in shell scripts to capture the message ID, verify delivery, or log sends:

# Capture the message ID after sending
msg_id=$(nylas email send \
  --to "user@example.com" \
  --subject "Automated" \
  --body "This is automated." \
  --json --yes | jq -r '.id')

echo "Sent message: $msg_id"

Graph API vs. Nylas CLI

Here's what sending an Outlook email looks like with Graph API versus Nylas CLI:

StepGraph APINylas CLI
App registrationAzure AD portal, configure redirect URIsNot needed
PermissionsAdd Mail.Send, get admin consentNot needed
AuthenticationMSAL library, acquire tokens, handle refreshnylas auth config (once)
Send emailPOST to /me/sendMail with JSON bodynylas email send --to ...
AttachmentsBase64-encode in JSON, upload session for >3 MB--attach ./file.pdf
Token refreshHandle 401, rotate refresh tokensAutomatic
Multi-providerMicrosoft onlyGmail, Outlook, Exchange, Yahoo, iCloud, IMAP
Lines of code50-100+ (Python/Node with MSAL)1 command

Outlook-specific tips

Read receipts

Outlook supports read receipt requests via the Disposition-Notification-To header. When you send through Nylas CLI, the recipient's Outlook client may prompt them to send a read receipt depending on their settings.

Sensitivity labels

M365 sensitivity labels (Confidential, Internal, etc.) are applied at the tenant level by Microsoft Information Protection. Messages sent through Nylas CLI respect your tenant's default labeling policy. If your org requires a specific label, your Exchange admin can set auto-labeling rules that apply to all outbound email.

Shared mailbox sending

To send from a shared mailbox, connect it as a separate grant:

# Connect the shared mailbox
nylas auth login
# Follow the OAuth flow for the shared mailbox

# Send from the shared mailbox using its grant ID
nylas email send <shared-mailbox-grant-id> \
  --to "vendor@partner.com" \
  --subject "PO #4521" \
  --body "Purchase order attached." \
  --attach ./po-4521.pdf

Outlook sending limits

Microsoft 365 enforces a limit of 10,000 recipients per day and 30 messages per minute for most plans. According to Microsoft's Exchange Online limits documentation, exceeding these triggers a temporary block. For bulk sends, add a sleep 2 between messages in your script.

Transport rules, journaling, and send-as policy

Outlook and Microsoft 365 mail flows are often shaped by tenant-wide Exchange Online rules long before a message reaches the recipient. Transport rules, journaling, DLP, quarantine policies, and send-as permissions can all change delivery outcomes even when the send command succeeds. That is a Microsoft-specific operational concern that the Yahoo and iCloud send guides simply do not have.

If an Outlook send behaves differently across two mailboxes, the cause is often mailbox policy or tenant routing policy, not the body or attachment syntax.

Shared mailbox and team queue workflows

Outlook teams also use shared mailboxes for procurement, support, recruiting, and executive operations far more than consumer providers do. Sending from support@company.com or ap@company.com is a normal Microsoft 365 workflow, not an edge case. That is why this page goes deeper on grants, shared identities, and organization-owned senders than the generic terminal guide.

Next steps