Developer documentation

Omailer v3.0.0

Full-featured email marketing and infrastructure plugin for WordPress. Replaces wp_mail() with a configurable SMTP stack and a complete marketing suite on top.

WordPress plugin WP 6.0 · PHP 8.0+ Updated 2026-04-24
01 · Overview

What Omailer does

Replaces the default wp_mail() transport with a configurable SMTP stack, then layers a full email marketing suite on top.

👥
Subscriber Management
Import/export, tags, custom fields, segmentation
📧
Campaign Builder
Visual & HTML editors, template engine, spam scoring
Automation Sequences
Trigger-based drip email workflows
🛡️
Deliverability Tools
SPF/DKIM/DMARC checker, send-time optimisation
🔗
Bounce Webhooks
Mailgun, SendGrid & Postmark handlers
🔀
Smart Routing
Rule-based SMTP selection with failover
🖱️
Drag-and-Drop Builder
Table-based email export for all clients
🔲
Gutenberg Block
Native block + shortcode for subscribe forms
02 · Requirements & Installation

Getting set up

WordPress
6.0+
PHP
8.0+
MySQL / MariaDB
5.7+

Installation

1
Upload the omailer/ folder to /wp-content/plugins/.
2
Activate via Plugins → Installed Plugins.
3
On first activation the plugin creates all database tables, a default "General" mailing list, an unsubscribe page (slug: omailer-unsubscribe), schedules cron events, and flushes rewrite rules.
4
Navigate to Omailer in the WP admin menu to configure settings.

Uninstall

If Delete all data on uninstall is checked under Misc → Data Management before deactivating, all plugin tables, options, and pages are removed on uninstall.

03 · Plugin Constants

Plugin constants

Defined in omailer.php at load time.

ConstantDescription
OM_VERSIONCurrent plugin version (3.0.0)
OM_PATHAbsolute filesystem path to the plugin directory (trailing slash)
OM_URLURL to the plugin directory (trailing slash)
OM_BASENAMEPlugin basename (omailer/omailer.php)
04 · Database Schema

Database schema

All tables use the prefix {wpdb->prefix}om_. Use OM_DB::table( 'name' ) to get the full table name safely.

om_subscribers

ColumnTypeDescription
idBIGINT UNSIGNED PKAuto-increment
emailVARCHAR(255) UNIQUEEmail address
first_nameVARCHAR(100)First name
last_nameVARCHAR(100)Last name
statusENUMsubscribed, unsubscribed, bounced, pending
confirmation_tokenVARCHAR(64)Token for double opt-in
sourceVARCHAR(100)manual, import, shortcode, api, …
ip_addressVARCHAR(45)IPv4 or IPv6
confirmed_atDATETIMEWhen opt-in was confirmed
created_atDATETIMERow creation timestamp
updated_atDATETIMEAuto-updated on change

om_campaigns

ColumnTypeDescription
idBIGINT UNSIGNED PK
subjectVARCHAR(255)Email subject
preview_textVARCHAR(255)Pre-header text
html_contentLONGTEXTEmail body HTML
json_designLONGTEXTBuilder block data (JSON)
statusENUMdraft, scheduled, sending, sent, paused
list_idBIGINT UNSIGNEDTarget list (NULL = all subscribers)
scheduled_atDATETIMEWhen to send
total_sentINT UNSIGNEDRecipients reached
total_openedINT UNSIGNEDUnique opens
total_clickedINT UNSIGNEDUnique clicks
total_bouncedINT UNSIGNEDBounces

Other tables

TableDescription
om_listsNamed mailing lists
om_list_subscriberJunction: subscribers ↔ lists. PK: (list_id, subscriber_id)
om_sendsPer-subscriber send record — status, open/click tracking
om_eventsGranular log: open, click, bounce, unsubscribe, complaint
om_tagsTag definitions with name, slug
om_subscriber_tagsJunction: subscribers ↔ tags
om_custom_fieldsCustom field definitions — type, form display settings
om_subscriber_metaCustom field values per subscriber
om_automationsAutomation definitions — trigger type and status
om_automation_stepsOrdered steps per automation
om_automation_runsSubscriber progress through automations
05 · Settings Reference

