PHP hooks
Snipfire is built to be extended: Snipfire Pro itself plugs in only through these hooks. Put your code in a plugin or mu-plugin rather than a snippet, so it's in place before snippets run.
Access and permissions
snipfire_capability (filter)
The capability that gives full Snipfire access, besides administrators. Default manage_options. Not used on multisite, where only super admins have full access.
add_filter( 'snipfire_capability', fn() => 'edit_theme_options' );
snipfire_user_access (filter)
The access level for a user who isn't an administrator: none, view, design or full. With Snipfire Pro, the level comes from team access (at priority 10); use a later priority to override it for one person.
add_filter( 'snipfire_user_access', function ( $level, $user_id, $user ) {
return 'jane' === $user->user_login ? 'design' : $level;
}, 20, 3 );
snipfire_access_changed (action, Pro)
Fires when team access changes. Arguments: the role → level map before and after.
Snippets and history
snipfire_revisions_limit (filter)
How many versions of each snippet to keep. Default 10, or 50 with Pro.
add_filter( 'snipfire_revisions_limit', fn() => 100 );
snipfire_validate (filter)
Add your own errors or warnings before a snippet is saved. An error keeps the snippet switched off.
add_filter( 'snipfire_validate', function ( $result, $snippet ) {
if ( 'php' === $snippet['type'] && str_contains( $snippet['code'], 'eval(' ) ) {
$result['errors'][] = array( 'message' => 'eval() is not allowed on this site.', 'line' => 0 );
}
return $result;
}, 10, 2 );
$result is array( 'errors' => array( array( 'message' => …, 'line' => … ) ), 'warnings' => array( 'text', … ) ).
snipfire_snippet_output (filter)
Filters an HTML snippet's or shortcode's output before it's printed. Arguments: $html, $meta (the snippet's settings, without code).
snipfire_activity (action)
Fires on every change: $action (created, updated, enabled…), $snippet (settings, or null for settings changes), $details. The activity log listens to it; you can too, for example to post changes to Slack.
add_action( 'snipfire_activity', function ( $action, $snippet, $details ) {
if ( 'auto_disabled' === $action ) {
wp_mail( get_option( 'admin_email' ), 'Snippet switched off', $snippet['title'] . ': ' . ( $details['message'] ?? '' ) );
}
}, 10, 3 );
For auto_disabled, $details holds the error's message and line.
snipfire_active_snippets (action)
Fires after the active snippets are hooked in (not in safe mode or on Snipfire's own screens). Argument: active snippets, id => settings.
snipfire_error_message (filter)
Every snippet error message before Snipfire stores, logs or shows it. Use it to remove values that must never appear in an error. Snipfire Pro's secrets vault uses it to replace any secret's value with [secret:name].
add_filter( 'snipfire_error_message', fn( $message ) => str_replace( ACME_API_KEY, '[acme key]', $message ) );
Snippet runtime
snipfire_observe_snippets (filter)
Return true to be told when each snippet starts and stops running on this request, through the two actions below. Decided once per request, before any snippet runs. Off by default, so normal requests do no extra work. Snipfire Pro's snippet performance switches it on for the requests it measures.
snipfire_snippet_start, snipfire_snippet_end (actions)
Fire around each snippet's work while observing: running a PHP snippet's code and the callbacks it hooked in, rendering an HTML snippet or shortcode, adding CSS or JavaScript. Argument: the snippet's settings (no code). They can nest: a snippet's output may run another snippet.
add_filter( 'snipfire_observe_snippets', fn() => isset( $_GET['acme_trace'] ) && current_user_can( 'manage_options' ) );
add_action( 'snipfire_snippet_start', function ( $meta ) {
$GLOBALS['acme_started'][ $meta['id'] ] = hrtime( true );
} );
add_action( 'snipfire_snippet_end', function ( $meta ) {
$ms = ( hrtime( true ) - $GLOBALS['acme_started'][ $meta['id'] ] ) / 1e6;
error_log( sprintf( 'Snippet %d took %.2f ms', $meta['id'], $ms ) );
} );
snipfire_run_options (filter)
The choices for How it runs on the classic screens: value => label. Add-ons add their own (Snipfire Pro adds On a schedule). The editor app's list is the runOptions JavaScript filter.
snipfire_schedule_time_limit (filter, Pro)
The time limit of a scheduled snippet's run, in seconds of CPU time. Default 60; 0 leaves PHP's own limit. Not applied on WP-CLI. Arguments: $seconds, $meta (the snippet).
add_filter( 'snipfire_schedule_time_limit', fn( $seconds, $meta ) => 12 === $meta['id'] ? 300 : $seconds, 10, 2 );
snipfire_profile_max_bytes (filter, Pro)
How much performance data is written per day, in bytes. Default 2 MB; measurements past it are dropped for that day.
Conditions
snipfire_condition_types, snipfire_condition_labels, snipfire_condition_groups (filters)
Add your own condition rules. A type has a kind (value shape: none, text, multi, list, ids, keyvalue, number, range, code), ops, a stage (0 = any time, 1 = user known, 2 = page known) and a match callable that returns true, false or null ("can't tell", which never matches). Add 'php' => true to a type that runs code written into the rule, as the Custom PHP rule does: only people with full Snipfire access can then add or change snippets that use it.
Don't translate anything in snipfire_condition_types: it runs while snippets load. Put names in snipfire_condition_labels.
use Snipfire\Conditions;
add_filter( 'snipfire_condition_types', function ( $types ) {
$types['acme_member'] = array(
'kind' => 'none',
'ops' => Conditions::SET_OPS, // is, is_not
'stage' => 1, // needs the user
'match' => fn( $rule, $ctx ) => function_exists( 'acme_is_member' ) ? acme_is_member() : null,
);
return $types;
} );
add_filter( 'snipfire_condition_labels', function ( $labels ) {
$labels['acme_member'] = array( 'label' => __( 'Visitor is an Acme member', 'acme' ) );
return $labels;
} );
add_filter( 'snipfire_condition_groups', function ( $groups ) {
$groups[ __( 'Visitor', 'snipfire' ) ]['acme_member'] = __( 'Acme member', 'acme' );
return $groups;
} );
snipfire_visitor_country (filter, Pro)
The visitor's country for the Country rule: a two-letter code, or '' when unknown. Use it to plug in your own geolocation.
Smart tags
snipfire_smart_tags (filter)
The smart tags: name => array( label, group, kind ), where kind is text, url, number or json (how the value is escaped). Add 'arg' as a fourth item for a tag that takes an argument, like {custom_field:key}. Give the values with snipfire_smart_tag_value.
snipfire_smart_tag_value (filter)
A tag's value on the current page, unescaped: Snipfire escapes it for HTML or JavaScript. Arguments: $value (null if Snipfire doesn't know the tag), $name, $arg (the part after :, or ''), $post (the post the page or the loop is on, or null).
add_filter( 'snipfire_smart_tags', function ( $tags ) {
$tags['acme_points'] = array( 'Loyalty points', 'Logged-in user', 'number' );
return $tags;
} );
add_filter( 'snipfire_smart_tag_value', function ( $value, $name ) {
return 'acme_points' === $name ? (string) acme_points( get_current_user_id() ) : $value;
}, 10, 2 );
Values are worked out once per request (per post, in a loop).
snipfire_smart_tags_order (filter, Pro)
The WooCommerce order the order smart tags and {woocommerce_data} use: a WC_Order, or false for none. By default it's the order on the order-received page, when the address has its order key. Return an order to use the tags on a custom thank-you page; check yourself that the visitor may see it.
Page code
snipfire_page_scripts_post_types (filter)
The post types that get the Snipfire panel and page code. Default: public post types that support the editor, except attachments.
add_filter( 'snipfire_page_scripts_post_types', fn( $types ) => array_diff( $types, array( 'product' ) ) );
Updates
snipfire_disable_update_check (filter)
Return true to stop Snipfire from checking snipfirewp.com for new versions, like the SNIPFIRE_DISABLE_UPDATE_CHECK constant. The default is true when the constant is set or the plugin folder is a Git checkout. See Updating Snipfire.
snipfire_update_url (filter)
The address of the latest Snipfire zip, which the update check asks about and WordPress downloads. Default https://snipfirewp.com/download/snipfire.zip, or the SNIPFIRE_UPDATE_URL constant. For example, point a staging site at a test build. The server must answer a HEAD request with a file name like snipfire-0.17.0.zip in its Content-Disposition header, and serve the zip's signature at the same address plus .sig. The address must use https:// (plain http:// only on local and development sites); otherwise Snipfire falls back to snipfirewp.com.
Every update is still checked against the release keys built into Snipfire, whatever the address: see Signed updates. There's deliberately no filter to add keys.
Support and monitoring
snipfire_system_info (filter)
The system info sections: id => array( 'title' => …, 'rows' => array( array( label, value ), … ) ). Add a section for your add-on. Never add secrets, tokens, code or personal data: people paste this into support tickets.
add_filter( 'snipfire_system_info', function ( $sections ) {
$sections['acme'] = array(
'title' => 'Acme add-on',
'rows' => array( array( 'Version', ACME_VERSION ), array( 'Cache', acme_cache_enabled() ? 'on' : 'off' ) ),
);
return $sections;
} );
snipfire_site_status (filter)
The answer of GET /status. Add-ons add themselves under addons, for example $status['addons']['acme'] = array( 'version' => ACME_VERSION );. Never add secrets.
Importers
snipfire_import_sources (filter)
The list of importer classes. Add your own class extending Snipfire\Import\Source to import from another plugin.
AI abilities
snipfire_abilities (action)
Fires after Snipfire registers its WordPress abilities (WordPress 6.9+). Argument: the Snipfire\Abilities object. Register your own abilities here, in the snipfire category, and pass Snipfire\Abilities::meta() as their meta so they're only offered to AI assistants and the REST API when Tools & Safe Mode → AI assistants is on.
add_action( 'snipfire_abilities', function () {
wp_register_ability( 'acme/list-snippet-notes', array(
'label' => __( 'List snippet notes', 'acme' ),
'description' => __( 'Lists the notes Acme keeps about snippets.', 'acme' ),
'category' => Snipfire\Abilities::CATEGORY,
'input_schema' => array( 'type' => 'object', 'default' => array() ),
'output_schema' => array( 'type' => 'array' ),
'execute_callback' => 'acme_snippet_notes',
'permission_callback' => Snipfire\Abilities::allowed( 'view' ),
'meta' => Snipfire\Abilities::meta( true ), // read-only
) );
} );
Abilities::allowed( 'view' | 'design' | 'full' ) checks the user's Snipfire access level, as Snipfire's own abilities do.
Secrets and AI assistant (Pro)
snipfire_ai_default_models(filter): the default model of each AI assistant provider,provider => model(anthropic,openai,compatible). They're shown as placeholders in the settings, and used when the Model field is empty. Set your own defaults for the sites you build.snipfire_ai_rate_limit(filter): AI requests per person per hour. Default 30.
The secrets vault has no filters for its keys or values, on purpose. PHP snippets read secrets with snipfire_secret( 'name' ).
Agency hub and network snippets (Agency)
snipfire_hub_timeout(filter): seconds the agency hub waits for each client site, 3 to 60. Default 15.snipfire_hub_sites_changed(action): a site was added to or removed from the hub.snipfire_network_probe_sites(filter): which sites a network snippet's test load goes to, in order. Arguments:$sites(site IDs: the main site and up to two others),$snippet(the snippet being switched on, ornullfor several).snipfire_network_app_config(filter): the editor app's start-up settings on the network screen.snipfire_network_activity,snipfire_network_active_snippets(actions): the network snippets' versions ofsnipfire_activityandsnipfire_active_snippets, so their IDs never reach a site's listeners. Any other set of snippets an add-on keeps (aStorescope) getssnipfire_{scope}_activityandsnipfire_{scope}_active_snippets.
Activity log (Pro)
snipfire_activity_months(filter): months to keep. Default 12.snipfire_log_activity(filter): returnfalseto skip an entry. Arguments:true,$action.
Testing mode (Pro)
snipfire_viewer_sees_tests(filter): whether the current visitor sees snippets in testing mode. Default: users who can edit snippets.
Building an add-on
| Hook | Kind | For |
|---|---|---|
snipfire_boot( Plugin ) |
action | Register add-on features, before any snippet runs |
snipfire_supports_{feature} |
filter (bool) | Declare a feature: testing, consent, scss, activity, hook_finder, team, git, schedule, profiler, selector |
snipfire_supports_selector |
filter (bool) | An add-on places HTML snippets on element:selector. It can render them like any location with Snipfire\Runtime::applies( $meta ) and Snipfire\Runtime::html( $meta, $shape ) |
snipfire_css_compiler |
filter (callable) | Compile SCSS/LESS |
snipfire_viewer_sees_test, snipfire_test_runs_early, snipfire_test_snippet_ran |
filter, filter, action | Testing mode |
snipfire_app_config, snipfire_ui_schema |
filters | What the editor app receives |
snipfire_before_app_script |
action | Enqueue scripts before the editor app |
snipfire_tools_sections |
action | Add a section to the Tools page |
snipfire_classic_fields( $snippet ) |
action | Add fields to the classic editor |
snipfire_run_options |
filter | How it runs choices on the classic screens |
Snipfire runs snippets while plugins are still loading, so it loads active plugins named snipfire-* itself, before running snippets, and fires snipfire_boot. Name your add-on's folder and main file alike, snipfire-something/snipfire-something.php, to be loaded that early. A -premium folder (snipfire-something-premium/snipfire-something.php, as Freemius downloads are named) works too.
REST routes of add-ons can use Snipfire\Rest\Controller::permission( 'view' | 'design' | 'full' ) as their permission callback. The editor app's JavaScript extension points are in Editor app extensions.
Something unclear or missing? Tell us