Newsletter plugin
- Quick start
- Writing newsletters
- Scheduling
- Subscribers & forms
- Welcome & lead magnet
- Pro plugin
- Templates
- AI Agents
Facebook posting plugin
General
For developers
Filter and action hooks the free plugin exposes. Companion plugins (including the pro plugin) extend the free plugin entirely through these hooks — no core file modifications.
Subscriber lifecycle
do_action( 'otts_subscriber_created', int $id, string $email, string $first_name )— fires after a successful new subscription. Used internally by the welcome email and the lead-magnet handler. Hook this to push the subscriber to an external CRM.do_action( 'otts_subscriber_unsubscribed', int $id, string $email )— fires when someone clicks unsubscribe.
Send pipeline
apply_filters( 'otts_send_recipients', array $subscribers, WP_Post $post )— filter the recipient list before a send. The pro plugin's Stripe paywall uses this to droppast_due/cancelledpaid subscribers from paid newsletter sends.apply_filters( 'otts_render_for_subscriber', string $html, array $subscriber, WP_Post $post )— per-recipient HTML filter. The paywall returns a teaser + Subscribe CTA for free subscribers receiving paid issues; the open-rate tracker injects a 1×1 pixel at priority 99.
Email providers
apply_filters( 'otts_provider_choices', array $choices )— add labels to the Email Provider dropdown.apply_filters( 'otts_make_provider', ?OTTS_Provider_Base $provider, string $slug, array $config )— return a provider instance for your custom slug. SubclassOTTS_Provider_Baseand implementslug(),label(),config_fields(),test_connection(),send_campaign().
Forms
apply_filters( 'otts_form_types', array $types )— add slugs (e.g.popup,slidein) to the supported form-type list. Render via CSS classes.otts-form-{type}.
Editor & audit
apply_filters( 'otts_editor_action', ?string $result, string $action, string $text, string $tone )— register a custom AI editor action. Return the rewritten text ornullto defer.apply_filters( 'otts_audit_checks', array $checks, int $newsletter_id, WP_Post $post )— append or transform pre-send audit checks. Each check is['id' => ..., 'label' => ..., 'status' => 'pass|warn|fail', 'note' => ...].
Settings extension
apply_filters( 'otts_settings_tabs', array $tabs )— add tabs to One Two Three Send → Settings.do_action( 'otts_render_settings_tab_{tab}' )— render the body of your custom tab. Companion plugin's callback echoes form fields directly inside the active form element.do_action( 'otts_save_settings_{tab}' )— handle the POST when your tab is saved. Nonce verification is already done by the free plugin.
REST API
POST /wp-json/otts/v1/subscribe— public, takesemail, optionalfirst_name, honeypotwebsite.GET /wp-json/otts/v1/unsubscribe— public, takesemail+tokenquery params.GET /wp-json/otts/v1/lead-magnet/download— public, takese(email) +x(expires) +s(HMAC).POST /wp-json/otts/v1/generate— admin-only, triggers a generation.POST /wp-json/otts/v1/audit/{id}— admin-only, runs the audit on a newsletter.POST /wp-json/otts/v1/editor/{action}— admin-only, runs an editor action.POST /wp-json/otts/v1/send/{id}— admin-only, sends a newsletter immediately.
Newsletter templates (pro plugin)
Subclass OTTS_Pro_Template_Base and register your template at plugins_loaded via OTTS_Pro_Templates::register( $instance ). The four required methods:
slug()— unique key stored in_otts_templatepost metalabel()— name shown in the One Two Three Send → Templatesdescription()— one-paragraph explanation rendered next to the radiobuild_prompt( array $args )— return the full Claude prompt for your structure. Receivestone,length,topic. The first line of Claude’s expected response must beSubject: ...followed by a blank line, then the HTML body.render_email( string $content_html, WP_Post $post, array $subscriber )— wrap the stored HTML in your styled email shell. Hooks the free plugin’sotts_render_for_subscriberfilter at priority 5 (before paywall and open-rate).
See includes/templates/class-template-newsroom.php in the pro plugin for a full reference implementation.
Other filters
apply_filters( 'otts_claude_model', string $model )— override the Claude model used for generation and editor actions. Defaultclaude-sonnet-4-5.
Full hook reference
Every apply_filters / do_action call across the four plugins (One Two Three Send + Pro, One Two Three Post + Pro) that’s meant for extension consumption. Internal-only hooks (audit pipeline intermediates, debug counters) are not listed. Auto-generated from the source on 2026-05-17 — verify against the latest plugin tag if you’re depending on signature stability.
One Two Three Send (free)
otts_subscriber_created— action(int $id, string $email, string $first_name). Fires only for net-new subscriber rows.otts_subscriber_unsubscribed— action(int $id, string $email).otts_subscriber_bounced— action(int $id, string $email, string $bounce_state).otts_send_recipients— filter(array $subscribers, WP_Post $newsletter). Mutate the recipient list before a send (Pro paywall uses this).otts_render_for_subscriber— filter(string $html, array $sub, WP_Post $post). Mutate the per-recipient body (Pro paywall, click-tracker, open-tracker all use this).otts_audit_checks— filter(array $checks, WP_Post $post). Add / remove checks from the pre-send audit.otts_editor_action— filter(string|null $result, string $action, string $text, string $tone). Implement a custom AI editor action.otts_form_types— filter(array $types). Register additional signup-form types (Pro registers popup + slidein here).otts_make_provider— filter(?OTTS_Provider_Base $provider, string $slug, array $config). Register a custom email provider.otts_provider_choices— filter(array $choices). Add slugs to the provider selector dropdown.otts_claude_model— filter(string $model). Override the default Claude model used for editor + generator calls.otts_settings_tabs— filter(array $tabs). Add Settings tabs (Pro adds Provider, Network, etc).otts_render_settings_tab_{slug}— action(). Render the body of a Settings tab registered viaotts_settings_tabs.otts_save_settings_{slug}— action(). Save handler for a Settings tab.otts_settings_schedule_show_default— filter(bool $show). Hide the legacy Schedule UI in Settings (Pro uses this to take over scheduling).otts_settings_schedule_render_pre— action(). Inject a replacement notice in place of the suppressed Schedule UI.otts_scheduler_register_default_cron— filter(bool $register). Suppress the defaultotts_scheduled_sendcron registration (Pro replaces it).otts_scheduled_send— action. Cron event the default scheduler fires; only registered when the filter above returns true.otts_affiliate_hint_enabled— filter(bool $enabled). Disable the affiliate-hint helper globally.otts_affiliate_hint_services— filter(array $services). Replace the service map (claude / mailerlite / brevo / publer / etc).otts_signup_attribution_consent— filter(bool $allowed). Suppress the first-touch cookie until consent is granted.
One Two Three Send Pro
otts_pro_post_generated— action(int $post_id, array $args). Fires after the AI generator finishes building a draft.otts_pro_subscriber_type_changed— action(int $sub_id, string $old_type, string $new_type). Fires when free→paid or paid→cancelled happens.otts_subscriber_upgraded_to_paid— action(string $email). Stripe webhook fires this when an existing free row pays for the first time.otts_pro_ac_prompt— filter(string $prompt, array $directive). Mutate the Claude prompt for AI Agents (formerly Auto Content) generation.otts_pro_assembly_image_budget— filter(int $max_images, WP_Post $post). Cap the per-newsletter image count.otts_pro_audit_banned_patterns— filter(array $patterns). Add regexes to the banned-word audit.otts_pro_audit_image_hosts— filter(array $hosts). Whitelist additional image hosts in the audit.otts_pro_curated_headline_model— filter(string $model). Override the model for curated-pipeline headline generation.otts_pro_currency— filter(string $code). Override the Stripe currency (default USD).otts_pro_enable_web_search— filter(bool $enabled). Toggle Claude’s web-search tool in the structured pipeline.otts_pro_market— filter(string $market). Set the regional market hint for image picker / Viator sync.otts_pro_network_widget_auto_append— filter(bool $append, int $post_id). Per-post override for the Newsletter Network widget’s auto-append on blog posts.otts_pro_paywall_cta_url— filter(string $url, WP_Post $post). Override the destination of the paywall Subscribe CTA.otts_pro_send_install_ping— filter(bool $send). Disable the once-per-day install ping the updater makes to onetwothreesend.com.otts_pro_travel_brand_color— filter(string $hex). Override the Travel template’s brand colour (default deep green).otts_pro_travel_accent_color— filter(string $hex). Override the Travel template’s accent colour.otts_pro_unsplash_banned_terms— filter(array $terms). Add prohibited search terms to the Unsplash picker.otts_claude_model_structured— filter(string $model). Override the Sonnet model used by the structured-generation pipeline.otts_claude_model_judge— filter(string $model). Override the Haiku model used by the structured-pipeline Judge pass.
One Two Three Post (free)
ottpost_pre_publish_validate— filter(null|WP_Error $carry, int $post_id). Pre-publish validator chain. Return aWP_Errorto block; null to allow. Pro plugin’s validators all hook in here.ottpost_publish_args— filter(array $args, int $post_id). Mutate the args handed to the Facebook (or Publer) transport at publish time.ottpost_after_publish— action(int $post_id, array $result). Fires after every publish attempt (success or failure).ottpost_fb_oauth_connected— action. Fired after a successful Facebook OAuth (re-)connect. Pro uses this to auto-clear stale “insufficient permissions” first-comment errors.
One Two Three Post Pro
ottpost_pro_ai_filename_pattern— filter(string $regex). Override the regex used to detect AI-generated image filenames in the validator.ottpost_pro_send_install_ping— filter(bool $send). Disable the install ping the Pro updater sends to onetwothreesend.com.
Plus the Pro validators themselves all hook ottpost_pre_publish_validate at priority 10. The first-comment auto-detection (Phase 6) hooks the same filter at priority 100 so it runs after the validator chain has finished.
Source & license
One Two Three Send is GPLv2-or-later. The full source ships with the plugin — read it, fork it, contribute back. Bug reports and feature requests go to our contact page.
Free-plugin extension hooks (v2.0.13+)
Three integration points the free plugin exposes so an add-on (like One Two Three Send Pro’s unified Weekly Schedule) can replace the legacy Settings → Schedule UI without forking the free plugin. Free behaviour is unchanged when no listener is attached.
apply_filters( 'otts_settings_schedule_show_default', true )— returnfalseto hide the free plugin’s legacy schedule form on the Settings → Schedule tab. An extension that owns scheduling fully should hide the legacy UI to avoid two competing controls.do_action( 'otts_settings_schedule_render_pre' )— runs at the top of the Schedule tab, before the form renders. An extension uses this to inject a replacement notice or a link to its own scheduling page.apply_filters( 'otts_scheduler_register_default_cron', true )— returnfalseto suppress the free plugin registering its defaultotts_scheduled_sendWP-Cron event on activation. An extension that handles its own cron scheduling should suppress the default to avoid duplicate registrations.
Together these hooks let a Pro extension fully replace the scheduler UI + cron without modifying the free plugin’s source. The Pro plugin’s unified Weekly Schedule uses all three.