Settings reference

Stored in a single serialised array option: om_settings.

KeyTypeDefaultDescription
from_namestringBlog nameDefault sender name
from_emailstringAdmin emailDefault sender email
reply_tostringAdmin emailDefault reply-to address
batch_sizeint50Subscribers per cron batch
batch_delayint2Seconds to sleep between batches
track_opensbooltrueInsert tracking pixel
track_clicksbooltrueWrap links with tracking redirects
double_optinboolfalseRequire email confirmation
unsubscribe_pageintauto-createdPost ID of the unsubscribe page
PHP$settings  = get_option( 'om_settings', [] );
$from_name = $settings['from_name'] ?? get_bloginfo('name');
06 · Shortcodes

Using shortcodes

[om_subscribe]

AttributeDefaultDescription
list(all)List ID to subscribe to
titleOptional form heading
buttonSubscribeSubmit button text
show_namenoSet yes to show first/last name fields
styledefaultdefault, inline, stacked, popup, slide-in
tagsComma-separated tag slugs applied on subscribe
triggerdelayFor popup/slide-in: delay, exit, scroll
delay3Seconds (delay) or scroll % (scroll trigger)
[om_subscribe]
[om_subscribe title="Join our list" show_name="yes" list="2" tags="newsletter"]
[om_subscribe style="popup" trigger="exit"]
[om_subscribe style="slide-in" trigger="scroll" delay="50"]
💡Custom fields with show_in_form = 1 are automatically rendered between the name and email rows.

[om_subscriber_count]

Displays the total active subscriber count as a plain number.

We have [om_subscriber_count] subscribers.
07 · PHP Helper Functions

Global helper functions

om_send_email()

PHPom_send_email( string $to, string $subject, string $body, array $headers = [] ): bool

om_send_email(
    'user@example.com',
    'Your Order Confirmation',
    '<p>Thanks for your order!</p>',
    [ 'Reply-To: orders@mysite.com' ]
);

om_subscribe()

Subscribe an email address programmatically. Returns the subscriber ID on success, false on failure.

PHPom_subscribe( string $email, array $data = [] ): int|false
// $data: first_name, last_name, list_id, source, meta (field_key => value)

$sub_id = om_subscribe( 'jane@example.com', [
    'first_name' => 'Jane',
    'list_id'    => 3,
    'source'     => 'checkout',
    'meta'       => [ 'company' => 'Acme Corp' ],
] );

om_unsubscribe()

PHPom_unsubscribe( string $email ): bool

om_add_tag() / om_remove_tag()

PHPom_add_tag( int $subscriber_id, string $tag_slug ): void
om_remove_tag( int $subscriber_id, string $tag_slug ): void

om_add_tag( 42, 'vip' );
08 · Hooks & Filters

Hooks & filters

Actions

HookArgumentsDescription
om_tag_added$subscriber_id, $tag_idFires when a tag is assigned
om_subscriber_bounced$email, $providerFires on bounce webhook
om_subscriber_unsubscribed$email, $providerFires on unsubscribe webhook
om_subscriber_complaint$email, $providerFires on spam complaint webhook
PHPadd_action( 'om_subscriber_bounced', function( string $email, string $provider ) {
    error_log( "Bounce from {$provider} for {$email}" );
}, 10, 2 );

// Tag a WooCommerce customer on order completion
add_action( 'woocommerce_order_status_completed', function( int $order_id ) {
    $order = wc_get_order( $order_id );
    $sub   = OM_Subscriber::get_by_email( $order->get_billing_email() );
    if ( $sub ) om_add_tag( (int) $sub->id, 'customer' );
} );

Filters

PHPadd_filter( 'wp_mail', function( array $args ): array {
    $args['headers'][] = 'Bcc: archive@mycompany.com';
    return $args;
} );
09 · AJAX Endpoints

AJAX endpoints

ℹ️All admin endpoints require the om_admin nonce and manage_options capability. Pass nonce: OM.nonce from the localised OM object.

Subscribers

