How to React to Lightspeed Retail (X-Series) Events

How to react to Lightspeed Retail (X-Series) Events

In the Lightspeed Retail (X-Series) system it is possible to set up webhooks. These allow events to be generated and sent to a particular given end point when something happens, for example, inventory is updated. You can then handle these events in the system that is catching them and do whatever is appropriate.

What you'll need

Please install Postman, or alternatively, use your favourite software development environment.

With simplicity in mind, we will use a GUI to connect to Lightspeed Retail (X-Series)'s API for this tutorial. Please feel free to use a software development platform instead, but this tutorial will use a GUI tool called Postman.

You should also know how to connect to Lightspeed Retail (X-Series)'s API. See the Quick Start tutorial.

Overview

Creating Webhooks

The primary way of creating webhooks for OAuth authorized apps is via the /api/webhook endpoint as described here.

Webhooks can also be added “manually” through the hidden API setup page in the Lightspeed Retail (X-Series) backend. This page can be accessed by adding /setup/api at the end of the store’s URL, i.e. https://<<domain_prefix>>.retail.lightspeed.app/setup/api.

More details about which webhooks are available and the payloads they return can be found here.

Key Concerns

When receiving entities from the Lightspeed Retail (X-Series) webhook system, it is important to understand the structure of the payload that is returned from the Lightspeed Retail (X-Series) system so that you can successfully map it to whatever you are trying to acccomplish, for example, updating an external system. The Lightspeed Retail (X-Series) system tends to use UUIDs for ids, so you will need to think about how you are going to map this to what you are trying to accomplish. Hopefully, you can use the UUID, but in some cases that will not be possible and so an alternate approach has to be found. We would highly recommend you document these decisions clearly so that both your team mates and your future self will know what you have done and why.

Testing Webhooks

The easiest way to test webhooks is to use one of the free online tools that listen for API requests, but again, use whatever you are most comfortable using. In these examples we will use RequestBin.

Once you have logged into RequestBin you will be given a URL that can be used as the URL for the webhook.

Now that we have the URL we can create a webhook. Enter the hidden URL https://<<domain_prefix>>.retail.lightspeed.app/setup/api and you should see a page that looks like this:

Webhooks API Main Page

You can now click on the Add Webhook and add a webhook.

Webhooks API Add Page

Adding a Product

If we navigate now to Products https://<<domain_prefix>>.retail.lightspeed.app/products/ we can now add a new product. In this case we will just add a minimal amount of information:

  • Name: A Test Product 001
  • Brand: Generic Brand
  • Product Type: General
  • Supplier: Supplier 1
  • Supplier Code: SUPP-1
  • Supply Price: $2.75

Once you click Save the product will be saved and should appear in the Products list (may require a refresh). In your RequestBin you will see an entry for each outlet, and possibly more, depending on what information you have provided.

The payloads will look similar to this:

{
    "root": {
        "domain_prefix": "weggieincl",
        "environment": "dev",
        "payload": {
            "attributed_cost": "2.75",
            "count": 0,
            "id": "d5ea9f64-40c3-8e43-b314-a5aaf7e80e73",
            "outlet": {
                "id": "0242ac12-0002-11e9-e8c4-659494e196e3",
                "name": "Massey (Sale)",
                "tax_id": "00000000-0002-0002-0002-000000000003",
                "time_zone": "Pacific/Auckland"
            },
            "outlet_id": "0242ac12-0002-11e9-e8c4-659494e196e3",
            "product": {
                "active": true,
                "attributed_cost": null,
                "base_name": "A Test Product 001",
                "button_order": null,
                "categories": [],
                "deleted_at": null,
                "description": "",
                "handle": "atestproduct001",
                "id": "ad85c7cc-cc13-ba7f-ce8a-3602b8fe3d12",
                "name": "A Test Product 001",
                "sku": "10013",
                "source": "USER",
                "source_id": null,
                "source_variant_id": null,
                "supply_price": "2.75",
                "taxes": [
                    {
                        "outlet_id": "0242ac12-0002-11e9-e8c4-659494dde2eb",
                        "tax_id": "00000000-0002-0002-0002-000000000003"
                    },
                    {
                        "outlet_id": "0242ac12-0002-11e9-e8c4-659494e196e3",
                        "tax_id": "00000000-0002-0002-0002-000000000003"
                    },
                    {
                        "outlet_id": "0242ac12-0002-11e9-e8c4-6594953122f6",
                        "tax_id": "00000000-0002-0002-0002-000000000003"
                    },
                    {
                        "outlet_id": "0242ac12-0002-11e9-e8c4-6594953eb634",
                        "tax_id": "00000000-0002-0002-0002-000000000003"
                    }
                ],
                "variant_options": [],
                "variant_parent_id": null
            },
            "product_id": "ad85c7cc-cc13-ba7f-ce8a-3602b8fe3d12",
            "reorder_point": "0",
            "restock_level": "0",
            "version": 1165
        },
        "retailer_id": "00000000-0001-0001-0001-000000000001",
        "type": "inventory.update"
    }
}

