Appearance
Backoffice → Kiosk Relations
How backoffice settings flow to the Kiosk self-service device.
Overview
Kiosks are self-service ordering terminals (Android tablets) placed in-store. A kiosk's behaviour is driven almost entirely by the device profile assigned to it, plus the kiosk's location settings (tipping, languages, receipt content, notes) and the location's branding profile (Settings → Brand, /settings/brand), which supplies the kiosk's logos, fonts and colours. Menus reach the kiosk through the profile, not the device. Most settings are pushed to the device by a ReloadMenu re-sync event rather than requiring a restart.
┌─────────────────────────────────────────────────────────────────────────┐
│ BACKOFFICE │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Device │ │ Menus │ │ Location │ │ Payments │ │
│ │ Profiles │ │ │ │ (collect_ │ │ (per-device │ │
│ │ │ │ │ │ tips) │ │ terminal) │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
└─────────┼───────────────┼───────────────┼───────────────┼───────────────┘
│ │ │ │
▼ ▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────────────┐
│ KIOSK │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Device │ │ Menu │ │ Tips / │ │ Payment │ │
│ │ Config │ │ Display │ │ Dining │ │ Terminal │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘The kiosk app (
upvendo-kiosk) is an Android client. How it visually renders any of these values (layouts, copy, transitions) is not verified here; this doc maps the back-office settings and the backend data that drive the kiosk.
How a kiosk is configured
A kiosk device (Device, type Kiosk) carries only a handful of its own fields — its name, its location_id, an optional device_profile_id, and its paired payment terminal. Everything about the ordering experience (menus, dining options, idle behaviour, screensaver, schedule, receipt handling, customer flow, order-number display) lives on the device profile it points at. The menu is not set on the device.
| Device-level setting | Field | Kiosk impact |
|---|---|---|
| Device Name | name | Identifier in the back office / reports (unique per merchant) |
| Assigned Location | location_id | Which location's data, in-house settings, and tipping the kiosk uses |
| Device Profile | device_profile_id | The profile that supplies menus and all kiosk settings (optional) |
| Payment Terminal | viva_wallet_terminal_id / stripe_terminal_id / external_data.square | The card reader paired to this device, per payment provider |
See Kiosk Setup, Device Profiles, and Payments.
Device Profile → Kiosk
These are the actual fields on a device profile (app/RawModels/DeviceProfile.php, validated by StoreDeviceProfileRequest). Each profile is location-scoped and its device type is hardcoded to Kiosk. Saving a profile fires a ReloadMenu event so assigned devices re-sync.
| Profile setting | Field(s) | Kiosk impact |
|---|---|---|
| Default Menu | custom_default_menu, default_menu_id | The menu served to the kiosk. When custom_default_menu is off, the location's in-house default menu is used; when on, default_menu_id applies. |
| Extra Menus | extra_menu_ids | Additional menus served alongside the default. When custom_default_menu is on, the profile's own extra_menu_ids are used; when it is off, the location's in-house extra_menu_ids are used instead (DeviceProfile::getExtraMenus() DeviceProfile.php:316-337). Only the profile-owned list is editable in the profile form. |
| Dining Option 1 / 2 | dining_option_1, dining_option_2 | Two slots, each For Here, Takeout, or None; the two must differ. A slot set to None removes that choice. |
| Custom dining pictures | use_custom_pictures_for_dining_options, dining_option_1/2_content_id | When on, custom images per non-None dining option; otherwise default plate/shop images. |
| Idle Timeout | idle_timeout_type, idle_timeout_seconds, show_warning_for_seconds | Ends an idle session and clears entered data. Standard = warning after 45 s, shown 15 s (fixed by backend); Custom = your own values (min 1 s each). |
| Idle Screensaver | idle_screensaver.enabled, idle_screensaver.minutes, screensaver_content_id | Optional full-screen image after N idle minutes. Separate from the idle timeout. |
| Menu Item Columns | menu_item_columns | Item grid layout: 2 or 3 columns (default 2). |
| Order Number Display | order_number_display | What the customer sees after ordering: queue_number, order_number, or both (default both). |
| Customer Identification | allow_customer_identification, customer_identification_methods | When on, customers can identify via the enabled methods: member_code (default on), email, phone. With loyalty active, identified customers see points/rewards. |
| Sell Without Stock | sell_without_stock | Whether out-of-stock items can still be ordered (toggle shown only when the location tracks inventory). |
| Use Pager | use_pager, pager_instructions | Whether the kiosk hands out a pager plus the title/subtitle instructions shown. |
| Reset Language On Complete | reset_language_on_order_complete | Whether the kiosk resets to the default language after an order completes. |
| Receipt Options | receipt_options.print/email/phone, auto_print_receipt | Which receipt-delivery options are offered (defaults print: true, email: true, phone: false) and whether receipts auto-print. |
| Section Selection | section_selection_enabled, section_ids | When on, the customer picks a section/table (section_ids required, max 6). |
| Order Instructions | order_instructions.* | Optional instruction text shown to the customer — one message for all, or separate per dining option. |
| Device Schedule | schedule_type, schedule | When the kiosk is active: Default (location hours), Custom (per-day ranges), or Deactivated. |
Fields that do NOT exist on a device profile: there is no theme/dark-mode toggle, no language list/default-language, no "show images"/image-size, no category-layout, no "upsell enabled", no "tips enabled", and no screensaver media-type or "schedule enabled" toggle. That does not mean the kiosk has no visual theming — colours, fonts and logos come from the location's branding profile (below), not the device profile. Tipping, upsells, languages, receipt content, and notes all come from elsewhere too (below).
Tipping is a location setting, not a profile field
Tip collection is not a device-profile field. The kiosk reads tipping from the location's in-house settings collect_tips object. When the kiosk menu data is generated, tipping_enabled is taken from location->getInHouseSetting()['collect_tips']['enabled'] (DeviceProfile::generateMenuData()). For Kassanet-integrated menus (Hendrickx / Vanhoutte) this is further gated by the POS tipCalculatorActivated flag from the Kassanet API. See Tips.
| Backoffice setting | Field | Kiosk impact |
|---|---|---|
| Collect Tips (location in-house) | in_house_setting.collect_tips.enabled | Whether the kiosk offers a tip step |
| Tip Options | collect_tips.options (default [10, 15, 20]) | Percentage buttons shown |
| Calculate Tips | collect_tips.calculate_tips | Stored preference (After taxes / Before taxes) — never read by any pricing code. The real tip base is country-dependent: US = pre-VAT (totalExcludingVAT), non-US = VAT-inclusive post-discount (totalBeforeTip) (TransactionService.php:1225, :712). Kassanet (Hendrickx / Vanhoutte) menus use the same base without subtracting the discount (TransactionService.php:694). |
Upsells are a separate feature
Upsell suggestions on the kiosk are not a device-profile toggle. They are configured as Upsell Groups (Marketing → Upsell Groups), scoped per location, and served to the location's ordering channels. See Upsell Groups. (Channel-side rendering on the kiosk is not verified here.)
Languages come from Settings → Languages
The kiosk's language list is not a profile field. genKioskDetails() ships languages[] — every Language record with status Published in the merchant's tenant database (Device::getMerchantData() Device.php:967) — and default_language, taken from Location::getPreferredLanguage() (Device.php:921). Both are typed on the kiosk side as languages / default_language (upvendo-kiosk/src/types/auth.ts:14-15). Configured at Settings → Languages (/settings/languages). The profile's only language-related field is reset_language_on_order_complete.
Receipt content comes from Settings → Receipts
Location::getReceiptSetting() is pushed to the kiosk as receipt_settings (Device.php:929), configured at Settings → Receipts (/settings/receipts). This is distinct from the profile's receipt_options / auto_print_receipt, which only decide which delivery options are offered and whether receipts auto-print.
Notes come from the location's in-house settings
allow_notes (per-item notes) and allow_order_notes (order-level notes) reach the kiosk from the location's in-house settings, not the device profile (Device.php:1025-1027). They are gated independently; a device saved before the split has no allow_order_notes and falls back to allow_notes.
Branding → Kiosk
The kiosk's look comes from the location's branding profile, not from the device profile. Device::genKioskDetails() merges a branding block built by getBrandingData() from Location::getBrandingProfile() (Device::getBrandingData(), Device.php:854-885, merged into genKioskDetails() at :1049), which the kiosk consumes as ShopDetails.branding (upvendo-kiosk/src/types/auth.ts:93-109). Configured at Settings → Brand (/settings/brand, individual profiles at /settings/brand/profiles/:id).
| Branding-profile field | Kiosk effect |
|---|---|
logo_url | Brand logo shown in the kiosk UI |
receipt_logo_url | Logo embedded on printed/emailed receipts (base64) |
favicon_url | Icon for the web/browser build |
banner_url | Banner image asset |
heading_text_font | Font used for headings |
body_text_font | Font used for body text |
primary_color | Primary brand colour |
secondary_color | Secondary brand colour |
primary_text_color | Primary text colour |
secondary_text_color | Secondary text colour |
primary_background_color | Primary background colour |
secondary_background_color | Secondary background colour |
promo_code_font | Font for promo-code display |
promo_code_font_color | Text colour for promo-code display |
promo_code_background_color | Background colour for promo-code display |
Alongside branding, the payload also carries primary_color_palette from the same profile (Device.php:885). Exactly how the kiosk app applies each value on screen is not verified here.
Menus → Kiosk
A menu reaches a kiosk only through the device profile. The menu itself does not carry a "default menu" flag — that relationship is set on the profile (default_menu_id / extra_menu_ids).
| Backoffice setting | Kiosk impact |
|---|---|
| Menu visibility includes Kiosk | The menu is eligible to appear on kiosks for that location |
| Item name / description / price / image | Rendered on item cards (kiosk-app rendering not verified here) |
| Item availability / stock | Hidden or shown as unavailable; sell_without_stock on the profile governs ordering out-of-stock items |
| Allergens / dietary preferences | Stored per item and served to the kiosk |
| Display groups & order | The grouping/order of items the customer browses |
Backend guards protect live kiosks: a menu used by a kiosk profile that has active devices cannot be set to Draft/Archive, and its Kiosk visibility cannot be removed (both return HTTP 400). See Menus, Menu Items, and Modifiers.
Payments → Kiosk
Kiosk/in-person card payments use the location's in-person provider. A terminal is paired per device, not on the payment profile.
| Backoffice setting | Field | Kiosk impact |
|---|---|---|
| Viva Wallet terminal (default in-person provider) | viva_wallet_terminal_id | Kiosk takes card payments through that Viva terminal |
| Stripe Terminal reader (Stripe in-person) | stripe_terminal_id | Paired reader the kiosk uses for card payments |
| Square Terminal (when Square POS connected) | external_data.square | Square handles payments; profiles/Stripe/Viva are bypassed |
| Test mode (per location) | payment_test_mode (on the location) | Simulated payments, no real charge |
There is no single "terminal" select with a "None" option — a device simply has no terminal paired until one is added, and each device has one paired reader at a time. See Payments.
Kiosk payment flow (backend-confirmed):
Customer completes order
│
▼
Tips step (only if the location's collect_tips.enabled is true)
│
▼
Payment:
├── Card → terminal activated (Viva / Stripe reader / Square)
│ status updates published to the device via Firebase
│ (Submitting → Processing → Succeeded / Failed / Canceled)
│
└── Free order (total 0) → no provider, marked complete automatically
│
▼
Order confirmation (queue/order number per order_number_display)Kiosk transactions are idempotency-keyed: if a completed transaction already exists for a given idempotency key, the existing order is returned instead of creating a duplicate.
Dining Options
Each profile has two dining-option slots, each chosen from For Here, Takeout, or None (DiningOptions::deviceProfile()). The two slots must differ. Setting one slot to None means the customer is not asked to choose and all orders take the other option.
For Here (Dine In)
Profile: dining_option_1 or dining_option_2 = "For Here"
↓
Kiosk: "For Here" choice available (skipped if the other slot is None)
↓
Customer selects → order labelled "For Here"Takeout
Profile: dining_option_1 or dining_option_2 = "Takeout"
↓
Kiosk: "Takeout" choice available (skipped if the other slot is None)
↓
Customer selects → order labelled "Takeout"Remote Control (FCM)
The back office can send remote-control commands to a kiosk over Firebase Cloud Messaging (FCM). Commands are delivered to all FCM tokens registered for that device and only apply to Kiosk-type devices (DeviceService::remoteControl() returns early for any non-Kiosk device).
| Command | Value (RemoteControlCommands enum) |
|---|---|
| Restart | restart |
| Logout | logout |
| Firmware update | firmware_update |
| Update APK | update_apk |
Device Status (Online / Offline)
A kiosk's status is derived (Device::getStatus()), not stored as a free-text field. The DeviceStatuses enum also defines Needs Attention and Critical Issue, but kiosk status resolves to Online or Offline:
- Offline if the device is not activated, has never checked in (
last_seen_atnull), hasis_online === false, or was last seen longer ago than the offline threshold. - Online if it is activated and was seen within the threshold (
devices.offline_threshold_seconds, default 900 s / 15 minutes).
Real-Time Sync
Pushed to the device on save (ReloadMenu re-sync)
- Menu assignment and content (default + extra menus, display groups, items)
- Device profile settings (dining options, idle behaviour, screensaver, schedule, receipt options, customer flow, order-number display)
- Item availability and stock
Saving or updating a device profile, or updating/reordering a menu, dispatches a ReloadMenu event for the location so assigned kiosks re-sync their configuration and menu data. (A device restart is not required for these changes.)
Sent over FCM (real time)
- Remote-control commands (restart / logout / firmware update / update APK) —
DeviceService::remoteControl()→SendFcmBroadcastJob(DeviceService::remoteControl,DeviceService.php:1500-1508)
Sent over Firebase Realtime Database
- Kiosk payment status at
devices/{deviceId}/payment-status(Submitting / Processing / Succeeded / Failed / Canceled), plusqueue_number/order_number/receipt_no/payment_methodonce each is known —FirebaseTrait::publishPaymentStatus()(FirebaseTrait::publishPaymentStatus,FirebaseTrait.php:43). This is not FCM.
Via the kiosk transaction API
- Order submission (idempotency-keyed)
- Payment processing
- Receipt handling
Troubleshooting
Kiosk Not Showing a Menu
Possible Causes:
- No device profile assigned, or the profile has no default menu
- If the profile uses a custom default menu,
default_menu_idis not set - The menu's visibility does not include Kiosk, or the menu has no items
- Network connectivity issue
Backoffice Check:
- Device Management → Devices → confirm a device profile is assigned
- Device Management → Profiles → confirm the default menu (or that the location's in-house default menu exists)
- Menus → confirm the menu includes the Kiosk channel and has items
Payment Terminal Not Working
Possible Causes:
- No terminal paired to the device
- Terminal offline or wrong terminal paired
- Network issue
Backoffice Check:
- Device Management → Devices → open the device → Reader / Terminal section
- Verify the terminal is online in the provider dashboard (Viva / Stripe / Square)
Kiosk Shows Offline
Possible Causes:
- Device not activated or hasn't checked in
- Not seen within the offline threshold (default 15 minutes)
- Network/power problem
Backoffice Check:
- Confirm the device is activated and powered on
- Check the device's connectivity; status returns to Online once it checks in within the threshold
Tips Not Showing on the Kiosk
Possible Causes:
collect_tips.enabledis off in the location's in-house settings (tipping is not a profile field)- For Kassanet (Hendrickx / Vanhoutte) locations, the POS
tipCalculatorActivatedflag is off
Backoffice Check:
- In-House Settings → Collect Tips → enable and set options
- For Kassanet locations, confirm tipping is enabled on the POS side