ActionMethodParametersReturns
om_subscriber_togglePOSTid, statussuccess/error
om_delete_subscriberPOSTidsuccess/error
om_bulk_delete_subscribersPOSTids[]success/error
om_export_subscribersGETnonceCSV download
om_import_subscribersPOSTcsv_file, list_id{imported, skipped}

Campaigns

ActionParametersReturns
om_save_campaignid?, subject, from_name, from_email, reply_to, list_id, html_content, …{campaign_id}
om_delete_campaignidsuccess
om_duplicate_campaignid{new_id}
om_send_campaigncampaign_idsuccess
om_send_testcampaign_id, test_emailsuccess
om_load_templatetemplate_id{html}

Tags & Custom Fields

ActionParametersReturns
om_create_tagname{id, name, slug}
om_delete_tagidsuccess
om_assign_tagsubscriber_id, tag_idsuccess
om_unassign_tagsubscriber_id, tag_idsuccess
om_create_custom_fieldlabel, field_key, field_type, options, show_in_form, is_required{id}
om_delete_custom_fieldidsuccess

SMTP & Deliverability

ActionParametersReturns
om_save_connectionid?, name, provider, host, port, encryption, auth, username, password, is_primary, is_backup{id}
om_test_connectionid, test_emailsuccess/error + message
om_check_spam_scoresubject, body{score, grade, issues[]}
om_check_domaindomain{spf, dkim, dmarc, score}
om_send_window{suggestion, top_windows[]}

Public endpoints (no auth)

ActionParametersDescription
om_subscribenonce, email, first_name?, last_name?, list_id?, tags?, cf_*Subscribe form submission
om_openc (campaign_id), s (subscriber_id)Track open pixel
om_clickc, s, urlTrack link click
om_unsubscribetokenUnsubscribe via token
10 · Subscriber Management

Managing subscribers

Class: OM_Subscriber

PHPOM_Subscriber::insert( array $data ): int|false
OM_Subscriber::get_by_email( string $email ): object|null
OM_Subscriber::get_by_id( int $id ): object|null
OM_Subscriber::update( int $id, array $data ): void
OM_Subscriber::count( string $status = 'subscribed' ): int
OM_Subscriber::get_all( array $args ): array   // args: search, status, list_id, per_page, page
OM_Subscriber::add_to_list( int $subscriber_id, int $list_id ): void

CSV import format

email,first_name,last_name,status
john@example.com,John,Doe,subscribed
jane@example.com,Jane,,subscribed

status defaults to subscribed if omitted. Duplicate emails are skipped.

11 · Tags & Segmentation

Tags & segmentation

PHPOM_Tags::get_all(): array                              // includes subscriber_count
OM_Tags::create( string $name ): int|false            // slug auto-generated
OM_Tags::delete( int $tag_id ): void                  // removes all assignments
OM_Tags::add_to_subscriber( int $sub_id, int $tag_id ): void
// fires: do_action( 'om_tag_added', $subscriber_id, $tag_id )
OM_Tags::remove_from_subscriber( int $sub_id, int $tag_id ): void
OM_Tags::get_for_subscriber( int $sub_id ): array
OM_Tags::add_by_slug( int $sub_id, string $slug ): void  // creates tag if needed
12 · Custom Fields & Merge Tags

Custom fields & merge tags

Merge tags

TagReplaced with
{{first_name}}Subscriber's first name
{{last_name}}Subscriber's last name
{{email}}Subscriber's email address
{{display_name}}First name if set, otherwise email local-part
{{unsubscribe_url}}One-click unsubscribe URL
{{field_key}}Value of custom field with that key
PHP$html         = '<p>Hi {{first_name}},</p><p>Your company: {{company}}</p>';
$subscriber   = OM_Subscriber::get_by_email( 'jane@example.com' );
$personalised = OM_Custom_Fields::apply_merge_tags( $html, $subscriber );

Field types

TypeRenders as
text<input type="text">
number<input type="number">
url<input type="url">
date<input type="date">
select<select> with comma-separated options
checkbox<input type="checkbox">
13 · Mailing Lists

Mailing lists

Subscribers can belong to one or more named lists. The "General" list is created on activation and cannot be deleted.

