Naar de inhoud
BoilerBuddy

API

API-referentie

Alle endpoints, velden en antwoorden van de API voor apparaten, rechtstreeks uit het contract (OpenAPI 3.1).

Basis-URL
https://boilerbuddy.eu/api/v1
Contract
BoilerBuddy device API 1.0.0
OpenAPI-specificatie

De referentie komt uit het contract en is in het Engels.

Inleiding

The API a BoilerBuddy device — our own BoilerBuddy Alpo, or something you build yourself — uses to fetch its configuration and upload readings.

Ownership. This file is the contract: the server implements it, and the device software follows it. A change to the API is made here first.

Time. All timestamps are ISO-8601 with a timezone. The server stores UTC.

Idempotency. A reading is identified by device, sensor and measured_at (to the second). Uploading the same reading twice is harmless: the first one wins and the second counts as a duplicate. The Idempotency-Key header is accepted but the server does not depend on it.

Partial acceptance. A well-formed batch always gets 200, also when some readings are rejected, duplicates or for unknown sensors. The device may then delete the batch from its buffer, except the rejected readings marked retry: true: it keeps those and sends them again in a later batch. 422 means the batch as a whole is malformed and resending it unchanged will not help.

Limits. Per token: 60 requests per minute in total, of which at most 10 uploads to /device/readings. Per IP address: 600 requests per minute. A 429 carries Retry-After; wait that long and resend the same batch. Catching up on a buffer therefore goes at up to 10 000 readings per minute. Within a batch, readings may fall on at most 7 calendar days (UTC): send the buffer in order, oldest first, and a batch stays within a day or two. A device may quarantine at most 25 000 readings for unknown sensors per day.

Authenticatie

Authorization: Bearer …

bb_live_<device id>_<secret> for a live device, bb_test_… for a simulated one. Shown once when the device is created or the token replaced.

GET/device/config

What to read, and how

Met token (Authorization: Bearer)

Sensors of the device's circuit with where each lives on the controller, the controller's protocol and connection settings, and the scan interval. Poll with If-None-Match to get 304 when nothing changed.

Parameters

VeldWaarOmschrijving
If-None-Match
string
header

Antwoorden

  • 200

    Current configuration

    Schema: DeviceConfig

    Headers: ETag

  • 304

    Unchanged since the given version

  • 401

    Missing, unknown, revoked or replaced token, or an inactive device

    Schema: Error

  • 429

    More than 60 requests per minute for this token, more than 10 uploads of readings per minute, or more than 600 requests per minute from this address. Respect Retry-After.

    Headers: Retry-After

POST/device/readings

Upload a batch of readings

Met token (Authorization: Bearer)

Parameters

VeldWaarOmschrijving
Idempotency-Key
string (uuid)
header

Optional; the batch_uuid. Correctness does not depend on it.

Body van het verzoek

Schema: ReadingsBatch

{
    "batch_uuid": "0d4f6c1e-8a51-4c43-9b6b-3c1f0f7a2e11",
    "readings": [
        {
            "sensor": "T1",
            "value": 61.4,
            "measured_at": "2026-10-08T11:59:00Z"
        },
        {
            "sensor": "R2",
            "value": 1,
            "measured_at": "2026-10-08T11:59:00Z"
        },
        {
            "sensor": "T5",
            "value": null,
            "quality": "no_response",
            "measured_at": "2026-10-08T11:59:00Z"
        }
    ]
}

Antwoorden

  • 200

    Batch processed. Delete it from the buffer.

    Schema: ReadingsResult

  • 401

    Missing, unknown, revoked or replaced token, or an inactive device

    Schema: Error

  • 422

    The request as a whole is invalid; do not resend it unchanged

    Schema: ValidationError

  • 429

    More than 60 requests per minute for this token, more than 10 uploads of readings per minute, or more than 600 requests per minute from this address. Respect Retry-After.

    Headers: Retry-After

  • 503

    Temporarily unavailable; retry with backoff

POST/device/status

Heartbeat

Met token (Authorization: Bearer)

