Skip to content

Actions & Filters

PrimoCRM exposes WordPress actions (events you can react to) and filters (values and registries you can modify). All hooks use the primocrm_ prefix.

PrimoCRM itself ships no paid code and no licence checks. Everything the Pro add-on adds, it adds through the hooks on this page, so anything Pro can do here your own plugin can do too.

React to these with add_action.

Hook Arguments Fires when
primocrm_contact_created $contact_id A contact is created
primocrm_contact_updated $contact_id, $changed_keys A contact is updated ($changed_keys are the changed field names)
primocrm_contact_deleted $contact_id, $email A contact is deleted (the address is passed because the row is already gone)
primocrm_contact_status_changed $contact_id, $new_status, $old_status A contact’s status changes
primocrm_contact_unsubscribed $contact_id A contact unsubscribes
primocrm_contact_resubscribed $contact_id A contact opts back in
primocrm_double_optin_confirmed $contact_id A contact confirms a double opt-in link
primocrm_contact_tag_added $contact_id, $tag_id A tag is applied
primocrm_contact_tag_removed $contact_id, $tag_id A tag is removed
primocrm_contact_list_added $contact_id, $list_id Added to a list
primocrm_contact_list_removed $contact_id, $list_id Removed from a list
Hook Arguments Fires when
primocrm_form_submitted $form_id, $entry_id, $contact_id, $data A PrimoCRM form is processed
primocrm_email_opened $contact_id, $campaign_id An email open is tracked
primocrm_email_link_clicked $contact_id, $campaign_id, $url A tracked link is clicked

These are the write side of the campaign and template screens. A plugin that adds its own settings to a campaign stores them here, next to the campaign the user just saved.

Hook Arguments Fires when
primocrm_campaign_saved $campaign_id, $request, $is_new A campaign is created or updated ($request is the WP_REST_Request)
primocrm_campaigns_deleted $campaign_ids One or more campaigns are deleted
primocrm_email_template_saved $template_id, $request, $is_new An email template is created or updated
primocrm_health_check_tick none The five-minute queue health check runs, a ready-made slot for periodic work
Hook Arguments Fires when
primocrm_loaded $plugin Inside REST route registration. A catalog handshake, not a reliable boot hook
primocrm_automations_paused_for_handlers $automation_ids Automations were paused because a trigger or action handler disappeared
primocrm_automations_resumed_for_handlers $automation_ids Those automations were resumed because the handler came back

Example:

add_action( 'primocrm_contact_created', function ( $contact_id ) {
error_log( "New PrimoCRM contact: {$contact_id}" );
} );
add_action( 'primocrm_email_link_clicked', function ( $contact_id, $campaign_id, $url ) {
// Score the contact, notify a channel, etc.
}, 10, 3 );

These collect extendable things. Full recipes are in Extending PrimoCRM.

Filter Value Purpose
primocrm_automation_triggers array Register or modify automation triggers
primocrm_automation_actions array Register or modify automation actions
primocrm_condition_options array (fields, operators) Add condition-builder fields and operators
primocrm_form_field_types array (type to class) Register custom form field types
primocrm_migration_sources array (key to source) Register migration source providers
primocrm_smart_code_catalog array Add entries to the smart-code picker
primocrm_smart_codes $content, $contact, $context Replace your own smart codes while an email is rendered
primocrm_mail_providers array (id to descriptor) Register an API email provider, with the settings it needs
primocrm_smtp_send $result, $provider, $atts, $settings Do the sending for your provider. Return null and PrimoCRM falls back to wp_mail
primocrm_email_editors array (type to descriptor) Register an email editor as a template type
primocrm_double_optin_sources array of keys Which contact sources require double opt-in

A catalog entry with no registered handler is listed as unavailable rather than hidden, so give it a requires_plugin string naming what it needs. PrimoCRM shows that as “Requires X” and refuses to let it be chosen.

The REST payload for one campaign or template, on its way to the admin app. Use these to send your own fields to a UI you injected through the JavaScript seams below.

Filter Value Purpose
primocrm_campaign_response $campaign, $campaign_id Attach your fields to a single campaign response
primocrm_email_template_response $template, $template_id Attach your fields to a single template response
primocrm_pre_send_campaign $taken_over, $campaign, $settings, $request Return a WP_REST_Response to take the send over entirely. Anything else and PrimoCRM sends normally
primocrm_campaign_sent_elsewhere $handled, $campaign Return true when your code already queued this campaign, so the built-in queue builder skips it
Filter Value Purpose
primocrm_brand_name string The brand name shown in admin
primocrm_brand_menu_icon string The admin menu icon
primocrm_brand array The brand descriptor localized to the app
primocrm_show_upsell bool Return false to remove the “Upgrade” button from the admin header. The Pro add-on does this for you
primocrm_upgrade_url string Where that button points
primocrm_show_reports_menu bool Return true when you add a tab through primocrm.reports.tabs, so the WordPress sidebar lists Reports. Off by default, because without a tab the screen has nothing to show. The Pro add-on does this for you
Filter Value Purpose
primocrm_send_batch_size int Emails per campaign worker batch
primocrm_send_rate_per_hour int Campaign send throttle
primocrm_automation_batch_limit int Rows per automation worker batch
primocrm_email_body_retention_days int How long rendered email bodies are retained
primocrm_async_automation_delivery bool Toggle async delivery of automation emails
primocrm_auto_create_contact_status $status, $source Status for contacts auto-created by triggers or user sync
primocrm_require_postal_address bool Whether a postal address is required before a campaign can be sent
primocrm_trust_proxy_headers bool Whether to trust proxy headers for the submitter IP on forms
primocrm_migration_step_budget float (seconds, default 10) How long one import or migration request may work before it hands back and the next request continues

The admin app is a React single-page app, and these wp.hooks filters are how another plugin puts its own UI inside it. There are no standalone add-on pages.

Each filter returns descriptors with a mount callback, not React elements: the app and your plugin may be running different React versions, so PrimoCRM gives you a DOM node and you render into it yourself.

Filter Purpose
primocrm.settings.tabs Add a tab to the Settings screen
primocrm.reports.tabs Add a tab to Reports. The Reports screen and its app menu item exist only while something has registered one; for the WordPress sidebar entry, also return true from primocrm_show_reports_menu
primocrm.campaigns.rowActions Add an action to a campaign row
primocrm.campaignCreate.sections Add a section to a numbered step of the campaign wizard
primocrm.campaignDetails.panels Add a panel to the campaign detail screen
primocrm.emailEditor.init Build your editor into the container PrimoCRM renders for your template type
primocrm.emailEditor.destroy Tear that editor down again