Skip to main content

Loss Prevention on Self Checkout (SCO) - Integration Guide

Last updated by Alex Rochira on March 17th, 2026

Overview

This document provides insight and guidance into the Edgify REST API endpoints for implementing Loss Prevention on self checkout (SCO) or cashier lane POS. Its purpose is to offer an understanding of the functionality, facilitate the estimation of integration efforts and ultimately to guide and assist with coding and development of the integration itself.

Who Should Read This Document?

This document is intended for developers and those responsible for integrating the Edgify solution 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

This document outlines the process of integrating the Edgify solution with a POS application software via REST APIs. The Edgify Agent is the main active software component in the Edgify solution, with which the POS application will communicate.

The Edgify Agent can be installed on Microsoft Windows OS, where it runs as a service, or on most modern Linux distributions which support Docker, where it runs within a Docker container. See our installation section for more.

Loss Prevention Flows - Video

Below is a short video of a shopper session in which there is a successful scan, a barcode switch and then a scan avoidance event:

False Selections of Barcodeless Items - Video

You can see here an example of a Strongpoint - SCO showcasing the false-selection flow

Prerequisites for integration

  • Edgify Agent installed in your development environment
  • USB or IP camera feed (which is free for Edgify to control)
  • Internet connection allowing outbound communication to dashboard.production.edgify.ai

The API Flow (Start Here)

Make sure you couple the information here with information from our REST APIs page, to ensure you understand the functionality and use cases for each endpoint.

APIs Flow

alt text

POST /events - Session State Management

POST http://localhost:8090/api/v2/events

This endpoint (with below payloads) should be called upon the beginning and end of each shopper's session. This triggers the Edgify solution to begin and to stop recording a session (video of a single customer transaction).

First interaction: Notifies the beginning of a shopper session.

{
"name": "StartCustomerSession",
"transactionId": "12345",
"shopperId": "<shopper-loyalty-card-id>",
"assistantId": "<assistant-001>"
}

Notes:

  • "transactionId" is optional but recommended. It allows mapping our analytics to the retailers. You can also pass this transaction ID at a later time during the session or the end of the session.
  • "shopperId" is optional but recommended. Attaches loyalty card number to a session video, unlocking powerful analytics for repeat offences.
  • "assistantId" is optional but recommended. Attaches employee ID to a session video.

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.

Event: Pay Now: Notifies that the shopper wishes to pay

{
"name": "PayNow",
"transactionId": "12345"
}

Notes:

  • "transactionId" is not a mandatory field

This API should be called when the shopper opts for the “pay now” option and moves to the tender process/screen. This endpoint enables our Left In Cart product. It will also prevent Edgify from triggering non-scan alerts while the shopper is handling payments.

Event: Last interaction: Marks the end of a shopper session.

{
"name": "StopCustomerSession",
"transactionId": "12345"
}

Should be called right before the customer moves to payment, to ensure we don't capture any payment or card information. Tells Edgify to stop analyzing the video feed and saves the session. There is a configurable timeout on the Edgify side in case a StopCustomerSession call is not received.

Event: Start intervention: Notifies the beginning of an attendant intervention

{
"name": "StartIntervention",
"transactionId": "12345",
"assistantId": "<assistant-001>"
}

This API should be called when a store attendant intervenes with the shopper session. Allows the system to identify attendant actions and to not raise loss prevention alerts during.

Event: Stop intervention: Marks the end of a shopper session.

{
"name": "StopIntervention",
"transactionId": "12345"
}

This API should be called when a store attendant stops intervening with the shopper session.

POST /capture-and-save

Endpoint

POST http://localhost:8090/api/v2/capture-and-save

Description :

Captures an image from the camera feed and saves it with a unique sample ID. Records essential metadata, including barcode, category, weight, price, etc. Once the sample is successfully written, the Edgify Agent returns a unique sample ID.

Request Example:

{
"label": "049068930450",
"captureTrigger": "barcode",
"labelTrigger": "scannerScale",
"category": "grocery",
"family": "beverages",
"name": "Diet Coke 20oz",
"count": 1,
"price": 1.95,
"totalPrice": 0.95
}

Request Parameters

ParameterExplanationValues (Explanation)
labelBarcode or SKUString: (E.g., "4201, 48398882320001")
captureTriggerMechanism which prompted the captureBarcode: (Barcode scanner/handheld scanner), ManualEntry: (Picklist/lookup)
labelTriggerMechanism which triggered the label assignmentSpecific barcode capture method (E.g., ScannerScale, HandScanner, ManualEntry)
categoryBroad classification/grouping for the item (this list is not exhaustive. You may pass custom values which more closely reflect your store estate)Grocery: (All consumable items), Household, PersonalCare, etc.
familySub-classification (this list is not exhaustive. You may pass custom values which more closely reflect your store estate)Dairy, BabyCare, Meat, Alcohol, etc.
nameCommon name of the itemString: (E.g., "Diet Coke 20oz")
countThe quantity of discrete, sellable units for a single transaction eventInteger: (E.g., 1)
priceUnit price of the item before discount or adjustmentsFloat: (E.g., 1.95)
totalPriceNet price after discount or adjustmentsFloat: (E.g., 0.45)

