Building a custom integration, changing how the wishlist button looks, or reacting when a customer saves a product? SaveTo Wishlist gives you WordPress filters and actions for each of these jobs, plus a REST API for reading and changing wishlists. This article lists the hooks and routes that ship with SaveTo Wishlist Lite 1.1.8, with the arguments each one receives and a working example for the common cases.
Before you start
- You need SaveTo Wishlist Lite and WooCommerce active on the site. SaveTo Wishlist Pro and Registry add their own hooks, which are not covered here.
- Every hook name starts with
stwlite_. - Put your code in a small custom plugin or a code snippets plugin. Add placement filters (see below) in a plugin file, not in a theme
functions.phpfile. - Every hook and route in this article exists in SaveTo Wishlist Lite 1.1.8. A hook that is not listed here is not part of the public surface, so do not rely on it.
Hooks
Each hook below lists its type, the arguments it receives, and what it does. When you register a callback that takes more than one argument, pass the argument count as the fourth parameter of add_filter() or add_action(), as the examples show.
Button output
These filters change the wishlist button markup before it prints. The plugin passes the final markup through wp_kses(), so tags and attributes outside its allow list are removed. To allow more tags, use the stwlite_allowed_button_html filter, which receives the allow list array.
| Hook | Type | Arguments | What it does |
|---|---|---|---|
stwlite_wishlist_button_html | Filter | $button_html, $product, $placement, $style, $button_label | Filters the markup of every wishlist button the plugin builds. It runs even when the button is hidden, in which case $button_html is an empty string. |
stwlite_wishlist_button_shop_html | Filter | $button_html, $product, $placement, $style, $color_options | Filters the button on shop and archive pages. |
stwlite_wishlist_button_product_html | Filter | $button_html, $product, $placement, $style, $color_options | Filters the button on single product pages. If your callback returns an empty string, no button prints. |
stwlite_wishlist_cart_button_html | Filter | $button_html | Filters the cart page button that saves the whole cart. |
stwlite_wishlist_button_cart_item_html | Filter | $button_html, $product, $style, $color_options | Filters the per-item button in the cart. |
stwlite_wishlist_button_show_button | Filter | $show (default true), $product | Return false to hide the button for a product. |
stwlite_wishlist_button_allow_product_multiple_lists | Filter | $allow (default true), $product | Return false to remove the “Choose list” drop-down from the button. |
stwlite_before_wishlist_button_shop_page, stwlite_after_wishlist_button_shop_page | Action | None | Fire before and after the shop page button prints. |
stwlite_allowed_button_html | Filter | $allowed_html | Filters the wp_kses() allow list for button markup. |
This example adds a short note after the button for products on sale:
add_filter( 'stwlite_wishlist_button_html', function ( $button_html, $product, $placement, $style, $button_label ) {
if ( '' === $button_html || ! $product instanceof WC_Product || ! $product->is_on_sale() ) {
return $button_html;
}
return $button_html . '<span class="my-sale-note">On sale</span>';
}, 10, 5 );
Button placement, style, and labels
These filters change where the button appears and how it looks. Each one receives the saved setting and returns the value to use.
| Hook | Type | Arguments | What it does |
|---|---|---|---|
stwlite_wishlist_button_shop_placement | Filter | $placement | Placement on shop and archive pages. |
stwlite_wishlist_button_product_placement | Filter | $placement | Placement on single product pages. |
stwlite_wishlist_button_shop_style, stwlite_wishlist_button_product_style, stwlite_wishlist_button_cart_style | Filter | $style | Button style for each page type. |
stwlite_wishlist_button_shop_label, stwlite_wishlist_button_selected_shop_label, stwlite_wishlist_button_cart_label | Filter | $label | Button text for the unsaved state, the saved state, and the cart button. |
stwlite_wishlist_button_shop_color_options, stwlite_wishlist_button_product_color_options, stwlite_wishlist_button_cart_color_options | Filter | $color_options | The color settings array for each page type. |
Shop placements are top_right, top_left, above_title, below_title, above_button, below_button, and disable. Product page placements are top_image_right, top_image_left, top_cart, bottom_cart, inline_cart, top_title, bottom_title, and disable. The plugin reads the placement once, when it boots on the setup_theme hook at priority 100. A placement filter must therefore be added before that point, so register it in a plugin file and not in a theme functions.php file. The style, label, and color filters run each time a button renders, so they work from any code.
add_filter( 'stwlite_wishlist_button_product_placement', function ( $placement ) {
return 'bottom_cart';
} );
For the matching settings screen, see How to customize wishlist button placement and styles in WooCommerce.
Wishlist data
These filters change wishlist data as the plugin reads it. They run on many requests, including the ownership and permission checks, so keep your callbacks fast. Do not change the id, user_id, status, or is_public values, because the plugin reads them to decide who can see a wishlist.
| Hook | Type | Arguments | What it does |
|---|---|---|---|
stwlite_collection_data | Filter | $collection | Filters the raw database row each time one wishlist is loaded by ID, URL code, or name. |
stwlite_collections_data | Filter | $data | Filters the array of wishlist objects returned for a list of wishlists. |
stwlite_single_collection_data | Filter | $data | Filters a single wishlist object after a save, and in the admin wishlist route. |
stwlite_collection_items_data | Filter | $data | Filters the array of item objects for a wishlist. |
stwlite_all_wishlists | Filter | $collections, $params | Filters the list result (data, total, currentPage) before the frontend returns it. |
stwlite_collection_data_to_update | Filter | $data, $params | Filters the column values just before the plugin writes a wishlist to the database. |
stwlite_options | Filter | $options | Filters the options array the plugin passes to the wishlist frontend script. |
This example removes the description from wishlist objects in list responses:
add_filter( 'stwlite_collections_data', function ( $collections ) {
foreach ( $collections as $collection ) {
$collection->description = '';
}
return $collections;
} );
User wishlist access
These filters decide who can read or own a wishlist. For a read request, the plugin first allows administrators with the manage_options capability, then the wishlist owner, then public published wishlists, then its own share rules. Only after those checks does it call stwlite_user_wishlist_access.
| Hook | Type | Arguments | What it does |
|---|---|---|---|
stwlite_user_wishlist_access | Filter | $granted (default null), $data, $user_id, $request | Return true to allow read access, false to refuse it, or null to let the plugin decide. $data is the wishlist object, $user_id is 0 for a guest, and $request is the REST request when there is one. |
stwlite_user_owns_wishlist | Filter | $owns, $collection_id, $user_id, $collection | Whether the current user owns the wishlist. It only runs for a wishlist that exists. |
stwlite_show_all_wishlists_for_admin | Filter | $show (default false) | Return true to let administrators see every customer wishlist in the frontend wishlists route. |
This example lets shop managers read any wishlist:
add_filter( 'stwlite_user_wishlist_access', function ( $granted, $wishlist, $user_id, $request ) {
if ( $user_id && user_can( $user_id, 'manage_woocommerce' ) ) {
return true;
}
return $granted;
}, 10, 4 );
Wishlist actions
Use these actions to react when something changes, for example to sync data to a CRM or write a log entry.
| Hook | Type | Arguments | When it fires |
|---|---|---|---|
stwlite_before_product_added_to_wishlist | Action | $product_id, $quantity, $collection_id, $user_id | Before a product is saved to a wishlist from the button or the plugin’s AI abilities. |
stwlite_after_product_added_to_wishlist | Action | $product_id, $quantity, $collection_id, $user_id, $added | After that save. $added is false when nothing was written. $collection_id is the raw request value and is often 0, which means the default wishlist. |
stwlite_before_product_removed_from_wishlist | Action | $product_id, $collection_id, $variation_ids | Before a product is removed from a wishlist with the button. |
stwlite_after_product_removed_from_wishlist | Action | $product_id, $collection_id, $variation_ids | After a product is removed from a wishlist, from the button, the REST API, or the AI abilities. |
stwlite_before_product_added_to_cart, stwlite_after_product_added_to_cart | Action | $collection_id, $product_id, $quantity, $variation_id | Before and after a wishlist item is moved to the cart. |
stwlite_before_save_collection | Action | $data | Before a wishlist is written. $data is the array of column values. |
stwlite_after_save_collection | Action | $data, $params | After a wishlist is written. $data is the saved wishlist object. |
stwlite_after_insert_collection_item, stwlite_after_update_collection_item | Action | $data | After an item row is inserted or updated. |
stwlite_before_delete_collections | Action | $ids | Before wishlists are deleted. |
stwlite_after_delete_collections | Action | $ids, $deleted | After wishlists are deleted. $deleted is the number of deleted wishlists. |
This example stores the last product a customer saved in their user meta:
add_action( 'stwlite_after_product_added_to_wishlist', function ( $product_id, $quantity, $collection_id, $user_id, $added ) {
if ( ! $added || ! $user_id ) {
return;
}
update_user_meta( $user_id, 'my_last_wishlisted_product', (int) $product_id );
}, 10, 5 );
Admin settings tabs and controls
The settings screen builds its tabs and fields from arrays. Use these filters to add your own tab, section, or field, or to change an existing one.
| Hook | Type | Arguments | What it does |
|---|---|---|---|
stwlite_admin_setting_tabs | Filter | $tabs | Final filter for the full tabs array. |
stwlite_admin_setting_controls | Filter | $controls | Final filter for the full controls array. |
stwlite_admin_data_tables | Filter | $data_tables (default empty array) | Registers data tables for the settings app. |
stwlite_admin_setting_default_tabs, stwlite_admin_setting_default_parent_tabs, stwlite_admin_setting_default_child_tabs | Filter | $tabs | Earlier filters for the tabs array, the top-level tab, and the child tabs. |
stwlite_admin_setting_default_general_tab, stwlite_admin_setting_default_wishlist_page_tab, stwlite_admin_setting_default_wishlist_guest_tab, stwlite_admin_setting_default_wishlist_button_tab, stwlite_admin_setting_default_importer_tab | Filter | $tab | Filters one child tab. The _sections variants of these names filter that tab’s sections. |
stwlite_admin_setting_default_controls | Filter | $controls | Earlier filter for the controls array. |
stwlite_admin_setting_default_general_tab_controls, stwlite_admin_setting_default_wishlist_page_tab_controls, stwlite_admin_setting_default_wishlist_guest_tab_controls, stwlite_admin_setting_default_wishlist_button_tab_controls | Filter | $controls | Filters the controls of one tab. |
Tabs sit under the saveto_wishlist parent key. The child tab keys are general, wishlist_page, wishlist_guest, wishlist_button, and importer. Each child tab holds sort, key, label, icon, and a sections array. The controls array is keyed as $controls[ 'saveto_wishlist' ][ $tab_key ][ $section_key ], and each section holds a list of control arrays. A control has a type (for example switch or select), a label, an id that is the option name, a description, and a default. Controls of type switch use the values yes and no.
This example adds a switch to the General tab. It assumes the section key save_to_wishlist_settings, which is the first section of that tab.
add_filter( 'stwlite_admin_setting_default_general_tab_controls', function ( $controls ) {
$controls['save_to_wishlist_settings'][] = array(
'type' => 'switch',
'label' => __( 'Log wishlist adds', 'my-plugin' ),
'id' => 'my_plugin_log_wishlist_adds',
'description' => __( 'If Yes, write an entry to the debug log when a customer saves a product.', 'my-plugin' ),
'options' => array(
'yes' => __( 'Yes', 'my-plugin' ),
'no' => __( 'No', 'my-plugin' ),
),
'default' => get_option( 'my_plugin_log_wishlist_adds', 'no' ),
);
return $controls;
} );
The settings screen saves each control value through the admin settings route described later in this article. Two dynamic filters let you handle a save or a button yourself:
stwlite_group_settings_{action}is a filter that receives the saved values as$optionswhen the settings payload contains a key namedaction. It returns the response array withstatusandmessage.stwlite_trigger_{action}is a filter that receives the request parameters as$paramswhen the action route is called. It returns an array withstatusandmessage, and an optionalredirectURL.
Frontend translations
| Hook | Type | Arguments | What it does |
|---|---|---|---|
stwlite_frontend_translations | Filter | $i18n | Filters the map of translation keys to translated strings that the plugin passes to the wishlist app. Return the array. A non-array return value is ignored. |
The wishlist page loads these strings twice per request, so your callback should merge in a map and avoid heavy work. This example changes one string. Use a key that already exists in the array.
add_filter( 'stwlite_frontend_translations', function ( $i18n ) {
$i18n['just_now'] = __( 'a moment ago', 'my-plugin' );
return $i18n;
} );
Usage tracking data
When a store has allowed usage tracking, the plugin sends a periodic check-in. These filters let a companion plugin add to that data. Values must be scalars, and they must not carry credentials or personal data.
| Hook | Type | Arguments | What it does |
|---|---|---|---|
stwlite_usage_checkin_data | Filter | $data | Filters the whole check-in payload. |
stwlite_usage_settings | Filter | $settings | Filters the settings name and value pairs in the payload. |
stwlite_usage_effectiveness | Filter | $data, $start, $end | Filters the weekly effectiveness metrics. $start and $end are UTC dates in Y-m-d H:i:s format. |
The “Usage Tracking” setting on the General tab controls consent. If a store sets it to No, the plugin does not report, whatever your filters return.
REST API
All routes use the namespace saveto-wishlist/v1, so the full path is https://your-site.com/wp-json/saveto-wishlist/v1/ followed by the path in the tables. They follow normal WordPress REST authentication. Browser code sends the X-WP-Nonce header for a logged-in user, and other clients can use an application password with HTTP Basic authentication. A refused request returns the rest_forbidden_context error with status 401 for a logged-out caller or 403 for a logged-in one.
Customer routes
These routes power the wishlist button and the wishlist page.
| Method and path | Who can call it | What it does |
|---|---|---|
POST /frontend/endpoints | Logged-in users. Guests are refused unless a permission filter allows them. | Runs one wishlist action chosen by the action parameter. See the action table below. |
GET /frontend/endpoints/wishlists | Same as above. | Returns data, total, and currentPage for wishlists. Customers see their own wishlists. Passing is_public=true lists other customers’ public wishlists. |
GET /frontend/endpoints/wishlist-items | Logged-in users. A guest can read only a public wishlist named by collection_id. | Returns the items of the wishlist in the required collection_id parameter. The caller must also be allowed to view that wishlist, or the route returns a 401 or 403 error. |
POST /frontend/endpoints/guest-wishlist-products | Guests only. A logged-in user gets an error. | Takes a products array of objects with productId, quantity, priority, and addedAt. Returns the matching product data for a wishlist stored in the browser. |
The POST /frontend/endpoints route always answers HTTP 200 with a status field, so check status in the body. A caller who does not own the wishlist gets status set to error. Only users with the manage_options capability can write to another user’s wishlist.
action value | Parameters it reads | Hook it fires first |
|---|---|---|
add_wishlist | name, description, status, is_default, is_public, url_code, emails, user_ids, allowed_roles, allowed_edit_emails | stwlite_before_api_save_collection_add_wishlist with $data |
update_wishlist | id plus any field from add_wishlist. Only fields you send are updated. | stwlite_before_api_save_collection_update_wishlist with $data |
remove_wishlist | ids | None |
add_wishlist_item | collection_id, product_id, variation_id, quantity, priority, notes | stwlite_before_api_save_collection_item_add_wishlist_item with $data |
update_wishlist_item | id plus the fields from add_wishlist_item | stwlite_before_api_save_collection_item_update_wishlist_item with $data |
move_wishlist_item | id, collection_id | stwlite_before_api_save_collection_item_move_wishlist_item with $data |
remove_wishlist_item | collection_id, product_id, variation_id | stwlite_after_product_removed_from_wishlist fires after the removal with $product_id, $collection_id, $variation_ids |
update_wishlist_item_position | lists | None |
This request lists the wishlists of the account that owns the application password:
curl -u "username:application-password" \
"https://your-site.com/wp-json/saveto-wishlist/v1/frontend/endpoints/wishlists"
These filters change who passes the customer route checks and what the guest route returns:
| Hook | Type | Arguments | What it does |
|---|---|---|---|
stwlite_api_user_permissions_check | Filter | $response, $request | Final result for POST /frontend/endpoints and GET /frontend/endpoints/wishlists. Return true or a WP_Error. |
stwlite_api_user_force_access | Filter | $force (default false), $request | Return true to let a logged-out caller through those two routes. |
stwlite_check_any_user_has_permission | Filter | $response, $request | Final result for the wishlist-items route. |
stwlite_check_any_user_has_permission_validation | Filter | $valid (default false), $data, $request | Return true to let a logged-out caller read a wishlist that is not public. |
stwlite_check_guest_permission | Filter | $response, $request | Final result for the guest-wishlist-products route. |
stwlite_guest_wishlist_products_data | Filter | $products, $product_data | Filters the product list that the guest route returns. |
Admin routes
The routes below serve the plugin’s own admin screens, and they may change between releases. Prefer the hooks above for integrations. Every admin route requires the manage_options capability. Use the stwlite_api_admin_permissions_check filter ($response, $request) to change that, and take care when you widen access, because these routes can write site options and delete wishlists.
| Method and path | What it does |
|---|---|
POST /admin/wishlists | Creates or updates a wishlist from the request body. It returns a 422 error if you add items that are not saved yet to a wishlist whose status is draft. |
DELETE /admin/wishlists | Deletes the wishlists in the ids parameter. The response lists count and skipped. A wishlist is skipped when it is the last wishlist of its owner. |
GET /admin/wishlists/collections | Returns a page of wishlists with data, total, and currentPage. |
GET /admin/wishlists/collection/{id} | Returns one wishlist by numeric ID. |
GET /admin/wishlists/items | Placeholder route that returns an empty data list. |
POST /admin/settings/save | Saves settings. The body is a list of objects with key and value. A value of true or false is stored as yes or no. |
POST /admin/settings/action | Runs the stwlite_trigger_{action} filter for the action parameter. |
GET /admin/importer/sources | Lists the wishlist plugins the importer detected on the site. |
GET /admin/importer/preview/{source} | Returns the item counts and a preview for one source. |
POST /admin/importer/import | Starts an import. It takes source and an optional options object (merge_existing, preserve_default, preserve_privacy, batch_number, job_id). A user can start one import every 300 seconds. |
GET /admin/importer/import/{job_id} | Returns the progress of an import job. |
DELETE /admin/importer/import/{job_id} | Cancels an import job. |
The built-in import sources are yith, ti, moreconvert, and wpc. Use the stwlite_available_importers filter, which receives and returns an array that maps a source slug to an importer class name, to register another one.
Frequently asked questions
Why does my placement filter do nothing?
The plugin reads the placement when it boots on the setup_theme hook at priority 100. Add the filter from a plugin file. A theme functions.php file loads after that point.
Why does my button markup lose a tag or attribute?
The plugin runs the button markup through wp_kses(). Add the tag or attribute to the allow list with the stwlite_allowed_button_html filter.
Why does the save route return HTTP 200 for a refused request?
POST /frontend/endpoints always answers with HTTP 200 and puts the result in the status field of the body. Read status and message instead of the HTTP code.
What’s next
- How to extend SaveTo Wishlist Pro with hooks and the REST API
- How to extend SaveTo Wishlist Registry with hooks and the REST API
- How to use AI abilities in SaveTo Wishlist
Need help?
If you have a question or run into any issues, we’re here to help.
- Premium users: Open a support ticket
- Free users: Visit our community forum
