Configuration¶
The device keeps one persistent configuration document, versioned and validated, covering display behavior, audio, network proxy, the management UI, device-access bridge policy, NFC, Wi-Fi/Bluetooth and more. This page covers how to read and change it; the other guides link back here for the fields specific to their topic.
Reading the current configuration¶
Returns the full effective configuration as JSON. A representative (default) document looks like:
{
"schema": 1,
"device": { "name": "front-desk-1" },
"display": {
"rotation": 0, "forced_mode": null, "resume_on_boot": false,
"screens": {
"HDMI-A-1": { "rotation": 0, "forced_mode": null, "url": null, "browser": { "backend": "wpe" } }
}
},
"audio": { "output": "hdmi", "volume": 80, "mute": false },
"browser": { "backend": "wpe" },
"proxy": { "url": null, "insecure": false },
"waiting": { "show_status": true },
"content": { "startup_bundle": "" },
"ui": { "enabled": true },
"splash": { "boot": { "enabled": true }, "shutdown": { "enabled": true } },
"devices": {
"origins": [],
"hid": { "enabled": false, "allow": [] },
"serial": { "enabled": false, "allow": [] },
"usb": { "enabled": false, "allow_driver_detach": false, "allow": [] }
},
"nfc": {
"enabled": false, "backend": "auto", "origins": [],
"i2c": { "bus": 1, "address": 36 }, "spi": { "bus": 0, "cs": 0 },
"poll": { "interval_ms": 250 }, "feedback": { "on_detect": true }
},
"wifi": {
"enabled": false, "mode": "station", "country": null, "networks": [],
"ap": { "ssid": "", "psk": "", "band": "2.4", "channel": 0 }
},
"bluetooth": { "enabled": false, "name": null, "discoverable": false }
}
Web UI: Config page shows and edits this document.
Changing configuration¶
Send only the fields you want to change as a partial JSON object to PATCH:
curl -X PATCH http://10.99.0.1/api/v1/config \
-H 'Content-Type: application/json' \
-d '{"device": {"name": "checkout-3"}, "audio": {"volume": 60}}'
The response echoes the resulting full configuration plus an applied map, keyed by dotted field
path (e.g. "device.name", "audio.volume"), with one of two values per field you touched:
live— the change took effect immediately, no reboot needed.reboot_required— the value was persisted and will take effect starting from the next boot; the currently running device is unaffected until then.
This is an honesty guarantee: the device never claims a change is live unless it genuinely is.
Fields that are live today: device.name, display.rotation (for the primary/rendered screen),
audio.volume/audio.mute, ui.enabled, the devices.* bridge policy, nfc.*, and wifi.*/
bluetooth.* (daemon start/stop/reconfigure happens immediately). Everything else — including
display.forced_mode, per-screen rotation on non-primary screens, audio.output, and
content.startup_bundle — is reboot_required in the current release; the persisted value is
always correct and takes effect at the next boot regardless.
An invalid patch (unknown field, bad enum value, value out of range, a schema that doesn't
match the current one) is rejected wholesale with 400 bad_request — nothing from that request
is applied, so you never end up with a half-written config.
Every successful PATCH also emits a config_changed event ({paths, config}) on the event
stream (see Events), so other subscribed hosts, the waiting page, and the
management UI all stay in sync without polling.
Web UI: Config page → edit a field → Save; the page shows whether each change applied live or needs a reboot.
Device name¶
Applies live (sets the device's hostname immediately) and is what shows up in GET /device, the
waiting page's status strip, and the management UI header.
Display settings¶
display.rotation — 0 | 90 | 180 | 270, degrees clockwise. Applies live for the primary
screen (pushed to the browser without a restart) but the change is only guaranteed to survive
future reboots once persisted — which PATCH already does. For 90°/270°, content on the rotated
screen must be authored for the swapped logical viewport (window.innerWidth/innerHeight do
not auto-swap; only the visual output is rotated).
display.forced_mode — force a specific video mode instead of the display's preferred EDID
mode, as "<connector>:<WxH@R>" (e.g. "HDMI-A-1:1920x1080@60"). reboot_required — the mode is
applied at boot before the browser starts.
display.resume_on_boot — if true, the device restores whatever was on screen (URL or bundle)
across a reboot instead of returning to its normally-configured target. reboot_required.
display.screens.<connector-id> — per-screen overrides (rotation, forced mode, startup URL,
browser backend) for boards with more than one output. In the current release the device still
renders on a single (primary) screen only, so per-screen settings for a secondary connector
are accepted and persisted but not yet rendered; use GET /display/screens to see the connector
ids the device recognizes on your board.
See Displaying content → change rotation for a worked example.
Startup target¶
content.startup_bundle — name of a content bundle to show at boot,
default "" (none). Only consulted at boot time; reboot_required; doesn't navigate the
currently running display (POST /display/bundle does that live).
Boot target resolution, in order (each step falls through to the next on failure):
display.resume_on_bootistrueand a saved state exists → restore it.content.startup_bundleis set, and that bundle exists and validates → show it.- The primary screen's configured startup
url(display.screens.<primary>.url) → navigate there. - Otherwise → the waiting page.
With everything at defaults, the device always boots to the waiting page.
Audio¶
See the dedicated Audio guide for audio.volume/audio.mute/audio.output.
Proxy¶
curl -X PATCH http://10.99.0.1/api/v1/config \
-d '{"proxy": {"url": "http://10.99.0.2:3128", "insecure": false}}'
proxy.url is used by the kiosk browser (and the device's own outbound requests) for all
network access — useful when the device only has USB connectivity and the host shares its own
network access via a local proxy. proxy.insecure: true disables TLS certificate verification
through the proxy; the supported alternative to that is uploading a trusted CA chain (PUT
/api/v1/files/certificates/ca) so certificates validate normally.
Management UI toggle¶
ui.enabled (default true) controls whether the device serves the built-in
management UI at http://10.99.0.1/. Applies live — flipping it off
immediately makes / return 404 while /api/v1/* keeps working; flipping it back on restores
the UI without a restart.
Device-access bridge (HID/USB/serial passthrough)¶
devices.origins and the per-domain devices.hid/devices.serial/devices.usb blocks control
which page origins, and which specific peripherals plugged into the Pi's USB-A host ports, a
displayed page may reach through the navigator.hid/navigator.usb/navigator.serial
polyfills. All three domains are disabled by default. This is a Pi 4/Pi 5-only capability (no
free USB host port on the Zero 2 W); the block is accepted but inert there. Example enabling a
specific USB-serial scanner for a trusted page origin:
curl -X PATCH http://10.99.0.1/api/v1/config -d '{
"devices": {
"origins": ["https://pos.example.com"],
"serial": {"enabled": true, "allow": [{"usbVendorId": 1027, "usbProductId": 24577}]}
}
}'
Applies live (re-evaluated on the page's next requestDevice()/open() call). This is a
specialized capability outside the scope of this page; the fields themselves are documented in
the configuration reference.
NFC, Wi-Fi and Bluetooth¶
These have their own dedicated guides, since enabling them involves more than a single config field: NFC, Wireless (Wi-Fi/Bluetooth).
Splash images¶
See Boot and shutdown splash for the splash.boot.enabled/splash.shutdown.enabled
toggles and how to upload the images themselves.
Odoo IoT Box emulation¶
See Odoo IoT Box emulation for the odoo block.
Schema versioning¶
The configuration document carries a "schema" integer. You don't need to set it when patching —
the device rejects a patch whose schema doesn't match the current version, which exists so
older tooling fails loudly instead of writing a config it doesn't fully understand, rather than
silently corrupting fields it doesn't recognize.
See also¶
- Configuration reference — full field-by-field schema.
- Displaying content, Audio, Boot and shutdown splash, NFC, Wireless, Odoo IoT Box emulation — topic guides that build on this page.