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
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.
| Constant | Description |
OM_VERSION | Current plugin version (3.0.0) |
OM_PATH | Absolute filesystem path to the plugin directory (trailing slash) |
OM_URL | URL to the plugin directory (trailing slash) |
OM_BASENAME | Plugin 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
| Column | Type | Description |
id | BIGINT UNSIGNED PK | Auto-increment |
email | VARCHAR(255) UNIQUE | Email address |
first_name | VARCHAR(100) | First name |
last_name | VARCHAR(100) | Last name |
status | ENUM | subscribed, unsubscribed, bounced, pending |
confirmation_token | VARCHAR(64) | Token for double opt-in |
source | VARCHAR(100) | manual, import, shortcode, api, … |
ip_address | VARCHAR(45) | IPv4 or IPv6 |
confirmed_at | DATETIME | When opt-in was confirmed |
created_at | DATETIME | Row creation timestamp |
updated_at | DATETIME | Auto-updated on change |
om_campaigns
| Column | Type | Description |
id | BIGINT UNSIGNED PK | |
subject | VARCHAR(255) | Email subject |
preview_text | VARCHAR(255) | Pre-header text |
html_content | LONGTEXT | Email body HTML |
json_design | LONGTEXT | Builder block data (JSON) |
status | ENUM | draft, scheduled, sending, sent, paused |
list_id | BIGINT UNSIGNED | Target list (NULL = all subscribers) |
scheduled_at | DATETIME | When to send |
total_sent | INT UNSIGNED | Recipients reached |
total_opened | INT UNSIGNED | Unique opens |
total_clicked | INT UNSIGNED | Unique clicks |
total_bounced | INT UNSIGNED | Bounces |
Other tables
| Table | Description |
om_lists | Named mailing lists |
om_list_subscriber | Junction: subscribers ↔ lists. PK: (list_id, subscriber_id) |
om_sends | Per-subscriber send record — status, open/click tracking |
om_events | Granular log: open, click, bounce, unsubscribe, complaint |
om_tags | Tag definitions with name, slug |
om_subscriber_tags | Junction: subscribers ↔ tags |
om_custom_fields | Custom field definitions — type, form display settings |
om_subscriber_meta | Custom field values per subscriber |
om_automations | Automation definitions — trigger type and status |
om_automation_steps | Ordered steps per automation |
om_automation_runs | Subscriber progress through automations |
05 · Settings Reference
Settings reference
Stored in a single serialised array option: om_settings.
| Key | Type | Default | Description |
from_name | string | Blog name | Default sender name |
from_email | string | Admin email | Default sender email |
reply_to | string | Admin email | Default reply-to address |
batch_size | int | 50 | Subscribers per cron batch |
batch_delay | int | 2 | Seconds to sleep between batches |
track_opens | bool | true | Insert tracking pixel |
track_clicks | bool | true | Wrap links with tracking redirects |
double_optin | bool | false | Require email confirmation |
unsubscribe_page | int | auto-created | Post 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]
| Attribute | Default | Description |
list | (all) | List ID to subscribe to |
title | | Optional form heading |
button | Subscribe | Submit button text |
show_name | no | Set yes to show first/last name fields |
style | default | default, inline, stacked, popup, slide-in |
tags | | Comma-separated tag slugs applied on subscribe |
trigger | delay | For popup/slide-in: delay, exit, scroll |
delay | 3 | Seconds (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
| Hook | Arguments | Description |
om_tag_added | $subscriber_id, $tag_id | Fires when a tag is assigned |
om_subscriber_bounced | $email, $provider | Fires on bounce webhook |
om_subscriber_unsubscribed | $email, $provider | Fires on unsubscribe webhook |
om_subscriber_complaint | $email, $provider | Fires 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
| Action | Method | Parameters | Returns |
om_subscriber_toggle | POST | id, status | success/error |
om_delete_subscriber | POST | id | success/error |
om_bulk_delete_subscribers | POST | ids[] | success/error |
om_export_subscribers | GET | nonce | CSV download |
om_import_subscribers | POST | csv_file, list_id | {imported, skipped} |
Campaigns
| Action | Parameters | Returns |
om_save_campaign | id?, subject, from_name, from_email, reply_to, list_id, html_content, … | {campaign_id} |
om_delete_campaign | id | success |
om_duplicate_campaign | id | {new_id} |
om_send_campaign | campaign_id | success |
om_send_test | campaign_id, test_email | success |
om_load_template | template_id | {html} |
Tags & Custom Fields
| Action | Parameters | Returns |
om_create_tag | name | {id, name, slug} |
om_delete_tag | id | success |
om_assign_tag | subscriber_id, tag_id | success |
om_unassign_tag | subscriber_id, tag_id | success |
om_create_custom_field | label, field_key, field_type, options, show_in_form, is_required | {id} |
om_delete_custom_field | id | success |
SMTP & Deliverability
| Action | Parameters | Returns |
om_save_connection | id?, name, provider, host, port, encryption, auth, username, password, is_primary, is_backup | {id} |
om_test_connection | id, test_email | success/error + message |
om_check_spam_score | subject, body | {score, grade, issues[]} |
om_check_domain | domain | {spf, dkim, dmarc, score} |
om_send_window | — | {suggestion, top_windows[]} |
Public endpoints (no auth)
| Action | Parameters | Description |
om_subscribe | nonce, email, first_name?, last_name?, list_id?, tags?, cf_* | Subscribe form submission |
om_open | c (campaign_id), s (subscriber_id) | Track open pixel |
om_click | c, s, url | Track link click |
om_unsubscribe | token | Unsubscribe 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.
12 · Custom Fields & Merge Tags
Custom fields & merge tags
Merge tags
| Tag | Replaced 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
| Type | Renders 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 type | Fires when |
subscribe | A subscriber confirms their subscription |
tag_added | A specific tag is added to a subscriber |
form_submit | A shortcode form is submitted |
wp_hook | A custom WP action fires (trigger_value = hook name) |
Step types
| Type | Description |
delay | Wait N minutes / hours / days before next step |
send_email | Send an email — param = subject, body = HTML |
add_tag | Add a tag (slug in param) |
remove_tag | Remove a tag (slug in param) |
update_field | Set a custom field — param = field_key=value |
condition | Branch: 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}/
| Provider | Bounce / Fail | Unsubscribe | Spam complaint |
| Mailgun | bounced, failed | unsubscribed | complained → mark unsub + fire action |
| SendGrid | bounce, blocked, dropped | unsubscribe, group_unsubscribe | spamreport → mark unsub + fire action |
| Postmark | HardBounce | Unsubscribe, ManuallyDeactivated | SpamComplaint |
ℹ️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
| Grade | Score range |
| A | 0 – 10 |
| B | 11 – 20 |
| C | 21 – 35 |
| D | 36 – 50 |
| F | 51+ |
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' => [...] ]
| Check | Points |
| 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
| Type | Description |
text | Rich HTML paragraph |
heading | <h2> heading |
image | Responsive image with optional link |
button | CTA button with configurable colours and alignment |
divider | Horizontal rule with configurable thickness/colour |
spacer | Vertical whitespace |
columns | Two-column layout (table-based, email-safe) |
html | Raw 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.
| Attribute | Type | Maps to shortcode |
title | string | title |
button | string | button |
list | string | list |
show_name | boolean | show_name |
tags | string | tags |
style | string | style |
trigger | string | trigger |
delay | number | delay |
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 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:
| Key | Default | Description |
enabled | false | Enable alerts |
alert_email | admin email | Where to send alerts |
throttle_mins | 60 | Minimum minutes between alerts |
include_body | false | Include 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
29 · Misc Settings
Misc settings
| Key | Default | Description |
disable_all_emails | false | Block all outbound WP mail (staging/dev use) |
hide_delivery_errors | false | Suppress delivery error notices in admin |
hide_plugin_alerts | false | Suppress plugin banner notices |
hide_dashboard_widget | false | Remove Omailer widget from WP dashboard |
weekly_summary | false | Send weekly delivery report to admin |
summary_email | admin email | Recipient for weekly summary |
delete_on_uninstall | false | Drop all data on plugin uninstall |
30 · JavaScript APIs
JavaScript APIs
OmAdmin (om-admin-v3.js)
| Method | Description |
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)
| Method | Description |
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
- 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()
- Changed
om_create_field / om_delete_field renamed to om_create_custom_field / om_delete_custom_field
- 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
- 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.