The only difference between the payloads will be the outlet, outlet_id and version information in this case.

Understanding the Payload

An initial scan of these payloads may have you wondering what you are going to do with all those ids that are listed. However, a closer look will show that nearly all the ids are expanded to include the details of what the id points to. In this payload we have ids for outlet and product, but we also have details for both the outlet and product referenced by those ids. The only id that is not expanded in this case is the tax id (as this only referenced in the expanded outlet section). If you need the details for the tax id you would need to make a call to https://<<domain_prefix>>.retail.lightspeed.app/api/taxes/02dcd191-ae2b-11e9-f336-cd2e6d864598.

Editing a Product

If you now edit the product you created above and add some stock for Massey and Newmarket you should get new payloads for those two stores with update counts.

{
    "root": {
        "domain_prefix": "weggieincl",
        "environment": "dev",
        "payload": {
            "attributed_cost": "2.75",
            "count": 5,
            "id": "d5ea9f64-40c3-8e43-b314-a5aaf7e80e73",
            "outlet": {
                "id": "0242ac12-0002-11e9-e8c4-659494e196e3",
                "name": "Massey (Sale)",
                "tax_id": "00000000-0002-0002-0002-000000000003",
                "time_zone": "Pacific/Auckland"
            },
            "outlet_id": "0242ac12-0002-11e9-e8c4-659494e196e3",
            "product": {
                "active": true,
                "attributed_cost": null,
                "base_name": "A Test Product 001",
                "button_order": null,
                "categories": [],
                "deleted_at": null,
                "description": "",
                "handle": "atestproduct001",
                "id": "ad85c7cc-cc13-ba7f-ce8a-3602b8fe3d12",
                "name": "A Test Product 001",
                "sku": "10013",
                "source": "USER",
                "source_id": null,
                "source_variant_id": null,
                "supply_price": "2.75",
                "taxes": [
                    {
                        "outlet_id": "0242ac12-0002-11e9-e8c4-659494dde2eb",
                        "tax_id": "00000000-0002-0002-0002-000000000003"
                    },
                    {
                        "outlet_id": "0242ac12-0002-11e9-e8c4-659494e196e3",
                        "tax_id": "00000000-0002-0002-0002-000000000003"
                    },
                    {
                        "outlet_id": "0242ac12-0002-11e9-e8c4-6594953122f6",
                        "tax_id": "00000000-0002-0002-0002-000000000003"
                    },
                    {
                        "outlet_id": "0242ac12-0002-11e9-e8c4-6594953eb634",
                        "tax_id": "00000000-0002-0002-0002-000000000003"
                    }
                ],
                "variant_options": [],
                "variant_parent_id": null
            },
            "product_id": "ad85c7cc-cc13-ba7f-ce8a-3602b8fe3d12",
            "reorder_point": "0",
            "restock_level": "0",
            "version": 1173
        },
        "retailer_id": "00000000-0001-0001-0001-000000000001",
        "type": "inventory.update"
    }
}
{
    "root": {
        "domain_prefix": "weggieincl",
        "environment": "dev",
        "payload": {
            "attributed_cost": "2.75",
            "count": 11,
            "id": "00aec235-dd25-5280-9c41-75678683a9c6",
            "outlet": {
                "id": "0242ac12-0002-11e9-e8c4-659494dde2eb",
                "name": "Newmarket (Base)",
                "tax_id": "00000000-0002-0002-0002-000000000003",
                "time_zone": "Pacific/Auckland"
            },
            "outlet_id": "0242ac12-0002-11e9-e8c4-659494dde2eb",
            "product": {
                "active": true,
                "attributed_cost": null,
                "base_name": "A Test Product 001",
                "button_order": null,
                "categories": [],
                "deleted_at": null,
                "description": "",
                "handle": "atestproduct001",
                "id": "ad85c7cc-cc13-ba7f-ce8a-3602b8fe3d12",
                "name": "A Test Product 001",
                "sku": "10013",
                "source": "USER",
                "source_id": null,
                "source_variant_id": null,
                "supply_price": "2.75",
                "taxes": [
                    {
                        "outlet_id": "0242ac12-0002-11e9-e8c4-659494dde2eb",
                        "tax_id": "00000000-0002-0002-0002-000000000003"
                    },
                    {
                        "outlet_id": "0242ac12-0002-11e9-e8c4-659494e196e3",
                        "tax_id": "00000000-0002-0002-0002-000000000003"
                    },
                    {
                        "outlet_id": "0242ac12-0002-11e9-e8c4-6594953122f6",
                        "tax_id": "00000000-0002-0002-0002-000000000003"
                    },
                    {
                        "outlet_id": "0242ac12-0002-11e9-e8c4-6594953eb634",
                        "tax_id": "00000000-0002-0002-0002-000000000003"
                    }
                ],
                "variant_options": [],
                "variant_parent_id": null
            },
            "product_id": "ad85c7cc-cc13-ba7f-ce8a-3602b8fe3d12",
            "reorder_point": "0",
            "restock_level": "0",
            "version": 1174
        },
        "retailer_id": "00000000-0001-0001-0001-000000000001",
        "type": "inventory.update"
    }
}