Send periodically, also when there is nothing to upload, so the server can tell "offline" from "quiet". Every authenticated request updates last-seen; this one also stores the device's own status.

Body van het verzoek

Schema: DeviceStatus

Antwoorden

  • 204

    Stored

  • 401

    Missing, unknown, revoked or replaced token, or an inactive device

    Schema: Error

  • 422

    The request as a whole is invalid; do not resend it unchanged

    Schema: ValidationError

  • 429

    More than 60 requests per minute for this token, more than 10 uploads of readings per minute, or more than 600 requests per minute from this address. Respect Retry-After.

    Headers: Retry-After

POST/device/hello

Pair a new device

Zonder token

For a device without a token. It generates a uuid once, keeps it, and sends it here. Until a user claims the device, the answer is a pairing code to show (BB-XXXX-XXXX, valid 24 hours; poll every 15 seconds or so, the same code comes back until it expires, then a new one). The user enters the code in the portal. The next hello returns the token. Store it before anything else, then use it (/device/status or /device/config). Until the device has authenticated with it, every hello returns a new token and the previous one stops working, so a lost answer only means asking again. Once the device has used its token, claimed is true without a token.

If the owner makes a new key by hand in the portal, the pairing is closed: the next hello gets a new pairing code, and nothing revokes the key made by hand.

Keep the uuid secret until the device has used its token: whoever knows it collects one. Limited to 10 requests per minute per uuid, and 30 per minute per IP address (an IPv6 /64 counts as one address), so a few boxes behind one router can pair at the same time.

Body van het verzoek

Schema: Hello

{
    "device_uuid": "7b0e2f4c-1d3a-4e5b-9c6d-8f7a6b5c4d3e",
    "hw_serial": "00000000abcd1234",
    "firmware_version": "0.3.0"
}

Antwoorden

  • 200

    Pairing state

    Schema: HelloResult

    waiting

    {
        "claimed": false,
        "pairing_code": "BB-7KQ4-M2XD",
        "expires_at": "2026-10-10T12:00:00+00:00"
    }

    Claimed; the token is not used yet (repeated with a new token until it is)

    {
        "claimed": true,
        "token": "bb_live_12_aBcD…"
    }

    Claimed, and the device has used its token

    {
        "claimed": true
    }
  • 422

    The request as a whole is invalid; do not resend it unchanged

    Schema: ValidationError

  • 429

    More than 10 requests per minute for this uuid, or 30 from this address. Respect Retry-After.

    Headers: Retry-After

Schema's

ReadingsBatch

VeldOmschrijving
batch_uuid
string (uuid)

Stable per batch, also when resent after a crash.

clock
readingsverplicht

minItems: 1 · maxItems: 1000

Reading

VeldOmschrijving
sensorverplicht
string

Voorbeeld: T1

pattern: ^[A-Za-z0-9_.\-]{1,16}$

value
number | null

Required when quality is ok; may be null or left out otherwise. Numeric strings are accepted for compatibility. Absolute values above 1e9 are rejected.

quality
string

Only ok readings count towards averages, minima and maxima.

Mogelijke waarden: oksensor_faultout_of_rangeno_response

Standaard: ok

measured_atverplicht
string (date-time)

When the value was read. At most 5 minutes in the future and 365 days in the past. Stored to the second.

clock_synced
boolean

false if the wall clock was not yet synchronised when this reading was taken. The server then corrects measured_at from mono_ns and the batch's clock anchor, provided boot_id matches; otherwise the reading is rejected with clock_unsynced. Without a synchronised anchor that rejection carries retry: true: send the reading again once the clock is synchronised.

Standaard: true

boot_id
string

Required for clock correction.

mono_ns
integer

Monotonic clock at measurement.

ClockAnchor

The device clock at the moment of sending. Optional; only needed for clock correction.

VeldOmschrijving
boot_idverplicht
string

maxLength: 64

syncedverplicht
boolean
sent_atverplicht
string (date-time)
sent_mono_nsverplicht
integer

minimum: 0

ReadingsResult

VeldOmschrijving
acceptedverplicht
integer

Newly stored readings

duplicatesverplicht
integer

Readings the server already had

