Loss Prevention on Lane POS - Integration Guide
Last updated by Sahil Pahuja on March 7th, 2025
Overview
This document provides vendors with insights into the Edgify API endpoints for implementing Loss Prevention on Lane POS. Its purpose is to offer an understanding of the functionality and facilitate the estimation of integration efforts.
Who Should Read This Document?
This document is intended for developers and teams responsible for integrating the Edgify Agent with POS application software. It can also be helpful to non-technical individuals who wish to understand the relationship and flow between the Edgify Agent, hardware, and customers in a store environment.
Solution Description
The Edgify Agent is the active software component in the Edgify solution, with which the POS application will communicate. Outputs prediction objects when called by the application software.
The Edgify Agent can be installed on Microsoft Windows OS, where it will run as a service, or on most modern Linux distros that support Docker, where it runs within a Docker container.
API Flow for Integration by the POS App
POST /events - Session State Management
POST http://localhost:8090/api/v2/events
This endpoint (with various payloads) should be called upon the beginning and end of each shopper session, as well as during meaningful events in the flow. It will notify Edgify upon important events in the POS flow so that Edgify can analyze or ignore certain shopper or attendant interactions throughout the shopper's session.
First interaction: Notifies the beginning of a shopper session.
{
"name": "StartCustomerSession",
"transactionId": "12345"
"shopperId": "<shopper-loyalty-card-id>"
"assistantId": "<assistant-001>"
}
Notes:
- transactionId is not a mandatory field
- shopperId or assistantId are not mandatory fields.
- shopperId is used for hybrid tills on the Lane PoS.
This API should be called when the shopper starts interacting with the POS. This endpoint allows the system to start analyzing the video feed from the Edgify camera. The "transactionId" field is optional in case you want to correlate the POS application's transaction ids with Edgify's session ids. You can also pass this transaction id at a later time during the session (during the subsequent calls below such as the payment or end of the session).
Event: Pay Now: Notifies the shopper wishes to pay
{
"name": "PayNow",
"transactionId": "12345"
}
Should be called when the shopper opts for the “pay now” option. This endpoint allows the system to process a Loss Prevention analysis prior to the payment process. Edgify will be able to understand the context and will know not to trigger motion alerts while the shopper is handling payments. The "transactionId" field is optional in case you want to correlate the POS application's transaction ids with Edgify's session ids. You can pass this transaction id at any time during the session.
Event: Last interaction: Marks the end of a session.
{
"name": "StopCustomerSession",
"transactionId": "12345"
}
This API should be called when the shopper stops interacting with the POS. This endpoint allows the system to stop analyzing the video feed from the Edgify camera and regard the current feed as a single shopper session. It is best to use the “Thank You” screen. Note: If this event is not passed after a configurable time period, the Edgify system will automatically handle a session timeout and close the session. The "transactionId" field is optional in case you want to correlate the POS application's transaction IDs with Edgify's session IDs. You can pass this transaction ID at any time during the session.
Event: Start intervention: Notifies the beginning of a manager intervention
{
"name": "StartIntervention",
"transactionId": "12345"
"assistantId": "<assistant-001>"
}
Should be called when the attendant intervenes with the shopper session. This endpoint allows the system to identify all the following actions as attendant actions and not raise loss prevention issues during the attendant intervention. The "transactionId" field is optional in case you want to correlate the POS application's transaction ids with Edgify's session ids. You can pass this transaction id at any time during the session.
Event: Stop intervention: Marks the end of a manager intervention.
{
"name": "StopIntervention",
"transactionId": "12345"
}
Should be called when the attendant stops the intervention mode with the shopper session. This endpoint allows the system to identify the attendant intervention. The "transactionId" field is optional in case you want to correlate the POS application's transaction ids with Edgify's session ids. You can pass this transaction id at any time during the session.
POST /capture-and-save
Endpoint
POST http://localhost:8090/api/v2/capture-and-save
Description :
This endpoint's primary function is to capture an image from the camera feed and save it with a unique sample ID. It records essential item details, including barcode, category, weight, price, and image source. Upon successful storage, the system returns a unique sample ID.
Request Body Example:
{
"barcode": "ABC1234",
"captureTrigger": "barcode",
"category": "Drinks",
"count": 1,
"family": "Milk",
"groupId": "b932de54-41ca-4f5a-99d5-b9db779f8888",
"imgSource": "ip-camera-SCO-1",
"label": "4201",
"labelTrigger": "RegularMenuSelection",
"name": "Org Milk",
"price": 0.95,
"totalPrice": 0.95,
}
Notes:
- "groupId" is not a mandatory field
Request Body Parameters
| Parameter | Explanation | Values (Explanation) | |
|---|---|---|---|
| barcode | Unique identifier of the product | String: (E.g., "ABC1234") | |
| captureTrigger | Mechanism/flow which prompted the capture | menu: (Triggered via menu selection) | |
| category | The category of the item | Drinks: (Indicates Drink items) | |
| count | The number of items captured | Integer: (E.g., 1) | |
| family | The family classification of the item | Fruits: (Belongs to the Milk family) | |
| groupId | Unique identifier for the group the item belongs to | String: (E.g., "b932de54-41ca-4f5a-99d5-b9db779f8888") | |
| imgSource | Source of the image capture | String: (E.g., "ip-camera-SCO-1") | |
| label | Label identifier for the product | String: (E.g., "4201") | |
| labelTrigger | Mechanism that triggered the label assignment | RegularMenuSelection: (Label assigned through regular menu selection) | |
| name | Name of the item | String: (E.g., "Org Milk") | |
| price | Unit price of the item | Float: (E.g., 0.95) | |
| totalPrice | Total price after discounts or adjustments | Float: (E.g., 0.95) |
Response Body Parameters
| Parameter | Explanation |
|---|---|
| sampleId | Unique identifier for the stored sample |
Response Example
The response is a JSON object containing a unique sampleId.
{
"sampleId": "9c9847e6-591b-4f29-a988-922da9e56484"
}
PATCH /sample - Barcode input
PATCH http://localhost:8090/api/v2/samples/{sample_id}
This endpoint should be called only for existing samples. ANY field can be passed in the body for updating this sample’s data or for adding any additional metadata. Do not call this endpoint for a sample id that doesn't exist.
Calling PATCH sample, will require only the sample id (in the url) and should allow the developer to add or update any field. This PATCH method will not have any match strength response.
Can be called with each barcode scan once the application gets additional metadata it wants to append and pass to Edgify. This endpoint updates the matching sample data using the sample id provided by the previous capture-and-predict response.
Example Request (Barcode):
ANY of the following fields can be passed, all of them are OPTIONAL:
{
"barcode": "53453534535224",
"captureTrigger": "barcode",
"category": "drinks",
"count": 2,
"family": "Drinks",
"image": base64 image
"label": "4245401",
"labelTrigger": "barcode_scanner",
"name": "Coca-Cola 1.5L",
"price": 2.95,
"totalPrice": 2.95,
}
Example Response (Barcoded items):
Success: 200 - Existing sample was changed
Error codes: 400 - Bad request (e.g. bad format in the body fields)
{
"errors": [
"string"
]
}
401 - Unauthorized
"string"
404 - Sample does not exist
{
"errors": [
"string"
]
}
500 - Internal Server Error
{
"errors": [
"string"
]
}
DELETE /sample
DELETE http://localhost:8090/api/v2/samples/{sample_id}
Description : Deletes the passed Sample from the Edgify system. For specialized use cases. Please consult your Customer Success Team.
Webhook APIs for Loss Alerts
To handle alerts, ensure your application is configured to serve the different types of webhook alert endpoints and can accept the body as described below.
As part of the integration, ensure your application can:
- Accept requests at the relevant endpoint.
- Parse the incoming JSON payload.
- Validate the payload structure and data types.
- Store or process the relevant information as needed by your application.
- Respond with appropriate HTTP status codes.
- Log errors for troubleshooting
You can use tools like Postman to simulate webhook requests and test the endpoint.
Webhook - Alert Non-Match
The "Alert Non-Match" webhook is triggered when a discrepancy is detected between the image and the barcode scan. It provides detailed information about the mismatch, including images captured during the event. The image is stored in Base64 format.
POST http://localhost:3050/alert-non-match
Request body example: The webhook will send a JSON payload with the following structure:
{
"issueId": "b613a9b9-ff88-4949-9110-67232af9df93",
"label": "ABC",
"name": "Kool-Aid",
"capturedAt": "2024-05-16T15:04:05.000Z",
"predictedLabel": "XYZ",
"predictedName": "Lego",
"image": "<base64 image>"
}
Webhook - Alert Non-Scan:
The "Alert Non-Scan" webhook is triggered when an expected scan does not occur. It provides information about the non-scan event, including the relevant images captured. The image is stored in Base64 format.
POST http://localhost:3050/alert-non-scan
Request body example: The webhook will send a JSON payload with the following structure:
{
"issueId": "b613a9b9-ff88-4949-9110-67232af9df94",
"capturedAt": "2024-05-16T15:04:05.000Z",
"predictedLabel": "",
"predictedName": "",
"images": ["<base64 image>","<base64 image>"],
"representativeImg": "<base64 image>"
}
Sending a Response Event
Once a user interacts with the alert, the system must send an HTTP POST request to our webhook with the selected response
Response Event Types
| Event Type | Description |
|---|---|
| Assistant Confirmed | Store assistant confirms the loss event |
| Assistant Rejected | Store assistant rejects the loss event |
| Shopper Confirmed | Shopper confirms the loss event |
| Shopper Rejected | Shopper rejects the loss event |
Response Payload Format
The system must send the response payload in the following format:
Response from Assistant screens
{
"name": "Assistant Confirmed",
"groupId": "<the groupId received in the alert body>",
"assistantId": "assistant-001",
}
Response from Shopper screens
{
"name": "Shopper Confirmed",
"groupId": "<the groupId received in the alert body>",
"shopperId": "<shopper-loyalty-card-id>",
}
Notes:
- Ensure your application can handle and process base64 encoded images.
- assistantId or shopperId are not mandatory fields.
- Response from Shopper screen is relevant only for hybrid PoS checkout.
The API is controlled through the config.yaml parameters:
Webhooks: enabled: true url: http://localhost:3050