PHPOM_Subscriber::create_list( string $name, string $description = '' ): int
OM_Subscriber::delete_list( int $list_id ): void         // subscribers NOT deleted
OM_Subscriber::add_to_list( int $subscriber_id, int $list_id ): void
OM_Subscriber::remove_from_list( int $subscriber_id, int $list_id ): void
ℹ️Set list_id = NULL or 0 on a campaign to target all active subscribers.
14 · Campaigns

Building campaigns

PHPOM_Campaign::get_all(): array
OM_Campaign::get_by_id( int $id ): object|null
OM_Campaign::save( array $data ): int|false
OM_Campaign::delete( int $id ): void
OM_Campaign::duplicate( int $id ): int|false    // creates a draft copy

// Dispatch immediately
OM_Sender::dispatch( int $campaign_id ): void

The cron event om_send_scheduled fires hourly and processes campaigns with status = 'scheduled' and scheduled_at <= NOW(). All merge tags are replaced per subscriber at send time.

15 · Automation Sequences

Automation sequences

Triggers

Trigger typeFires when
subscribeA subscriber confirms their subscription
tag_addedA specific tag is added to a subscriber
form_submitA shortcode form is submitted
wp_hookA custom WP action fires (trigger_value = hook name)

Step types

TypeDescription
delayWait N minutes / hours / days before next step
send_emailSend an email — param = subject, body = HTML
add_tagAdd a tag (slug in param)
remove_tagRemove a tag (slug in param)
update_fieldSet a custom field — param = field_key=value
conditionBranch: checks field_key=value; skips next step if false

Triggering from code

PHPadd_action( 'user_purchase', function( int $subscriber_id ) {
    OM_Automation::trigger( 'wp_hook', $subscriber_id, 'user_purchase' );
} );

// Cron: om_process_automations every 5 minutes, processes up to 100 active runs
16 · Double Opt-In

Double opt-in

When double_optin is enabled, new subscribers are created with status = 'pending' and a random token. A confirmation email links to:

/?om_action=confirm&token={confirmation_token}

On visit: status → subscribed, token cleared, confirmed_at set, OM_Automation::trigger( 'subscribe', ... ) fires.

💡Override the confirmation email by unhooking OM_Subscriber::send_confirmation_email(), or use the standard wp_mail filter.
17 · Bounce & Complaint Webhooks

Bounce & complaint webhooks

Register these URLs with your email provider. Supported: mailgun, sendgrid, postmark.

https://yoursite.com/omailer/webhook/{provider}/
ProviderBounce / FailUnsubscribeSpam complaint
Mailgunbounced, failedunsubscribedcomplained → mark unsub + fire action
SendGridbounce, blocked, droppedunsubscribe, group_unsubscribespamreport → mark unsub + fire action
PostmarkHardBounceUnsubscribe, ManuallyDeactivatedSpamComplaint
ℹ️The last 50 raw payloads are stored in om_webhook_log for debugging.
18 · Spam Score Checker

Spam score checker

PHP$result = OM_Spam_Score::analyse( string $subject, string $body ): array;
// Returns: [ 'score' => 12, 'grade' => 'B', 'issues' => [ [...], ... ] ]

Grading

GradeScore range
A0 – 10
B11 – 20
C21 – 35
D36 – 50
F51+

Checks performed

  • Spam trigger words (weighted dictionary, 24+ terms)
  • ALL-CAPS words in subject / excessive exclamation marks (>3)
  • Missing unsubscribe link
  • Very short body (<50 chars) or high image-to-text ratio
  • Too many links (>15) or URL shorteners (bit.ly, tinyurl, etc.)
  • Subject too short (<10) or too long (>70 chars)
  • Subject starting with Re: / Fwd: / excessive dollar signs (>3)
19 · Deliverability Tools

Deliverability tools

DNS record check

PHP$results = OM_Deliverability::check_domain( 'yourdomain.com' );
// Returns: [ 'domain', 'checked', 'score' => 85,
//            'spf' => [...], 'dkim' => [...], 'dmarc' => [...] ]
CheckPoints
SPF record found+30
SPF record valid (-all or ~all)+20
DKIM record found+35
DMARC record found+15

DKIM is tested against 14 common selectors including default, google, mail, sendgrid, postmark, and more.

