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
SalesOutletIdrequest 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, withrequestId.
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,/orderand/orders— every LoyaltyPlant→vendor call, including the healthcheck — theAuthorizationTokenheader is validated asSHA256-hex(apiKey)(lowercase hex) for the call'sSalesOutletId, and a mismatch is rejected. - Response, you → LP. The HTTP responses of
/order,/orders,/menu,/menu/revision, and/healthcheckcarry anAuthorizationTokenresponse header equal toSHA256-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/healthcheckreturnsAuthorizationToken: SHA256-hex(apiKeyResponse), like every other LoyaltyPlant→vendor response. - Webhook, you → LP. The status webhook you POST carries
SalesOutletIdplus anAuthorizationTokenequal toSHA256-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-hexof the relevant key — the request and the webhook both useSHA256-hex(apiKey), the response usesSHA256-hex(apiKeyResponse).
3. Healthcheck
-
GET /rest/healthcheckreturns 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 blanketUP. - 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 isDOWN. - 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/menuwith categories, items, reusable modifier groups, and the modifier pool, each referenced byrefIdwithin 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:
rootSortOrderon root categories andsortOrderon items/subcategories/modifiers, so LoyaltyPlant can order the menu as your POS intends. - Modifier groups carry
min/max/free(scalar integers):min > 0makes a selection from the group mandatory;min = 0makes the group optional;freemodifiers are not charged. - A
revision(epoch timestamp) is returned in theGET /rest/menuresponse, andGET /rest/menu/revisionreturns 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/menuresponse 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'staxesarray. - (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/orderit has not just reported it can handle (LoyaltyPlant healthchecks immediately before every create). -
POST /rest/orderaccepts and honors the standard order types: Pickup, Delivery (with theaddressblock and coordinates), Eat-in (withadditionalInfo.tableNumber), and Drive-through (per outlet — like pickup, with no address). TheOrderingTypeenum 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 isEAT_IN+tableNumber. - Order line items with their
modifiersare accepted, and an order-levelcommentis 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.chargebreakdown is honored: orderdiscount(a fixed amount; discount codes are not supported),tips,deliveryFee, plussubtotal/tax/total. - The order's
deliveryAttiming is honored:ASAPgoes to preparation immediately;CUSTOMis prepared for the givendatetime. - The
createOrderresponse returnsposIdand a customer-facingqueueNumber. - The
/order(and/orders) responses carry the responseAuthorizationToken=SHA256-hex(apiKeyResponse)— verify it is present, or LoyaltyPlant discards the response and the order is treated as failed (cross-check §2). -
POST /rest/ordersreturns the currentstate.statusandqueueNumberfor each requestedposId, and includes astate.reasonwhenever the status isCANCELEDorFAILED. - (conditional) If your POS charges card-in-store or card-in-app, the
payment.methodis read and the maskedpanis handled as masked (never a clear PAN).
6. Status webhooks
- The webhook is POSTed to
{lpBaseUrl}/digitalordering/pos/v2/integratedon every order status change, carrying the minimal payload LoyaltyPlant acts on:orders[].id(theposId),orders[].queueNumber, andorders[].state.status. -
state.statusis always one of the nineOrderStatusvalues, 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 200accepted: true. It does not produceINTERNAL_ERROR/500. Detect a silently skipped status via thegetOrderspoll path; do not retry it with backoff. - The
WebhookResponseis processed by branching onaccepted: only HTTP 200 withaccepted: truemeans delivered. The four failure codes are handled per their HTTP status —READ_ERROR/INVALID_JSON/VALIDATION_ERROR(400) andINTERNAL_ERROR(500). - Retries are applied correctly: a deterministic 400 (
VALIDATION_ERROR/INVALID_JSON/READ_ERROR) is fixed before resending, whileINTERNAL_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/ordersreturns 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/ordersandGET /rest/healthcheckrespond well within LoyaltyPlant's client timeouts of 5 s connect / 60 s read. -
GET /rest/menuandGET /rest/menu/revisionrespond 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 anyadditionalInfo.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