WordPress.org

Plugin Directory

SendForce Mail SMTP Relay — Free WP SMTP & Email API Plugin for Amazon SES, SendGrid, Mailgun, Brevo, Postmark, Zoho ZeptoMail & Gmail

SendForce Mail SMTP Relay — Free WP SMTP & Email API Plugin for Amazon SES, SendGrid, Mailgun, Brevo, Postmark, Zoho ZeptoMail & Gmail

Description

SendForce Mail SMTP is a free, open-source WordPress SMTP plugin that fixes the “WordPress emails not being delivered” problem in minutes. Send WooCommerce order emails, Contact Form 7 / WPForms / Gravity Forms submissions, password resets, registration confirmations, and any other wp_mail() traffic through Amazon SES, SendGrid, Mailgun, Brevo (Sendinblue), Postmark, Zoho ZeptoMail, Gmail API, Microsoft 365, or any standard SMTP server — with full email logging, queue, retry, and failure alerts.

Why SendForce Mail SMTP

  • 100% Free, no premium upsell. Every feature you see is included — no “pro version” lock screens, no email-volume cap, no provider unlock paywall. Compare with WP Mail SMTP, Easy WP SMTP, Post SMTP, or FluentSMTP and you’ll find SendForce’s free tier matches or beats their paid tiers.
  • 24+ providers, one plugin. Built-in presets and HTTP API integrations for the most popular transactional email services — pick what fits your budget, deliverability needs, and region.
  • Built-in debugging. Live SMTP debug log on test email, raw API request/response capture in the email log, “Copy Debug Info” button for support tickets, and WP-CLI commands. Designed for developers and agencies who need to diagnose email problems fast.
  • Multiple connections with automatic fallback. Configure a primary connection (e.g. Amazon SES) plus a fallback (e.g. Mailgun). If the primary returns an error, the fallback takes over — no email lost.

Supported email providers

HTTP API sending (recommended — faster and more reliable than SMTP):

  • Amazon SES (AWS Simple Email Service)
  • SendGrid (by Twilio)
  • Mailgun (by Sinch)
  • Brevo (formerly Sendinblue)
  • SparkPost
  • Postmark
  • Zoho ZeptoMail
  • Microsoft 365 / Outlook (Microsoft Graph API)
  • Gmail / Google Workspace (Gmail API)
  • SMTP2GO
  • SMTP.com
  • Elastic Email
  • SocketLabs
  • Moosend
  • SendLayer
  • Mailjet
  • MailerSend
  • Mandrill (by Mailchimp)
  • Resend
  • Netcore Email API (formerly Pepipost)

SMTP sending (works with any provider, including custom servers):

  • Gmail / Google Workspace SMTP (smtp.gmail.com)
  • Microsoft 365 / Outlook SMTP (smtp.office365.com)
  • Yahoo Mail SMTP
  • Zoho Mail SMTP
  • Turbo SMTP
  • Any other SMTP server (host, port, username, password)

Features

  • Multiple connections with automatic fallback — configure a primary connection (e.g. Amazon SES) plus a fallback (e.g. Mailgun, SMTP, or PHP mail). If the primary returns an error, the fallback is attempted automatically. Both attempts are logged so you have a full audit trail; recovered emails carry a “Recovered via fallback” badge in the log detail view.
  • Smart Routing Rules — send different emails through different connections based on rules you define. Match by recipient domain, recipient pattern (glob), sender email or domain, source plugin (auto-detected for WooCommerce, Contact Form 7, WPForms, Gravity Forms, Ninja Forms, Fluent Forms, Forminator, Easy Digital Downloads, MemberPress, LearnDash, MailPoet, BuddyPress, bbPress and more), subject contains, time of day, or weekday. Rules are evaluated top-down (first match wins) and can target a single connection, a round-robin pool, or WordPress phpmail(). Includes inline rule editor, master enable/disable toggle, “Test Routing” tool, and a “via rule:” badge on the Email Log so you can see exactly which rule fired for any send.
  • One-click provider presets — pre-filled host, port, encryption, and API endpoint for every supported provider. Just paste your API key or password.
  • Email Log — every outgoing email logged with status, recipient, subject, headers, error message, and timestamp. Filter by status, search by keyword, filter by date range, export to CSV or JSON.
  • Email Queue with retry — queue mode prevents rate-limit errors and timeouts on bulk sends. Failed emails auto-retry on exponential backoff (5 min 15 min 45 min).
  • Email Test page — send a test email with live SMTP debug log output. Diagnose connection problems instantly.
  • Failure alerts — push delivery failures to Telegram, Slack, Discord, Microsoft Teams, Google Chat, Pushover, or any custom webhook.
  • Sender Identity override — force every email to use a custom From name and From address site-wide.
  • Conflict detection — warns you if another SMTP plugin (WP Mail SMTP, FluentSMTP, Easy WP SMTP, Post SMTP, etc.) is active so you can deactivate it before things break.
  • Encrypted credential storage — SMTP passwords and API keys are encrypted at rest using AES-256-CBC with WordPress security salts.
  • WP-CLI commandswp sendforce test, wp sendforce queue process, wp sendforce log list, wp sendforce sysinfo.
  • Developer hookssendforce_before_send, sendforce_after_send, sendforce_log_entry, sendforce_api_request, sendforce_api_response for extending the plugin without forking.
  • WP_DEBUG_LOG integration — optional mirroring of SMTP and API debug output to debug.log.
  • Automatic log cleanup — configurable retention period (default 30 days).
  • Privacy-friendly — optional disable of email-body logging for GDPR / privacy compliance.
  • Multisite compatible.

Works with all major WordPress plugins

SendForce Mail SMTP hooks into WordPress’s core wp_mail() function, so it works with every plugin that sends email — including WooCommerce, Easy Digital Downloads, Contact Form 7, WPForms, Gravity Forms, Ninja Forms, Fluent Forms, Forminator, MemberPress, LearnDash, BuddyPress, bbPress, MailPoet (transactional), and any custom plugin or theme that uses wp_mail().

