Een backend koppelen
Als datahouder stel je de data uit je gebouwbeheersysteem (BMS), energieplatform of BIM-model beschikbaar zonder dat een ander dat systeem ooit rechtstreeks aanspreekt. Je connector neemt de verzoeken aan, toetst ze aan de overeenkomst en stuurt alleen wat mag door naar je systeem. Deze pagina beschrijft wat je daarvoor bouwt en instelt.
Je systeem blijft achter de data plane
Section titled “Je systeem blijft achter de data plane”Een verzoek van een dataontvanger gaat zo. Zijn app roept de gateway van zijn eigen connector aan. Die zet er het toegangstoken van de levering bij, ondertekent het verzoek en stuurt het naar het publieke adres van jouw data plane. Jouw data plane controleert het verzoek en stuurt het door naar je systeem. Het antwoord gaat dezelfde weg terug, ondertekend door jouw data plane. Beide kanten leggen verzoek, antwoord en handtekeningen vast als bewijs.
Je systeem hoeft dus alleen verkeer van je eigen connector te accepteren: van de data plane voor de
data, en van de control plane voor de tests en het ophalen van de punten als je een product
samenstelt. Het adres en de sleutel
van je systeem staan niet in de catalogus en gaan niet mee met een overeenkomst. De beheer-API
toont de sleutel gemaskeerd en geeft hem nooit terug. In de database van je connector staat de
sleutel versleuteld, met BL_DATA_KEY (zie
geheimen in de database); bewaar die sleutel apart
van je back-up.
Of de data van jouw eigen gebouw is of van een rechthebbende voor wie je levert, maakt voor je systeem niet uit. In het eerste geval bied je de data zelf aan als product. In het tweede geval beslist de rechthebbende wie de data krijgt, en levert jouw data plane namens hem (erkenning en aanwijzing).
Systemen en koppelingen
Section titled “Systemen en koppelingen”Je connector kent je backend als systeem en elk gebouw daarin als koppeling. Systeem en gebouw zijn geen paar: een cloudplatform bedient honderden gebouwen, maar één BMS per gebouw kan ook.
Een systeem (in de API: een bron) leg je één keer vast:
| Gegeven | Wat |
|---|---|
| naam | zodat je het herkent |
| adres | waarop je data plane het systeem bereikt, bijvoorbeeld http://bms.intern:9000; alleen voor je eigen netwerk |
| sleutel | optioneel: een header met een waarde, zoals X-Api-Key of Authorization, die de data plane bij elk verzoek meestuurt |
| statuspad | optioneel: een pad dat zegt of het systeem werkt, zoals health |
| sturen toestaan | zonder dit stuurt de data plane alleen GET door; met dit ook POST, PUT en PATCH |
| protocol | buildinglinks (standaard) of generic, voor een andere API |
Een koppeling is één gebouw in dat systeem, met wat afwijkt van de standaard:
| Gegeven | Standaard | Anders als… |
|---|---|---|
| gebouw | het BAG-pand, bijvoorbeeld nl.bag.pand.0014100040022681 |
|
| id in het systeem | het BAG-id zelf | je systeem het gebouw anders noemt, bijvoorbeeld 4711 |
| sleutel | die van het systeem | dit gebouw een eigen sleutel heeft |
| levert | meetwaarden | het systeem voor dit gebouw de indeling levert, of beide |
De data plane routeert, hij vertaalt niet. Naar buiten blijft het pad
/v1/buildings/nl.bag.pand.0014100040022681/…, en alle controles gebeuren op dat pad. Pas als een
verzoek is goedgekeurd, zet de data plane in het adres naar je systeem alleen het deel
v1/buildings/{gebouw} om naar v1/buildings/{id in het systeem}, met de sleutel van de koppeling.
Body, query en de rest van het pad blijven zoals ze zijn.
Een systeem zonder koppelingen bedient niets. Bij het Buildinglinks-protocol komt een verzoek
alleen door voor de gebouwen die gekoppeld zijn. Een systeem met het protocol generic heeft geen
koppelingen: daar bepaal je met toegestane paden wat bereikbaar is, met * voor één padsegment en
** voor de rest, bijvoorbeeld api/sites/4711/**. Noem minstens één pad: zonder paden laat de
data plane niets door. Moet de hele API bereikbaar zijn, zeg dat dan met alleen **; een leeg veld
betekent dat niet. Afbakenen per meetpunt kan bij generic niet; het pad is de fijnste afbakening.
Wijzig je een systeem of een koppeling, dan geldt dat voor een levering namens een rechthebbende vanaf het volgende verzoek, en voor een eigen product vanaf de volgende start van de levering.
Wat je backend moet implementeren
Section titled “Wat je backend moet implementeren”De data plane kijkt niet naar de inhoud van verzoeken en antwoorden. De apps van dataontvangers rekenen er wel op dat elk systeem in de dataspace dezelfde API heeft: het Buildinglinks-protocol, versie 1. Spreekt je systeem een eigen API, dan zet je er een vertaallaag voor die dit protocol spreekt, en koppel je die laag als systeem.
| Methode | Pad | Wat je backend doet |
|---|---|---|
GET |
/v1/buildings/{gebouw}/points |
discovery: de meetpunten en stuurpunten, elk met een Brick-klasse, bij getallen een eenheid, of het punt leesbaar of schrijfbaar is, en voor schrijfbare punten de grenzen |
GET |
/v1/buildings/{gebouw}/points/values |
actuele waarden: per punt de laatste waarde met tijdstip en kwaliteit |
POST |
/v1/buildings/{gebouw}/setpoints |
setpoints: nieuwe waarden voor schrijfbare punten, met een ontvangstbevestiging |
PATCH |
/v1/buildings/{gebouw}/fallback-policy |
fallback-policy: veilige waarden en een time-out, met een ontvangstbevestiging |
POST |
/v1/buildings/{gebouw}/commands |
groepscommando’s, zoals het vermogen van alle batterijen, met een ontvangstbevestiging |
GET |
/v1/buildings/{gebouw}/topology |
indeling: verdiepingen, ruimtes en welk punt waar zit, als de koppeling de indeling levert |
Voor alleen lezen zijn discovery en actuele waarden genoeg. De andere endpoints heb je nodig als je sturen of de indeling aanbiedt. Het formele contract, met alle berichten, queryparameters en foutcodes, staat in de naslag bij de Backend-API.
Daarnaast verwacht de data plane dit van je backend:
- Het gebouw in het pad is het BAG-id, of de id die je bij de koppeling hebt opgegeven.
- Filteren op punten. Mag een dataontvanger maar een deel van de punten, dan noemt hij ze met
point_ids(herhaald of met komma’s). De data plane controleert dat elk genoemd punt in de overeenkomst valt en geeft het verzoek ongewijzigd door; je backend moet dan ook alleen die punten geven. - Binnen 20 seconden antwoorden. Daarna geeft de data plane het op. Een body is hooguit 16 MiB.
- Alleen een paar headers. De data plane geeft de body, de query,
Content-Type,Accepten de sleutel van het systeem of de koppeling door. Het toegangstoken en de handtekening van de dataontvanger komen niet bij je backend.
TechnischWat de data plane bij elk verzoek controleert
Een verzoek komt binnen op /public/{pad} van je data plane. Die controleert in deze volgorde:
- Het toegangstoken (
Authorization: Bearer) is door deze data plane uitgegeven en hoort bij een levering die loopt. Een gepauzeerde of beëindigde levering krijgt403. - Bij een levering namens een rechthebbende: de erkenning en aanwijzing is nog actief.
- Het recht.
GET,HEADenOPTIONSvragen leesrecht, elke andere methode het recht om te sturen. Daarna de methodes en paden die het systeem toestaat, zonder..of.in het pad. - De afbakening uit de overeenkomst: het gebouw in het pad hoort erbij, en bij een deel van de
punten noemt een leesverzoek zijn punten (
point_ids) en zet een setpoint alleen die punten. Verzoeken voor het hele gebouw, zoalsinclude=, groepscommando’s en de fallback-policy, worden dan geweigerd. Een begintijd in de overeenkomst geldt ook voorfrom,startensince. - De handtekening in
BL-Request-Signature: van de contractpartij, hooguit 60 seconden oud en niet eerder gebruikt. Vraagt de overeenkomst ondertekende verzoeken, dan weigert de data plane een verzoek zonder handtekening.
Een geweigerd verzoek bereikt je backend niet. Het krijgt 401 unauthenticated als het token of de
handtekening niet klopt, en 403 forbidden als het buiten de levering, de overeenkomst of het systeem
valt. Een goedgekeurd verzoek gaat naar je systeem. De data plane tekent het antwoord (BL-Response-Signature) en legt verzoek en antwoord
vast in het bewijslog.
Setpoints en ontvangstbevestigingen
Section titled “Setpoints en ontvangstbevestigingen”Sturen gaat langs dezelfde route als lezen, met extra waarborgen. Een voorwaarde die sturen toestaat, vraagt standaard ondertekende verzoeken, en een aanvraag om te sturen keurt de rechthebbende altijd zelf goed.
- De app van de dataontvanger stuurt nieuwe waarden naar zijn gateway, met het tijdstip van
uitgifte, en eventueel een geldigheid (
valid_until) en een idempotentiesleutel. - De gateway tekent het verzoek en legt het vast voordat het weggaat.
- Je data plane controleert recht, afbakening en handtekening, en legt de schrijfactie vast vóórdat
je systeem hem krijgt. Lukt dat vastleggen niet, dan gaat de schrijfactie niet door
(
503 evidence_unavailable). - Je systeem controleert elk punt: het bestaat, het is schrijfbaar, de waarde heeft het juiste type en valt binnen de grenzen uit de discovery.
- Je systeem antwoordt met
202en een ontvangstbevestiging (receipt). Zijn alle waarden aangenomen, dan is de statusaccepted. Zijn er een paar geweigerd, danpartially_accepted, met per geweigerd punt de reden. Is niets aangenomen, dan antwoordt je systeem met een fout en is er niets uitgevoerd.
Een ontvangstbevestiging zegt dat je systeem de opdracht heeft aangenomen, niet dat de ruimte al de
nieuwe temperatuur heeft. Met verify_after zeg je na hoeveel tijd de app de waarde kan teruglezen.
Komt hetzelfde verzoek met dezelfde idempotentiesleutel nog eens, bijvoorbeeld na een time-out, dan
geef je dezelfde ontvangstbevestiging terug en voer je de opdracht niet twee keer uit. Een verzoek
waarvan valid_until voorbij is, weiger je.
De fallback-policy vangt uitval van de dataontvanger op. Hij legt per punt een veilige waarde vast, met een time-out. Komt er binnen die tijd geen aangenomen setpoint meer, dan zet je systeem de veilige waarden zelf. Het gebouw blijft dan niet op de laatste optimalisatie hangen.
TechnischVoorbeeld van een setpoint en een ontvangstbevestiging
De app stuurt dit naar POST /gateway/{alias}/v1/buildings/nl.bag.pand.0599100000701251/setpoints
op zijn eigen gateway. Jouw systeem ontvangt het als
POST /v1/buildings/nl.bag.pand.0599100000701251/setpoints, of met de id uit de koppeling.
{ "issued_at": "2026-09-29T14:05:00Z", "valid_until": "2026-09-29T14:20:00Z", "idempotency_key": "rtm-01-4711", "setpoints": [ { "point_id": "9b41d0aa", "value": 21.5 }, { "point_id": "3c7e11f0", "value": 35.0 } ]}Het tweede punt ligt boven het maximum. Het systeem neemt het eerste aan en antwoordt met
202 Accepted:
{ "buildinglinks_version": "1.0", "building_id": "nl.bag.pand.0599100000701251", "receipt_id": "rcpt-019A4F2C7B3E0001", "received_at": "2026-09-29T14:05:01Z", "status": "partially_accepted", "accepted_count": 1, "rejected": [ { "code": "value_out_of_range", "point_id": "3c7e11f0", "message": "Value 35.0 exceeds max 26.0" } ], "verify_after": "PT10S"}Instellen in de beheeromgeving
Section titled “Instellen in de beheeromgeving”Je hebt de rol operator of admin nodig.
- Systeem toevoegen. Ga naar Systemen en kies Systeem toevoegen. Vul naam, adres, eventueel de sleutel en een statuspad in, en zet Sturen toestaan aan als je sturen wilt aanbieden. Klik op Systeem testen.
- Gebouwen koppelen. Kies op de pagina van het systeem Gebouw koppelen. Zoek het gebouw in de BAG, vul de id in je systeem in als die afwijkt, eventueel een eigen sleutel, en wat het systeem voor dit gebouw levert. Test de koppeling.
- Aanbieden of leveren.
- Is het gebouw van jou, kies dan onder Gebouwen Gebouw toevoegen, Over de data beslissen en de herkomst Uit een eigen systeem. Daarna bied je producten aan onder Producten.
- Lever je voor een ander, kies dan Data leveren: je kiest het systeem en de koppeling, en biedt de rechthebbende aan om namens hem te leveren. Heeft de rechthebbende jou al aangewezen, dan accepteer je die aanwijzing onder Erkenningen en kies je daar het systeem.
TechnischHetzelfde via de beheer-API
De beheer-API staat onder /api/v1 op de control plane. Een beheersysteem gebruikt een API-sleutel
die een admin maakt onder Instellingen, API-sleutels, en stuurt die mee als
Authorization: Bearer. Een systeem met twee gekoppelde gebouwen, waarvan je systeem er één met een
eigen id kent:
POST /api/v1/sourcesContent-Type: application/json
{ "id": "noord-bms", "name": "Gebouwbeheersysteem Noord", "protocol": "buildinglinks", "health_path": "health", "objects": ["nl.bag.pand.0014100040022681", "nl.bag.pand.0014100040011234"], "links": [{ "object": "nl.bag.pand.0014100040011234", "local_id": "4711" }], "backend": { "base_url": "http://bms.intern:9000", "auth_header_name": "X-Api-Key", "auth_header_value": "…", "allowed_methods": ["GET", "POST", "PATCH"] }}Elk gebouw in objects is een koppeling; links noemt alleen wat afwijkt (local_id,
auth_header_value, provides met data, topology of both). allowed_methods bepaalt welke
methodes de data plane doorstuurt: ["GET"] om te lezen, ["GET", "POST", "PATCH"] om ook te sturen.
Noem er minstens één: een lege lijst weigert de API, want de data plane laat dan niets door.
allowed_paths laat je bij het Buildinglinks-protocol weg: de koppelingen vullen het, met
v1/buildings/{gebouw}/** per gebouw. Bij generic noem je de paden zelf, ["**"] voor de hele
API; ook daar weigert de API een lege lijst. Met PUT /api/v1/sources/{id} wijzig je het systeem. Stuur je de gemaskeerde sleutel ongewijzigd terug,
dan blijft de opgeslagen sleutel staan. Een gebouw dat een product of levering gebruikt, kun je niet loslaten.
De verbinding testen
Section titled “De verbinding testen”Je test op twee niveaus, allebei vanaf de control plane van je connector, met verzoek en antwoord zoals in een terminal:
- Systeem testen doet
GET {adres}/{statuspad}met de sleutel van het systeem. Zonder statuspad kijkt de test alleen of het adres antwoordt. - Koppeling testen doet de discovery van één gebouw,
GET …/v1/buildings/{id}/points(of…/topologyvoor een koppeling die alleen de indeling levert), en geeft het aantal punten.
Gaat het mis, dan zegt de test waar: het systeem is niet bereikbaar, de sleutel is geweigerd, het
gebouw is onbekend, of het antwoord is geen lijst van punten. Via de API kan het ook, met
POST /api/v1/sources/{id}/test (met object voor een koppeling) of POST /api/v1/sources/test voor
een systeem dat nog niet is opgeslagen.
Het hele pad, van de app van een dataontvanger tot je systeem, test de dataontvanger met de verbindingstest bij zijn abonnement (zie een app koppelen). Die zegt ook of het misgaat bij zijn eigen data plane, bij de jouwe, bij de overeenkomst of bij je systeem.