Hooks and Filters
WP Stories provides action hooks and filter hooks that developers can use to customize plugin behavior. All hooks listed below are verified in the plugin source code.
Filter Hooks
Section titled “Filter Hooks”wp_stories_enable_image_optimization
Section titled “wp_stories_enable_image_optimization”Controls whether uploaded story images are automatically optimized (resized and compressed).
Default: true
File: public/class-wp-stories-submit-user-stories.php
add_filter( 'wp_stories_enable_image_optimization', function( $enabled, $attachment_id ) { // Disable optimization for a specific attachment return $enabled;}, 10, 2 );wp_stories_image_quality
Section titled “wp_stories_image_quality”Sets the JPEG compression quality for optimized story images.
Default: 85
File: public/class-wp-stories-submit-user-stories.php
add_filter( 'wp_stories_image_quality', function( $quality ) { return 75; // Lower quality for smaller file sizes} );wp_stories_max_image_dimension
Section titled “wp_stories_max_image_dimension”Sets the maximum width or height (in pixels) for optimized story images. Images larger than this are scaled down proportionally.
Default: 2048
File: public/class-wp-stories-submit-user-stories.php
add_filter( 'wp_stories_max_image_dimension', function( $dimension ) { return 1920;} );wp_stories_min_image_size_for_optimization
Section titled “wp_stories_min_image_size_for_optimization”Sets the minimum file size (in bytes) before optimization kicks in. Images smaller than this threshold are left untouched.
Default: 500000 (500 KB)
File: public/class-wp-stories-submit-user-stories.php
add_filter( 'wp_stories_min_image_size_for_optimization', function( $size ) { return 200000; // Optimize images over 200 KB} );wp_stories_allowed_image_types
Section titled “wp_stories_allowed_image_types”Defines the allowed image file extensions for story uploads.
Default: ['jpg', 'jpeg', 'png', 'gif']
File: includes/wp-stories-functions.php
add_filter( 'wp_stories_allowed_image_types', function( $types ) { $types[] = 'webp'; return $types;} );wp_stories_allowed_video_types
Section titled “wp_stories_allowed_video_types”Defines the allowed video file extensions for story uploads.
Default: ['mp4', 'mov', 'wmv', 'mpeg', '3gp']
File: includes/wp-stories-functions.php
add_filter( 'wp_stories_allowed_video_types', function( $types ) { $types[] = 'webm'; return $types;} );wp_stories_allowed_cvr_img_types
Section titled “wp_stories_allowed_cvr_img_types”Defines the allowed image file extensions for story collection cover images.
Default: ['png', 'gif', 'jpg', 'jpeg']
File: includes/wp-stories-functions.php
add_filter( 'wp_stories_allowed_cvr_img_types', function( $types ) { return array( 'jpg', 'jpeg', 'png' );} );wp_stories_allowed_cvr_img_size
Section titled “wp_stories_allowed_cvr_img_size”Sets the maximum file size (in bytes) for story collection cover images.
File: includes/wp-stories-functions.php
add_filter( 'wp_stories_allowed_cvr_img_size', function( $bytes ) { return 2 * 1024 * 1024; // 2 MB} );wp_stories_avatar_url
Section titled “wp_stories_avatar_url”Filters the avatar URL used for story circles.
File: includes/wp-stories-functions.php
add_filter( 'wp_stories_avatar_url', function( $avatar_url, $user_id, $size ) { // Return a custom avatar URL return $avatar_url;}, 10, 3 );wp_stories_author_name
Section titled “wp_stories_author_name”Filters the display name shown on story circles.
File: includes/wp-stories-functions.php
add_filter( 'wp_stories_author_name', function( $name, $user_id ) { // Customize the displayed author name return $name;}, 10, 2 );wp_stories_login_url
Section titled “wp_stories_login_url”Filters the login URL displayed to logged-out users in the story activity feed widget.
Default: wp_login_url()
File: public/widgets/class-wp-stories-activity-feed-widget.php
add_filter( 'wp_stories_login_url', function( $url ) { return home_url( '/custom-login/' );} );wp_stories_template_settings
Section titled “wp_stories_template_settings”Filters the BuddyPress template used for the stories profile tab.
Default: 'members/single/plugins'
File: public/class-wp-stories-public.php
add_filter( 'wp_stories_template_settings', function( $template ) { return 'members/single/custom-template';} );wp_stories_PeepSoUser_per_page
Section titled “wp_stories_PeepSoUser_per_page”Sets the number of PeepSo user stories loaded per page (pagination).
Default: 10
File: public/class-wp-stories-public.php, public/peepso-user-stories-view.php
add_filter( 'wp_stories_PeepSoUser_per_page', function( $per_page ) { return 20;} );wp_story_activity_count
Section titled “wp_story_activity_count”Sets the number of stories fetched for activity feeds.
Default: 50
File: includes/wp-stories-functions.php
add_filter( 'wp_story_activity_count', function( $count ) { return 100;} );wp_stories_delete_stories_attachement
Section titled “wp_stories_delete_stories_attachement”Sets the number of days after which expired story attachments are permanently deleted.
Default: 7
File: includes/wp-stories-functions.php
add_filter( 'wp_stories_delete_stories_attachement', function( $days ) { return 14; // Keep attachments for 14 days} );wp_stories_story_ids_select_ajax
Section titled “wp_stories_story_ids_select_ajax”Controls whether the story IDs selector in the Story Box metabox uses AJAX loading.
Default: false
File: admin/wp-stories-metabox.php
add_filter( 'wp_stories_story_ids_select_ajax', '__return_true' );wb_stories_loaders
Section titled “wb_stories_loaders”Controls whether skeleton loading placeholders are shown while stories load.
Default: true
File: public/class-wp-stories-public.php
add_filter( 'wb_stories_loaders', '__return_false' );wb_stories_loaders_count
Section titled “wb_stories_loaders_count”Sets the number of skeleton loader placeholders displayed while stories are loading.
Default: 10
File: public/class-wp-stories-public.php
add_filter( 'wb_stories_loaders_count', function( $count ) { return 5;} );Action Hooks
Section titled “Action Hooks”wp_stories_before_story_submit
Section titled “wp_stories_before_story_submit”Fires before a new story is saved to the database. Use this to run custom validation or pre-processing.
File: public/class-wp-stories-submit-user-stories.php
add_action( 'wp_stories_before_story_submit', function() { // Custom logic before story is saved} );wp_stories_before_item_delete
Section titled “wp_stories_before_item_delete”Fires before a single story item is deleted via AJAX.
File: public/class-wp-stories-submit-user-stories.php
add_action( 'wp_stories_before_item_delete', function() { // Log item deletion or run cleanup} );wp_stories_before_story_delete
Section titled “wp_stories_before_story_delete”Fires before an entire story post is deleted via AJAX.
File: public/class-wp-stories-submit-user-stories.php
add_action( 'wp_stories_before_story_delete', function() { // Run cleanup tasks before story removal} );wp_stories_tab_contents
Section titled “wp_stories_tab_contents”Removed in 2.4.0. The legacy admin wrapper this fired from was replaced by the card-panel admin, and the hook went with it. It is listed here only so anyone who used it knows where it went.
Replace it with either:
wp_stories_admin_setting_tabs- a filter over the whole tab registry, to add a tab of your own (see below), or- the per-tab actions
wp_stories_general_settings_options,wp_stories_display_options_options,wp_stories_user_publishing_options,wp_stories_style_options_options,wp_stories_buddypress_integration,wp_stories_peepso_integration- to append fields to a tab that already exists.
wp_stories_general_settings_options
Section titled “wp_stories_general_settings_options”Fires after the default general settings options are rendered. Use this to add custom options to the General tab.
File: admin/inc/wp-stories-general-tab.php
add_action( 'wp_stories_general_settings_options', function( $settings ) { // Add custom fields to General Settings}, 10, 1 );wp_stories_display_options_options
Section titled “wp_stories_display_options_options”Fires after the default display options are rendered. Use this to add custom options to the Display Options tab.
File: admin/inc/wp-stories-display-options-tab.php
add_action( 'wp_stories_display_options_options', function( $options ) { // Add custom fields to Display Options}, 10, 1 );wp_stories_style_options_options
Section titled “wp_stories_style_options_options”Fires after the default styling options are rendered. Use this to add custom options to the Styling Options tab.
File: admin/inc/wp-stories-style-options-tab.php
add_action( 'wp_stories_style_options_options', function( $options ) { // Add custom fields to Styling Options}, 10, 1 );wp_stories_user_publishing_options
Section titled “wp_stories_user_publishing_options”Fires after the default user publishing options are rendered. Use this to add custom options to the User Publishing tab.
File: admin/inc/wp-stories-user-publishing-options-tab.php
add_action( 'wp_stories_user_publishing_options', function( $options ) { // Add custom fields to User Publishing Options}, 10, 1 );wp_stories_buddypress_integration
Section titled “wp_stories_buddypress_integration”Fires after the BuddyPress integration settings are rendered. Use this to add custom BuddyPress-related options.
File: admin/inc/wp-stories-integrations-tab.php
add_action( 'wp_stories_buddypress_integration', function( $integrations ) { // Add custom BuddyPress integration settings}, 10, 1 );wp_stories_peepso_integration
Section titled “wp_stories_peepso_integration”Fires after the PeepSo integration settings are rendered. Use this to add custom PeepSo-related options.
File: admin/inc/wp-stories-integrations-tab.php
add_action( 'wp_stories_peepso_integration', function( $integrations ) { // Add custom PeepSo integration settings}, 10, 1 );Access control (2.4.2+)
Section titled “Access control (2.4.2+)”wp_stories_user_can_view_story_item
Section titled “wp_stories_user_can_view_story_item”Filter. includes/wp-stories-functions.php:189
The single access check for member stories. Every surface that serves a story - the tray, the story-media AJAX endpoint, the view recorder and the like toggle - resolves through it, so this is the one place to change who may see what.
It already enforces: post type, post_status = publish (a deleted story is moved
to draft), the Story Expiry window, that the item exists and is not disabled,
and the item’s per-item visibility.
// Let editors read every member story, including private ones.add_filter( 'wp_stories_user_can_view_story_item', function ( $allowed, $post_id, $index, $user_id ) { if ( ! $allowed && user_can( $user_id, 'edit_others_posts' ) ) { return true; } return $allowed;}, 10, 4 );| Param | Type | Notes |
|---|---|---|
$allowed |
bool |
The decision so far. |
$post_id |
int |
Story post ID. |
$index |
int|null |
Item index in wb_story_items; null when checking the story itself. |
$user_id |
int |
User being checked. 0 for logged-out visitors. |
Returning
trueunconditionally re-opens the access bugs fixed in 2.4.2. Widen deliberately, and always keep a capability or ownership test in the condition.
Settings (2.4.2+)
Section titled “Settings (2.4.2+)”wp_stories_settings_schema
Section titled “wp_stories_settings_schema”Filter. includes/wp-stories-settings.php:181
The schema every option group is sanitized against. Register your own fields here so they get typed validation instead of the generic text fallback.
Types: flag, enum_flag, int (with min/max/default), enum, color,
list_enum, list_role, list_ext.
add_filter( 'wp_stories_settings_schema', function ( $schema ) { $schema['wp_stories_display_options']['my_addon_limit'] = array( 'type' => 'int', 'min' => 1, 'default' => 20, ); return $schema;} );Fields you do not declare are still preserved on save - they are passed through a
recursive text sanitizer rather than dropped - so an add-on that appends fields
via the wp_stories_*_options actions keeps working without registering here.
wp_stories_sanitized_settings
Section titled “wp_stories_sanitized_settings”Filter. includes/wp-stories-settings.php:339
The sanitized array, immediately before it is written. Args: $output, $input,
$group.
Story cleanup (2.4.2+)
Section titled “Story cleanup (2.4.2+)”wp_stories_delete_stories_batch_size
Section titled “wp_stories_delete_stories_batch_size”Filter. includes/wp-stories-functions.php:1881. Default 200.
How many stories per post type the daily cleanup handles in one run. Whatever is left over is picked up by the next run.
wp_stories_story_attachment_ids
Section titled “wp_stories_story_attachment_ids”Filter. includes/wp-stories-functions.php:1979. Args: $attachment_ids, $post_id.
The attachments deleted alongside a story. The default list contains only media
whose post_parent is the story, so images picked from the Media Library are
never removed.
Adding IDs here permanently deletes those attachments. Do not add media that might be used elsewhere on the site.
wp_stories_delete_stories_attachement
Section titled “wp_stories_delete_stories_attachement”Filter. Historic name, kept for back-compat. It controls the retention period in days, despite reading like an attachment toggle. Since 2.4.2 the period is a normal setting (Displaying Options > Delete After Expiry), and this filter applies on top of it.
Uploads
Section titled “Uploads”wp_stories_web_playable_extensions
Section titled “wp_stories_web_playable_extensions”Filter. includes/wp-stories-functions.php:1621. Args: $list, $type.
The extensions considered browser-playable for image or video.
wp_stories_restrict_to_web_playable
Section titled “wp_stories_restrict_to_web_playable”Filter. includes/wp-stories-functions.php:1638. Args: false, $type.
Return true to restrict uploads to the browser-playable list for that type.
wp_stories_admin_setting_tabs
Section titled “wp_stories_admin_setting_tabs”Filter. includes/admin/class-wpst-admin.php:106. Args: $tabs.
The admin tab registry. Each entry is keyed by tab slug with label, icon
(dashicon class) and group (main, settings or account).
add_filter( 'wp_stories_admin_setting_tabs', function ( $tabs ) { $tabs['my-addon'] = array( 'label' => __( 'My Add-on', 'my-addon' ), 'icon' => 'dashicons-admin-generic', 'group' => 'settings', ); return $tabs;} );wpst_install_catalog
Section titled “wpst_install_catalog”Filter. includes/admin/class-wpst-plugin-installer.php:207. Args: $catalog.
The Discover tab’s install catalog, keyed by plugin slug.
The catalog is an allow-list, and it is the reason the install endpoint cannot be pointed at arbitrary code. Anything you add here becomes installable by an administrator in one click, and its package must resolve on the store host. Do not add entries from untrusted input.
wpst_license_api_sslverify
Section titled “wpst_license_api_sslverify”Filter. edd-license/edd-plugin-license.php:120. Default true.
sslverify on the EDD licence API call.
PeepSo
Section titled “PeepSo”wp_stories_PeepSoUser_per_page
Section titled “wp_stories_PeepSoUser_per_page”Filter. public/peepso-user-stories-view.php:157 and
public/class-wp-stories-public.php:1590. Default 10.
Viewers loaded per page in the PeepSo story-viewer list.