unmappedverplicht
string[]

Sensor codes that are not part of the device's circuit. These readings are kept in quarantine for 90 days, so a wiring or configuration mistake can be found; they are not counted as accepted. At most 16 unknown codes per batch.

rejectedverplicht
object[]
rejected[].indexverplicht
integer

Position in readings

rejected[].retryverplicht
boolean

Keep this reading and send it again in a later batch. Only for clock_unsynced without a synchronised anchor, and batch_spans_too_many_days.

rejected[].reasonverplicht
string

Mogelijke waarden: invalid_iteminvalid_sensorinvalid_valuevalue_requiredinvalid_qualityinvalid_measured_atmeasured_at_in_futuremeasured_at_too_oldclock_unsyncedtoo_many_unknown_sensorsunmapped_quota_exceededbatch_spans_too_many_days

DeviceConfig

VeldOmschrijving
config_versionverplicht
string

Hash of the content; equals the ETag.

deviceverplicht
object
device.id
integer
device.name
string
device.mode
string

Mogelijke waarden: livesimulated

intervalsverplicht
object
intervals.scan_seconds
integer

Voorbeeld: 60

controllerverplicht
object | null

null while the device has no circuit

controller.brandverplicht
string

Voorbeeld: OEG

controller.modelverplicht
string

Voorbeeld: KSW-E, KSW, KS2W

controller.protocolverplicht
string

How the controller is read; now modbus_ascii or modbus_rtu. More protocols may follow, so take any string and skip a controller whose protocol you do not know.

Voorbeeld: modbus_ascii

controller.connectionverplicht

Settings in the shape of the protocol.

sensorsverplicht

SensorConfig

VeldOmschrijving
sensor
string

Voorbeeld: T1

name
string

Voorbeeld: Temperatuur zonnepaneel

type
string | null

Voorbeeld: collector_temp

category
string | null

Mogelijke waarden: temperaturerelayenergyflownull

unit
string | null

Voorbeeld: °C

position
integer | null

Which tank or panel

address

Where the controller keeps the value, in the shape of the controller's protocol; null when it does not have this sensor.

plausible_min
number | null
plausible_max
number | null

ModbusConnection

For modbus_ascii and modbus_rtu

VeldOmschrijving
slave_address
integer

Voorbeeld: 128

baudrate
integer

Voorbeeld: 9600

parity
string

Mogelijke waarden: NEO

bytesize
integer

Voorbeeld: 7

stopbits
integer

Voorbeeld: 1

timeout_ms
integer

Voorbeeld: 1000

ModbusAddress

For modbus_ascii and modbus_rtu. One 16-bit register, divided by 10 to the power of decimals.

VeldOmschrijving
register
integer

Voorbeeld: 38

decimals
integer

Voorbeeld: 1

signed
boolean
function_code
integer

Mogelijke waarden: 34

DeviceStatus

VeldOmschrijving
firmware_version
string

maxLength: 32

uptime_s
integer

minimum: 0

ip
string
rssi
integer

minimum: -120 · maximum: 0

buffered
integer

Readings waiting in the local buffer

minimum: 0

last_error
string

maxLength: 500

Hello

VeldOmschrijving
device_uuidverplicht
string (uuid)

Generated once by the device and kept; random (version 4).

hw_serial
string

For example the Pi's CPU serial

maxLength: 64

firmware_version
string

maxLength: 32

unit_key
string

Only a BoilerBuddy Alpo has one, from production. Claimed by the code on its sticker, it then gets its token without showing a code. Your own hardware leaves it out.

maxLength: 64

HelloResult

VeldOmschrijving
claimedverplicht
boolean
pairing_code
string

Only while unclaimed. Show it to the user.

pattern: ^BB-[2-9A-HJ-NP-Z]{4}-[2-9A-HJ-NP-Z]{4}$

expires_at
string (date-time)

Only while unclaimed

token
string

After the claim, until the device has authenticated with a token: each answer carries a new one and the previous stops working. Never sent once one was used.

Error

VeldOmschrijving
message
string

ValidationError

VeldOmschrijving
message
string
errors
object