Lightspeed POS developer reference

Code snippets, filters and background job details for extending Lightspeed POS.

Beka Rice Avatar

Written by

Last updated


Code snippets, filters and background job details for developers extending Lightspeed POS for WooCommerce. Everything here is for custom code: for setup and everyday fixes, start with Get started with Lightspeed POS and the troubleshooting pages for X-Series and R-Series.

Most hooks apply to one series only, so each one below says which. The plugin also ships its hooks reference in the docs/hooks/ folder of the plugin download.

When you’d use this

  • Add WooCommerce shipping or fees to R-Series sales, or change what a sale sends.
  • Change which order statuses create R-Series sales.
  • Turn on partial refund sync for X-Series.
  • Tune polling and batch sizes for a large X-Series catalog.
  • Inspect or run background jobs from the command line.

Code snippets

Ready-made snippets are in Kestrel’s public snippets repository at github.com/kestrelcommerce/snippets, under woo-extensions/lightspeed/. Follow our guide to safely adding custom code to WooCommerce before using one, and test on a staging site. All of them are for R-Series.

SnippetWhat it does
add-woo-shipping-to-lightspeed-orders.phpAdds WooCommerce shipping to the Lightspeed sale as a line for a placeholder "Woo Shipping" item, so the sale and order totals match
add-woo-fees-to-lightspeed-orders.phpAdds WooCommerce order fees to the sale as a line
add-woo-order-notes-to-lightspeed-receipt.phpPrints the customer’s order note on the Lightspeed receipt
skip-lightspeed-sync-for-unlinked-products.phpSkips syncing an order that contains any product not linked to Lightspeed, instead of syncing a partial sale. The order gets the snippet’s note and the plugin’s "Not synced to Lightspeed" note.
set-custom-sale-price.phpSets the WooCommerce sale price from another named Lightspeed price level (case-sensitive) when it’s lower
update-woo-tax-class.phpMaps a Lightspeed tax class to a WooCommerce tax class on import, for example Zero rate
debugging-new-products-not-importing.phpLogs each item the product changes job sees, to check whether a late creation time makes new items miss the import window, and shows how to widen it

R-Series filters

Change the sale before it’s sent

kestrel_lightspeed_pos_r_series_sale_sync_payload filters the complete sale payload (array $data, WC_Order $order) right before it’s sent to Lightspeed. It also runs for refund payloads.

add_filter( 'kestrel_lightspeed_pos_r_series_sale_sync_payload', function( $data, $order ) {
	// Inspect or change $data before it's sent to Lightspeed.
	return $data;
}, 10, 2 );

Add sale lines

wclsi_build_sale_lines filters the sale lines (array $sale_lines, float $shipping_total, int $ls_tax_id, WC_Order $order). $sale_lines['SaleLine'] holds the lines sent; $sale_lines['unlinked_items'] holds the order items left off because they aren’t linked. wclsi_build_partial_refund_lines does the same for partial refunds (array $sale_line, int $ls_tax_id, WC_Order_Refund $refund, WC_Order $order).

When you add a line, adjust the payment to match with wclsi_calc_sale_payment (float $payment, float $shipping_total, float $shipping_tax), which by default leaves shipping out, or wclsi_fees_to_remove_from_ls_payment (array $fees_to_remove, WC_Order $order), which lists the fee items taken off the payment. The shipping and fees snippets show the pattern.

Choose which order statuses create sales

wclsi_sales_sync_statuses filters the statuses (without the wc- prefix) that create a sale. It defaults to the Order statuses setting, or WooCommerce’s paid statuses.

add_filter( 'wclsi_sales_sync_statuses', function( $statuses ) {
	$statuses[] = 'on-hold';
	return $statuses;
} );

Map taxes and customers

  • wclsi_set_ls_tax_rate filters the Lightspeed tax category ID used for an order ($ls_tax_category_id, WC_Order $order), for example to map a rate the export didn’t match.
  • wclsi_customer_found filters the Lightspeed customer ID found for an order’s billing email (int $ls_customer_id, WC_Order $order).
  • wclsi_customer_poll_interval sets how often, in seconds, the plugin reads Lightspeed customers for matching (default 86400, one day).

Widen the look-back for new products

wclsi_get_look_back_time sets how many seconds before the last load the New Lightspeed products job looks for new items (default 60). Lightspeed sometimes timestamps an item later than it was created, so it can miss the window. Support commonly uses about four hours:

add_filter( 'wclsi_get_look_back_time', function( $seconds ) {
	return 4 * HOUR_IN_SECONDS;
} );