Response Parameters

ParameterExplanation
sampleIdUnique 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}

USed to update an existing sample. Fields sent in the payload will be appended to the existing sample. Do not call this endpoint for a sample which doesn't exist.

This PATCH call will not trigger a 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.

Request Example (Barcode):

ANY of the following fields can be passed, all of them are OPTIONAL:

{
"label": "049068930450",
"captureTrigger": "barcode",
"labelTrigger": "scannerScale",
"category": "grocery",
"family": "beverages",
"name": "Diet Coke 20oz",
"count": 1,
"price": 1.95,
"totalPrice": 0.95
}

Response Example (Barcoded items):

Success: 200 - Existing sample was changed

Error codes: 400 - Bad request (e.g. bad format in the body fields)

401 - Unauthorized

404 - Sample does not exist

500 - Internal Server Error

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 Edgify.

Loss Alerts - Webhook APIs

The Edgify Agent utilizes webhooks to provide real-time alerts to the POS upon the detection of potential loss events. To handle alerts, ensure your application is configured to serve the following:

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

Alert Non-Match

Triggered when a mismatch is detected between the image and the barcode scan (Also known as Ticket/Barcode Switching). The alert provides detailed information about the mismatch, including a base64-encoded image of the scanned item.

POST http://localhost:3050/alert-non-match

Alert example: The Edgify Agent will send a webhook with the following JSON payload structure:

{
"groupId": "b613a9b9-ff88-4949-9110-67232af9df93",
"label": "834782000381",
"name": "Kool-Aid Tropical",
"capturedAt": "2024-05-16T15:04:05.000Z",
"predictedLabel": "9923810024436",
"predictedName": "Rib-eye Steak 20oz",
"image": "<base64 image>"
}

Alert Non-Scan:

Triggered when an item enters the bagging area without a corresponding scan event. The alert provides information about the non-scan event, including 1-3 base64-encoded images tracking the item.

POST http://localhost:3050/alert-non-scan

Alert example: The Edgify Agent will send a webhook with the following JSON payload structure:

{
"groupId": "b613a9b9-ff88-4949-9110-67232af9df94",
"capturedAt": "2024-05-16T15:04:05.000Z",
"images": ["<base64 image>","<base64 image>"],
"representativeImg": "<base64 image>"
}

Alert Left-in-Cart (LIC):

Notifies the POS of any unscanned items in the shopping cart when the customer moves to complete the transaction. Works with baskets and carts (trolleys). Differs from the previous Webhook alerts in that it is a synchronous, request-response rather than an alert.

The POS should call the below API endpoint when the customer moves to tender screen. Usually triggered by a "Pay Now" or "Finish and Pay" button.

POST http://localhost:8090/api/v2/detect-cart-loss

Request Example

{
"name": "PayNow",
"transactionId": "12345"
}
  • TransactionID is optional

Response Example

The response is a JSON array containing a unique ID and an image highlighting the unscanned items.

[
{
"sampleId": "<sample UUID>",
"type": "LEFT_IN_CART",
"image": "<base64-encoded image>",
"capturedAt": "<ISO 8601 timestamp>",
"sessionId": "<Current session UUID>"
}
]

Validating the Alert with a Response

Feedback from each loss alert enables the Edgify solution to continuously improve and maintain high performance. You can send feedback using the below response formats:

POST http://localhost:8090/api/v2/events

Response Types

Event TypeDescription
Assistant ConfirmedStore assistant confirms the loss event
Assistant RejectedStore assistant rejects the loss event
Shopper ConfirmedShopper confirms the loss event
Shopper RejectedShopper rejects the loss event

These responses will be tied to some form of user input, such as on-screen buttons which enable the customer or staff to clear the alerts.

Response Payload Format - Non-scan and Non-match

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>"
}

assistantID and shopperID are optional

Response Payload Format - Left in Cart

Slightly different to the Non-scan/match responses, as LIC uses sampleID rather than groupID:

Response from Assistant screens

{
"name": "Assistant Confirmed",
"sampleId": "<the sampleID received in the alert body>",
"assistantId": "<assistant-001>"
}

Response from Shopper screens

{
"name": "Shopper Confirmed",
"sampleId": "<the groupId received in the alert body>",
"shopperId": "<shopper-loyalty-card-id>"
}

assistantID and shopperID are optional

Notes:

  • Ensure your application can handle and process base64 encoded images.
  • assistantId or shopperId are not mandatory fields.

Webhooks can be enabled / disabled from within the Edgify Agent config

Final Checks

Return to https://docs.edgify.ai/docs/Integration/ and ensure you've implemented all important configurations.