---
title: "Lightspeed POS developer reference"
url: "https://kestrelwp.com/docs/lightspeed-pos-developer-reference/"
description: "Code snippets, filters and background job details for extending Lightspeed POS."
published: "2026-10-06T20:27:17+00:00"
modified: "2026-10-06T20:27:17+00:00"
---

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](/docs/get-started-with-lightspeed-pos/) and the troubleshooting pages for [X-Series](/docs/troubleshoot-lightspeed-x-series/) and [R-Series](/docs/troubleshoot-lightspeed-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](https://github.com/kestrelcommerce/snippets), under `woo-extensions/lightspeed/`. Follow our guide to [safely adding custom code to WooCommerce](https://kestrelwp.com/blog/add-custom-code-to-woocommerce/) before using one, and test on a staging site. All of them are for R-Series.

| Snippet | What it does |
|---|---|
| [`add-woo-shipping-to-lightspeed-orders.php`](https://github.com/kestrelcommerce/snippets/blob/main/woo-extensions/lightspeed/add-woo-shipping-to-lightspeed-orders.php) | Adds 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.php`](https://github.com/kestrelcommerce/snippets/blob/main/woo-extensions/lightspeed/add-woo-fees-to-lightspeed-orders.php) | Adds WooCommerce order fees to the sale as a line |
| [`add-woo-order-notes-to-lightspeed-receipt.php`](https://github.com/kestrelcommerce/snippets/blob/main/woo-extensions/lightspeed/add-woo-order-notes-to-lightspeed-receipt.php) | Prints the customer’s order note on the Lightspeed receipt |
| [`skip-lightspeed-sync-for-unlinked-products.php`](https://github.com/kestrelcommerce/snippets/blob/main/woo-extensions/lightspeed/skip-lightspeed-sync-for-unlinked-products.php) | Skips 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.php`](https://github.com/kestrelcommerce/snippets/blob/main/woo-extensions/lightspeed/set-custom-sale-price.php) | Sets the WooCommerce sale price from another named Lightspeed price level (case-sensitive) when it’s lower |
| [`update-woo-tax-class.php`](https://github.com/kestrelcommerce/snippets/blob/main/woo-extensions/lightspeed/update-woo-tax-class.php) | Maps a Lightspeed tax class to a WooCommerce tax class on import, for example Zero rate |
| [`debugging-new-products-not-importing.php`](https://github.com/kestrelcommerce/snippets/blob/main/woo-extensions/lightspeed/debugging-new-products-not-importing.php) | Logs 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:

| Hook | What it does |
|---|---|
| `wclsi_poll` | Checks Lightspeed for changed items and updates linked products |
| `check_for_new_ls_prods` | Checks Lightspeed for new items |
| `wclsi_ls_attribute_sets_poll` | Checks Lightspeed for new matrix attribute sets |
| `wclsi_ls_customer_ids_poll` | Reads Lightspeed customers for matching, daily |
| `wclsi_taxonomy_poll` | Imports Lightspeed manufacturers as brands, daily |
| `wclsi_sync_changes_to_lightspeed` | Sends 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

- [Sync sales to Lightspeed R-Series](/docs/sync-sales-to-lightspeed-r-series/)
- [Sync orders to Lightspeed X-Series](/docs/sync-orders-to-lightspeed-x-series/)
- [Troubleshoot Lightspeed R-Series](/docs/troubleshoot-lightspeed-r-series/)
- [Troubleshoot Lightspeed X-Series](/docs/troubleshoot-lightspeed-x-series/)