Skip to main content

Integration Checklist

Read first: Order Injection Guide · Status Webhook Guide · Message & Error Codes

This is the normative go-live gate for a Digital Ordering API integration. Every item is objectively verifiable — you either did it or you did not — and LoyaltyPlant runs an equivalent checklist during certification. Work through it against the contract; field-level truth is the Digital Ordering API specification, which this checklist links to and never restates.

Remember the inversion: you host the five /rest/* endpoints and LoyaltyPlant is the HTTP client; the order-status webhook is the one call that runs from your side to LoyaltyPlant (see Ordering Flows). Items marked (conditional) apply only if you implement that capability — skip them otherwise and note the skip in your test report.

1. Contract conformance

  • All five endpoints are hosted under your {vendorBaseUrl} at the exact paths: GET /rest/healthcheck, GET /rest/menu, GET /rest/menu/revision, POST /rest/order, POST /rest/orders.
  • {vendorBaseUrl} is reachable from LoyaltyPlant over TLS (HTTPS), and the per-environment base URL is taken from onboarding — not hard-coded to any host seen in this documentation or the spec.
  • Every response is application/json; request and response payloads validate against the schemas in the spec — field types, required fields, and enum values match (the spec lint passes).
  • The SalesOutletId request header is resolved to the correct outlet/menu/configuration on your side, and is configurable per outlet — not hard-coded.
  • The menu response uses the documented shape: the catalog wrapped under menu, with requestId.

2. Authentication

There are three distinct auth directions; certify each one separately. The full model is in General Requirements.

  • Request, LP → you. On /menu, /menu/revision, /healthcheck, /order and /ordersevery LoyaltyPlant→vendor call, including the healthcheck — the AuthorizationToken header is validated as SHA256-hex(apiKey) (lowercase hex) for the call's SalesOutletId, and a mismatch is rejected.
  • Response, you → LP. The HTTP responses of /order, /orders, /menu, /menu/revision, and /healthcheck carry an AuthorizationToken response header equal to SHA256-hex(apiKeyResponse) (the response key) — verified present and correct, because LoyaltyPlant discards the whole response (treating the operation as failed) when it is missing or wrong.
  • Healthcheck response carries the response token too — verify /rest/healthcheck returns AuthorizationToken: SHA256-hex(apiKeyResponse), like every other LoyaltyPlant→vendor response.
  • Webhook, you → LP. The status webhook you POST carries SalesOutletId plus an AuthorizationToken equal to SHA256-hex(apiKey) — the same hashed token as every LoyaltyPlant→vendor call. Verified against a real outlet key, the webhook is accepted.

Note: All three auth directions use SHA256-hex of the relevant key — the request and the webhook both use SHA256-hex(apiKey), the response uses SHA256-hex(apiKeyResponse).

3. Healthcheck

  • GET /rest/healthcheck returns a flat JSON map of service-name → UP/DOWN, listing the real services whose failure would break order acceptance (e.g. POS, DB) — not a blanket UP.
  • When any service reports DOWN, the outlet is correctly treated as unavailable: LoyaltyPlant runs the healthcheck before every order and does not inject an order if any service is DOWN.
  • The check is fail-safe: no response, a timeout, an error, or an unparseable body must result in the outlet being treated as unavailable rather than letting orders through. The healthcheck accounts for the absence of an internet connection and other failures.

4. Menu

  • The full menu is returned to GET /rest/menu with categories, items, reusable modifier groups, and the modifier pool, each referenced by refId within the response.
  • Category nesting never exceeds two levels (Category → Subcategory → Item, or Category → Item); a root category contains either subcategories or items, not both; subcategories contain items only.
  • Sort order is supplied: rootSortOrder on root categories and sortOrder on items/subcategories/modifiers, so LoyaltyPlant can order the menu as your POS intends.
  • Modifier groups carry min/max/free (scalar integers): min > 0 makes a selection from the group mandatory; min = 0 makes the group optional; free modifiers are not charged.
  • A revision (epoch timestamp) is returned in the GET /rest/menu response, and GET /rest/menu/revision returns the same value, so LoyaltyPlant can cache the menu and skip re-pulling an unchanged one.
  • The stop-list works by omission: an item absent from the next GET /rest/menu response is automatically marked unavailable in LoyaltyPlant — there is no separate delete call.
  • Titles and descriptions for categories, items and modifiers are multilingual, keyed by ISO 639-1 language code (e.g. en, es, fr).
  • Per-item prices are returned in pricesByOrderingType, with tax percentages broken down by ordering type (PICKUP, EAT_IN, DELIVERY) in each price's taxes array.
  • (conditional) If your POS uses combos, they are handled per-POS as ordinary items + modifier groups (with a B1G1 flag where applicable) — a generic combo contract is not part of the Digital Ordering API and is not expected on the wire.

5. Orders

  • An order is only ever injected after a healthy healthcheck: verify your POS never receives a POST /rest/order it has not just reported it can handle (LoyaltyPlant healthchecks immediately before every create).
  • POST /rest/order accepts and honors the standard order types: Pickup, Delivery (with the address block and coordinates), Eat-in (with additionalInfo.tableNumber), and Drive-through (per outlet — like pickup, with no address). The OrderingType enum additionally defines CATERING, which has no current production usage (verify against your POS model before relying on it). A reader should expect to see all five values in the spec enum. Dine-in at a table is EAT_IN + tableNumber.
  • Order line items with their modifiers are accepted, and an order-level comment is accepted.
  • Reward items in presents[] are added to the order at price 0 (e.g. a 100% discount or an explicit zero price) — they never increase the total the customer pays.
  • The payment.charge breakdown is honored: order discount (a fixed amount; discount codes are not supported), tips, deliveryFee, plus subtotal/tax/total.
  • The order's deliveryAt timing is honored: ASAP goes to preparation immediately; CUSTOM is prepared for the given datetime.
  • The createOrder response returns posId and a customer-facing queueNumber.
  • The /order (and /orders) responses carry the response AuthorizationToken = SHA256-hex(apiKeyResponse) — verify it is present, or LoyaltyPlant discards the response and the order is treated as failed (cross-check §2).
  • POST /rest/orders returns the current state.status and queueNumber for each requested posId, and includes a state.reason whenever the status is CANCELED or FAILED.
  • (conditional) If your POS charges card-in-store or card-in-app, the payment.method is read and the masked pan is handled as masked (never a clear PAN).

6. Status webhooks

  • The webhook is POSTed to {lpBaseUrl}/digitalordering/pos/v2/integrated on every order status change, carrying the minimal payload LoyaltyPlant acts on: orders[].id (the posId), orders[].queueNumber, and orders[].state.status.
  • state.status is always one of the nine OrderStatus values, matched exactly — an unknown value (typo, wrong case, unsupported status) is not rejected: it is caught per-order, logged, and skipped (that order's state is not advanced), while the webhook still returns HTTP 200 accepted: true. It does not produce INTERNAL_ERROR/500. Detect a silently skipped status via the getOrders poll path; do not retry it with backoff.
  • The WebhookResponse is processed by branching on accepted: only HTTP 200 with accepted: true means delivered. The four failure codes are handled per their HTTP status — READ_ERROR/INVALID_JSON/VALIDATION_ERROR (400) and INTERNAL_ERROR (500).
  • Retries are applied correctly: a deterministic 400 (VALIDATION_ERROR/INVALID_JSON/READ_ERROR) is fixed before resending, while INTERNAL_ERROR (500) and transport failures are retried with backoff. Re-delivering an already-applied status is a safe no-op.
  • (conditional) The poll path is supported as the safety net: POST /rest/orders returns the current state so a dropped webhook is reconciled on the next poll cycle (recommended — run push with poll as a backstop).

7. Performance

  • POST /rest/order, POST /rest/orders and GET /rest/healthcheck respond well within LoyaltyPlant's client timeouts of 5 s connect / 60 s read.
  • GET /rest/menu and GET /rest/menu/revision respond within the menu timeouts of 30 s connect / 180 s read.
  • HTTP redirects on these calls are accounted for — LoyaltyPlant follows a lax redirect policy when calling you.

8. Onboarding & certification

LoyaltyPlant verifies the integration before it goes live; this section mirrors that verification process so there are no surprises at the gate.

  • LoyaltyPlant is notified about a week ahead of the planned completion date, so the verification slot and test plan can be scheduled; if the date slips, a new one is agreed.
  • The certification test cases are run on your side and a test report is produced against the LoyaltyPlant test plan.
  • Access to your test environment is provided to LoyaltyPlant, which verifies the integration against its test plan; flagged cases are fixed and re-verified until all pass.
  • Once every case passes, LoyaltyPlant considers the integration complete and ready for deployment.
  • The first deployment to a POS terminal happens with LoyaltyPlant representatives present, or is recorded on video and shared with LoyaltyPlant.

Note: Some onboarding values are confirmed at kickoff, not in this contract: your per-environment {vendorBaseUrl} and {lpBaseUrl}, the request and response API keys (apiKey / apiKeyResponse), the menu sync interval, and any additionalInfo.customFields. Your LoyaltyPlant integration manager provisions these and shares the test plan, test app, and certification test cases.


Once every applicable item is checked and your test report is complete, contact your LoyaltyPlant integration manager (or integrations@loyaltyplant.com) to schedule the certification run.

← Back to Overview