Best send time

PHP$window = OM_Deliverability::get_best_send_window( int $days = 30 ): array;
// Returns: [ 'suggestion' => '...', 'top_windows' => [ ['day','hour','label','opens'], ... ] ]
20 · Drag-and-Drop Email Builder

Email builder

Block types

TypeDescription
textRich HTML paragraph
heading<h2> heading
imageResponsive image with optional link
buttonCTA button with configurable colours and alignment
dividerHorizontal rule with configurable thickness/colour
spacerVertical whitespace
columnsTwo-column layout (table-based, email-safe)
htmlRaw HTML code block

JavaScript API

JSOmBuilder.init( 'my-container-id' );
const html = OmBuilder.exportHTML();
OmBuilder.setBlocks( OmBuilder.getBlocks() );

window.omBuilderExport = function( html ) {
    document.getElementById('om_html_content').value = html;
};
💡Exported HTML is table-based and compatible with Gmail, Outlook, Apple Mail, and all major clients. Max-width 600px with inline styles.
21 · Gutenberg Block

Gutenberg block

Block name: omailer/subscribe-form — registered in assets/js/om-gutenberg.js. Dynamic block: save() returns null, output rendered server-side.

AttributeTypeMaps to shortcode
titlestringtitle
buttonstringbutton
liststringlist
show_namebooleanshow_name
tagsstringtags
stylestringstyle
triggerstringtrigger
delaynumberdelay
22 · Public Subscribe Forms

Public subscribe forms

assets/js/om-public.js is enqueued on all front-end pages. Handles fetch-based inline submissions, overlay lifecycle, custom field serialisation, and auto-close after subscribe.

TriggerBehaviour
delayOpens after N seconds (default 3)
exitOpens when mouse moves above the viewport top
scrollOpens when user scrolls past N% of the page
ℹ️All CSS is injected into <head> at runtime — no separate stylesheet needed. Dismissal state is persisted in sessionStorage.
23 · SMTP Configuration

SMTP configuration

Connections are stored in om_smtp_connections as an associative array keyed by UUID.

PHPOM_SMTP::get_connections(): array
OM_SMTP::get_connection( string $id ): array|null
OM_SMTP::get_primary_id(): string|null
OM_SMTP::get_backup_id(): string|null
OM_SMTP::get_presets(): array

Built-in presets: Gmail, Outlook / Office 365, Yahoo, SendGrid, Mailgun, Amazon SES, Postmark, SparkPost, Brevo, Zoho Mail, Custom.

On primary connection failure, Omailer retries with the backup connection. If both fail, falls back to PHP's mail().

24 · Smart Email Routing

Smart email routing

OM_Smart_Routing routes outbound emails to specific SMTP connections based on recipient domain, email type, or source plugin. Rules evaluate top-to-bottom; first match wins.

  • Recipient domain — send all @bigcorp.com emails via a dedicated connection
  • Email typetransactional, notification, or marketing
  • Source plugin — e.g. woocommerce, contact-form-7
25 · Email Log

Email log

All outbound emails are logged — subject, recipient, status, source plugin, timestamps. Body content is optional via om_log_settings['log_body'].

PHPOM_Email_Log::get_logs( array $args ): array      // args: search, status, per_page, page
OM_Email_Log::get_status_counts(): array
OM_Email_Log::resend( int $log_id ): bool
OM_Email_Log::export( string $format ): void       // 'csv' or 'json', streams download

Logs are purged after 90 days (configurable via om_log_retention_days).

26 · Alerts

Failure alerts

Sends an admin email when an outbound email fails. Settings in om_alert_settings:

KeyDefaultDescription
enabledfalseEnable alerts
alert_emailadmin emailWhere to send alerts
throttle_mins60Minimum minutes between alerts
include_bodyfalseInclude failed email body in alert
27 · Transactional Email Controls

Email controls

Toggle WordPress core email notifications on/off without code — new user registration, password reset, auto-updates, comment notifications, and more.

PHPOM_Email_Controls::get_controls_list(): array
OM_Email_Controls::get_settings(): array
28 · Performance & Queue

Performance & queue

