Skip to content

Configuration reference

The device's persistent configuration is a single JSON document, read with GET /api/v1/config and updated with PATCH /api/v1/config (partial updates — send only the fields you want to change). It is stored at /data/config.json on the device, schema-versioned (schema: 1), written atomically.

curl http://10.99.0.1/api/v1/config
curl -X PATCH http://10.99.0.1/api/v1/config \
  -H 'content-type: application/json' \
  -d '{"display": {"rotation": 90}}'

Every PATCH response reports, per field actually present in the patch, whether it was applied live or is reboot_required (persisted immediately, effective at the next boot or subsystem restart) in the applied map, keyed by dotted path (e.g. "display.rotation"). Unknown fields are rejected 400 bad_request with no partial write — a typo never gets silently ignored. A successful PATCH also emits the config_changed event (see Events).

Live-apply is incremental

A field's applied value can widen from reboot_required to live as the corresponding subsystem lands (spec 04). As of this writing, genuinely live fields are: ui.enabled, device.name, display.rotation (primary screen), and audio.volume/audio.mute. Everything else currently reports reboot_required, even though it is persisted immediately — check the live applied map from your own PATCH response rather than hard-coding this list, since it changes across releases.

device

Field Type Default Validation / notes
device.name string Applied live via sethostname (also at boot). Used as the kernel hostname and, unless bluetooth.name is set, the Bluetooth adapter alias.

display

Field Type Default Validation / notes
display.rotation integer enum 0 \| 90 \| 180 \| 270 0 Primary screen's rotation applies live (CSS output transform).
display.forced_mode string, e.g. "1920x1080@60" null (auto-detect) reboot_required.
display.resume_on_boot boolean false reboot_required.
display.screens.<id>.rotation integer enum 0 \| 90 \| 180 \| 270 inherits display.rotation Keyed by screen id from GET /display/screens. Non-primary screen rotation is reboot_required.
display.screens.<id>.forced_mode string inherits display.forced_mode reboot_required.
display.screens.<id>.url string (URI) null (waiting page) Startup URL for that screen.
display.screens.<id>.browser.backend string enum wpe inherits browser.backend

Per-screen entries are board-gated: single-output boards (e.g. Pi Zero 2 W) only accept the primary screen's id; any other id is 400 screen_not_found. v1 renders single-screen only — per-screen config is accepted and persisted, but multi-output rendering is future work; see the board matrix.

audio

Field Type Default Validation / notes
audio.output string enum hdmi hdmi reboot_required (routing).
audio.volume integer 80 0100. Applied live (ALSA amixer).
audio.mute boolean false Applied live.

browser

Field Type Default Validation / notes
browser.backend string enum wpe wpe Only one backend exists in v1; the field is future-proofing.

proxy

Field Type Default Validation / notes
proxy.url string (URI) null (no proxy) e.g. http://10.99.0.2:3128. Exported to the WebKit network proxy API.
proxy.insecure boolean false Disables TLS verification through the proxy when true. Prefer uploading a CA chain (PUT /files/certificates/ca) instead of this.

waiting

Field Type Default Validation / notes
waiting.show_status boolean true Whether the built-in waiting page shows device/network status.

ui

Field Type Default Validation / notes
ui.enabled boolean true Applied live — takes effect on the next HTTP request, no restart. Serves the embedded management SPA at / when true; when false, / returns 404 and only /api/v1 is exposed. The trusted-link model means the SPA is unauthenticated either way (spec 00); this flag only controls whether it's served.

content

Field Type Default Validation / notes
content.startup_bundle string "" (none) Name of the bundle to display at boot. Consulted only at boot — PATCH persists immediately but always reports reboot_required; it never navigates the running display (use POST /display/bundle for that). Validated for name syntax only — may reference a bundle uploaded later. A missing/invalid bundle at boot falls through to the waiting page.

splash

Field Type Default Validation / notes
splash.boot.enabled boolean true reboot_required — consumed by the boot init script.
splash.shutdown.enabled boolean true reboot_required — consumed by the shutdown init script.

The images themselves are managed out-of-band via PUT/GET/DELETE /files/splash and /files/splash/shutdown (see REST & WebSocket), not in this block.

devices — device-access bridge policy

Policy surface controlling which page origins and which HID/USB/serial peripherals on the Pi's USB-A ports the displayed page may reach via the navigator.hid/usb/serial polyfills. Pi 4 / Pi 5 only — accepted but inert on boards without a USB host port (e.g. Zero 2 W). All domains default disabled; a device is reachable only if it matches an allow entry and the page origin is in devices.origins. Applied live (re-evaluated on the next request*/open call from the page).

