R-Keeper connector
The live reference implementation of Fooodo's POS connector contract — what data crosses the boundary, the sync cadences, loyalty pricing, and how failures are retried in production.
R-Keeper is the live reference implementation of Fooodo's POS connector contract — the only connector running in production today, and the shape every additional connector lands on. The contract itself is POS-agnostic; new connectors are scoped per-customer and quoted on demand. This page documents how the R-Keeper round-trip actually works: what data crosses the boundary, in which direction, and what you should expect when something goes wrong. POS-specific method names below are R-Keeper's; equivalents on other connectors map to the same connector-contract operations. POS vendors evaluating whether their system can be connected should start with the POS integration requirements instead — it's the evaluation-level spec this page is the live reference for.
What Fooodo asks R-Keeper for
| Request | Purpose |
|---|---|
GetRestaurantMenu | Fetch the menu (products, categories, prices) |
GetRestaurantModifiersGroups | Fetch modifier groups and ingredients |
GetRestaurantStatus | Check whether the restaurant is online |
GetTableStatus | Get table-level order state |
GetTableOrderStatus | Get full order details |
These are read-only calls — Fooodo never modifies the R-Keeper menu, prices, modifiers, or reference data.
What Fooodo writes to R-Keeper
| Request | Purpose |
|---|---|
PostOrder | Create or update an order; lock orders |
PostPayOrder | Mark an order as paid |
PostSendMessage | Send a kitchen message |
PostApplyPersonalCard | Apply loyalty cards |
Order creation and mark-paid are the two writes that sit on the critical path; both are queued, retried with exponential backoff, and logged in detail. The other two are supplementary.
Per-restaurant configuration
R-Keeper credentials live in the restaurant config. Each restaurant in Fooodo carries:
- API endpoint URL
- Restaurant ID inside R-Keeper
- Optional loyalty pricing endpoint
- Optional loyalty card program code
- Optional menu item code for tip routing
Different restaurants in the same Fooodo company can point at different R-Keeper instances. This is how multi-location chains operate today.
R-Keeper sync cadences
The integration polls and pushes on a few different schedules. Operators and partner-support engineers care about these because they set expectations for how quickly a back-office change shows up in the customer-facing app — and how quickly a customer-facing order shows up at the terminal.
| What runs | Cadence | Why |
|---|---|---|
| Restaurant online/offline check | every minute | Flip the customer app to read-only the moment R-Keeper goes unreachable |
| Pull new orders from R-Keeper | every 4 minutes | Pick up orders waiters opened at the terminal so guests can pay through Fooodo |
| Refresh in-progress order state | every 2 minutes | Catch waiter edits (added items, modifiers, voids) on already-pulled orders |
| Refresh menu-item availability | every 5 minutes | Reflect "sold out" and re-enables back to the customer app within minutes |
| Full menu / category / price refresh | daily at 09:00 | Pre-service sync of the canonical menu — set the day's prices and structure |
Writes from Fooodo to R-Keeper are not scheduled — they go out as soon as the event happens (order created, order paid, message sent) and retry on backoff if R-Keeper rejects. Retry budgets are documented under Failure handling below.
Per-table loyalty pricing
Tables can opt into loyalty pricing in two ways, and a restaurant can run both shapes at once:
- Post-hoc card application. When the table is configured with a restaurant-level loyalty card code, Fooodo applies the card after the order is exported — R-Keeper recalculates the line items with the card's discount logic and Fooodo reflects the adjusted totals back to the guest. The order is exported once and then patched.
- URL-routed alternate prices. When the table is configured with a loyalty-pricing connector URL, Fooodo asks R-Keeper for the loyalty-priced menu before the guest sees prices. The cart, the modifier prices, the totals — everything the guest sees is already the loyalty price. This is the lower-latency path and is the one we deploy on new chains.
Whichever shape a table uses, coupon-code discounts are layered on top correctly: a guest with a loyalty card can still redeem a coupon, and the coupon discount reaches R-Keeper independently of the loyalty path. This was not always the case — older builds gated coupons behind the post-hoc path and skipped them on URL-routed tables — so partners onboarding off an older deployment should verify their R-Keeper side counts both effects.
Loyalty pricing comes from R-Keeper. Fooodo does not maintain a parallel discount table.
Staff and order attribution
Fooodo records which waiter is associated with each order so admin reports can break down service and tipping by staff. The flow has two pieces:
- Staff directory sync runs nightly at 08:45 and refreshes the waiter list from the chain's staff system — names, job titles, and the external staff IDs that map to R-Keeper. Waiters are not created manually in Fooodo; the admin list is read-only.
- Order attribution happens when an order is pulled or updated from R-Keeper. R-Keeper's order status response carries the assigned waiter ID; Fooodo matches it against the synced staff directory and writes the attribution to the order. Bill-split children inherit the parent's waiter.
Today this attribution is admin-visible only. The payment page exposes the waiter on the operator side; there is no guest-facing waiter card on the order confirmation yet. That surface is on the roadmap.
Failure handling
The integration is designed to tolerate R-Keeper being briefly unavailable. Three failure modes you will see in practice:
Locked orders
R-Keeper returns specific error codes (2219 or 13) when a waiter has the order open at the back-office terminal. Fooodo's response:
- Mark the order
Locked - Retry the export on an exponential-backoff schedule (5 s → 10 s → 20 s → 40 s → 80 s — five attempts, ~155 seconds total)
- If all retries fail, escalate to
Error
In normal operation, locks last seconds. A persistent lock usually means a waiter walked away with the order open at a terminal and the fix is to close it in R-Keeper directly.
Mark-paid retries
When R-Keeper rejects the PostPayOrder call (most often because the order is locked at a terminal), Fooodo retries on a slower schedule: 60 s → 120 s → 180 s → 240 s → 300 s — five attempts over ~17 minutes. The slower cadence is deliberate: a paid order is reconciling, not blocking service, so we give R-Keeper time to free up.
If all five retries exhaust, the order ends in Error. The Fooodo payment is still recorded; R-Keeper needs a quick manual reconciliation by the waiter.
Restaurant unreachable
If R-Keeper itself is down or the restaurant has gone offline, Fooodo flips the restaurant to read-only automatically (the every-minute availability check picks it up):
- The customer-facing menu still loads from cache — guests don't see a broken page
- New orders are blocked at checkout with a clear message
- Existing orders queue updates rather than dropping them
Once R-Keeper comes back, the queued operations drain and the restaurant returns to normal — no operator intervention required.
Logging
The integration writes structured logs at four cuts: the full request/response trail, a failures-only feed, an order-scoped feed, and a state-transition feed. For partner support cases, the request log is the first place to look — the most common cause of "the order didn't reach the kitchen" is a momentary R-Keeper outage that the retry budget didn't cover, and the failures feed surfaces it immediately.
Mock mode for development
Development environments can run with R-Keeper traffic mocked end-to-end — useful for partner integrators who want to test against the Fooodo menu app without a live R-Keeper instance. Staging mirrors production by hitting the R-Keeper test environment; production never runs in mock mode.
What the kitchen sees
The contract is: an order created through Fooodo arrives in R-Keeper indistinguishably from an order created at a terminal. Same printer routing, same KDS behavior, same reports. Kitchen staff do not need to know Fooodo exists, and waiters keep using R-Keeper exactly as they did before.
This is intentional. The integration is invisible by design; the operating system extends R-Keeper, it does not replace it.
POS integration requirements
Evaluation spec for POS vendors — connectivity requirements, the connector contract, performance envelope, failure semantics, and how an integration is scoped.
Payments
How payments flow through Fooodo — supported methods and currencies, Mollie under the hood, tip and donation routing, the payment state machine, and webhook reconciliation.