Microsoft marked Send-MailMessage obsolete years ago, and the warning still sits at the top of the Send-MailMessage cmdlet reference: “The Send-MailMessage cmdlet is obsolete. This cmdlet doesn’t guarantee secure connections to SMTP servers.” It still runs, but Microsoft won’t fix bugs or add features, and it can’t talk to modern OAuth mailboxes. So if you’ve got a PowerShell script that fires off alerts or reports, you’re now picking a replacement you’ll actually maintain.
The usual reflexes are System.Net.Mail.SmtpClient (also marked obsolete) or wiring up SMTP credentials by hand. Both leave you managing app passwords, TLS settings, and a relay host. This guide shows a REST replacement you call straight from Invoke-RestMethod, and it’s honest about the cases where plain SMTP is still the better call.
What can I use instead of Send-MailMessage in PowerShell?
Section titled “What can I use instead of Send-MailMessage in PowerShell?”The supported replacement is a REST email API you call with Invoke-RestMethod, not another SMTP cmdlet. Microsoft’s own guidance points to MailKit or Microsoft Graph because both Send-MailMessage and System.Net.Mail.SmtpClient are obsolete. A unified send endpoint goes further: one POST reaches Gmail, Microsoft 365, Yahoo, and IMAP without per-host SMTP config.
Send-MailMessage was convenient because it shipped in the box. Its replacement has to keep that one-liner feel while fixing the security gap. A hosted API does this by moving authentication off your script: you send JSON to POST /v3/grants/{grant_id}/messages/send with a bearer token, and the provider’s OAuth, TLS, and DKIM are handled upstream. No app password sits in your .ps1 file, and the same call works whether the connected mailbox is Google or Microsoft. The send request caches its response for 1 hour per grant when you pass an Idempotency-Key, so a retried scheduled task won’t double-send.
How do I send email from a PowerShell script on Windows?
Section titled “How do I send email from a PowerShell script on Windows?”Build a hashtable, convert it to JSON, and POST it to the send endpoint with Invoke-RestMethod. On Windows PowerShell 5.1 or PowerShell 7, this replaces the obsolete Send-MailMessage in about eight lines. The API accepts to, subject, and body at minimum, and a single bearer token authenticates every request instead of a stored SMTP password.
The call below sends one message through a connected mailbox. It targets POST /v3/grants/{grant_id}/messages/send, which works identically across Google, Microsoft 365, and Yahoo, so the same script serves any team regardless of provider. Read the API key from an environment variable so it never lands in source control.
$headers = @{ "Authorization" = "Bearer $env:NYLAS_API_KEY" "Content-Type" = "application/json"}
$body = @{ subject = "Nightly backup completed" body = "All 14 jobs finished successfully at $(Get-Date -Format o)."} | ConvertTo-Json -Depth 4
Invoke-RestMethod -Method Post ` -Uri "https://api.us.nylas.com/v3/grants/$env:NYLAS_GRANT_ID/messages/send" ` -Headers $headers ` -Body $bodySchedule it with Task Scheduler the same way you ran the old cmdlet. The deeper walkthrough, including storing the key securely and adding schtasks triggers, lives in send email from a PowerShell script.
How do I send email from a bash script or cron job?
Section titled “How do I send email from a bash script or cron job?”Use curl to POST JSON to the same send endpoint, then drop the line into crontab. The REST approach means a Linux cron job and a Windows scheduled task hit identical infrastructure, so one mailbox integration covers both. No sendmail binary, no local MTA, and no /etc/postfix to configure on the host.
This curl call posts to POST /v3/grants/{grant_id}/messages/send with the same JSON shape the PowerShell version uses. It’s handy for cron-driven reports or alerting from a CI runner, and the response returns the sent message ID for logging. Pass an Idempotency-Key header (max 256 characters) if a flaky cron host might retry within the hour.
curl --request POST \ --url "https://api.us.nylas.com/v3/grants/${NYLAS_GRANT_ID}/messages/send" \ --header "Authorization: Bearer ${NYLAS_API_KEY}" \ --header "Content-Type: application/json" \ --data '{ "to": [{ "name": "Dana Operator", "email": "[email protected]" }], "subject": "Nightly backup completed", "body": "All 14 jobs finished successfully." }'To run the same logic inside a container instead of a host cron, the transactional send guide covers sending from a verified domain without a user mailbox, which suits unattended notification workloads.
Send-MailMessage vs a REST email API
Section titled “Send-MailMessage vs a REST email API”Send-MailMessage authenticates with a plaintext SMTP password and can only reach a host you configure. A REST API authenticates with a bearer token and reaches every connected provider through one call. The table below compares the obsolete cmdlet, raw SMTP through MailKit, and the unified send endpoint on the work a script actually does.
| Task | Send-MailMessage (obsolete) | SMTP through MailKit | Unified REST API |
|---|---|---|---|
| Status | Deprecated, no fixes | Supported | Supported |
| Auth | Plaintext SMTP password | App password or OAuth token | One bearer token |
| Modern OAuth mailboxes | No | Manual XOAUTH2 setup | Built in |
| Multiple providers | One SMTP host | One host per provider | Gmail, Microsoft, Yahoo, IMAP |
| Delivery tracking | None | None | tracking_options for opens and links |
The tracking_options row is the practical reason teams switch: SMTP gives you no read receipt or click data, while the send endpoint accepts an opens and links flag in the same payload. That said, the comparison isn’t one-sided, which the next section covers.
When does System.Net.Mail or plain SMTP still work?
Section titled “When does System.Net.Mail or plain SMTP still work?”Plain SMTP still works when you send to one internal relay that allows anonymous or basic auth, and you don’t need OAuth, tracking, or multi-provider reach. A locally hosted SMTP server on port 25 inside a trusted network is a legitimate case where System.Net.Mail or MailKit stays simpler than any API call. Don’t add a dependency you won’t use.
System.Net.Mail.SmtpClient is marked obsolete, but obsolete means unmaintained, not removed: it still compiles and runs on .NET and Windows PowerShell today. If your script sends to a fixed corporate Exchange relay over the LAN, with no public mailbox and no delivery analytics, the obsolete path costs nothing extra. That equation changes once you need to send through a Google or Microsoft 365 mailbox that requires OAuth, want per-message open and link tracking, or have to support more than one provider. At that point the 8-line Invoke-RestMethod replacement removes the app-password and TLS maintenance that basic SMTP keeps handing you.
What’s next
Section titled “What’s next”- Send email from a PowerShell script for the full Task Scheduler and secure-key setup
- Transactional send to send from a verified domain without a user mailbox
- Microsoft Graph API alternative if your scripts target Outlook and Microsoft 365
- Getting started with Nylas to create a project, connect a mailbox, and get your first grant