Guide
Outlook CLI: Send Email from Terminal
Use an Outlook CLI to send Microsoft 365 email from terminal without Graph API setup, Azure AD registration, MSAL token code, or SMTP config. This guide covers send, schedule, HTML body, JSON output, and shared-mailbox flows from the terminal without standing up an Azure AD app for each one.
Written by Caleb Geene Director, Site Reliability Engineering
Reviewed by Hazik
How do you use an Outlook CLI to send email from terminal?
Connect Outlook or Microsoft 365 once, then send with nylas email send --to user@example.com --subject "Hello" --body "Hi" --yes. This gives you an Outlook CLI send workflow without registering an Azure AD app, wiring MSAL, or configuring SMTP.
The same command covers the exact Outlook command line send email use case that usually sends developers into Graph API app registration or SMTP credential setup.
The problem with sending Outlook email programmatically starts in Microsoft's setup flow.
Sending Outlook email programmatically requires 4-5 setup steps through Microsoft's ecosystem: registering an Azure AD application, configuring Mail.Send API permissions, obtaining admin consent, integrating the MSAL authentication library, and handling token refresh. For a one-off send from a terminal or shell script, that overhead turns a 10-second task into a 45-minute project.
Microsoft's recommended send path is the Graph API /sendMail endpoint. 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 installs in under 30 seconds on macOS, Linux, or Windows and requires no Azure AD app registration, no Graph API credentials, and no MSAL library. The binary is a single Go executable under 20 MB that auto-detects your platform and architecture during install.
On macOS or Linux, install via Homebrew. On Windows — where over 80% of Outlook desktop users run according to Statcounter — use the PowerShell one-liner. Both methods verify SHA-256 checksums automatically. See the getting started guide for additional install options.
2. Authenticate your Outlook account
Authentication connects Nylas CLI to your Outlook or Microsoft 365 mailbox through a single OAuth2 flow, replacing the multi-step Azure AD app registration that Graph API requires. The entire process takes about 2 minutes and stores credentials securely in your system keyring so you don't re-authenticate on every command.
Go to dashboard-v3.nylas.com, create an application, and connect your Outlook or M365 mailbox. Then configure the CLI with your API key. According to Microsoft's OAuth 2.0 documentation, access tokens expire after 60-90 minutes — Nylas handles refresh automatically behind the scenes.
nylas auth config
# Paste your API key when prompted
# Verify the connection
nylas auth whoami
# => Authenticated as you@company.com (Microsoft)Credentials persist in your OS keyring (macOS Keychain, Windows Credential Manager, or Linux libsecret). You won't need to re-authenticate unless you revoke the grant.
3. Send from a user mailbox, alias, or shared mailbox
Microsoft 365 organizations typically manage 3-5 mailbox identity types per department — user mailboxes, shared mailboxes, distribution groups, resource rooms, and send-as aliases. Nylas CLI sends from any of these identities by targeting the correct grant ID. The key question is not just "can I send a message?" — it is "which mailbox identity should this message come from?"
To send from your personal Outlook mailbox, run the command without a grant ID — the CLI uses your default authenticated mailbox. To send from a shared mailbox or department identity, pass the shared mailbox's grant ID as the first argument. Each grant maps to exactly one mailbox identity in Exchange Online.
# 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 4521 is ready for review." \
--yesIf send-as or shared mailbox rights are missing, Microsoft 365 returns a 403 error and rejects the send even though the command syntax is correct. According to Microsoft's Exchange Online permissions documentation, shared mailbox delegation changes can take up to 60 minutes to propagate. That error is an Exchange Online policy issue, not a CLI issue.
4. Distribution lists, aliases, and team queues
Microsoft 365 distribution lists and M365 groups expand to all member mailboxes server-side, meaning a single nylas email send command can reach hundreds of recipients without client-side address resolution. Enterprises with 500+ employees commonly maintain 20-50 distribution lists for departments, project teams, and compliance routing. Nylas CLI sends to these lists the same way it sends to individual addresses — by specifying the list address in the --to flag.
The examples below show two common patterns: broadcasting to an engineering distribution list and routing a contract update to legal via CC with a compliance BCC. The --bcc flag keeps compliance or journaling addresses hidden from the visible recipient list, which is a frequent requirement in regulated industries like finance and healthcare.
# 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 are ready for review in the contract system." \
--yes5. HTML bodies and business workflow email
Outlook business email frequently carries invoices, approvals, statements of work, and procurement updates. The current CLI send command accepts HTML through --body, supports hosted templates through --template-id, and keeps file delivery separate: publish the file in your document system and send the link, or use the Nylas API/SDK when a MIME attachment is required.
Outlook's rendering engine processes HTML through Microsoft Word's HTML parser, so some CSS properties render differently than in Gmail or Apple Mail. Keep automated HTML bodies small, inline critical styles, and let tenant-level DLP and transport rules inspect the final message after the send completes.
nylas email send \
--to "client@example.com" \
--subject "Invoice #1042" \
--body "<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>" \
--yes6. Schedule delivery and respect Microsoft 365 limits
Scheduled email delivery in Microsoft 365 lets you compose a message now and have it sent at a specific future time, which is useful for reaching recipients in different time zones or queuing follow-ups during off-hours. Microsoft 365 enforces a limit of 30 messages per minute and 10,000 recipients per day on most plans, so bulk or automated send loops need deliberate pacing to avoid temporary blocks.
The --schedule flag accepts natural-language time expressions like "tomorrow 9am" or "next Monday 2pm EST". For bulk send loops, a 2-second sleep between sends keeps you well under Microsoft's 30-messages-per-minute ceiling and avoids triggering the temporary sending block that lasts up to 24 hours.
# 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.txt7. JSON output for scripting and audit trails
Adding --json to any nylas email send command returns the full message object as structured JSON, including the message ID, grant ID, thread ID, timestamps, and sender/recipient addresses. This output is useful for logging send events, feeding message IDs into downstream queue processors, or satisfying compliance audit trails that require proof of delivery attempt with exact timestamps.
The JSON response typically weighs 400-600 bytes per message and follows the Nylas API message schema. Piping the output through jq extracts specific fields — the message ID for tracking, the thread ID for conversation threading, or the date for audit logs.
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"
}In shell scripts, you can capture the message ID from the JSON output using jq and use it for downstream processing — verifying delivery status, appending to an audit CSV, or passing to a ticket system. The .id field is a unique 16-character string that maps to the message in the Nylas API.
# 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
Microsoft Graph API requires 50-100 lines of Python or Node.js code to send a single Outlook email when you include Azure AD app registration, MSAL token acquisition, token refresh handling, and the actual POST request. Nylas CLI reduces that to 1 command with zero lines of code and no Azure portal configuration. The comparison below breaks down each step across both approaches.
| 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 ... |
| File delivery | Base64-encode in JSON, upload session for >3 MB | Send a link, or use the Nylas API/SDK for attachments |
| 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
Microsoft 365 has tenant-level policies, compliance features, and mailbox types that don't exist in consumer email providers like Gmail or Yahoo. The following tips cover Outlook-specific behaviors — read receipts, sensitivity labels, shared mailboxes, rate limits, and transport rules — that affect how sent email is processed after it leaves the CLI.
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. According to Microsoft's documentation, recipients can decline read receipt requests, so don't rely on them for delivery confirmation in automated workflows.
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 regardless of the sending client.
Shared mailbox sending
To send from a shared mailbox, connect it as a separate grant through the OAuth flow. Each shared mailbox gets its own grant ID, and you reference that ID when sending. The user account connecting the shared mailbox must have Full Access or Send As permissions in Exchange Online — delegation changes can take up to 60 minutes to propagate.
# 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 4521 is ready for review."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
Microsoft 365 mail flows are shaped by tenant-wide Exchange Online rules that process messages before they reach the recipient. Transport rules can redirect, block, or modify messages based on sender, recipient, subject, or content patterns. Journaling copies all email to a compliance mailbox. DLP policies scan attachments and body content against over 100 built-in sensitive information types. According to Microsoft's mail flow rules documentation, transport rules are evaluated in priority order, and the first matching rule can stop further processing.
If an Outlook send behaves differently across two mailboxes, the cause is usually mailbox policy or tenant routing policy, not the CLI command syntax.
Shared mailbox and team queue workflows
Microsoft 365 organizations use shared mailboxes for procurement, support, recruiting, and executive operations. According to Microsoft's shared mailbox documentation, shared mailboxes don't require a separate license for mailboxes under 50 GB. Sending from support@company.com or ap@company.com through Nylas CLI works by connecting the shared mailbox as a separate grant and specifying its grant ID in the send command.
Next steps
Once you can send Outlook email from the terminal, you can extend your workflow to reading and filtering your Outlook inbox, managing calendar events, connecting AI agents to your mailbox, or switching to a different provider entirely — Nylas CLI supports 6 providers with the same command syntax.
- 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