External Services

SendForce Mail Relay is an email-delivery plugin. It does NOT connect to any external service automatically. A connection is made ONLY after you explicitly configure a provider and either (a) WordPress sends an outgoing email, or (b) you click “Send Test Email”, or (c) you enable a notification channel and an email-failure alert is dispatched.

For each configured email provider, the data sent on every send is the same set of fields WordPress would otherwise hand to the local mail() function: the recipient address(es), CC, BCC, Reply-To, From name/address, subject, message body, headers, and attachments. No additional telemetry or analytics is transmitted by this plugin.

For each configured notification channel (Telegram, Slack, Discord, Microsoft Teams, Google Chat, Pushover, custom Webhook), the data sent is a short status message you compose in the plugin (default: failed-delivery summary or daily summary).

Email-delivery providers (configurable in Settings Connections)

SendGrid (by Twilio)
* Service: https://sendgrid.com
* When data is sent: every WordPress email when SendGrid is the active connection, plus on test-email click.
* Endpoint: https://api.sendgrid.com/v3/mail/send
* Privacy Policy: https://www.twilio.com/en-us/legal/privacy
* Terms of Service: https://www.twilio.com/en-us/legal/tos

Mailgun (by Sinch)
* Service: https://www.mailgun.com
* When data is sent: same as above, but to Mailgun.
* Endpoint: https://api.mailgun.net/v3/{domain}/messages (US) or https://api.eu.mailgun.net/v3/{domain}/messages (EU).
* Privacy Policy: https://www.mailgun.com/legal/privacy-policy/
* Terms of Service: https://www.mailgun.com/legal/terms/

Amazon SES (AWS Simple Email Service)
* Service: https://aws.amazon.com/ses/
* When data is sent: same.
* Endpoint: https://email.{region}.amazonaws.com/v2/email/outbound-emails (region you select).
* Privacy Policy: https://aws.amazon.com/privacy/
* Terms of Service: https://aws.amazon.com/service-terms/

Brevo (formerly Sendinblue)
* Service: https://www.brevo.com
* Endpoint: https://api.brevo.com/v3/smtp/email
* Privacy Policy: https://www.brevo.com/legal/privacypolicy/
* Terms of Service: https://www.brevo.com/legal/termsofuse/

SparkPost
* Service: https://www.sparkpost.com
* Endpoint: https://api.sparkpost.com/api/v1/transmissions
* Privacy Policy: https://www.sparkpost.com/policies/privacy/
* Terms of Service: https://www.sparkpost.com/policies/tou/

Postmark
* Service: https://postmarkapp.com
* Endpoint: https://api.postmarkapp.com/email
* Privacy Policy: https://postmarkapp.com/privacy-policy
* Terms of Service: https://postmarkapp.com/terms-of-service

Elastic Email
* Service: https://elasticemail.com
* Endpoint: https://api.elasticemail.com/v4/emails
* Privacy Policy: https://elasticemail.com/legal/privacy-policy
* Terms of Service: https://elasticemail.com/legal/terms-of-use

SMTP2GO
* Service: https://www.smtp2go.com
* Endpoint: https://api.smtp2go.com/v3/email/send
* Privacy Policy: https://www.smtp2go.com/privacy/
* Terms of Service: https://www.smtp2go.com/terms/

Netcore Email API (formerly Pepipost)
* Service: https://netcorecloud.com
* Endpoint: https://emailapi.netcorecloud.net/v5/mail/send
* Privacy Policy: https://netcorecloud.com/privacy-policy
* Terms of Service: https://netcorecloud.com (request the latest terms via the contact form on the homepage; Netcore does not publish a stable terms-of-use URL).

SMTP.com
* Service: https://www.smtp.com
* Endpoint: https://api.smtp.com/v4/messages
* Privacy Policy: https://www.smtp.com/policies/privacy-policy/
* Terms of Service: https://www.smtp.com/legal/terms/

SocketLabs
* Service: https://www.socketlabs.com
* Endpoint: https://inject.socketlabs.com/api/v1/email
* Privacy Policy: https://www.socketlabs.com/legal/privacy-policy/
* Terms of Service: https://www.socketlabs.com/legal/

Moosend (by Sitecore)
* Service: https://moosend.com
* Endpoint: https://api.moosend.com/v3/
* Privacy Policy: https://moosend.com/privacy-policy/
* Terms of Service: https://moosend.com/terms/

SendLayer
* Service: https://sendlayer.com
* Endpoint: https://console.sendlayer.com/api/v1/email
* Privacy Policy: https://www.sendlayer.com/privacy-policy/
* Terms of Service: https://www.sendlayer.com/terms-of-service/

Mailjet (by Sinch)
* Service: https://www.mailjet.com
* Endpoint: https://api.mailjet.com/v3.1/send
* Privacy Policy: https://www.sinch.com/legal/privacy-notice/
* Terms of Service: https://www.sinch.com/legal/terms-of-service/

MailerSend
* Service: https://www.mailersend.com
* Endpoint: https://api.mailersend.com/v1/email
* Privacy Policy: https://www.mailersend.com/legal/privacy-policy
* Terms of Service: https://www.mailersend.com/legal/terms-of-use

Mandrill (by Mailchimp)
* Service: https://mailchimp.com/developer/transactional/
* Endpoint: https://mandrillapp.com/api/1.0/messages/send.json
* Privacy Policy: https://mailchimp.com/legal/privacy/
* Terms of Service: https://mailchimp.com/legal/terms/

Resend
* Service: https://resend.com
* Endpoint: https://api.resend.com/emails
* Privacy Policy: https://resend.com/legal/privacy-policy
* Terms of Service: https://resend.com/legal/terms-of-service

Zoho ZeptoMail
* Service: https://www.zoho.com/zeptomail/
* Endpoint: https://api.zeptomail.com/v1.1/email
* Privacy Policy: https://www.zoho.com/privacy.html
* Terms of Service: https://www.zoho.com/zeptomail/terms.html

