Download OpenAPI specification:Download
Contract for the Digital Ordering API — the API that connects a restaurant chain's POS / ordering back end to LoyaltyPlant's mobile-app ordering.
YOU (the vendor / POS integration) implement and host the five /rest/... endpoints below. LoyaltyPlant is the HTTP
client: it calls your endpoints to fetch your menu, run a healthcheck,
push orders, and poll order state. The only call that travels the other way
is one webhook that you send to LoyaltyPlant when an order's status
changes (modeled as the orderStatusWebhook callback on POST /rest/order).
You never call LoyaltyPlant to create an order; LoyaltyPlant drives the flow
by calling you.
LoyaltyPlant calls this before creating an order to confirm your
integration and its downstream services are up. Return a flat JSON object
mapping each service name you choose to its status (UP / DOWN).
If any value is DOWN, LoyaltyPlant will not place an order at this
outlet. No request body is sent.
Respond within ~5 s connect / 60 s read — LoyaltyPlant's client timeouts.
| SalesOutletId required | integer Numeric id of the establishment this call targets. This is LoyaltyPlant's outlet id, not yours: LoyaltyPlant assigns it and gives it to you during onboarding. Resolve it to the correct venue/menu/configuration on your side. Sent on every LoyaltyPlant→vendor call and on the status webhook. |
| AuthorizationToken required | string Authentication token. For all Digital Ordering API calls this is the
lowercase SHA-256 hex of the shared API key for the outlet
( |
{- "POS": "UP",
- "DB": "UP",
- "KITCHEN": "UP"
}Returns the full menu for the outlet identified by SalesOutletId:
categories, reusable modifier groups, the modifier pool, and items, plus
a revision (epoch timestamp). LoyaltyPlant imports this and maps your
ids to its own catalog. LoyaltyPlant typically calls GET /rest/menu/revision first and only calls this when the revision changed
(see that operation). No request body.
The top-level object is wrapped under a menu key and also carries a
requestId.
Respond within ~30 s connect / 180 s read — LoyaltyPlant's client timeouts.
| SalesOutletId required | integer Numeric id of the establishment this call targets. This is LoyaltyPlant's outlet id, not yours: LoyaltyPlant assigns it and gives it to you during onboarding. Resolve it to the correct venue/menu/configuration on your side. Sent on every LoyaltyPlant→vendor call and on the status webhook. |
| AuthorizationToken required | string Authentication token. For all Digital Ordering API calls this is the
lowercase SHA-256 hex of the shared API key for the outlet
( |
{- "requestId": "f0c1a2b3-4d5e-6f70-8a9b-0c1d2e3f4a5b",
- "menu": {
- "revision": 1718280000,
- "categories": [
- {
- "refId": "cat-burgers",
- "id": "100",
- "code": "BURGERS",
- "title": {
- "en": "Burgers",
- "es": "Hamburguesas"
}, - "root": true,
- "rootSortOrder": 1
}
], - "modifiers": [
- {
- "refId": "mod-bacon",
- "id": "9001",
- "code": "BACON",
- "title": {
- "en": "Extra bacon"
}, - "pricesByOrderingType": {
- "PICKUP": {
- "value": 1.5,
- "taxes": [ ]
}
}
}
], - "modifierGroups": [
- {
- "refId": "grp-extras",
- "id": "500",
- "code": "EXTRAS",
- "title": {
- "en": "Extras"
}, - "min": 0,
- "max": 3,
- "free": 0,
- "modifiers": [
- {
- "modifier": "mod-bacon",
- "selected": false,
- "sortOrder": 1
}
]
}
], - "items": [
- {
- "refId": "item-cheeseburger",
- "id": "1",
- "code": "CB01",
- "title": {
- "en": "Cheeseburger",
- "es": "Hamburguesa con queso"
}, - "description": {
- "en": "Beef patty with cheese"
}, - "sortOrder": 1,
- "pricesByOrderingType": {
- "PICKUP": {
- "value": 6.9,
- "taxes": [
- 10
]
}, - "EAT_IN": {
- "value": 7.5,
- "taxes": [
- 10
]
}
}, - "categories": [
- {
- "category": "cat-burgers",
- "sortOrder": 1
}
], - "modifierGroups": [
- {
- "refId": "emb-extras-cb01",
- "basedOn": "grp-extras",
- "id": "500",
- "code": "EXTRAS",
- "title": {
- "en": "Extras"
}, - "min": 0,
- "max": 2,
- "free": 1,
- "modifiers": [
- {
- "modifier": "mod-bacon",
- "selected": false,
- "sortOrder": 1
}
]
}
]
}
]
}
}Returns just the current menu revision (an epoch timestamp), so
LoyaltyPlant can cheaply decide whether to re-pull the full menu. It must
equal the revision inside GET /rest/menu. LoyaltyPlant skips the full
menu when the revision is unchanged, and re-pulls when it changed, when it
has no cached value, or when the returned revision is lower than its
cache. No request body.
Fail-open: if this pre-check fails for any reason, LoyaltyPlant falls back
to a full GET /rest/menu.
Respond within ~30 s connect / 180 s read — LoyaltyPlant's client timeouts.
| SalesOutletId required | integer Numeric id of the establishment this call targets. This is LoyaltyPlant's outlet id, not yours: LoyaltyPlant assigns it and gives it to you during onboarding. Resolve it to the correct venue/menu/configuration on your side. Sent on every LoyaltyPlant→vendor call and on the status webhook. |
| AuthorizationToken required | string Authentication token. For all Digital Ordering API calls this is the
lowercase SHA-256 hex of the shared API key for the outlet
( |
{- "revision": 1718280000
}LoyaltyPlant calls this to place a new order at your POS. The request is
wrapped under an order key. Return the POS order id (posId) and the
customer-facing queueNumber.
After the order is created, you notify LoyaltyPlant of every
subsequent status change by POSTing the order-status webhook — see the
orderStatusWebhook callback on this operation.
Reward items arrive in presents[]: the POS must charge 0 for them.
Amounts are decimal strings/numbers; pan is masked when present.
Respond within ~5 s connect / 60 s read — LoyaltyPlant's client timeouts.
| SalesOutletId required | integer Numeric id of the establishment this call targets. This is LoyaltyPlant's outlet id, not yours: LoyaltyPlant assigns it and gives it to you during onboarding. Resolve it to the correct venue/menu/configuration on your side. Sent on every LoyaltyPlant→vendor call and on the status webhook. |
| AuthorizationToken required | string Authentication token. For all Digital Ordering API calls this is the
lowercase SHA-256 hex of the shared API key for the outlet
( |
| X-LP-Webhook-Url required | string <uri> LoyaltyPlant's base URL for the order-status webhook callback. Sent by
LoyaltyPlant on every |
required | object (Order) A Digital Ordering order. Used both as the Required-ness is direction-dependent and business-driven, so it is
not expressed as schema | ||||||||||||||||||||||||
| |||||||||||||||||||||||||
{- "order": {
- "id": "LP-ORD-1001",
- "type": "PICKUP",
- "client": {
- "name": "Maria",
- "lastName": "Lopez",
- "email": "maria@example.com",
- "phoneNumber": "+34911223344"
}, - "deliveryAt": {
- "type": "ASAP"
}, - "comment": "No onions please",
- "payment": {
- "method": {
- "type": "CASH"
}, - "charge": {
- "subtotal": 6.9,
- "tax": 0.69,
- "total": 7.59
}
}, - "additionalInfo": {
- "loyaltyPlantOrderId": 5550001,
- "customFields": {
- "source": "app"
}
}, - "items": [
- {
- "id": "1",
- "price": 6.9,
- "count": 1,
- "modifiers": [
- {
- "id": "9001",
- "price": 0,
- "count": 1
}
]
}
]
}
}{- "posId": "POS-9F3A21",
- "queueNumber": "A17"
}{- "orders": [
- {
- "id": "POS-9F3A21",
- "queueNumber": "A17",
- "state": {
- "status": "COOKING"
}
}
]
}Returns the current state of one or more orders, looked up by the POS ids
you returned from createOrder. It is a read, expressed as POST
because the id list travels in the request body (wrapped under
orderPosIds). LoyaltyPlant uses it to poll / reconcile order lifecycle
when a webhook was missed.
Each returned order carries its state.status (one of the nine
OrderStatus values); state.reason is required when the status is
CANCELED or FAILED, and an optional structured state.posErrorType
may accompany it (see the PosErrorType enum).
Respond within ~5 s connect / 60 s read — LoyaltyPlant's client timeouts.
| SalesOutletId required | integer Numeric id of the establishment this call targets. This is LoyaltyPlant's outlet id, not yours: LoyaltyPlant assigns it and gives it to you during onboarding. Resolve it to the correct venue/menu/configuration on your side. Sent on every LoyaltyPlant→vendor call and on the status webhook. |
| AuthorizationToken required | string Authentication token. For all Digital Ordering API calls this is the
lowercase SHA-256 hex of the shared API key for the outlet
( |
| orderPosIds required | Array of strings POS order ids (the |
{- "orderPosIds": [
- "POS-9F3A21",
- "POS-9F3A22"
]
}{- "requestId": "7e9c2a14-3b6d-4f80-91ac-5d2e6f7a8b90",
- "orders": [
- {
- "id": "POS-9F3A21",
- "queueNumber": "A17",
- "type": "PICKUP",
- "state": {
- "status": "COOKING"
}, - "items": [
- {
- "id": "1",
- "count": 1
}
]
}, - {
- "id": "POS-9F3A22",
- "queueNumber": "A18",
- "type": "PICKUP",
- "state": {
- "status": "AWAITING_PICKUP"
}, - "items": [
- {
- "id": "1",
- "count": 2
}
]
}
]
}