R-Keeper integration
How Fooodo round-trips with R-Keeper in production, in operational detail.
R-Keeper is the only POS connector running in production today. This page documents how the integration actually works — what data crosses the boundary, in which direction, and what you should expect when something goes wrong.
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 does not modify R-Keeper menus, prices, modifiers, or any reference data. R-Keeper is the source of truth.
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 payment confirmation are the two writes that matter most for service. Both are queued, retried with exponential backoff, and logged.
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.
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 error codes 2219 or 13 when a waiter has the order open in the back-office terminal. Fooodo's response:
- Mark the order
Locked - Retry export with exponential backoff: 5 s, 10 s, 20 s, 40 s, 80 s
- 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 and the only fix is to close the order in R-Keeper.
Mark-paid retries
When R-Keeper rejects the PostPayOrder call (often because the order is locked), Fooodo retries on a longer schedule: 60 s, 120 s, 180 s, 240 s, 300 s. Five attempts, total ~17 minutes. The slower cadence reflects that paid orders are not blocking service — they are reconciling.
If all retries exhaust, the order is in Error. The Fooodo payment is still recorded; the R-Keeper side needs a manual reconciliation by the waiter.
Restaurant unreachable
If R-Keeper itself is down or the restaurant is offline, Fooodo flips the restaurant to read-only:
- The customer-facing menu still loads (cached)
- New orders are blocked
- Existing orders queue updates
Once R-Keeper comes back, the queued operations drain and the restaurant returns to normal.
Logging
When an integration issue arises, four log channels are relevant:
rkeeper-requests— every API call with payload and responserkeeper-errors— only the failuresrkeeper-orders— order-specific operationsorder-flow— state transitions on the Fooodo side
For partner support cases, the request log is usually 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.
Mock mode for development
Development environments can run with RKEEPER_MOCK=true. In this mode, all R-Keeper traffic is faked — useful for partner integrators who want to test against the Fooodo menu app without a live R-Keeper instance.
Production never runs in mock mode. Staging mirrors production by hitting the R-Keeper test environment.
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.