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 Newsletter → 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 Newsletter → Templates dropdown
  • 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.

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.