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@latest

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 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:00

Add --yes to skip the confirmation prompt in scripts:

nylas email send --to user@example.com --subject "Automated alert" --body "Disk usage at 90%." --yes

4. 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.docx

Outlook 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.pdf

7. 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:

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 \
  --grant <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.

Next steps