Field Type Default Validation / notes
devices.origins array of string [] Shared cross-domain allow-list of page origins, e.g. ["https://pos.example.com", "file://"].
devices.hid.enabled boolean false
devices.hid.allow[] array of {vendorId, productId, usagePage?, usage?} (integers) []
devices.serial.enabled boolean false
devices.serial.allow[] array of {usbVendorId, usbProductId} (integers) []
devices.usb.enabled boolean false
devices.usb.allow_driver_detach boolean false If true, displayd may detach a kernel driver to claim an allow-listed interface. Never detaches usbhid/usb-storage or the gadget controller. Dangerous — can break OS function; leave false unless you know you need it.
devices.usb.allow[] array of {vendorId, productId, classCode?, subclass?, protocol?, serialNumber?} (integers/string) []

nfc — reader & tags (spec 10)

Policy + binding surface for the optional NFC capability. Accepted on all boards, inert where no reader is present. When nfc.enabled is false (the default), every /api/v1/nfc/* endpoint returns 503 nfc_disabled.

Field Type Default Validation / notes
nfc.enabled boolean false A backend/bus change reinitialises the reader.
nfc.backend string enum auto \| pn532-i2c \| pn532-spi \| acr122u auto
nfc.origins array of string [] Page origins permitted to use the NFC API/events.
nfc.i2c.bus integer 1
nfc.i2c.address integer 36 (0x24)
nfc.spi.bus integer 0
nfc.spi.cs integer 0
nfc.poll.interval_ms integer 250 Minimum 50. Applies live.
nfc.feedback.on_detect boolean true Auto LED/buzzer feedback on tag detection. Applies live.

wifi — station / AP (spec 12)

Accepted on all boards; inert (503-gated) where GET /device's capabilities.wifi is false (the virt/generic model profiles). All wireless fields apply live (daemon start/stop/reconfigure).

Field Type Default Validation / notes
wifi.enabled boolean false Master switch: unblocks rfkill and starts wpa_supplicant. Requires wifi.country to be set — enabling without a valid country is rejected 400 bad_request with no partial write.
wifi.mode string enum station \| ap station
wifi.country string, pattern ^[A-Z]{2}$ (ISO 3166-1 alpha-2) — (no default) Required (non-null) whenever wifi.enabled is true.
wifi.networks[] array of WifiNetwork [] Ordered station credentials; first match wins, ties broken by priority. Mirrored by GET/PUT /wifi/networks.
wifi.ap WifiApConfig AP-mode parameters.

WifiNetwork

Field Type Default Validation / notes
ssid string — (required) 1–32 characters.
psk string WPA passphrase (8–63 chars) or 64-hex-digit PSK. Omitted/empty ⇒ open network. Returned verbatim on reads (trusted-link model — no redaction).
security string enum auto \| wpa2-psk \| wpa3-sae \| open auto auto = wpa_supplicant key-management negotiation based on psk presence.
hidden boolean false Actively scan for this SSID (hidden network).
priority integer 0 Higher wins among visible configured networks.

WifiApConfig

Field Type Default Validation / notes
ssid string 1–32 characters.
psk string WPA2 passphrase, 8–63 chars, required to start an AP. Returned verbatim on reads.
band string enum "2.4" \| "5" "2.4" "2.4" is valid on every board; "5" is rejected 400 on boards without dual-band radios (e.g. Zero 2 W). AP is WPA2-PSK only; "5" is restricted to non-DFS channels.
channel integer 0 (auto) 0 = displayd picks a fixed non-DFS channel for the band; otherwise must be a valid channel of that band/regulatory domain.

bluetooth (spec 12)

Accepted on all boards; inert (503-gated) where GET /device's capabilities.bluetooth is false. enabled: true starts dbus-daemon + bluetoothd and powers the adapter — applied live.

Field Type Default Validation / notes
bluetooth.enabled boolean false
bluetooth.name string null (inherits device.name) Adapter alias.
bluetooth.discoverable boolean false Whether the adapter is discoverable while powered.

Full example

{
  "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 },
  "ui":      { "enabled": true },
  "content": { "startup_bundle": "" },
  "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": null, "psk": null, "band": "2.4", "channel": 0 }
  },
  "bluetooth": { "enabled": false, "name": null, "discoverable": false }
}

See the configuration guide for task-oriented walkthroughs (rotating the display, setting up a proxy, restricting the device-access bridge) and the Wireless guide for wifi/bluetooth setup end to end.