Skip to main content

LoyaltyPlant In-Store Loyalty API (1.7)

Download OpenAPI specification:Download

The API enables POS systems to interact with LoyaltyPlant's loyalty program:

  • Process loyalty for in-store orders, identifying customer by QR-code, ID or phone number.
  • Apply rewards (menu items, discounts, payments) to orders.
  • Notify LP about closed orders to credit loyalty points.
  • Handle order refunds.

One-Time Salt

Obtaining one-time salt for request signing

Get one-time salt

Returns a one-time salt and its ID used to compute the AuthorizationToken header for subsequent API calls. Each salt can only be used once.

Request Body schema: application/json
required
establishmentId
required
integer >= 1

LoyaltyPlant establishment ID. Should be configurable on the POS side.

Responses

Request samples

Content type
application/json
{
  • "establishmentId": 5259
}

Response samples

Content type
application/json
{
  • "saltId": 12345,
  • "salt": "abcdefghijklmnop"
}

Process Transaction

Processing QR codes and phone-based customer identification

Process loyalty transaction

Processes a LoyaltyPlant QR code scanned from the customer's mobile app, identifies/creates a customer by phone number (in-store user acquisition), or looks up an existing customer by publicClientId.

Returns reward items, discounts, and payments that should be applied to the order. Response includes customer tier name and points balance (v1.7+).

QR code flow: Send qrCode field with the scanned string. Phone number flow (v1.6+): Send phoneNumber instead of qrCode, along with optional subscribedToPushNotifications and clientName. Public client ID flow (v1.7+): Send publicClientId instead of qrCode/phoneNumber for direct client lookup by existing public ID.

Identification priority: if more than one of qrCode, phoneNumber, publicClientId is sent, they are resolved in priority order qrCodephoneNumberpublicClientId. If none of the three is sent, the request is rejected with message code 503 (QR code invalid).

Customer tier and balance (v1.7+): customer.customerTier and customer.customerPointsBalance are best-effort enrichment. If the tier/balance lookup is unavailable, both fields are returned as null and the request is still accepted (accepted: true) — treat their absence as "unknown", not as a failure.

Note: QR codes are valid for 5 minutes and can only be used once.

header Parameters
SaltId
required
integer

Salt ID received from the one-time-salt endpoint.

AuthorizationToken
required
string

SHA256 hash of apiPrivateKey + salt. Used in API v1.4+.

DigestUtils.sha256Hex(apiPrivateKey + salt)
Request Body schema: application/json
required
qrCode
string

QR code string from LoyaltyPlant mobile app. Valid for 5 minutes, single use. Required if phoneNumber and publicClientId are not provided.

phoneNumber
string

Guest phone number in international format, e.g. +13479436134. Used for in-store user acquisition.

subscribedToPushNotifications
boolean
Default: false

If true, the client will receive push notifications. Only applicable with phoneNumber or publicClientId.

clientName
string

Client name for account creation. Only applicable with phoneNumber.

publicClientId
integer or null

Existing customer public ID. If provided, used for direct client lookup instead of QR code or phone number. The client must belong to the same partner.

orderId
required
string

Order identifier in POS.

required
object (PosInfo)
total
number <decimal>

Current order total.

object (Order)

Order contents with menu items and order-level discounts (v1.5+).

Responses

Request samples

Content type
application/json
Example
{
  • "qrCode": "1234567878901234567890",
  • "orderId": "1234567",
  • "total": 1234.3,
  • "posInfo": {
    },
  • "order": {
    }
}

Response samples

Content type
application/json
Example
{
  • "accepted": true,
  • "menuItems": [
    ],
  • "discounts": [ ],
  • "messages": [
    ],
  • "customer": {
    },
  • "payment": {
    },
  • "hasEverRewardedPoints": true,
  • "maxPointsPercent": 10
}

Order Lifecycle

Order closure and refund notifications

Complete order

Same as v1.6. Notifies LoyaltyPlant that the order has been paid and closed.

header Parameters
SaltId
required
integer

Salt ID received from the one-time-salt endpoint.

AuthorizationToken
required
string

SHA256 hash of apiPrivateKey + salt. Used in API v1.4+.

DigestUtils.sha256Hex(apiPrivateKey + salt)
Request Body schema: application/json
required
phoneNumber
string

Guest phone number (v1.6, in-store acquisition flow). Not present in the monolith order-closed contract.

subscribedToPushNotifications
boolean

Push notification subscription flag (v1.6, in-store acquisition flow). Not present in the monolith order-closed contract.

clientName
string

Client name (v1.6, in-store acquisition flow). Not present in the monolith order-closed contract.

establishmentId
required
integer

LoyaltyPlant establishment ID.

orderId
required
string

Order identifier in POS.

orderTotal
required
number <decimal>

Final order total.

pointsReward
integer

Override points reward amount. Warning: usually should NOT be specified. Only use to force a specific number of bonus points instead of the calculated amount.

required
object (Order)

Order contents with menu items and order-level discounts (v1.5+).

required
Array of objects (Payment)

List of payments applied to the order.

object (Waiter)

Responses

Request samples

Content type
application/json
Example
{
  • "establishmentId": 1234,
  • "orderId": "1234",
  • "orderTotal": 12.35,
  • "order": {
    },
  • "payments": [
    ],
  • "waiter": {
    }
}

