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.
By Caleb Geene
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 Nylas CLI
# macOS / Linux (Homebrew)
brew install nylas/nylas-cli/nylas
# macOS / Linux / WSL (shell script)
curl -fsSL https://cli.nylas.com/install.sh | bash
# Windows (PowerShell)
irm https://cli.nylas.com/install.ps1 | iex
# Or build from source (requires Go 1.23+)
go install github.com/nylas/cli/cmd/nylas@latest2. 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 a basic email
nylas email send \
--to "colleague@company.com" \
--subject "Q2 planning doc" \
--body "Hi — the planning doc is ready for review."The CLI confirms delivery:
✓ Email sent successfully
Message ID: abc123def456
To: colleague@company.com
Subject: Q2 planning doc
Sent at: 2026-03-28T10:15:00-04:00Add --yes to skip the confirmation prompt in scripts:
nylas email send --to user@example.com --subject "Automated alert" --body "Disk usage at 90%." --yes4. Send with attachments
# Single file
nylas email send \
--to "manager@company.com" \
--subject "Monthly report" \
--body "Report attached." \
--attach ./report.pdf
# Multiple files
nylas email send \
--to "team@company.com" \
--subject "Design assets" \
--body "See attached mockups." \
--attach ./mockup-v1.png \
--attach ./mockup-v2.png \
--attach ./specs.docxOutlook and M365 accounts support attachments up to 25 MB per message. The CLI auto-detects the MIME type from the file extension.
5. Send HTML email
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>"Use --html instead of --body for rich formatting. Outlook renders standard HTML and inline CSS.
6. CC and BCC recipients
nylas email send \
--to "alice@company.com" \
--cc "bob@company.com" \
--cc "carol@company.com" \
--bcc "compliance@company.com" \
--subject "Contract update" \
--body "Updated terms attached." \
--attach ./contract-v3.pdf7. Schedule email for later
# Send in 2 hours
nylas email send \
--to "team@company.com" \
--subject "Standup reminder" \
--body "Standup in 15 minutes." \
--schedule 2h
# Send tomorrow at 9 AM
nylas email send \
--to "client@example.com" \
--subject "Follow-up" \
--body "Checking in on the proposal." \
--schedule "tomorrow 9am"
# Send on a specific date
nylas email send \
--to "team@company.com" \
--subject "Quarter kickoff" \
--body "Q3 starts Monday." \
--schedule "2026-06-29 08:00"8. Send to distribution lists
Outlook distribution lists and M365 groups work like any other recipient. Send to the group address:
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."The distribution list expands server-side. Your sent message shows the group address, not individual recipients.
9. JSON output for scripting
Add --json to get structured output you can pipe into jq or feed to another program:
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:
| Step | Graph API | Nylas CLI |
|---|---|---|
| App registration | Azure AD portal, configure redirect URIs | Not needed |
| Permissions | Add Mail.Send, get admin consent | Not needed |
| Authentication | MSAL library, acquire tokens, handle refresh | nylas auth config (once) |
| Send email | POST to /me/sendMail with JSON body | nylas email send --to ... |
| Attachments | Base64-encode in JSON, upload session for >3 MB | --attach ./file.pdf |
| Token refresh | Handle 401, rotate refresh tokens | Automatic |
| Multi-provider | Microsoft only | Gmail, Outlook, Exchange, Yahoo, iCloud, IMAP |
| Lines of code | 50-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 \
--grant <shared-mailbox-grant-id> \
--to "vendor@partner.com" \
--subject "PO #4521" \
--body "Purchase order attached." \
--attach ./po-4521.pdfOutlook 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.
Next steps
- Send email from terminal — cross-provider guide covering Linux, macOS, and Windows
- List Outlook emails from terminal — read, search, and filter your Outlook inbox
- Manage Outlook calendar from CLI — create events, check availability, schedule meetings
- Give AI agents email access via MCP — connect Claude, Cursor, or VS Code to your Outlook inbox
- Full command reference — every flag and subcommand documented