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
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.3.2) |
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 with 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 with type and form display settings |
om_subscriber_meta | Custom field values per subscriber |
om_automations | Automation definitions with 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 | (none) | {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, 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:
| 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
- ChangedInternal stability and admin polish
- 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.