Abuja Digital Studio · Est. 2018
Start a Project
Docs
OMailer
Full-stack email infrastructure for WordPress — built-in SMTP, subscriber CRM, campaign builder with drag-and-drop editor, A/B testing, automation sequences, open/click/bounce tracking, WooCommerce integration, lead scoring, and REST API. No SaaS required.
omailer
Developer documentation

Omailer v3.3.2

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.3.2)
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 with 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 with type and form display settings
om_subscriber_metaCustom field values per subscriber
om_automationsAutomation definitions with 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(none){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, so no separate stylesheet is 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, Microsoft 365, Amazon SES, Zoho, Custom SMTP. (Bounce / complaint webhook ingestion is supported separately for Mailgun, SendGrid, and Postmark; see Bounce Tracking.)

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 type: transactional, 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.3.2Maintenance release
  • ChangedInternal stability and admin polish
v3.3.1Major 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.