Response samples

Content type
application/json
{
  • "accepted": true,
  • "pointsReward": 10,
  • "pointsSpent": 20,
  • "errorCode": 0,
  • "errorText": ""
}

Refund order

Same as v1.6. Notifies LoyaltyPlant that the order has been refunded.

header Parameters
SaltId
required
integer

Salt ID received from the one-time-salt endpoint.

AuthorizationToken
required
string

SHA256 hash of apiPrivateKey + salt. Used in API v1.4+.

DigestUtils.sha256Hex(apiPrivateKey + salt)
Request Body schema: application/json
required
establishmentId
required
integer

LoyaltyPlant establishment ID.

orderId
required
string

Order identifier in POS.

Responses

Request samples

Content type
application/json
{
  • "establishmentId": 5259,
  • "orderId": "68acf80c0d0a7e6d2fd19946"
}

Response samples

Content type
application/json
{
  • "accepted": true,
  • "errorCode": 0,
  • "errorText": ""
}

Configuration

Per-outlet POS/kiosk loyalty configuration fetch

Fetch credential

Returns the loyalty configuration for one sales outlet, selected by the identifier query parameter (or midIdentifier when present) and routed by the {pos} path segment.

The request has no body and no auth handshake (no one-time-salt / AuthorizationToken), and always responds with HTTP 200 — success or failure is conveyed by the accepted field of the response envelope.

accepted: true — the configuration is returned in config. The outlet is now marked applied (fetch-once); subsequent fetches for the same identifier return accepted: false.

accepted: false (with config: null) when any of:

  • the configuration was already fetched for this identifier (fetch-once), or
  • no configuration is bound to the identifier, or
  • the {pos} segment is not a registered handler.

Persist the configuration you receive as the source of truth for the outlet; accepted: false is not an error to retry.

version — optional plugin/integration version string; currently accepted but not persisted by the service.

Note: the service also accepts the trailing-slash form POST /v1/pos/{pos}/.

path Parameters
pos
required
string
Enum: "generic" "clover" "iiko" "rkeeper"

POS-type segment that selects the configuration handler:

  • generic — the default; use it unless your POS needs custom configuration.
  • clover — Clover.
  • iiko — Syrve/iiko.
  • rkeeper — r_keeper/Restera.

LoyaltyPlant assigns the segment during onboarding. The currently deployed service registers clover, iiko, and rkeeper; confirm generic is enabled for your integration rather than assuming it.

query Parameters
identifier
required
string

The outlet's lookup key for the configuration. Must be a stable, globally-unique value that already lives in the POS — assigned once, at provisioning, and never overwritten afterwards (do not reuse a mutable field such as a display name or a value the POS regenerates on update/reinstall). The outlet is resolved by this value on every fetch, so if it changes the outlet can no longer be matched.

midIdentifier
string

Alternative outlet-lookup identifier (a merchant/MID-level code) added for the rkeeper integration, used in place of the standard identifier. When present, it overrides identifier as the lookup key for this request; when omitted, identifier is used.

version
string

Optional plugin/integration version string. Accepted but not persisted.

fullRestaurantCode
string

Alternative outlet-lookup identifier added for the rkeeper integration, used in place of the standard identifier. Present on the deployed service (v0.1.3) but not in the current source checkout; documented here to match the deployed contract.

Responses

Response samples

Content type
application/json
Example

The generic POS type returns just the core fields every integration needs. Only fields with a value are present.

{}

Fetch loyalty configuration

Returns the complete per-outlet loyalty configuration — connection, program rules, and the kiosk-facing presentation and legal settings. Used identically by every device type: a cashier POS reads the connection and program fields and ignores the presentation fields; a self-service kiosk uses all of them. Only fields with a value are present (null fields omitted).

Unlike POST /v1/pos/{pos}, this endpoint is not fetch-once — it is safe to call on every device startup. Compare the returned revision (a monotonic epoch-second stamp) with your cached copy and re-apply the configuration when revision increases; this is how admin-panel changes reach devices without reprovisioning. accepted: false here means the outlet is not bound / loyalty is not configured (guest mode) — never a fetch-once rejection.

Authentication. Protected by the same AuthorizationToken scheme as the In-Store Loyalty API (SHA-256 of the outlet's decrypted apiPrivateKey + a one-time-salt). The apiPrivateKey is obtained once from the password field of POST /v1/pos/{pos}; the configuration payload itself never contains the key.

Caching. The response carries an ETag equal to the revision; send it back as If-None-Match to get 304 Not Modified when nothing changed. The revision in the body is the authoritative change signal; the ETag is a bandwidth optimization.

Authorizations:
AuthorizationToken
path Parameters
establishmentId
required
integer <int32>

The LoyaltyPlant establishment (sales outlet) ID, obtained from POST /v1/pos/{pos}.

query Parameters
identifier
string

Optional outlet identifier, accepted as a fallback lookup key for a device that has not yet cached its establishmentId.

header Parameters
If-None-Match
string

An ETag (a revision) from a previous response. When it matches the current revision, the service returns 304 Not Modified with no body.

Responses

Response samples

Content type
application/json
Example
{
  • "accepted": true,
  • "config": {
    }
}