Microsoft 365 / Outlook (Microsoft Graph API and SMTP)
* Service: https://www.microsoft.com/microsoft-365
* Endpoint (API): https://graph.microsoft.com/v1.0/users/{user}/sendMail (OAuth token from https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token)
* Endpoint (SMTP): smtp.office365.com:587
* Privacy Policy: https://privacy.microsoft.com/en-US/privacystatement
* Terms of Service: https://www.microsoft.com/en-us/servicesagreement/

Gmail / Google Workspace (Gmail API and SMTP)
* Service: https://workspace.google.com
* Endpoint (API): https://gmail.googleapis.com/gmail/v1/users/me/messages/send (OAuth token from https://oauth2.googleapis.com/token)
* Endpoint (SMTP): smtp.gmail.com:587
* Privacy Policy: https://policies.google.com/privacy
* Terms of Service: https://policies.google.com/terms

Notification channels (configurable in Settings Alerts)

These services are contacted ONLY when a notification channel is enabled AND a delivery failure (or daily-summary) event triggers an alert. The data sent is the short status text shown in the plugin’s alert preview.

Telegram (Bot API)
* Service: https://telegram.org
* When data is sent: when a Telegram channel is enabled, a failed-delivery alert or daily-summary alert is generated.
* Endpoint: https://api.telegram.org/bot{token}/sendMessage
* Privacy Policy: https://telegram.org/privacy
* Terms of Service: https://telegram.org/tos

Pushover
* Service: https://pushover.net
* Endpoint: https://api.pushover.net/1/messages.json
* Privacy Policy: https://pushover.net/privacy
* Terms of Service: https://pushover.net/terms