Storefront stock checks

  • kestrel_lightspeed_pos_r_series_pull_inventory_on_add_to_cart decides whether to fetch Lightspeed stock when a product is added to the cart (bool $pull_inventory, int $product_id, int $quantity, int $variation_id). It’s false for detected crawlers. Return false to skip the call.
  • kestrel_lightspeed_pos_r_series_storefront_api_timeout sets the timeout, in seconds, for Lightspeed calls made while serving a shopper.
  • wclsi_inventory_sync_error filters the message shoppers see when the stock check fails.

X-Series filters

Change the sale before it’s sent

kestrel_lightspeed_pos_x_series_sale_sync_payload filters the complete sale payload (array $sale_data, WC_Order $order) right before it’s sent to Lightspeed. If you remove register_sale_payments, the payment is recorded on a later sync instead.

add_filter( 'kestrel_lightspeed_pos_x_series_sale_sync_payload', function( $sale_data, $order ) {
	// Inspect or change $sale_data before it's sent to Lightspeed.
	return $sale_data;
}, 10, 2 );

Sync partial refunds on X-Series

Partial refunds aren’t synced by default. kestrel_lightspeed_pos_x_series_sync_partial_refunds (default false) turns on an experimental sync that records each partial refund as a return sale linked to the original sale. The plugin reads it once while WordPress loads plugins, so add it in a must-use plugin or another plugin’s main file; a theme’s functions.php loads too late. Test it on a staging site first.

add_filter( 'kestrel_lightspeed_pos_x_series_sync_partial_refunds', '__return_true' );

The order notes say "Partial refund synced to Lightspeed (Return Sale ID: …)" or "Lightspeed partial refund sync failed: …".

Tune polling and batch sizes

For large catalogs or slow servers:

  • kestrel_lightspeed_pos_x_series_polling_products_poll_size and kestrel_lightspeed_pos_x_series_polling_customers_poll_size: how many items each polling run checks (default 5, minimum 1).
  • kestrel_lightspeed_pos_x_series_pull_products_batch_size and kestrel_lightspeed_pos_x_series_pull_customers_batch_size: how many items each import batch processes (default 50, minimum 1).
  • kestrel_lightspeed_pos_x_series_image_download_batch_size: how many images each image download run fetches (default 10).
add_filter( 'kestrel_lightspeed_pos_x_series_pull_products_batch_size', function( $batch_size ) {
	return 25;
} );

Keep the activity log longer

kestrel_lightspeed_pos_x_series_activity_log_retention_period sets how long activity log records are kept (a DateInterval, default two months), and kestrel_lightspeed_pos_x_series_activity_log_cleanup_interval how often the cleanup runs (default one month).

add_filter( 'kestrel_lightspeed_pos_x_series_activity_log_retention_period', function( $period ) {
	return new DateInterval( 'P6M' );
} );

Background jobs and WP-CLI

Both series run their background work through WooCommerce’s Action Scheduler, which needs WP-Cron (or a system cron calling wp-cron.php) to run.

R-Series jobs use the wclsi group:

HookWhat it does
wclsi_pollChecks Lightspeed for changed items and updates linked products
check_for_new_ls_prodsChecks Lightspeed for new items
wclsi_ls_attribute_sets_pollChecks Lightspeed for new matrix attribute sets
wclsi_ls_customer_ids_pollReads Lightspeed customers for matching, daily
wclsi_taxonomy_pollImports Lightspeed manufacturers as brands, daily
wclsi_sync_changes_to_lightspeedSends a queued WooCommerce product change to Lightspeed

X-Series job hooks start with kestrel_lightspeed_pos_x_series_.

# R-Series: pending, failed, and run now
wp action-scheduler list --status=pending --group=wclsi
wp action-scheduler list --status=failed --group=wclsi
wp action-scheduler run --group=wclsi

R-Series database tables

The R-Series integration keeps its link data in these tables (with your database prefix): wclsi_items, wclsi_item_shops, wclsi_item_prices, wclsi_item_images, wclsi_item_e_commerce, wclsi_item_categories, wclsi_item_attribute_sets, wclsi_customers and wclsi_tax_lookup.

In wclsi_items, item_id is the Lightspeed item ID (item_matrix_id for matrix items), wc_prod_id the linked WooCommerce product, wclsi_import_date and wclsi_last_sync_date the import and last update times, and wclsi_is_synced whether the product is linked. To list items loaded but never imported:

SELECT * FROM wp_wclsi_items WHERE wc_prod_id IS NULL;

The Import date and Last update columns in the Product Importer show Lightspeed’s own timestamps, not your server’s time.

Common issues

My filter has no effect

Check the series: an r_series or wclsi_ hook does nothing on an X-Series connection, and the reverse. Filters that change sales only run when sales or order sync is on.

The shipping snippet makes the sale unbalanced

The snippet adds a sale line and raises the payment by the shipping total. Check that the placeholder item’s ID is set and that no other code changes the payment through wclsi_calc_sale_payment.

Keep learning