For developers

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 drop past_due / cancelled paid 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. Subclass OTTS_Provider_Base and implement slug(), 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 or null to 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, takes email, optional first_name, honeypot website.
  • GET /wp-json/otts/v1/unsubscribe — public, takes email + token query params.
  • GET /wp-json/otts/v1/lead-magnet/download — public, takes e (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_template post meta
  • label() — name shown in the One Two Three Send → Templates
  • description() — one-paragraph explanation rendered next to the radio
  • build_prompt( array $args ) — return the full Claude prompt for your structure. Receives tone, length, topic. The first line of Claude’s expected response must be Subject: ... 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’s otts_render_for_subscriber filter 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. Default claude-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 via otts_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 default otts_scheduled_send cron 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 a WP_Error to 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 ) — return false to 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 ) — return false to suppress the free plugin registering its default otts_scheduled_send WP-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.