Slack (Incoming Webhooks)
* Service: https://slack.com
* Endpoint: the webhook URL you paste (https://hooks.slack.com/services/…).
* Privacy Policy: https://slack.com/trust/privacy/privacy-policy
* Terms of Service: https://slack.com/legal

Discord (Webhooks)
* Service: https://discord.com
* Endpoint: the webhook URL you paste (https://discord.com/api/webhooks/…).
* Privacy Policy: https://discord.com/privacy
* Terms of Service: https://discord.com/terms

Microsoft Teams (Incoming Webhooks)
* Service: https://www.microsoft.com/microsoft-teams/
* Endpoint: the Teams webhook URL you paste.
* Privacy Policy: https://privacy.microsoft.com/en-US/privacystatement
* Terms of Service: https://www.microsoft.com/en-us/servicesagreement/

Google Chat (Incoming Webhooks)
* Service: https://chat.google.com
* Endpoint: the Google Chat webhook URL you paste.
* Privacy Policy: https://policies.google.com/privacy
* Terms of Service: https://workspace.google.com/terms/premier_terms.html

Custom Webhook
* When data is sent: when a custom Webhook channel is enabled and an alert is generated, a JSON payload is POSTed to the URL you paste.
* Endpoint: the URL you paste.
* Privacy Policy / Terms: governed by the operator of the URL you provide.

Bundled third-party libraries (no network call)

Chart.js (https://www.chartjs.org) — bundled locally at assets/js/chart.umd.min.js (v4.5.0). Used to render the email-stats chart on the dashboard. Loaded entirely from your own server; no CDN call is made.

Privacy Policy

SendForce Mail Relay stores the following data in your WordPress database:

  • Email logs (if logging is enabled): recipient email address, subject, headers, email body (optional), sent status, error messages, and timestamps.
  • Email queue (if queue is enabled): same data as above, plus retry attempt count and next retry time.
  • Plugin settings: your SMTP host, port, username, and encrypted password or API key.

Email body logging can be disabled in Settings > Queue & Logging. Log entries are automatically deleted after the configured retention period (default: 30 days). All data is deleted when the plugin is uninstalled.

We recommend adding a note to your site’s privacy policy if you log email content, informing users that outgoing email metadata may be stored.

Screenshots

  • Modern dashboard with connection status, email statistics, and 7-day send chart.
  • Settings page with visual provider grid and SMTP/API tab switcher.
  • Email Log with status filters, keyword search, and date range filtering.
  • Email Test page with live SMTP debug log output.
  • Email Queue management with retry statistics.

Installation

  1. Upload the sendforce-mail-relay folder to the /wp-content/plugins/ directory, or install directly through the WordPress plugin installer.
  2. Activate the plugin through the Plugins menu in WordPress.
  3. Go to SendForce Mail Relay > Settings to configure your SMTP server or API credentials.
  4. Select your email provider from the provider grid.
  5. Enter your credentials and click Save Settings.
  6. Go to SendForce Mail Relay > Email Test and send a test email to verify your configuration.

FAQ

Do I need an SMTP server or API account?

Yes. You need access to an SMTP server or a transactional email provider account. Most providers (Gmail, Outlook, etc.) offer SMTP access. Transactional services like SendGrid, Mailgun, or Amazon SES offer both SMTP and HTTP API access.

Is my SMTP password or API key stored securely?

Yes. All credentials are encrypted using AES-256-CBC encryption with a key derived from your WordPress security salts before being stored in the database.

What does the email queue do?

When enabled, emails are stored in a database queue instead of being sent immediately. A WP-Cron job processes the queue every 5 minutes, sending emails in batches. Failed emails are retried with exponential backoff (5 min 15 min 45 min).

Will SendForce Mail Relay send ALL WordPress emails including WooCommerce?

Yes. SendForce Mail Relay hooks into WordPress’s phpmailer_init action (SMTP mode) or pre_wp_mail filter (API mode). Since every plugin — including WooCommerce, contact form plugins, and membership plugins — sends email via WordPress’s wp_mail() function, SendForce Mail Relay will handle 100% of outgoing emails.

Will this plugin conflict with other SMTP plugins?

You should only use one SMTP plugin at a time. Please deactivate any other SMTP or email plugins (FluentSMTP, WP Mail SMTP, etc.) before activating SendForce Mail Relay.

What data is logged?

By default, SendForce Mail Relay logs: recipient email, subject, headers, status (sent/failed), error message if any, and timestamp. Email body logging is optional and can be disabled in Settings > Queue & Logging for privacy compliance.

What happens to my data when I uninstall the plugin?

All plugin data (database tables, settings, and scheduled events) is completely removed when you delete the plugin through the WordPress admin.

Does this plugin track me or phone home?

No. SendForce Mail Relay does not collect any data, make any analytics calls, or communicate with any external server except when sending emails through your configured provider.

Reviews

May 1, 2026 1 reply
As a freelance web developer working with a marketing agency, I need to set up SMTP for every website I build. However, my clients aren’t always ready to provide SMTP credentials, which forces me to fall back on PHP mail — and that almost always ends up sending emails straight to the spam folder. This plugin completely solved that problem. I can now use PHP Mail instead, and it works perfectly. Honestly, this is a game-changer for my workflow.
Read all 2 reviews

Contributors & Developers

“SendForce Mail SMTP Relay — Free WP SMTP & Email API Plugin for Amazon SES, SendGrid, Mailgun, Brevo, Postmark, Zoho ZeptoMail & Gmail” is open source software. The following people have contributed to this plugin.

Contributors

Changelog

For the complete release history with full detail on every change, see CHANGELOG.md (also bundled with the plugin) or open the What’s New tab inside SendForce in your WordPress admin. The section below is condensed to fit within WordPress.org’s 5,000-word changelog limit.

1.0.30

  • New: Filter Email Log by connection. A new “Connection” dropdown in the Email Log filter bar lets you narrow the table down to emails sent through a specific provider (Amazon SES, SendGrid, Microsoft 365, etc.). Useful when you run several connections side-by-side and want to audit just one — e.g. “show me everything WooCommerce sent via SendGrid this week”. Options are populated from connections that actually have log entries (no empty dropdown choices). Existing log rows from before 1.0.30 don’t carry a provider value and stay visible under the “All connections” default.
  • New: “Copy diagnostic snapshot” button on the Email Log detail modal. Bundles the log ID, timestamp, status, error, headers, attachments, and the (already-redacted) raw API request/response into one clipboard-friendly text block — pre-formatted for pasting straight into a support ticket. Plugin / WordPress / PHP versions are included automatically. No more copy-paste-merge of six different fields when reporting an issue.
  • Internal: Added provider column to wp_sendforce_logs (varchar(50), indexed). Populated automatically going forward from the captured debug payload (when available) or the active connection’s configured provider. Existing rows are not back-filled; only new log entries get the column populated. dbDelta handles the schema migration on plugin upgrade.

1.0.29

  • Fix: Stray gray “pill” elements rendering on the What’s New tab (introduced in 1.0.28). The changelog accordion’s markdown-to-HTML converter wrapped backtick-delimited text in <code> tags but didn’t escape the inner content first — so when a bullet contained literal <tag> text inside backticks (very common in technical changelogs), the browser interpreted the embedded text as a real HTML tag and produced floating empty <code> elements that rendered as gray pills against the page background. Fix: esc_html() the raw line before applying the markdown transformations. The two regex rules (**bold** <strong> and backtick <code>) now run on already-escaped text, guaranteeing balanced tags and no HTML injection from bullet content. Affects 1.0.28 only.
  • Fix: WordPress 6.7+ “Translation loading triggered too early” notice caused by the custom cron schedule’s display label being translated inside the cron_schedules filter. The filter fires whenever WP looks up scheduled events (which can happen before init, during plugins_loaded or even setup_theme), so calling __( …, 'sendforce-mail-relay' ) there ran the translation function before the textdomain was loaded. Both filter callbacks (in the activator and the mailer) now use a literal English string for the cron schedule’s display label — that label is only shown in dev tools like WP Crontrol or wp-cli cron list, never user-facing, so leaving it untranslated is harmless. Resolves the deprecation notice on sites running WP_DEBUG with WP 6.7 or newer.

1.0.28

  • New: Test send now shows the full provider response inline on the Email Test page. When a test fails the headline shows the short reason (“Failed to send via Microsoft 365 API”) and a new “Provider response” panel below it shows the raw HTTP response body — pretty-printed when it’s JSON, with a one-click Copy button for support tickets. Previously the full Graph/SES/Mailgun response was only visible by clicking into the Email Log; users debugging a misconfiguration now see everything they need without leaving the test page.
  • New: HTTPS warning on webhook URL fields (Slack, Discord, Microsoft Teams, Google Chat, generic Webhook). As soon as you paste/type a URL starting with http:// an inline red warning appears explaining that the alert message (site name, failed recipient, error details) would be sent unencrypted. Catches typos and homemade webhook receivers before save.
  • New: “Welcome to v{X.Y.Z}” admin notice after the plugin updates, with a one-click button to the in-admin “What’s New” tab. Per-user dismissible, only shows once per version, never on fresh installs or on SendForce’s own settings pages (where the tab is already visible). Helps users discover what changed without having to read the wp.org changelog page.
  • New: Failure-alert rate limiting, default 5-minute window per channel. A burst of failed sends (e.g. WooCommerce sending 100 order emails through an expired API key) no longer floods Slack/Telegram/Discord/Teams with 100 identical pings — within the window the first one fires, the rest are counted and rolled up into a “(+N additional failures were suppressed)” note on the next alert after the window expires. Filter sendforce_alerts_throttle_seconds to change the window length, set it to 0 to disable throttling entirely.
  • Docs: Added CHANGELOG.md with the complete, full-detail release history for every version (no length limit). The condensed Changelog in this readme.txt covers the WordPress.org 5,000-word cap; CHANGELOG.md carries the full story for anyone who wants it.
  • In-admin: The What’s New tab now reads from CHANGELOG.md when present (falls back to readme.txt). Browse every release’s full bullet list inline in WordPress without going to wp.org or GitHub.
  • Older entries (1.0.0 – 1.0.20) in this readme.txt are now one-line summaries; their full detail lives in CHANGELOG.md and the in-admin Changelog accordion. Recent releases (1.0.21+) remain fully detailed in both places.

1.0.27

  • Security (defense-in-depth): Bulk-delete logs and queue retry/delete actions now require manage_options capability in addition to the existing nonce check. The forms that issued these requests already live on admin-only pages, so a non-admin couldn’t normally obtain a valid nonce — but explicit capability checks match WordPress.org Plugin Check expectations and protect against any future flow that might mint a nonce in a less-restricted context.
  • Privacy: Email Log now stores only the filename basenames of attachments instead of full filesystem paths. Previously rows like /var/www/example.com/wp-content/uploads/woocommerce_uploads/2026/invoice-12345.pdf were stored verbatim — leaking the server directory layout, shared-hosting username paths, and customer order IDs from WooCommerce invoice filenames. From 1.0.27 the same row stores only invoice-12345.pdf. Existing rows are not rewritten; only new log entries get basename-only.

1.0.26

Sending — attachments and multipart_text across all 18 API providers.

  • New: Attachments now work in API mode for every supported provider. 1.0.25 added it for Amazon SES and Gmail; 1.0.26 closes the gap for the remaining 16: SendGrid, Brevo, Mailgun, Postmark, Mailjet, MailerSend, Mandrill, Pepipost / Netcore, SocketLabs, SMTP2GO, SMTP.com, SendLayer, SparkPost, Elastic Email, Resend, and Moosend. Plugin-attached files (WooCommerce PDF invoices, Contact Form 7 / WPForms / Gravity Forms uploads, LearnDash / LifterLMS certificates, anything passed to wp_mail()‘s 5th argument) are now base64-encoded into each provider’s native JSON schema and delivered through the API.
  • New: “Multi-Part Plain Text” setting now applies to API-mode sends across the board. When the message is HTML and the toggle is on, the auto-derived text/plain alternative is included via each provider’s native multipart fields. Improves deliverability — Gmail and other inbox providers favour multipart-alternative messages, and plain-text-only clients get a readable fallback. Previously this setting was silently ignored in API mode for the 16 providers covered here.
  • Known limitation: Moosend’s transactional API has a single Content field with no IsContentHtml companion for both parts, so it can’t represent multipart_text — its attachments work, just not the dual-body part.
  • Special-case: Mailgun’s attachment endpoint requires multipart/form-data (not JSON like the other 17), so a new build_multipart_form_data() helper in the base class assembles the request body correctly while still capturing the response into the debug log.
  • Internal: Two new base-class helpers — prepare_attachments() (base64 + MIME-type detection + safe file reads) and get_plain_text_alt() (HTMLplain conversion when multipart_text is on) — give each provider ~5 lines of mapping code instead of duplicating the same boilerplate 18 times.

Email Queue — actual error messages + on-demand processing.

  • Fix: Queued emails that fail now record the actual provider error in the queue row’s error column. Previously every failure showed a generic “Failed to send email.” message, regardless of whether the cause was an expired OAuth token, an unverified sender domain, a wrong API key, or a network glitch. The queue processor now hooks wp_mail_failed during each send attempt and stores the real WP_Error message (e.g. AADSTS70008: refresh token expired, MessageRejected: Email address is not verified, Domain not found). Users on the Queue tab can finally see WHY a send failed without digging into the Email Log.
  • New: “Process Queue Now” button on the Queue tab. Triggers the same processor that WP-Cron runs every 5 minutes, but on demand — useful right after fixing a connection or when testing the queue end-to-end. Disabled when there’s nothing queued; only visible when the queue is actually enabled in Settings.
  • New: Queue-disabled info notice on the Queue tab. Previously visiting the tab with the feature off showed an empty table with no explanation; now there’s a clear “Queue is disabled — emails are sent immediately. Enable in Settings Email Queue.” banner with a deep link.
  • New: “Next automatic run” timestamp shown above the queue list when the queue is enabled, formatted using the site’s date/time format. Removes the mystery of “when will my queued emails actually go out”.
  • Fix: Test sends bypass the queue. With queue mode enabled, clicking “Send Test” used to queue the message and report immediate success — but the actual delivery wouldn’t happen until the next cron run, which made the test page useless for verifying provider configuration. A new one-shot SendForce_Queue::$bypass_once flag tells the queue interceptor to skip the next wp_mail call, so test sends always go out synchronously regardless of queue mode.

Admin — release history and roadmap in one place.

  • New: “What’s New” tab combines the upcoming-features roadmap (formerly “What’s Next”) and the full release history into one navigation entry. Roadmap cards render at the top as before; the changelog sits below as a collapsible accordion.
  • New: Collapsible changelog accordion, FAQ-style. Each release row shows version + change count; click to expand the bullets. The current installed version auto-expands; everything else stays collapsed by default — ~60% less vertical space than rendering everything flat. Uses native HTML <details>/<summary> so it’s keyboard-accessible and works without JavaScript.
  • Renamed: “What’s Next” “What’s New”. The old ?tab=upcoming and ?tab=changelog URLs both still resolve as back-compat aliases so any bookmarks or in-plugin links keep working.

1.0.25

  • New: Email attachments now work with Amazon SES API mode (and continue to work with Gmail API mode). Previously the SES mailer ignored the $attachments parameter completely — WooCommerce PDF invoices, contact-form file uploads, LMS certificates, and similar plugin-generated attachments would silently disappear when sent via SES. Now they’re MIME-encoded and delivered correctly via multipart/mixed. Gmail API mode also routes through the new shared MIME builder so its attachment handling matches.
  • New: “Multi-Part Plain Text” setting now applies to SES and Gmail API modes. When a connection is in API mode, an HTML email is now sent with both text/html and an auto-derived text/plain alternative (via multipart/alternative). Improves deliverability — Gmail and other inbox providers favour multipart-alternative messages, and plain-text-only mail clients (some IT environments, accessibility tools) get a readable fallback. Previously this setting was silently ignored in API mode.
  • Fix: Subjects and From-names containing non-ASCII characters (Café, résumé, François, Müller, 주문, etc.) are now RFC 2047 base64-encoded (=?UTF-8?B?…?=) before being sent. Without this, strict MTAs would reject the message and lenient ones would render the headers as garbage.
  • Refactor: SES and Gmail mailers now share a single build_raw_mime() helper in the base API mailer class. Same MIME format, same encoding rules, same attachment handling — verified against test fixtures covering single-part, multipart/alternative, multipart/mixed (with attachments), Bcc-in-headers (Gmail) vs Bcc-via-API (SES), and non-ASCII headers.

1.0.24

  • Fix (critical for OAuth users): Microsoft 365 / Gmail “Connect with provider” no longer fails with AADSTS50011 / redirect URI mismatch. Root cause: the authorize URL was built with add_query_arg(), which does NOT URL-encode the values it inserts. The & characters inside our redirect_uri query value were left bare, so Microsoft / Google parsed &tab=settings&sf_oauth=callback as additional top-level query parameters and saw the redirect_uri truncated at the first &. The fix uses http_build_query() with PHP_QUERY_RFC3986 so every value is properly percent-encoded. Affects every OAuth setup whose Web Redirect URI contains more than one query parameter (i.e. all current SendForce OAuth setups). The get_redirect_uri() hardening from 1.0.23 was real but addressed a different scenario; this is the actual cause of the AADSTS50011 reports.
  • New: OAuth consent now opens in a popup window instead of replacing the settings page. Click “Connect with provider” a centered 600×720 popup opens with the Microsoft / Google sign-in flow after consent the popup closes itself and the settings page refreshes with a success notice. You no longer lose your unsaved settings or scroll position.
  • Graceful fallback: if the browser blocks the popup (popup blocker on, or running in an embedded frame), SendForce falls back to the previous full-page redirect behaviour automatically — no error, no lost flow.
  • The OAuth callback page is now a small branded “Completing connection…” splash screen with a spinner that signals the parent window via THREE independent channels and closes itself: localStorage event (primary, survives cross-origin opener severance under Cross-Origin-Opener-Policy), postMessage (fallback when window.opener survives), and parent-side popup-closed polling (final safety net — reloads the settings tab so the connection panel refreshes from the DB even if both signal channels fail). This eliminates the “popup closed but status didn’t update” issue some users hit when the browser’s COOP behaviour broke the opener relationship after returning from Microsoft / Google.
  • UX: Replaced the remaining native browser alert() / confirm() dialogs in the OAuth flow (“Save the connection first”, “Disconnect this account?”, error toasts) with the styled sfShowConfirmModal already used elsewhere in the plugin. Consistent look across the admin UI.
  • UX: Save Connection button now shows an inline spinner and a “Validating data. Please wait…” status message while the connection save is in flight — same pattern FluentSMTP uses. Makes it clear the click registered and the save is in progress, especially on slow networks where the previous “Saving…” text alone wasn’t obvious enough.
  • Fix: Microsoft 365 send failures now show the actual Graph API error instead of a generic “Failed to send email via Microsoft 365 API” message. The Outlook mailer was bypassing the base class’s debug-capture helper, so the HTTP status and response body from graph.microsoft.com were never recorded. It now uses make_request() so the real error (ErrorAccessDenied, MailboxNotEnabledForRESTAPI, AADSTS70008 token expired, etc.) is appended to the failure message and stored in the Email Log. Also captures token-endpoint failures from the OAuth refresh flow (previously silent).
  • Setup help: Rewrote the Gmail and Microsoft 365 OAuth setup instructions in the connection editor to cover the steps users keep getting stuck on — the OAuth consent screen + Test users requirement for Gmail (without which Google returns access_denied “App is being tested”), the separate Authorised JavaScript origins field (origin-only, no path), and an explicit warning about Azure’s confusing “Value vs Secret ID” two-column UI for client secrets (the most common AADSTS7000215 cause). Includes the format of valid Client IDs/Secrets so users can sanity-check what they pasted.
  • Setup help: Added a “JavaScript origin” copy field for Gmail setups, alongside the existing “Web Redirect URI” field. Google’s OAuth client form has two URL fields with different requirements; previously SendForce only documented one of them.
  • Setup help: Added client-side and server-side rejection of GUID-shaped secrets for OAuth providers — pasting an Azure Secret ID into the Client Secret field is now blocked with a clear explanation, both as a live red warning under the field and as a hard rejection on save. Prevents the AADSTS7000215 “invalid client secret” loop where the user repeatedly pastes the wrong column from Azure.
  • Fix: Zoho ZeptoMail “Invalid API Token found” (TM_4001 / SERR_157) when users paste the full Zoho-enczapikey TOKEN string from ZeptoMail’s docs into the Send Mail Token field. The mailer now strips the Zoho-enczapikey prefix automatically before constructing the Authorization header, so both pure-token and full-header paste styles work.
  • Setup help: Added a per-provider API-key hint rendered inline under the API Key field (where supplied by the provider config). First use: ZeptoMail and Amazon SES fields now explain where to find their credentials, format gotchas, and the verified-domain / sandbox requirements.
  • Fix: Amazon SES send failures now show the actual SES API error (same fix as Outlook). The SES mailer was bypassing the base class’s debug-capture helper, so HTTP status and response body from email.<region>.amazonaws.com were never recorded. It now uses make_request() so real errors like MessageRejected (sandbox, unverified address), MailFromDomainNotVerifiedException, SendingPausedException, or Throttling appear in the failure message and the Email Log.
  • Fix: Gmail API send and token-fetch failures now surface the actual Google error. Same root cause as Outlook/SES: the Gmail mailer bypassed make_request() for the send call, and silently swallowed token-endpoint errors during the OAuth refresh. Errors like invalidArgument (malformed raw email), failedPrecondition (delegation issue), quotaExceeded, or invalid_grant (refresh token revoked / 90-day expiry) are now surfaced. Also captures pre-flight failures (missing client_id / client_secret / refresh_token) with a clear explanation.
  • Fix: Mailgun API send failures now surface the actual Mailgun error. The Mailgun mailer bypassed make_request() so errors like “Domain not found”, 401 wrong-region, “freemail recipient unsupported”, or “Forbidden” never showed in the UI. Switched to make_request() with application/x-www-form-urlencoded body so debug capture works while preserving Mailgun’s required form-encoded body format.
  • Fix: Tolerate users pasting Bearer TOKEN (instead of just TOKEN) into the API Key field for SendGrid, Resend, MailerSend, SMTP.com, and SendLayer. Provider docs typically show Authorization: Bearer xxxxx in their cURL examples, and users frequently copy the whole Bearer xxxxx string. The mailer now strips a leading Bearer if present so both paste styles work. Same auto-strip already applies to ZeptoMail’s Zoho-enczapikey prefix; refactored to use a shared strip_auth_prefix() helper in the base API mailer class.
  • Fix: All API keys are now trim()ed automatically before use. Catches the common case where a user copy-pastes a key from a provider dashboard and accidentally grabs trailing whitespace or a stray newline. Previously some providers (notably SendGrid) would return a generic 401 with no useful explanation when the auth header contained non-printable characters.

1.0.23

  • Fix: Gmail / Microsoft 365 OAuth redirect URI is now constructed defensively so security and caching plugins (e.g. Wordfence, LiteSpeed Cache) that filter admin_url cannot strip the query string from the path and produce a truncated redirect_uri. Resolves the intermittent AADSTS50011 (redirect URI mismatch) errors some users hit when clicking “Connect with provider” for Microsoft 365. The final URL is unchanged — only the construction is now immune to third-party admin_url filters.
  • i18n: Email Log “Next run” timestamp now uses the site’s configured date/time format (Settings General) instead of a hardcoded English style. Sites running in non-English locales will now see the cleanup schedule formatted naturally.
  • Hardening: Expanded the wp_kses whitelist used to render channel logos on the Alerts tab — adds xmlns:xlink, role, aria-label, width, height, fill-rule, clip-rule, the <g> element, and a few missing path/circle attributes. No behaviour change for the bundled icons; future channel logos with these attributes will render instead of being silently stripped.
  • Tweak: ajax_cleanup_logs_now handler now explicitly returns after each wp_send_json_error(). wp_send_json_error already terminates via wp_die, so behaviour is unchanged — purely a clarity improvement.

1.0.21

  • New: “Clean Now” button on the Email Log. Run the auto-clean immediately instead of waiting for the next scheduled cron — useful after lowering retention or before exporting logs. Backed by a new sendforce_cleanup_logs_now AJAX endpoint (capability + nonce protected) that reports how many rows were removed.
  • New: Auto-clean status row above the Email Log table — at a glance shows the configured retention period, when the next automatic cleanup is scheduled, a deep-link to change the setting, and the “Clean Now” action.
  • New: “via rule: ” badge on Email Log entries — when an email was redirected by a Smart Routing rule, the log row now shows which rule fired (read from the stored debug payload). Makes it trivial to confirm that routing is working as intended.
  • New: “Connection missing” warning on routing rule cards. When a rule points at a connection that has been deleted, the rule card now shows an amber warning badge — previously such rules failed silently at runtime and emails fell through to the primary connection without explanation.
  • New: Real channel logos on the Alerts tab — Slack, Discord, Microsoft Teams, Telegram, Google Chat, Pushover, and Webhook now render their actual brand SVG icons instead of two-letter initials. Inline-loaded and color-themed via currentColor for crisp rendering at any DPI.
  • Improvement: Test Routing tool parity. The “Test Routing” tool now evaluates rules through the exact same matcher used by the live mailer (shared rule_matches() + new build_test_context() helper), eliminating any drift between what the tool predicts and what actually happens at send time. Side benefit: the test tool now correctly evaluates time_between, weekday, to_pattern, and from_* conditions (previously partly stubbed in a duplicate local matcher).
  • Improvement: Routing-tab rule cards now render round-robin target lists (“Round-robin: SES, SendGrid”) and a friendly “WordPress phpmail()” label for the phpmail pseudo-target — round-robin runtime was already supported, this surfaces it correctly in the UI.
  • Improvement: A11y — channel icon buttons on the Alerts tab now expose an aria-label so screen readers announce the channel name instead of empty graphics.
  • Fix: Alert channels were occasionally wiped when saving the Summary Email form. The settings sanitizer used to ignore the incoming channels payload and re-load whatever was in the DB; in races between the AJAX channel toggles and the Summary Email form save this could clobber channel state. The sanitizer now respects the incoming payload when present and only falls back to DB state when the form omits channels.
  • Tweak: Summary Email body textarea reduced from 3 rows to 2 so the Alerts form fits more cleanly on smaller viewports.
  • Internal: Email-log retention cleanup (SendForce_Logger::cleanup_old_logs()) now returns the number of rows deleted (was void) so callers like the new “Clean Now” handler can surface the count to the user.

1.0.20

  • Admin top navigation layout fixes at narrow widths (tablet/mobile, mid-size laptops). Tab labels collapse to icon-only below 1340px. Logo gets proper left padding on mobile.

1.0.19

  • New: Smart Routing Rules — send different emails through different connections by recipient domain/pattern, source plugin (auto-detected for WooCommerce, CF7, WPForms, Gravity Forms, Ninja Forms, Fluent Forms, Forminator, EDD, MemberPress, LearnDash, MailPoet, BuddyPress, bbPress), subject contains, time of day, weekday. Top-down evaluation, first match wins. Includes Routing tab, inline rule editor, master enable toggle, from_email / from_domain conditions, “via rule” badge in Email Log, starter presets, “What’s Next” roadmap tab, styled confirm dialogs.

1.0.18

  • Fixed misleading “Connected” status on Gmail / Microsoft 365 OAuth panel — now checks API mode + Client ID + Client Secret + refresh token all present (not just any password field value). Added amber “partial credentials” state. Hint to switch to API mode when in SMTP. Polished status indicator dot.

1.0.17

  • Initialize oauth_email field on newly created connections so the schema is consistent from save time.

1.0.16

  • New: One-click “Connect with Google” and “Connect with Microsoft” OAuth buttons replace manual refresh-token generation via OAuth Playground / Azure CLI. Refresh token captured automatically, stored encrypted, panel shows “Connected as user@example.com” with Disconnect button. Bring-your-own-credentials — no third-party OAuth proxy. Microsoft 365 supports both delegated and app-only flows. Setup help panel with copy-able redirect URI.

1.0.15

  • Audited all 20 provider integrations against current docs. 18 verified spec-compliant. Fixed Moosend HTML content-type detection (explicit IsContentHtml flag in both HTML and plain-text paths).

1.0.14

  • Fixed Email Queue orphan-row bug: rows stuck in “processing” state after cron timeout / PHP crash now recover via a 10-minute lease (next_retry_at) on the next cron run.

1.0.13

  • Security: hardened PHP-mail fallback against header injection (strips CR/LF/null from To, Subject, headers). Validated attachment paths against safe roots (prevents path traversal). Sanitized fallback-connection label. Server-side wp_strip_all_tags on provider errors returned to the browser.

1.0.12

  • Renamed “Default Connection” “Primary Connection” throughout the admin UI (internal storage key unchanged). Provider status dot reflects configuration status (not brand colour). Fixed fallback flow so wp_mail() returns true on fallback success; clearer recovery message. Suppressed spurious SMTP-warning when active path was API or PHP-Mail.

1.0.11

  • Plugin Name header is now clean brand name “SendForce Mail Relay” (was long SEO title); Description is a single-line summary. WordPress.org listing keeps the longer SEO title for directory search.

1.0.10

  • New UI controls for Developer Debug toggle and Max Retry Attempts (1–10). Hardened From-email validation (admin notice on invalid). Replaced native <select> dropdowns with branded styled component (full keyboard support — Arrow/Enter/Escape). Default/Fallback selectors auto-refresh after connection save. Form-control polish (chevron, focus ring, hover, disabled states).

1.0.9

  • WordPress.org listing title tuned to “SendForce Mail SMTP Relay — Free WP SMTP & Email API Plugin for Amazon SES, SendGrid, Mailgun, Brevo, Postmark, Zoho ZeptoMail & Gmail” for better directory search. Internal product name unchanged.

1.0.8

  • New: Automatic fallback connection — when primary send fails, SendForce retries via configured fallback. Works across any API API, API SMTP, SMTP API, SMTP SMTP, or any PHP-mail combination. Both attempts logged; recovered emails get a “Recovered via fallback” badge. Improved readme Description for WordPress.org discoverability.

1.0.7

  • Public display name updated for WordPress.org search index discoverability. Refreshed Tags to highest-volume search terms. Polished connection-row Edit/Delete buttons with dashicons. Added Dashboard / Settings / Email Log / Queue / Test / Alerts sub-menu entries.

1.0.6

  • Shortened WordPress admin sidebar label to “SF Mail Relay” so it fits one line. Added sub-menu entries reachable from sidebar fly-out.

1.0.5

  • Removed accidental .DS_Store file from assets/images/providers/. Added phpcs:ignore annotations with rationale to interpolated table-name $wpdb queries. Restructured WP-CLI log-delete query for proper phpcs handling.

1.0.4

  • Updated bundled Chart.js to 4.5.0. Expanded External Services section in readme. Removed inline <style>/<script> blocks from PHP output (proper enqueue). Surface actual provider HTTP status / response body in failure messages. Added “Raw API request / response” panel to Email Log detail modal. Masked-dots placeholder for saved password/API-key fields. Hardened secret redaction (covers client_secret, response bodies, malformed-JSON fallback). Defused CSV formula injection on log export. Gated on-load DB schema upgrade to admin/WP-CLI. Added confirmation prompts to destructive WP-CLI commands.

1.0.3

  • Added WP-CLI commands (wp sendforce test, queue, log, sysinfo). Added developer hooks (sendforce_before_send, sendforce_after_send, sendforce_log_entry, sendforce_api_request, sendforce_api_response). Added Developer Debug setting to mirror SMTP/API debug to debug.log. Added raw API request/response capture in new debug_data log column (with Authorization redaction). Email Log export as CSV/JSON. “Copy Debug Info” button on Email Test screen. Replaced stale “SMTP-Manager” User-Agent on Resend with “SendForce-Mail-Relay”.

1.0.1

  • Added Microsoft 365 Graph API and Gmail API support. Added 5 new providers (SendLayer, Mailjet, MailerSend, Mandrill, Resend). Added Zoho ZeptoMail API. Added 3 new notification channels (Microsoft Teams, Google Chat, Custom Webhook). SVG provider logos. Collapsible provider grid. Fixed multiple API mailer bugs (SendLayer field names, Pepipost endpoint, SparkPost CC, SES BCC, MailerSend Reply-To). Added CC/BCC/Reply-To to Elastic Email, SMTP2GO, SMTP.com, SocketLabs, Moosend, Pepipost. Auto-set first connection as Default. Bundled Chart.js locally. Fixed all Plugin Check warnings. Renamed main file to sendforce-mail-relay.php.

1.0.0

  • Initial release. SMTP with 18 provider presets. HTTP API for 12 providers (SendGrid, Mailgun, SES, Brevo, SparkPost, Postmark, Elastic Email, SMTP2GO, Pepipost, SMTP.com, SocketLabs, Moosend). From name/email override. Email logging with search/filter. Email queue with exponential backoff retry. Email Test page with live SMTP debug. AES-256-CBC encrypted credential storage. Auto log cleanup. Modern admin dashboard with 7-day send chart.