Deleting a Product

When a product is deleted in Lightspeed Retail (X-Series), you will get a webhook sent with the UTC datetime deleted_at specified.

{
    "root": {
        "domain_prefix": "weggieincl",
        "environment": "dev",
        "payload": {
            "attributed_cost": "5.50",
            "count": 21,
            "id": "f6141e12-c435-dae2-53e2-d6e06876c61d",
            "outlet": {
                "id": "0242ac12-0002-11e9-e8c4-659494dde2eb",
                "name": "Newmarket (Base)",
                "tax_id": "00000000-0002-0002-0002-000000000003",
                "time_zone": "Pacific/Auckland"
            },
            "outlet_id": "0242ac12-0002-11e9-e8c4-659494dde2eb",
            "product": {
                "active": false,
                "attributed_cost": null,
                "base_name": "A Test Product 002",
                "button_order": null,
                "categories": [],
                "deleted_at": "2022-05-13T20:34:46Z",
                "description": "",
                "handle": "atestproduct002",
                "id": "08d107a2-0c70-683e-9eb7-58a1bfb27b13",
                "name": "A Test Product 002",
                "sku": "10014",
                "source": "USER",
                "source_id": null,
                "source_variant_id": null,
                "supply_price": "5.50",
                "taxes": [
                    {
                        "outlet_id": "0242ac12-0002-11e9-e8c4-659494dde2eb",
                        "tax_id": "00000000-0002-0002-0002-000000000003"
                    },
                    {
                        "outlet_id": "0242ac12-0002-11e9-e8c4-659494e196e3",
                        "tax_id": "00000000-0002-0002-0002-000000000003"
                    },
                    {
                        "outlet_id": "0242ac12-0002-11e9-e8c4-6594953122f6",
                        "tax_id": "00000000-0002-0002-0002-000000000003"
                    },
                    {
                        "outlet_id": "0242ac12-0002-11e9-e8c4-6594953eb634",
                        "tax_id": "00000000-0002-0002-0002-000000000003"
                    }
                ],
                "variant_options": [],
                "variant_parent_id": null
            },
            "product_id": "08d107a2-0c70-683e-9eb7-58a1bfb27b13",
            "reorder_point": "0",
            "restock_level": "0",
            "version": 1201
        },
        "retailer_id": "00000000-0001-0001-0001-000000000001",
        "type": "inventory.update"
    }
}

Updating and Deleting Webhooks

You can update webhooks with the PUT https://<<domain_prefix>>.retail.lightspeed.app/api/webhooks/:webhook_id endpoint and delete a webhook using the DELETE https://<<domain_prefix>>.retail.lightspeed.app/api/webhooks/:webhook_id endpoint.