This guide describes the plugin data and statistics endpoints exposed by APIPluginController, and the response models an integration should expect.
Both endpoints are GET requests scoped to a company plugin registration. They require the registration's shared secret in the SCANFIE-ACP request header. The registration must exist and be enabled, and requests for the same registration are serialized by a lock.
| Purpose | Endpoint | Successful response |
|---|---|---|
| Read products or catalog combos | /api/plugin/data/{companyPluginRegistrationId} | JSON array of PluginProduct or PluginCatalogCombo |
| Read sales statistics | /api/plugin/statistics/{companyPluginRegistrationId} | APIStatisticsResponse containing PluginStatisticsLine records |
Replace {companyPluginRegistrationId} with the ID of the configured company plugin registration. Use the same base URL as the Scanfie application.
Send the registration's shared secret in the SCANFIE-ACP header:
SCANFIE-ACP: <shared-secret>
The endpoint compares the value case-insensitively with the registration's configured shared secret. Do not put the secret in the URL. Requests without a valid secret are rejected with a JSON command-response error.
Both endpoints run the corresponding registered account hook before producing their endpoint data. The hook request contains the company transcribed as PluginCompany, the initialized hook data, and the plugin instance. A non-empty raw request body is also copied to the hook request's plugin-hook-payload field; integrations should normally call these GET endpoints without a body and use query parameters as documented below.
Only enabled registrations are processed. A concurrent request for the same registration is rejected. The controller sets the response content type to application/json; it does not explicitly set endpoint-specific HTTP status codes for these validation errors, so clients should inspect the JSON response rather than relying only on the HTTP status.
The controller does not add a not-found error when the registration ID does not resolve; in that case it may return an empty response. Treat an empty or non-JSON body as an unsuccessful integration response.
GET /api/plugin/data/{companyPluginRegistrationId}
SCANFIE-ACP: <shared-secret>
Optional query parameter:
| Name | Values | Behavior |
|---|---|---|
type | PRODUCTS, COMBOS | Selects the data array. Case-insensitive. If absent, the endpoint uses the data type returned by the data hook. An unrecognized non-empty value currently falls back to PRODUCTS. |
The data hook is selected from the registration's DATA hook registry and is expected to be an AbstractAccountHook. Its response should be an AccountPluginDataHookResponse with dataType set to PRODUCTS or COMBOS, unless the caller supplies type. The hook is still executed when type is supplied; the query parameter overrides the hook's returned type for choosing the endpoint output.
If no data type is available, the endpoint returns an API command-response error. If there are no registered data hooks, the current error text is "No statistics hooks found" (the message is shared with the statistics endpoint).
The endpoint converts the company's active products into a JSON array of PluginProduct. "Active" here means products for the registration's company that are not deleted.
| Field | JSON type | Notes |
|---|---|---|
internalId | string | Scanfie product ID. |
systemArticleId, externalId | string | Product identifiers used by the integration. |
name, internalName, subtitle, description | string | Display and internal text. |
isCombo | boolean | Whether the product represents a combo. |
productGroupExternalId | string | External product-group identifier. |
productGroup | object | PluginProductGroup: internalId, name, externalId. |
eanSku | string | EAN/SKU identifier. |
price, nettPrice, vatPercentage | number | Monetary values and optional VAT percentage. |
vatType | string | UNKNOWN, NONE, ZERO, HIGH, STANDARD, LOW, REDUCED_1, REDUCED_2, or SPECIAL. |
soldOut, hidden, dynamicProduct | boolean | Availability/display and dynamic-product flags. |
turnoverGroup | integer | Turnover group number. |
turnoverGroupExternalId | string | External turnover-group identifier. |
turnoverGroupEntity | object | PluginTurnoverGroup, when populated. |
syncMarker | string | Synchronization marker. |
dtDeleted | date/time | Deleted timestamp; active products normally have no value. |
imageUrl | string | URL of a non-deleted product image, when present. |
base64ImageList | array of strings | Base64 image data, when populated. |
productType | string | Product category such as FOOD, DRINKS, TICKET, or OTHER. |
productCounterColor | string | Counter display color. |
properties | array of objects | PluginCatalogProperty: name, externalId, internalId, deliverectTagId. |
Example (fields can be null or omitted depending on serialization and source data):
[
{
"internalId": "product-id",
"systemArticleId": "1001",
"name": "Coffee",
"externalId": "coffee",
"price": 3.0,
"nettPrice": 2.75,
"vatType": "HIGH",
"soldOut": false
}
]
The endpoint converts the company's catalog combos into a JSON array of PluginCatalogCombo. The combo model contains:
| Field | JSON type | Notes |
|---|---|---|
name | string | Combo name. |
systemArticleId, externalId, internalId | string | Scanfie and integration identifiers. |
sequence | integer | Display/order sequence. |
price | number | Combined price when configured; otherwise primary-product price, or 0. |
description | string | Combo description. |
primaryProduct | object | Main PluginProduct, when present. |
subselections | array of objects | PluginSubselection values describing selectable components. |
Each PluginSubselection can include name, externalId, internalId, catalogProduct, subselectionType, subselectionFunctionalType, shallowSubselection, sequence, required, products, productSelections, properties, minimumSelections, and maximumSelections. Nested products and primaryProduct values use the PluginProduct model above.
Example:
[
{
"name": "Lunch combo",
"systemArticleId": "2001",
"externalId": "lunch-combo",
"price": 12.5,
"primaryProduct": {
"internalId": "product-id",
"name": "Sandwich"
},
"subselections": []
}
]
GET /api/plugin/statistics/{companyPluginRegistrationId}?from=2026-09-01&to=2026-09-30
SCANFIE-ACP: <shared-secret>
Required query parameters:
| Name | Meaning |
|---|---|
from | Inclusive start boundary for submitted-order statistics. |
to | Inclusive end boundary for submitted-order statistics. |
Supported formats include yyyy-MM-dd, dd-MM-yyyy, yyyy-MM-dd HH:mm:ss, dd-MM-yyyy HH:mm:ss, and yyyyMMddHHmm. Prefer full timestamps (yyyy-MM-dd HH:mm:ss) when exact boundary times matter. A date-only to value is parsed at midnight, not adjusted to the end of that day.
The date range must be valid, from must not be after to, and the difference between the boundaries must not exceed 42 days.
The statistics hook is selected from the registration's STATISTICS hook registry and is expected to be an AbstractAccountHook returning AccountPluginStatisticsHookResponse. The response must set groupType:
groupType | Aggregation |
|---|---|
TURNOVERGROUPS | Group by turnover group. |
PRODUCTGROUPS | Group by product group. |
The hook chooses the group type; this endpoint has no query parameter for overriding it. The endpoint calculates and returns statistics after the hook executes.
A successful response is an APIStatisticsResponse:
{
"lines": [
{
"id": "group-id",
"turnoverGroupNumber": 1,
"externalId": "group-1",
"name": "Food",
"currency": null,
"quantity": 10,
"price": 100.0,
"nettPrice": 90.91,
"taxPrice": 9.09,
"taxRate": 10.0,
"statisticsGroupType": "TURNOVERGROUPS"
}
],
"errors": []
}
APIStatisticsResponse contains errors (an array of APIError) and lines (a collection of PluginStatisticsLine). The line fields are:
| Field | JSON type | Meaning |
|---|---|---|
id | string | Scanfie group ID; can be null for an unassigned group. |
turnoverGroupNumber | integer | Group number, or an internally generated fallback value. |
externalId | string | External group ID when available. |
name | string | Group name or fallback label. |
currency | string | Currency field in the model; may be unset. |
quantity | integer | Aggregated confirmed quantity. |
price | number | Gross total. |
nettPrice | number | Net total. |
taxPrice | number | Tax total (price - nettPrice). |
taxRate | number | Tax percentage when it can be determined. |
statisticsGroupType | string | TURNOVERGROUPS or PRODUCTGROUPS. |
Statistics include submitted, confirmed order lines in the requested range and exclude aborted, refused, returned, or deleted orders, zero-quantity lines, and registrations excluded from turnover.
Validation and processing errors are serialized as an API command response rather than the successful data array. The common shape includes an errors array; each APIError has errorMessage and severity (info, warning, or error). Error responses can also include command metadata such as messages, responseCode, and responseBody.
Examples of endpoint errors include invalid shared secret, disabled registration, a concurrent request, no matching hooks, a missing hook result type, an invalid date range, and a range exceeding 42 days. For robust integrations:
SCANFIE-ACP and check the response body is valid JSON.errors object/array before deserializing data.APIStatisticsResponse and inspect errors before consuming lines.errors as a failed call; do not interpret an error payload as an empty data set.The endpoint models are defined in the integration plugin API:
EnumDataType, AccountPluginDataHookResponseAccountPluginStatisticsHookResponse, EnumStatisticsGroupTypePluginProduct, PluginProductGroup, PluginCatalogPropertyPluginCatalogCombo, PluginSubselectionAPIStatisticsResponse, PluginStatisticsLineAPICommandResponse, APIError