When async mode is enabled, all wp_mail() calls are stored in om_email_queue and dispatched in the background by cron.

KeyDefaultDescription
async_enabledfalseEnable background queue
rate_limit_enabledfalseEnable rate limiting
rate_per_minute0Max per minute (0 = unlimited)
rate_per_hour0Max per hour
rate_per_day0Max per day

Cron events

EventScheduleCallback
om_send_scheduledhourlyOM_Sender::process_scheduled()
om_process_email_queueevery 5 minOM_Performance::process_queue()
om_process_automationsevery 5 minOM_Automation::process_runs()
om_weekly_summaryweeklyOM_Misc::send_weekly_summary()
29 · Misc Settings

Misc settings

KeyDefaultDescription
disable_all_emailsfalseBlock all outbound WP mail (staging/dev use)
hide_delivery_errorsfalseSuppress delivery error notices in admin
hide_plugin_alertsfalseSuppress plugin banner notices
hide_dashboard_widgetfalseRemove Omailer widget from WP dashboard
weekly_summaryfalseSend weekly delivery report to admin
summary_emailadmin emailRecipient for weekly summary
delete_on_uninstallfalseDrop all data on plugin uninstall
30 · JavaScript APIs

JavaScript APIs

OmAdmin (om-admin-v3.js)

MethodDescription
OmAdmin.showNotice( msg, type )Dismissable admin notice (success or error)
OmAdmin.bindTags()Tag create/delete handlers
OmAdmin.bindCustomFields()Custom field modal + CRUD
OmAdmin.bindAutomation()Automation builder UI
OmAdmin.bindSpamScore()Spam score checker
OmAdmin.bindDeliverability()DNS checker + send-window UI
OmAdmin.bindThemeToggle()Light/dark toggle (persisted in localStorage)

OmBuilder (om-builder.js)

MethodDescription
OmBuilder.init( containerId )Mount the builder
OmBuilder.exportHTML()Return full email HTML string
OmBuilder.getBlocks()Return current block array
OmBuilder.setBlocks( blocks )Load a block array into the builder
OmBuilder.render( containerId )Re-render canvas + properties panel

OM localised object

JSOM.ajax_url        // admin-ajax.php URL
OM.nonce           // om_admin nonce
OM.export_url      // CSV export URL
OM.log_export_csv  // Log CSV export URL
OM.log_export_json // Log JSON export URL
31 · Changelog

What's changed

v3.0.0Major release
  • NewSubscriber tags with om_tags + om_subscriber_tags tables
  • NewCustom subscriber fields with om_custom_fields + om_subscriber_meta tables
  • NewMerge tags: {{field_key}}, {{display_name}}
  • NewAutomation sequences — triggers, steps, cron runner
  • NewDouble opt-in with branded confirmation email and pending status
  • NewBounce & complaint webhook handlers (Mailgun, SendGrid, Postmark)
  • NewSpam score checker with grade A–F
  • NewDeliverability tools — SPF/DKIM/DMARC DNS checker + send-time optimisation
  • NewDrag-and-drop email builder (om-builder.js) with table-based export
  • NewGutenberg block omailer/subscribe-form
  • NewPopup and slide-in overlay forms (delay, exit, scroll triggers)
  • NewAdmin UI redesigned with light/dark theme toggle
  • NewGlobal helpers: om_send_email(), om_subscribe(), om_unsubscribe()
  • Changedom_create_field / om_delete_field renamed to om_create_custom_field / om_delete_custom_field
v2.1.0Infrastructure update
  • NewEmail log with resend, export (CSV/JSON), body viewer
  • NewFailure alerts with throttle
  • NewSMTP connections with failover (primary + backup)
  • NewSmart email routing by domain / type / source
  • NewEmail controls — toggle WP core notifications
  • NewAsync queue with rate limiting
v2.0.0Initial public release
  • NewSubscriber management, mailing lists, campaigns
  • NewBasic SMTP override, open/click tracking
  • NewShortcodes: [om_subscribe], [om_subscriber_count]
  • NewCampaign report with opens timeline and top links
✦ Need help?

Got a question about Omailer?

Reach out directly — Kenneth replies within 24 hours.