Kombine Flex Portal API · v1 · Integrationsvejledning

Byg din egen portal eller tilslut en AI-agent

Brug almindelig HTTPS/JSON (REST) til programmer og klientbiblioteker, eller tilslut en AI-agent gennem MCP. Begge bruger den officielle portals API og managerens eksisterende rettigheder. Du behøver ikke databaseadgang.

More1-værktøjer (beta 0.13.0)

Begge operationer bruger POST /api/v1/tenant/tools/..., en genbrugelig manager-bearertoken og JSON. Kræver aktiv manager, More1 (29), Bank Read og mindst ét ressourcegrant på sitets tenant; et bank- eller lokationsgrant er tilstrækkeligt. More1 er et værktøj på tenant-niveau. Det betroede site fastlægger tenant; der er ingen tenant- eller bankvælger. Maksimum 8 KiB pr. request; svar er no-store. Gem ikke input. Beregninger ændrer ingen data.

ConvertToolTime — POST .../time tager {format,value,timeZone,offset?}. Formater: ms2000, mi2000, ss2000, utc og local. Tal er decimalstrenge, aldrig JavaScript-tal. Brug browserens eksplicitte IANA-zone, fx Europe/Copenhagen. UTC er ISO 8601 med Z/forskydning; lokal tid er en ISO-datotid uden forskydning. Epoch er 2000-01-01T00:00:00Z. Kombine.Flex.Time definerer MS2000 (millisekunder), MI2000 (minutter) og SS2000 (sekunder). MI2000 her er UTC-minutter og adskiller sig fra reservationers lokale Log5-minutter.

curl -X POST "$API/api/v1/tenant/tools/time" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  --data '{"format":"local","value":"2026-10-25T02:30:00","timeZone":"Europe/Copenhagen"}'

Succes: success=true og result:{ms2000,mi2000,ss2000,utc,local,timeZone,offset,dateInputRounded,millisecondRemainder,secondRemainder}. Numerisk MS2000 bevarer hver millisekund. Datoindlæsning afkortes til bibliotekets 2 ms og markerer tab med dateInputRounded. Hele sekunder/minutter afkortes; resten returneres. Intervaller: MS2000 0–252455615999999, reversibel MI2000 0–2147483646, SS2000 0–2147483647. MI2000/SS2000 er null uden for deres interval; ingen wraparound. UTC og lokal tid skal passe i år 1–9999. Serverens lokale tidszone anvendes aldrig.

Tidsvalidering giver HTTP 400 med success=false,error,result:null,offsetChoices. Fejl: invalid-time-value, invalid-time-format, invalid-time-zone, time-out-of-range, invalid-local-time (sommertidshul), ambiguous-local-time og invalid-time-offset. Tvetydig tid returnerer begge {offset,utc}-valg. Send valget eksplicit igen, fx "offset":"+01:00". Vælg aldrig stiltiende en forekomst.

InspectToolKid — POST .../kid tager {"kid":"DIN_KID"}, højst 2048 tegn, og returnerer {canonicalKid,normalized,fields:[{name,value,enumName,enumNumber,index}]}. Værdier og tal er strenge. Den installerede Kid-parser og de faktiske skrivbare datafelter bruges, inklusive MainId. Nul-/standardfelter udelades; Settings/Icons bevarer nulbaserede indeks. Afledte egenskaber og skalare aliaser gentages ikke. Normalized angiver ændret/ignoreret input; sammenlign canonicalKid. Inspektion slår ikke objekter op, bekræfter ikke eksistens og giver ingen adgang.

Fejl: 400 ret input eller KID; 401 log ind igen; 403 manglende Tab/Read/scope; 503 prøv senere. Klientgenerering og downloadpakker opdateres ved betaforberedelse.

Widgets til din hjemmeside

Levende eksempler på de samme JavaScript-widgets som det anonyme overblik i portalen. Ingen iframe, konto eller API-nøgle.

Widgets til din hjemmeside — Kopiér HTML

Kopiér et eksempel til din egen side. Brug tenantens API-værtsnavn og det rigtige release-miljø. Modulet opretter sin egen Shadow DOM og indlæser sit stylesheet, så hjemmesidens CSS ikke ændrer kortene. Tekster, navne og standardfarver kommer fra API’et. Elementets bredde bestemmer layoutet: kortene bruger mindst 300 px og fordeles automatisk på rækker. Brug machineCount: 1, 2 eller 3 til forskellige tilfældigt valgte kørende maskiner. Brug mountProgressWidgets til et bestemt unit-, location- eller bank-KID.

Modulet sender kun GET-kald uden credentials. Disse offentlige widget-læsninger tillader eksterne origins; manager- og sessionsoperationer beholder deres CORS-begrænsninger til præcise origins. Har hjemmesiden Content Security Policy, skal API-origin tillades i script-src, connect-src, img-src og style-src; dynamisk fremdrift kræver også inline styles, købsgrafen kræver data: i img-src, og WashDoc-visninger kræver blob: i img-src. Brug HTTPS og en browser med ES-moduler, Shadow DOM, fetch og AbortSignal.timeout. Indsæt aldrig credentials. Data er live og kan være utilgængelige; 503 betyder utilgængelig, aldrig nul. Hvert kort prøver igen automatisk og markerer bevarede tal som utilgængelige efter fejl. ready-promise fortæller om initialiseringsfejl; unavailableText angiver en lokaliseret fejltekst ved opstart. Kald destroy(), når en widget-gruppe fjernes eller udskiftes. Polling stopper også automatisk, når elementet fjernes.

Antal kørende maskiner opdateres hvert 15. sekund; køb, reservationer og maskinpuljen hvert minut; aktive brugere hvert minut (API’et cacher antallet i fem minutter). Kald deler et snapshot på siden. Nedtællingen sker lokalt hvert sekund ud fra servertid, beholder valgte maskiner og viser afslutningen i 20 sekunder. Puljens afgrænsning og offentlige felter er uændrede; billederne er API-genererede SVG’er. Beløb holdes adskilt pr. valuta.

GetPublicWidgetPresentation — GET /api/v1/public/widgets

Findes på denne kildekodebranch; endnu ikke udgivet. Deploy API og Web fra samme release, før løsningen bruges på en offentlig hjemmeside.

KID: Unit / Location / Bank

Brug mountProgressWidgets(host, { apiBase, kid, language }) med et kanonisk Unit-, Location- eller Bank-KID fra dette API-site. Der kræves ikke login. Unit viser ét kort; Location viser alle offentlige units på lokationen; Bank viser alle offentlige units på alle bankens lokationer. Slettede objekter og deaktiverede lokationer udelades; deaktiverede units og underunits medtages. Kontodata, temperatur og ekstra indstillinger offentliggøres ikke. Hver side indeholder højst 100 units; Hent flere tilføjer næste side. Indlæste sider opdateres hvert minut med nye cursors. Ukendt resttid markeres; afsluttede kørsler forbliver på 100 %. Siderne er ikke et fastfrosset snapshot.

import { mountProgressWidgets } from "https://YOUR-TENANT-API/docs/widgets.mjs";
const widget = mountProgressWidgets(document.getElementById("machines"), {
  apiBase: "https://YOUR-TENANT-API", kid: "CANONICAL_KID", language: "da"
});
widget.ready.catch(console.error);
// widget.destroy();

GetPublicScopedUnitProgress — GET /api/v1/public/widgets/progress/{kid}?pageSize=100. Følg nextCursor med cursor og samme pageSize. Cursors udløber efter 15 minutter. 400: ugyldigt KID, anden tenant, sidestørrelse eller cursor; 404: ingen offentlig unit; 503: midlertidigt utilgængelig, respekter Retry-After. En tom lokation/bank returnerer et tomt items-array. Disse offentlige GET-kald tillader eksterne origins uden credentials.

Offentlig kvadratisk WashDoc-widget (ikke udgivet)

GetPublicWashDocWidget — GET /api/v1/public/widgets/washdoc offentliggør en begrænset visning fra sitets tenant uden login. Brug kanonisk documentKid til ét dokument eller unitKid til at følge enhedens gemte WashDocId, også dens senest afsluttede dokument. Parametrene udelukker hinanden. Udelad begge for at følge den senest startede, aktuelt kørende offentlige maskine; udvælgelsen er ikke tilfældig. API-sitet fastlægger tenant, som klienten ikke kan ændre.

Kun ikke-slettede banker, lokationer og enheder på aktiverede lokationer med offentlig synlighed medtages. Synligheden genkontrolleres gennem et afgrænset snapshot, der caches i højst 60 sekunder. Svaret indeholder documentKid, unitKid, lokaliserede unitName/locationName, startedAtUtc, nullable endedAtUtc, finished, tekstfri graphSvg og refreshAfterSeconds: 60. Den forenklede graf deler dataindlæsning, fortolkning af ældre målinger, cyklusgrænser, edge-data og renderere med den store graf. Den viser kurveform og peaks uden akser, tekster, målværdier eller rå måletabel. Beboere, betalinger, credentials og vilkårlige indstillinger offentliggøres ikke. Hele dokumentet, tabeller og Excel kræver fortsat login og de normale rettigheder.

mountWashDocWidget fra /docs/widgets.mjs viser ét kvadratisk kort med grafen som cover, maskin-/lokationsnavne og dato/klokkeslæt. Den indlæses efterhånden, når den er synlig, opdateres én gang i minuttet uden overlappende kald og holder pause, når den skjules. Funktionen returnerer ready og destroy(); kald destroy, før elementet erstattes. Den bruger Shadow DOM og det medfølgende stylesheet. Content Security Policy skal tillade API-origin i script-src, style-src og connect-src samt blob: i img-src. Ingen API-nøgle eller bearertoken er nødvendig. For WashDoc opfyldes ready efter første synlige indlæsningsforsøg, også når kortet viser en fejltekst; det garanterer ikke dokumentdata.

<div id="washdoc" style="width:300px"></div>
<script type="module">
import { mountWashDocWidget } from "https://YOUR-TENANT-API/docs/widgets.mjs";
const widget = mountWashDocWidget(document.getElementById("washdoc"), {
  apiBase: "https://YOUR-TENANT-API", documentKid: "CANONICAL_DOC_KID", language: "da"
});
widget.ready.catch(console.error);
// Brug unitKid: "CANONICAL_UNIT_KID" i stedet for documentKid for at følge én enhed.
// Udelad begge KID-parametre for den senest startede kørende offentlige maskine.
// widget.destroy();
</script>

HTTP 400: ugyldige, ikke-kanoniske, modstridende KID'er eller anden tenant; 404: ingen offentlig maskine/dokument; 422: ingen numerisk graf eller dokumentet overstiger grafgrænser; 503: optaget eller utilgængelig, respekter Retry-After: 60. Manglende grafik erstattes aldrig med opdigtede data. Svar er no-store; anonym GET-only CORS tillader eksterne sider uden credentials. Lokalt implementeret, ikke deployet; API og Web skal udgives sammen. Klientpakker opdateres ved betaforberedelse.

Adresser og automatiske koordinater

Implementeret i denne kildegren; endnu ikke deployet. Understøtter Bank, Location, Unit og beboere (User). Tenant, administratorer og installatører er ikke med. Send objektets kanoniske KID fra dette site. Adressen er objektets egne indstillinger uden arv fra overordnede objekter.

Operation IDMetode og stiFormål
GetObjectAddressGET /api/v1/addresses/{kid}Læs indstillinger og revision
UpdateObjectAddressPUT /api/v1/addresses/{kid}Gem Address og Zip med koordinatopslag
LookupObjectCoordinatesPOST /api/v1/addresses/{kid}/lookupSlå den aktuelle adresse op igen
SetObjectCoordinatesPUT /api/v1/addresses/{kid}/coordinatesGem manuelle koordinater
SetObjectCoordinateProvenancePUT /api/v1/addresses/{kid}/provenanceRet koordinaternes oprindelse

Kræver aktiv administrator, tildelt Tab (Users2 for beboere), matchende ressourceadgang, Bank Read og objektets Read-rettighed. Unit kræver også Location Read. Skrivning kræver desuden objektets Write-rettighed. Bank og User kræver adgang til hele banken eller tenanten. Objekter og overordnede objekter skal eksistere og må ikke være slettet. Deaktiverede lokationer kræver tenantadgang. Rettigheder kontrolleres igen under databaselås i hver transaktion.

curl -H "Authorization: Bearer ACCESS_TOKEN" "$API/api/v1/addresses/$KID"
curl -X PUT -H "Authorization: Bearer ACCESS_TOKEN" -H "Content-Type: application/json" \
  "$API/api/v1/addresses/$KID" \
  --data '{"address":"Bjerregade 5","zip":"8722","expectedRevision":"REVISION_FRA_GET"}'
curl -X POST -H "Authorization: Bearer ACCESS_TOKEN" -H "Content-Type: application/json" \
  "$API/api/v1/addresses/$KID/lookup" --data '{"expectedRevision":"SENESTE_REVISION"}'
curl -X PUT -H "Authorization: Bearer ACCESS_TOKEN" -H "Content-Type: application/json" \
  "$API/api/v1/addresses/$KID/coordinates" \
  --data '{"latitude":55771181,"longitude":9697176,"expectedRevision":"SENESTE_REVISION"}'

Sæt API til sitets HTTPS-basisadresse og KID til objektets returnerede ID. Brug den seneste revision ved hvert kald. Begge adressestrenge er påkrævet, men må være tomme; ydre mellemrum fjernes. Maksimum er 512 tegn for address og 64 for zip; kontroltegn afvises. Ændring af Address eller Zip udløser højst ét koordinatopslag. Zip med et postnummer alene udløser desuden ét opslag efter postbyen, også i Manuel. Sitets betroede tenant vælger landet: Team, Nortec og Electrolux bruger DK (fire cifre), Washco bruger GB (fuldt britisk postnummer med bogstaver, fx SW1A 1AA), og Finelec bruger FI (fem cifre, fx 00100). Andre tenants springer postbyopslaget over. Klienten kan ikke vælge landet. Koordinatopslag bruger også det tilknyttede land som regionsprioritering. Et præcist og entydigt resultat gemmes i Zip som fx 7470 Karup J. Allerede indtastet bytekst bevares; manglende, tvetydige eller fejlede opslag lader postnummeret stå alene. Postbyopslaget ændrer aldrig manuelle koordinater. Mangler postbyopslaget et bynavn, kan svaret fra opslag på hele adressen udfylde det, når land og postnummer matcher præcist. I Manuel udfylder dette adresseopslag kun Zip og bevarer koordinater og koordinatkilde. Ingen af opslagene gentages automatisk. Brug svarets zip og revision ved efterfølgende ændringer. Uændrede værdier udløser intet opslag; brug lookup til et bevidst nyt forsøg. Der er ingen automatiske baggrundsforsøg.

Adressetransaktionen indsætter begge felter og tømmer tidligere automatiske Latitude og Longitude og sætter AutoLatitudeLongitude til 0. Et vellykket opslag indsætter koordinatparret og UTC-måneden (1–12) samlet. Ved fejl forbliver automatiske koordinater tomme. Værdien 30 markerer manuelle koordinater, som bevares ved adresseændring; et eksplicit opslag erstatter dem kun ved succes. Samtidige ændringer giver Superseded uden overskrivning. Alt gemmes som nye historikposter i bankens Log2 for Bank/Location/Unit eller Log7 for User, med Sync=0. Succes bekræfter lagring, ikke enhedens kvittering.

LookupObjectCoordinates understøtter også en Location med tom Address: den slår det gemte Name plus Zip op. Kun ved succes gemmes Name som Address i samme transaktion som Latitude, Longitude og UTC-måneden. Fejlede navneopslag bevarer værdierne; manglende eller ugyldigt Name/Zip giver IncompleteAddress. Samtidig omdøbning gør svaret forældet. Der tilføjes intet ekstra opslag eller automatisk genforsøg.

Svaret indeholder kid, address, zip, latitude, longitude, autoLatitudeLongitude, revision og outcome. Koordinater og oprindelse kan være null. Koordinater er heltal i milliontedele grader: latitude ±90000000 og longitude ±180000000; nul er gyldigt. Outcome: Success, Unchanged, IncompleteAddress, ManualCoordinatesPreserved, ManualCoordinatesSaved, NoResult, TransientFailure, PermanentFailure, NotConfigured eller Superseded. HTTP 200 kan betyde gemt adresse med fejlet opslag; kontrollér outcome.

ProblemDetails med code: 400 ugyldigt input/KID, 401 ændret/udløbet session, 403 manglende rettighed, 404 manglende/usynligt/slettet objekt, 409 forældet revision (address-conflict), 503 utilgængeligt lager. Genindlæs ved 409. Læs tilbage efter 503, timeout eller mistet svar før et nyt forsøg: adressen kan allerede være gemt. Lokationsoversigten har et adressekort med redigering og kort over Enheder. Genererede klienter og downloadpakker er synkroniseret i version 0.4.2.

SetObjectCoordinateProvenance ændrer kun AutoLatitudeLongitude: 0 (ukendt), 1–12 (måned for automatisk opslag), 13 (ældre afventende status), 20 (ældre fejlstatus), 30 (manuel). Værdierne 1–12 kræver et gyldigt gemt koordinatpar; 30 kan vælges, før koordinater findes. Svaret er ProvenanceSaved; der startes hverken opslag eller automatiske genforsøg. canWrite er en visningsoplysning fra den aktuelle rettighedskontrol; hver skrivning kontrollerer rettigheder igen.

curl -X PUT -H "Authorization: Bearer ACCESS_TOKEN" -H "Content-Type: application/json" \
  "$API/api/v1/addresses/$KID/provenance" \
  --data '{"value":30,"expectedRevision":"LATEST_REVISION"}'

Portalen gemmer adresse og postnummer, når det enkelte felt mister fokus. Manuel vælger 30; Auto vælger 13. Tilstandsvalget alene kalder ikke Google. Kortet læser gemte koordinater hvert tiende sekund, mens siden er synlig, og opdateres ved ændringer. Disse læsninger starter aldrig geokodning. Hver adresseændring giver højst ét opslag, når kilden ikke er 30; succes gemmer UTC-måneden. Ingen baggrundsjob eller automatiske genforsøg efter fejl.

Download Windows-appen

Den endnu ikke udgivne app-kildekode i version 0.2.9 til Android, iPhone/iPad og Windows indeholder portalens brugerflade lokalt og kalder tenantens Portal API direkte. Almindelig brug kræver API'et og internet, men Webportalen behøver ikke køre. Eksterne offentlige portallinks kræver fortsat Webportalen. Den aktuelle Windows-rettelse genbygger ikke Android/iOS-pakker. De nye apps er endnu ikke signeret til kundedistribution eller udgivet.

Den primære side er https://{tenant}.kombine.technology/download; API'ets downloadside viser samme indhold. Beta bruger de tilsvarende beta-domæner. Download kræver ikke login; installation giver ingen adgang til data. Log ind normalt i appen.

GetPortalAppDownloadPage: GET /download leverer HTML på portalens ti sprog efter Accept-Language (engelsk som standard; dansk ved enkelte manglende oversættelser). DownloadPortalWindowsAppInstaller: GET /download/windows/{architecture}/portal.appinstaller henter installations-/opdateringsfilen. DownloadPortalWindowsPackage: GET /download/windows/{architecture}/{fileName} leverer den præcise versionerede MSIX. Architecture er x64 eller arm64.

curl --fail -o portal.appinstaller https://api.team.kombine.technology/download/windows/x64/portal.appinstaller

Åbn den hentede fil med Windows App Installer. Windows skal stole på pakkens signatur; WebView2 Runtime og internet er nødvendigt. Opdateringskontrol ved start afhænger af Windows og pc'ens politik. 404 betyder, at sitets tenant/miljø/arkitektur mangler en valideret udgave, eller at filnavnet er ukendt. Siden viser så utilgængelig download; der bruges aldrig en anden tenants pakke. Side/installationsfil bruger no-store; MSIX understøtter byteområder (206) og ETag (304). Parametre kan ikke vælge en anden tenant. Signerede kundepakker og levering af pakker ved deployment er endnu ikke konfigureret.

Enhedsdokumenter: tabel, CSV, XLS og SVG

WashDoc-historik for lokation og organisation (nyt, ikke udgivet)

GetLocationDocuments — GET /api/v1/locations/{locationKid}/documents kræver en aktiv manager-bearer, en WashDoc-fane, Location Read, Unit Read og en matchende adgang til sitets ressourcer. RetentionDays anvendes før sideinddeling. date=yyyy-MM-dd medtager hele UTC-datoen og tidligere historik siden 2000-01-01; lokationsruten bruger i dag som standard. Gentag unitKids for at vælge én eller flere kanoniske enheder på lokationen (højst 255). Uden valg bruges alle synlige enheder. Enheder fra en anden tenant/lokation afvises med 400, skjulte eller manglende valgte enheder med 404. Filtrering sker før sideinddeling; ikke-valgte synlige enheder forbliver i valgmulighederne.

GetBankDocuments understøtter også date, gentagne unitKids, search, sort=activity|documentId og direction=asc|desc. Uden date virker det eksisterende from/through-interval fortsat; kombinationen afvises med 400. locationKid begrænser organisationen til en lokation. search er et præcist numerisk Doc-ID eller dokument-KID i det valgte område. Enhedsnavne oversættes efter Accept-Language.

GET /api/v1/locations/LOCATION_KID/documents?date=2026-10-23&unitKids=UNIT_KID_1&unitKids=UNIT_KID_2&limit=25&offset=0
GET /api/v1/locations/LOCATION_KID/documents/count?date=2026-10-23&unitKids=UNIT_KID_1&unitKids=UNIT_KID_2
GET /api/v1/banks/BANK_KID/documents?date=2026-10-23&locationKid=LOCATION_KID
GET /api/v1/banks/BANK_KID/documents/count?date=2026-10-23

Send Authorization: Bearer ACCESS_TOKEN ved alle kald. GetLocationDocumentsCount og GetBankDocumentsCount returnerer {count, from, through} med samme filtre og rettigheder. Dokumenter tælles efter lokation/enhed/Doc-ID, ikke kun TagId. Hent tælleren separat; en langsom eller fejlet optælling må ikke blokere listen. Svaret har den eksisterende BankDocumentPage-form: items, locations, units, from, through, offset, limit, hasMore. Hent 1–100 rækker (standard 25), offset 0–100000; nulstil offset ved filterskift. Ingen målinger hentes. Grænserne på 1000 lokationer, 5000 enheder og 35000 metadatarækker samt 12 sekunders lagerfrist gælder fortsat. Ved 422 indsnævres området; 503 betyder optaget/utilgængelig. Portalen bruger samme liste/ikoner, én datovælger, automatisk lazy loading og separat vist/total på alle niveauer. Klienter og pakker opdateres ved beta-klargøring.

Brug et kanonisk Doc-KID med sitets tenant, bank, lokation, enhed og dokumentets TagId. Et enheds-KID, numerisk dokument-id eller en anden tenant accepteres ikke. Kaldene læser ét kendt dokument; de finder ikke dokument-id'er.

Søg efter dokumenter

GetBankDocuments: GET /api/v1/banks/{bankKid}/documents finder dokument-KIDs til WashDoc1/2. Kræver samme bearer, WashDoc-tab, Location Read, Unit Read og ressourceadgang som dokumentkald. Resultater og filtre indeholder kun tilladte lokationer/enheder, som er synlige efter RetentionDays.

Valgfri locationKid/unitKid afgrænser banken; en enhed vælger også sin lokation. from/through er inklusive ISO 8601-tider med eksplicit UTC-offset, som standard seneste 24 timer, højst 31 dage. Et dokument matcher, når en Cycle-indstilling med dokumentets TagId ligger i intervallet. lastActivityUtc er seneste matchende Cycle, ikke nødvendigvis dokumentets start/slut; de fulde grænser findes i tabellen.

curl --get -H "Authorization: Bearer ACCESS_TOKEN" \
  "https://api.team.kombine.technology/api/v1/banks/BANK_KID/documents" \
  --data-urlencode "locationKid=LOCATION_KID" \
  --data-urlencode "from=2026-09-01T00:00:00Z" \
  --data-urlencode "through=2026-09-02T00:00:00Z" \
  --data-urlencode "limit=25" --data-urlencode "offset=0"

Svaret har items (kid, locationKid, unitKid, locationName, unitName, unitIconKid, unitType, lastActivityUtc), locations, units, effektive from/through, offset, limit og hasMore. Enhedsvalg følger lokationsfilteret. Hent sider efter behov: limit 1–100 (standard 25), offset 0–100000. Nyeste aktivitet først, derefter lokation/enhed/dokument. Liveændringer kan forskyde sider; nulstil offset ved opdatering. Søgningen henter hverken målinger eller et samlet antal.

400: ugyldige filtre; 401: log ind igen; 403: manglende adgang; 404: skjult/manglende valgt objekt; 422: afgræns til én lokation (højst 1000 lokationer, 5000 enheder, 35000 indstillingsrækker); 503: utilgængelig/optaget, undgå automatiske gentagelser. Databasefrist 12 sekunder, højst to samtidige søgninger pr. API-proces og ét sekund i kø. Svar bruger no-store. Portalen har fælles søgevisning for WashDoc1/2, 25 dokumenter pr. side, forhåndsvisning af 200 tabelrækker samt fulde CSV/XLS/udskriftsdownloads. Søgefelter og grafakse bruger UTC; liste/tabel viser browserens lokale tid. Grafen vælger de første 16 felter med numeriske værdier. Genererede klienter og downloadpakker er synkroniseret i version 0.4.2.

WashDoc3: dokumenter fra én enhed (ikke udgivet)

GetUnitDocuments — GET /api/v1/units/{unitKid}/documents viser dokumenter fra præcis den kanoniske Unit-KID i sitets tenant. Kræver aktiv administrator-bearer, specifikt WashDoc3 (55), Location Read, Unit Read og passende ressourceadgang. Lokation og enhed skal være synlige efter RetentionDays før sideinddeling; skjulte eller manglende objekter giver 404. Både metadata- og historiklæsning afgrænses til denne enhed.

Brug date=yyyy-MM-dd (UTC; standard er i dag) for at medtage dokumenter fra hele den valgte dato og tidligere, tilbage til 2000-01-01. Denne liste for én enhed har ingen grænse på 31 dages historik. De tidligere requestparametre from/through er fjernet og giver 400; søgning på tværs af et bankområde beholder sin intervalkontrakt. Brug sort=activity|documentId og direction=asc|desc (standard activity faldende). Aktivitet er seneste matchende Cycle med dokumentets TagId senest ved slutningen af den valgte dato, returneret som lastActivityUtc; det er ikke dokumentets start/slut. Dokument-id sorteres numerisk; samme aktivitetstid afgøres af dokumentets identitet. Brug elementets kanoniske kid uændret til de eksisterende tabel-, graf- og downloadkald med deres adgangskontrol.

curl --get -H "Authorization: Bearer ACCESS_TOKEN" \
  "https://api.team.kombine.technology/api/v1/units/UNIT_KID/documents" \
  --data-urlencode "date=2026-10-21" \
  --data-urlencode "sort=activity" --data-urlencode "direction=desc" \
  --data-urlencode "limit=25" --data-urlencode "offset=0"

Svaret har items med samme elementfelter som GetBankDocuments. Effektiv from er UTC-epoken, og through er den valgte dato kl. 23:59:59.999Z. sort, direction, offset, limit og hasMore beskriver siden. Ingen lokations-/enhedsvalg, samlet optælling eller målinger hentes. limit er 1–100 (standard 25), offset 0–100000. Hent flere sider efter behov med samme dato og sortering; nulstil offset ved ændringer eller opdatering, da liveændringer kan forskyde sider. En synlig enhed uden match giver et tomt items-array.

GetUnitDocumentsCount — GET /api/v1/units/{unitKid}/documents/count?date=2026-10-21 returnerer særskilt {count, from, through} med samme dato, tenant, enhed og adgangskontrol. Kaldet tæller forskellige dokumentidentiteter direkte uden at bladre gennem sider eller hente målinger. Start det parallelt med første side, og vis hentede rækker, mens totalen afventes; en fejl i optællingen må ikke skjule listen. Nul er en gyldig total. Optælling og sider bruger separate øjebliksbilleder, så en kørende maskine kan ændre totalen; opdater begge for at afstemme dem. Brug samme dato til begge kald.

Fejl bruger ProblemDetails med code: 400 ugyldig KID/tid/sortering/side, 401 log ind igen, 403 manglende rettigheder, 404 skjult/manglende objekt, 422 for store metadata, 503 document-unavailable eller document-busy; prøv igen manuelt. Databasefrist 12 sekunder, højst to samtidige Unit-listesøgninger samt to uafhængige optællinger pr. API-proces og ét sekund i kø. Svar bruger no-store. Genererede klienter og downloadpakker synkroniseres ved beta-forberedelse.

OperationGET-stiResultat
GetUnitDocumentTable/api/v1/documents/{documentKid}/tableJSON-tabel med metadata
GetUnitDocumentHtml/api/v1/documents/{documentKid}/table.htmlHTML-tabel til visning og udskrift
DownloadUnitDocumentCsv/api/v1/documents/{documentKid}/table.csvUTF-8 CSV
DownloadUnitDocumentXls/api/v1/documents/{documentKid}/table.xlsBinær Excel 97–2003-projektmappe
GetUnitDocumentSvg/api/v1/documents/{documentKid}/graph.svgSVG-grafer
GetUnitDocumentSvg2/api/v1/documents/{documentKid}/graph2.svgSkalerbar WashDoc-graf (ikke udgivet)
GetUnitDocumentIcon/api/v1/documents/{documentKid}/icon.svgVisuel forhåndsvisning uden tekst (ikke udgivet)
GetUnitDocumentGraph/api/v1/documents/{documentKid}/graphSemantisk graf-JSON (ikke udgivet)
GetUnitDocumentPng/api/v1/documents/{documentKid}/graph.pngServergenereret PNG (ikke udgivet)
GetUnitDocumentNeighbors/api/v1/documents/{documentKid}/neighborsForrige og næste numeriske Doc ID (ikke udgivet)

GetUnitDocumentNeighbors bruger samme aktive administrator-bearersession, WashDoc1/2/3-fane, Location/Unit Read, tenant, ressourceadgang og RetentionDays-kontrol som fulde dokumenter. Svaret er {previous, next}; hver eksisterende nabo har et kanonisk kid og lastActivityUtc (seneste tidsstempel fra en tagget Cycle-post). Manglende naboer er null, og deres navigationswidgets skal skjules. Doc ID tæller ned: forrige er det nærmeste højere numeriske Doc ID (ældre dokument), og næste er det nærmeste lavere numeriske Doc ID (nyere dokument). Søgningen omfatter netop denne enheds fulde gemte historik; huller springes over, og listens dato, sortering og filtre ignoreres. Der hentes ingen målinger. Svar caches ikke, og lageropslag har en frist på 12 sekunder. Håndter 400 ved at rette dokumentets KID, 401/403 ved at kontrollere session/adgang, 404 som en manglende/skjult lokation, enhed eller valgt dokument og 503 med et manuelt genforsøg. Klientpakker opdateres ved betaforberedelse.

curl -H "Authorization: Bearer ACCESS_TOKEN" "https://api.team.kombine.technology/api/v1/documents/DOCUMENT_KID/neighbors"

Interaktive grafer og PNG til mail (ikke udgivet)

GetUnitDocumentGraph og GetUnitDocumentPng modtager dokumentets kanoniske KID, valgfrie states/settings og samme administrator-bearersession, WashDoc-fane, Location/Unit Read samt tenant-/ressource-/historikadgang som øvrige fulde dokumentkald. Klienten skal ikke levere en grafkonfiguration. Begge genbruger graph2's afgrænsede dataopslag, præcise cyklusgrænser og ældre edge-data; de er ikke offentlige widgetadresser.

curl -H "Authorization: Bearer ACCESS_TOKEN" \
  "https://api.team.kombine.technology/api/v1/documents/DOCUMENT_KID/graph" \
  --output document-graph.json
curl -H "Authorization: Bearer ACCESS_TOKEN" \
  "https://api.team.kombine.technology/api/v1/documents/DOCUMENT_KID/graph.png" \
  --output document.png

Graf-JSON indeholder documentKid, unitKid, unitName, finished, grafgrænserne fromMs2000/toMs2000 og præcise, nullable cyklusgrænser startedMs2000/endedMs2000. Alle tider er UTC-millisekunder siden 2000-01-01. series indeholder stabile kind/name/localization, label, semantisk group, role (measurement eller target), valgfri unit, step og ujævnt fordelte points. Hvert punkt har ms2000, nullable endelig value, isCalculated, isEdge og valgfri unit. Gemt null bryder kurven; et manglende punkt gør ikke. Ingen resampling, udglatning, visuel koordinatomregning eller beregning af manglende mængder. Brug kun faktiske punkter inden for vinduet til nærmeste måling ved mouseover; behold edge-punkter til beskåret tegning. JSON-målinger bevarer deres gemte enheder; blandede enheder giver null i seriens unit og må ikke tegnes på en forkert fælles skala. Ældre Level-tal får ikke automatisk liter eller en fysisk enhed, bortset fra den typespecifikke temperaturfortolkning nedenfor.

presentation indeholder den betroede, oprindelige afkodede unitType, kind (washer, dryer, dosing, energy eller generic) og den foretrukne groupOrder. Tegn kun grupper med målinger i denne rækkefølge, og behold derefter øvrige målte grupper. PNG og widget bruger samme typeprofil. Kun for Alliance-tørretumblere 19/29/30 er historisk Level den anden temperatur: gruppen bliver temperature2, label/localization bruger Temperature2, og almindelige tal får °C. LevelTarget/LevelUnfiltered bevares som særskilte legacyLevel-målinger med ukendt enhed. Almindelige Humidity-tal får kun % for disse tre typer. For QW-vaskemaskiner 15/16/17/25/31 er almindelige Spin-tal 0–3 ordinale trin uden rpm; JSON med angivet rotationsskala bevarer de gemte enheder. Ukendte enheder gættes aldrig. Numerisk fugtighed, spænding, strøm og frekvens understøttes, når de findes; manglende grupper giver ingen tomme graffelter. Den lille widget viser højst fire prioriterede numeriske kurver; dokumenter med kun doseringshændelser har fortsat ingen numerisk widgetvisning.

Ældre eState.Process (138) læses fra Live- og States-historikken i samme afgrænsede dokumentvindue, også for umærkede ældre målinger. En hændelse har kind="state", name="Process" og bevarer den oprindelige eProcess-byte: 1 forvask, 2 klarvask, 3 skyl, 4 andet skyl, 5 sidste skyl, 6 centrifugering, 7 færdig. Koderne er uafhængige af Cycle og CycleSub. Tekst og oversættelsesnøgle bruger tilsvarende fælles faseoversættelser uden at ændre den gemte kode; ukendte gyldige bytekoder bevares tydeligt. Faktiske skift i vinduet vises som fasemarkeringer og historiske mouseover-oplysninger i både browser og PNG. Gentagelser med uændret værdi skjules kun i markeringerne; alle gemte hændelser bevares. Process.Done erstatter ikke afgrænsningen via mærkede Cycle/Doc-ID-data.

events indeholder gemte Cycle/Process/CycleSub/Error-koder med præcise tider, enum-navne, fælles oversættelsesnøgler og oversatte tekster, når de findes. signals indeholder InOutletHeat-ændringer: normalt varme bit 32, indløb bit 1/2/4/8 og afløb bit 16. Alliance-tørretumblere 19/29/30 bruger i stedet varme bit 32|64 og airflow bit 16; indløb/afløb er null. Airflow er null for øvrige typer. Null betyder ukendt eller ikke relevant, ikke slukket; en kendt ændring gælder indtil den næste gemte ændring. dosing indeholder SoapInfo-produktnavne og gemte nullable durationMs, volumeMl, massGrams, proces og trin. Begge ældre tabulatorformater og pakkede mængde-/varighedsfelter understøttes; manglende mængder forbliver null. Interne kildetags offentliggøres ikke.

PNG-kaldet tegner samme semantiske data med den lokale Chart.js-renderer på ugennemsigtig hvid baggrund til vedhæftning eller indlejring i mail. Det kræver ingen åben browser og kalder ingen ekstern graftjeneste. Rendering er afgrænset; en utilgængelig renderer giver 503 document-renderer-unavailable. Begge kald kontrollerer adgang hver gang og bruger no-store; løbende HTTP-streaming er ikke implementeret. Eksisterende grænser gælder: højst 16 numeriske serier, 31 dage, 50.000 kildemålinger, 500.000 celler og 12 sekunders databasetidsgrænse. Ved 400: ret KID/feltvalg; 401/403: kontroller login/adgang; 404: manglende/skjult; 422: vælg færre felter; 503: prøv manuelt igen. Eksisterende SVG-/widgetkald bevares. Klientpakker genopbygges først ved beta-klargøring.

GetUnitDocumentSvg2 bruger samme bearer-login, tenant/adgang/historikgrænse og valgfrie states/settings. Målinger vises i bånd med stiplede målværdier på samme skala og gemte Cycle/Process/CycleSub/Error-markeringer. Doc ID finder mærket cyklushistorik: PREPARING starter cyklussen, første DONE afslutter den, og seneste mærkede målinger afgrænser en uafsluttet cyklus. Grafen får et minuts margen og varigheden rundes op til næste minut. Ældre umærkede målinger læses for præcis samme enhed i vinduet. Nærmeste edge-målinger for observerede felter søges højst en time udenfor og beskæres; ingen interpolation eller opdigtede målinger. Kendte ældre JSON-målinger fortolkes uden at ændre originaltekst eller omregne enheder. Højst 16 numeriske serier; 422 betyder ingen målinger eller for mange serier. SVG fylder begge dimensioner, mens tekst og streger bevares; tidsaksen er UTC. Den gamle graf bevares. Begge kontrollerer adgang før separat privat caching af afsluttede dokumenter og returnerer no-store; uafsluttede grafer læses på ny. Ved 401/403: kontroller login/adgang; 400: ret KID/felter; 404: manglende/skjult; 422: vælg færre felter; 503: prøv igen manuelt.

curl -H "Authorization: Bearer ACCESS_TOKEN" \
  "https://api.team.kombine.technology/api/v1/documents/DOCUMENT_KID/graph2.svg" \
  --output document.svg

Adgang og felter

Send administratorens bearer-token ved hvert kald. Kræver mindst én af WashDoc1 (54), WashDoc3 (55), WashDoc2 (75), Location Read, Unit Read og adgang til den pågældende tenant/bank/lokation. Slettede lokationer og enheder følger RetentionDays. Rettigheder kontrolleres også før opslag i SVG-cachen med de sædvanlige tidsbegrænsede administrator-, lokations- og enhedssnapshots.

historicalMeasurements=true tilføjer en fast dokumentprofil med tilladte temperatur-, niveau-, centrifugerings-, energi/effekt-, resttids- og procesfelter uafhængigt af enhedens aktuelle model. Standard er false for tabeller; graph2 og icon aktiverer profilen automatisk. Den giver ikke adgang til flere settings eller credentials og ændrer ingen gemte enhedstyper. Feltvalg og dokumentgrænser gælder stadig. GetUnitDocumentIcon genbruger samme autoriserede data, tidsvindue og cache for afsluttede grafer. Kurver forenkles med bevarede lokale peaks, uden tekst, akser eller målværdier. Det er en visuel forhåndsvisning, ikke en aflæsningsskala. Hent kun synlige ikoner løbende med begrænset samtidighed; brug et almindeligt dokumentikon ved 422 eller fejlede opslag. Erstat fx graph2.svg med icon.svg i bearer-eksemplet ovenfor og opret en billed-blob-URL.

states og settings er valgfrie lister med præcise enum-navne adskilt af komma, fx states=Temperature,Level&settings=Cycle for en understøttet vaskemaskine. Udelad en kategori for alle tilladte felter; en tom værdi vælger ingen. Som standard tillades kun synlige felter bundet til den aktuelle enhed. Credentials, skjulte felter og felter på andre objekter læses aldrig. Ukendte enhedstyper giver 422. Enum-navne er sproguafhængige; de faste tekster i eksporterne er foreløbig på engelsk.

Værdier og filer

JSON indeholder documentKid, unitKid, unitName, finished, tidsgrænser i MS2000, columns og rows. Celler følger kolonnernes rækkefølge. En null-celle betyder manglende værdi; en celle med null i text/value betyder gemt null. text bevarer originalværdien efter afkodning; value er et endeligt double-tal, når det er muligt. Brug text til store tal, der skal bevares præcist. Tabelværdier interpoleres eller afrundes ikke.

Kun kolonner med data et sted i hele det autoriserede dokument returneres, også ved eksplicit feltvalg. En kolonne er tom, når alle celler mangler en numerisk værdi, og teksten er null, tom eller kun består af blanktegn. Tallet nul, false og tekst med indhold er data. De resterende kolonner bevarer rækkefølgen, og hver rækkes cells følger den returnerede rækkefølge; rækkernes tidspunkter ændres ikke. Et helt tomt feltvalg har columns: [] og tomme cellearrays. Opbyg kolonneopslag fra columns ved hvert svar i stedet for at antage faste positioner eller at alle ønskede felter er med. CSV, XLS, HTML og portalens Excel-download bruger samme filtrerede tabel.

CSV har UTF-8 BOM, komma, anførselstegn og CRLF. Mulige formler i tekst uden en numerisk værdi får en apostrof foran. CSV kan ikke skelne mellem manglende, null og tom tekst. XLS er ægte BIFF8; tekst gemmes som strenge, aldrig formler. Tal med mere end 15 cifre og særlig formatering bevares som tekst. Begge formater indeholder MS2000 og ISO-tid i UTC. HTML kan udskrives i browseren. PDF indgår ikke.

Grafer og cache

SVG dannes direkte med .NET XML uden grafbibliotek, scripts eller eksterne assets. Hver numerisk serie får sin egen navngivne skala. Enums og booleske værdier tegnes som trin; gemte null/ugyldige målinger bryder linjen. Tidspunkter, som alene stammer fra andre serier, bryder den ikke. Vælg højst 16 numeriske serier. width er 480–2400, som standard 1200.

finished=true kræver en afsluttende Cycle og et InSync-tidsstempel mindst to minutter efter hele dokumentets slutning. Kun færdige SVG-filer gemmes i en privat diskcache uden for wwwroot, højst 24 timer. Nøglen indeholder tenantbundet KID, enhedstype/navn, felternes rækkefølge, bredde og rendererversion. Cachen begrænses til 128 MiB/256 filer; ved cachefejl renderes på ny. Ufærdige SVG'er og alle tabeller/filer er uden cache. HTTP bruger altid Cache-Control: no-store. X-Document-Complete angiver færdigstatus; denne header og Content-Disposition kan læses af konfigurerede CORS-klienter. Hent SVG med bearer-token og lav derefter en blob-URL til billedvisning.

curl -H "Authorization: Bearer ACCESS_TOKEN" \
  "https://api.team.kombine.technology/api/v1/documents/DOCUMENT_KID/table.csv?states=Temperature,Level&settings=Cycle" \
  --output document.csv

Grænser og fejl

Ét dokument, højst 31 dage, 128 kolonner, 50.000 kildeposter, 500.000 celler, 16.384 tegn pr. værdi og samlet 8 millioner tegn. Datalæsningen har 12 sekunders deadline. Højst to eksporter/renderinger kører samtidig pr. API-proces, med højst ét sekund i kø. Fejl giver aldrig et delvist dokument som succes.

400: ugyldigt KID, feltvalg eller bredde. 401: log ind igen. 403: manglende adgang. 404: dokument/objekt mangler eller er skjult af RetentionDays. 422: ukendt enhedstype, for stort dokument, ingen numeriske grafdata eller mere end 16 serier; begræns feltvalget, hvor det er relevant. 503: lagerfejl eller document-busy; vis utilgængelig og undgå automatiske gentagelsesløkker. Fejl er ProblemDetails med stabil code. Genererede klienter og downloadpakker er synkroniseret i version 0.4.2.

Kom i gang: Find API-adressen → log ind med en manager → hent /api/v1/session/me.

Hvad er klar nu? Driftsstatus, offentligt antal aktive brugere samt den indloggede managers navn, ikon, Tabs, adgang til banker/lokationer og handlingsrettigheder. Det er oplysningerne på portalens nuværende overblik. Users2-brugerlister findes via GetBankUsers. Users2-redigering og eksport er beskrevet nedenfor; andre tabs kan stadig mangle API-handlinger. En Tab i svaret betyder derfor ikke, at dens funktion allerede findes som API-kald.

Vælg tenantens API-domæne: https://api.{navn}.kombine.technology i produktion eller https://beta.api.{navn}.kombine.technology i beta. Navne: team, electrolux, portal, washco, finelec og nortec. Domænet er bundet til tenantens serverkonfiguration. KID, formularfelter og videresendte host-headere kan ikke skifte tenant. Tokens gælder kun deres tenant og miljø; log ind særskilt ved skift. En ukendt vært afvises med HTTP 400. Beta bruger aktuelt samme tenantdatabaser som produktion.

Beboerkvitteringer — GetUserReceipts (ikke udgivet)

GET /api/v1/users/{userKid}/receipts genbruger administratorens bearer-session. Kræver aktiv konto, Users2, User Read og adgang til hele beboerens bank. Lokationsadgang alene er utilstrækkelig. Det kanoniske beboer-KID skal tilhøre API-sitets tenant. Hver side kontrollerer det normale rettighedssnapshot (højst 60 sekunder gammelt); manglende og retention-skjulte beboere behandles ens.

API='https://localhost:20332'
USER_KID='A6Q3CoAC1b41Dh'
curl --get "$API/api/v1/users/$USER_KID/receipts" \
  -H "Authorization: Bearer $TOKEN" --data-urlencode 'offset=0'
# Brug nextOffset og revision fra det forrige svar:
curl --get "$API/api/v1/users/$USER_KID/receipts" \
  -H "Authorization: Bearer $TOKEN" \
  --data-urlencode "offset=$NEXT_OFFSET" --data-urlencode "revision=$REVISION"

Svaret indeholder userKid, revision, items[], nullable nextOffset og periodCount=24. En side har højst 20 hele kvitteringer. Offset tæller kvitteringer, ikke linjer. Start ved nul uden revision, og stop ved nextOffset:null. Hent først, når afsnittet eller listens slutning bliver synlig, med én forespørgsel ad gangen.

En kvittering har key, lokal date, nullable locationKid, locationName, period (nul er igangværende), provisional, kind (Purchase, Payment, Discount, TransferToRent), nullable currency, totalMinor, nullable vatMinor, balanceAfterMinor og lines[]. Linjer har nullable transaktions-kid, occurredAt med UTC-offset, nullable unitKid, unitName, alle beskrivelseslinjer i texts[], amountMinor og calculated. Beregnede afregningsposter har intet lagret transaktions-KID.

Beløb er 64-bit heltal i valutaens mindste enhed. En negativ lagret postering vises som positivt kvitteringsbeløb; tilbagebetalinger beholder det modsatte fortegn. Valutaer må ikke summeres sammen, og ukendt valuta forbliver null. Kvitteringens saldo er uden uudnyttet rabat i den aktuelle periode, modsat saldooverskriften. Rabat gælder kun DKK. Moms er indeholdt i totalen og vises kun for køb i kontantbanker med gyldig momssats; null betyder ukendt/ikke relevant. Grupper adskilles efter lokal dato, lokation, type, periode og valuta. Dokumenter med nulbeløb og Month-linjer udelades.

Historikken omfatter periode nul og de seneste 24 lagrede periodenumre inden for bogførings-/overvågningsfristerne. RetentionDays begrænser adgangen til slettede beboere. Hvert kald bruger et læsesnapshot med 12 sekunders databasefrist, højst to samtidige læsninger, 50.000 posteringer, 8 MiB lagret tekst og 1.000 dokumenter pr. kvittering. Kun Log-tabeller læses. Endpointet foretager ingen tilbagebetaling, betaling, eksport eller ændring. Genererede klienter opdateres ved betaudgivelsen.

Fejl: 400 invalid-user/invalid-page; 401 (kan være uden JSON); 403 forbidden; 404 not-found; 409 receipts-changed; 503 receipts-too-large/storage-busy/storage-timeout/storage-unavailable. Ved 409 ryddes indlæste sider, og der startes ved nul; bland aldrig revisioner. Ved 401/403/404 ryddes historikken, og indlæsningen stoppes. Ved 503 vises fejl med mulighed for manuelt genforsøg, aldrig opdigtede nulbeløb. For store eller fejlede svar afkortes ikke til tilsyneladende komplette kvitteringer.

Live logs

GetLiveLogs — GET /api/v1/diagnostics/live-logs bruger din genanvendelige manager-bearersession. Kræver en aktiv manager, Logs1, Managers Read og adgang til hele tenantens KID. Adgang til én bank er ikke nok. Sitet vælger tenant; klienten kan ikke vælge tenant eller server.

curl -H "Authorization: Bearer $TOKEN" "https://YOUR-PORTAL-API/api/v1/diagnostics/live-logs"

Svaret indeholder tenantKid, fetchedAtUtc, refreshAfterSeconds og tre servers: portal-api, equipment-api, portal-web. Hvert kort har errorCode (null ved succes) og events, nyeste først. Hændelser har timestamp, level, message-skabelon, category samt eventuelt statusCode, elapsedMilliseconds, bankId, userIds og traceId. Numeriske bank-/bruger-ID er diagnostik, ikke ressourcevælgere.

Hent højst hvert tredje sekund. Adgangen kontrolleres før cachelæsning. 401 betyder ugyldig/tilbagekaldt session; 403 betyder missing-logs-tab, missing-managers-read eller missing-tenant-access; 503 betyder utilgængeligt rettighedslager. Stop opdatering og ryd visningen ved 401/403. Kort kan uafhængigt returnere live-logs-not-configured eller live-logs-unavailable.

Hver proces gemmer højst 100 rensede hændelser i 15 minutter; genstart rydder historikken. Information fra applikationen og alle hændelser fra Warning og op indsamles. Kun godkendte diagnostikfelter og det aktuelle assistentspørgsmål bevares fra beskedargumenter; exceptiontekst udelades. Tomme kort kan betyde ingen nylige hændelser. Dette er hukommelsesbaseret diagnostik, ikke Logz.io-historik eller revisionsspor. Flere replikaer har hver sin buffer og samles ikke.

Chathændelser indeholder desuden det valgfrie felt question (op til 2000 tegn). Brugere med adgang til live logs kan se teksten. Vis den som tekst, aldrig som HTML; portalen viser den med fed skrift i stedet for pladsholderen.

Hændelser har også det valgfrie felt fields med filtrerede, strukturerede værdier. Indsæt matchende værdier som HTML-kodet tekst; portalen viser dem med fed skrift. Ukendte eller filtrerede pladsholdere bevares. Eksisterende felter bevares.

Når løbende videresendelse fra DO til Logz.io er aktiveret, læser dette endpoint indsamlerens seneste linjer i hukommelsen. Bufferen bevarer alle modtagne linjer; kun linjer med et genkendt niveau fra Warning og op kan sendes til Logz.io. Linjer uden genkendt niveau udelades fra afsendelsen. Afsendelsen kører uafhængigt af åbne sider. En synlig linje bekræfter modtagelse fra DigitalOcean, ikke accept hos Logz.io. Logs1 viser også dette panel med uændrede rettigheder. Videresendelsen kan have huller og dubletter efter afbrydelser.

DigitalOcean-runtime-logs

GetHostingLogs: GET /api/v1/hosting/logs. Kræver managerens bearer-session, Logs1, Managers Read og adgang til hele tenant. Loggene må efter udtrykkeligt ønske dække hele appen på tværs af tenants. Hosting1 alene er ikke nok.

curl -H "Authorization: Bearer $TOKEN" "$BASE/api/v1/hosting/logs?environment=beta&application=portal-api"

environment: beta/production; application: portal-api/equipment-api/portal-web. Svar: environment, application, fetchedAtUtc, lines (højst 100 tekstlinjer), truncated. Hent højst hvert tiende sekund. Indhold begrænses til 256 KiB; svar og fejl caches i ti sekunder. Øjebliksbilleder kan overlappe og er ikke en komplet historik. Vis tekst, aldrig HTML. Pause fryser visningen, mens adgang kontrolleres; skjul/forlad siden rydder data. Ingen MySQL-, build-, deploy- eller crash-logs.

400 invalid-hosting-selection; 401 ugyldig session; 403 missing-logs-tab, missing-managers-read eller missing-tenant-access; 503 hosting-not-configured eller hosting-unavailable. Ryd ved fejl og stop ved 401/403. Nøgler og signerede adresser bliver på serveren. Adgang kontrolleres før cache med managersnapshot på op til 60 sekunder. Med i klientpakker 0.4.2; tilgængelighed kræver operatørkonfiguration.

Hosting1 — driftsmålinger (ikke udgivet)

GetHostingMetrics: GET /api/v1/hosting/metrics?environment=beta&application=portal-api&hours=24. Genbrug managerens bearer-session. Kræver aktiv konto, Hosting1 (81), Managers Read og en KID-tilladelse til hele API-sitets tenant. Hvert kald, også et kald med cachede målinger, kontrollerer managerens rettighedssnapshot, som højst er 60 sekunder gammelt. Målingerne dækker den fælles infrastruktur på tværs af kunder, ikke den enkelte tenants forbrug.

environment: beta eller production (standard beta). application: portal-api, equipment-api, portal-web eller mysql (standard portal-api). hours: 1, 6 eller 24 (standard 24). API-sitet bestemmer ressourcerne; klienten kan ikke sende udbyder-ID, tenant eller URL. Beta og produktion kan pege på samme database.

curl --get 'https://beta.api.team.kombine.technology/api/v1/hosting/metrics' \
  -H "Authorization: Bearer $TOKEN" \
  --data-urlencode 'environment=beta' \
  --data-urlencode 'application=mysql' \
  --data-urlencode 'hours=6'

Svaret indeholder environment, application, fromUtc, toUtc, fetchedAtUtc, refreshAfterSeconds, metrics, bandwidth. Hver måling har name, unit, series, errorCode. Hver serie har component, instance, points; hvert punkt har timestampUtc og en numerisk value, som kan være null. App-målingerne er cpu/memory i percent og restarts i count. Bevar opdelingen pr. instans: genstarter er udbyderens måleserie, ikke en beregnet sum for perioden. MySQL returnerer cpu, memory, disk i procent som udbyderens klyngegennemsnit samt connections (tilsluttede MySQL-tråde) i count. Manglende serienavne vises med et serienummer; det er visningstekst, ikke en stabil objektidentitet.

For apps indeholder bandwidth felterne dateUtc, bytes, errorCode. Det er altid gårsdagens UTC-forbrug, uanset hours. Bytes er en decimalstreng uden fortegn, der bevarer 64-bit-præcision, eller null ved manglende data. For MySQL er bandwidth null. Tomme serier og null-værdier betyder manglende målinger, aldrig nul. Måletidspunkter kan være forsinkede; fetchedAtUtc angiver snapshot-tidspunktet, ikke hvor nye målingerne er.

Kontrollér alle errorCode-felter: et mislykket opslag giver hosting-source-unavailable uden serier (eller med null bytes), mens de øvrige målinger bevares i HTTP 200. Hvis alle opslag fejler: 503 hosting-unavailable. Deaktiveret eller ufuldstændig opsætning: 503 hosting-not-configured. Andre fejl: 400 invalid-hosting-selection; 401 kræver login; 403 missing-hosting-tab, missing-managers-read eller missing-tenant-access. De nævnte applikationsfejl med 400/403/503 bruger ProblemDetails med code. Forkerte query-typer bruger almindelige modelvalideringsfejl; 401 kan have en tom body. Udbyderens fejltekst og adgangsnøgler returneres ikke.

Respektér refreshAfterSeconds (60). Resultater og fejlede opslag caches i ét minut; browsersvar er no-store. Stop polling på skjulte sider, undgå overlappende kald, og fjern viste data efter 401/403. Delvise eller tomme data er ikke tegn på fejlfri drift. Kun de dokumenterede app- og Managed MySQL-målinger er tilgængelige; operationen udfører ikke SQL, ændrer ikke ressourcer og viser ikke logs eller kundespecifik fakturering. Genererede klienter og downloads synkroniseres ved betaudgivelsen.

TenantStatus1 — driftsstatus

GetTenantStatus: GET /api/v1/tenant/status?limit=200. Genbrug managerens bearer-session. Kræver TenantStatus1 (80), Bank Read, Location Read, Unit Read og en tilsvarende tenant/bank/lokations-KID-rettighed. Navigation på Tenant-niveau giver ikke adgang til alle data. Sitet bestemmer tenant; KID-adgang og RetentionDays for bank, lokation og enhed filtreres før begrænsningen. Lokationens Enabled skal være præcis 1. Manglende Deleted betyder nul; ugyldige eller fremtidige sletninger skjules.

Offline bruger tenantens Alive-tabel efter udtrykkelig tilladelse: UnitId = MainId, Cluster forskellig fra Test og seneste kontakt fra 100 dage siden til én time siden for Cash eller ét døgn siden for øvrige, ikke-tomme BankType-værdier, inklusive grænserne. AutoOutOfOrder bruger Log24 OutOfOrder = AutoOutOfOrder, udelader StartSMS_60 og TimeSMS_61 og henter et positivt heltals-Id fra AutoOutOfOrder-statens JSON. Typen kommer kun fra den oprindelige pakkede UnitType med (værdi >> 1) & 255. UnitType2 bruges hverken som tilsidesættelse eller reserve. Manglende/ugyldige typer udelades fra dette opslag.

Svaret indeholder measuredAtUtc, refreshAfterSeconds (30), sources og items. Hver kilde har kind, count, hasMore, errorCode. Hver hændelse har kind, kid, bankKid, locationKid, bankName, locationName, unitName, computerName, bankType, unitType, errorId, timestampUtc, iconKid, bankIconKid, locationIconKid. KID'er er kanoniske. unitType/errorId er heltal eller null. timestampUtc er seneste kontakt for Offline eller tidspunktet for OutOfOrder-indstillingen. Send Accept-Language for oversatte navnepladsholdere. Sortering: tidspunkt faldende, dernæst kind og KID. En enhed kan have begge hændelser.

bankIconKid og locationIconKid indeholder de gemte bank-/lokationsikoner fra Log24, hvor API’et indsætter objektnummeret i Kid.Text. Manglende eller ugyldige ikonindstillinger giver bank_building/house. Vis værdierne uændret med GET /api/v1/icon/{iconSet}/{iconKid}.svg, fx "/api/v1/icon/g/" + encodeURIComponent(item.locationIconKid) + ".svg". Brug bankKid/locationKid til links og genveje i arbejdsområdet. ComputerName, bankType og unitType er fortsat tilgængelige til supplerende tooltip; ikoner og genveje giver aldrig adgang.

Opslag kører parallelt på separate forbindelser med en samlet frist på 12 sekunder. En fejlet kilde får status-source-unavailable, count 0 og ingen hændelser, mens vellykkede kilder stadig returneres med HTTP 200. Kontrollér altid sources; et delvist svar betyder ikke fejlfri drift. Fejler alle, returneres 503 tenant-status-unavailable. 400 invalid-limit; 401 kræver login; 403-koder: missing-status-tab, missing-bank-read, missing-location-read, missing-unit-read, missing-resource-access. Fejl bruger ProblemDetails/code. Svar må ikke caches.

Begrænsninger: 1–1000 rækker pr. kilde, standard 200. hasMore angiver udtrykkeligt, at kun de nyeste vises; der er ingen cursor eller historisk eksport. Acceptér fremtidige nye kind-værdier. Opdater tidligst hvert 30. sekund, stop på skjulte sider og undgå overlappende kald. Genererede klienter og downloadpakker er synkroniseret i version 0.4.2.

const response = await fetch(apiBase + '/api/v1/tenant/status?limit=200', {
  headers: { Authorization: 'Bearer ' + accessToken, 'Accept-Language': 'en-GB' }
});
if (!response.ok) throw new Error('HTTP ' + response.status);
const status = await response.json();
for (const source of status.sources) {
  if (source.errorCode || source.hasMore) console.warn(source.kind, source);
}
console.table(status.items);

Lazy load — GetTenantStatusPage (ikke udgivet)

GET /api/v1/tenant/status/page?pageSize=25 leverer de samme tilladte hændelser i portioner på 1–100 rækker (standard 25). GetTenantStatus-kontrakten er uændret. Svaret har status (statussvaret ovenfor, men kun denne sides items), offset, totalCount, previousCursor, nextCursor. Kildernes optællinger gælder hele det afgrænsede udsnit. API’et læser op til 1.000 rækker pr. kilde én gang; hasMore advarer stadig om afkortning. Efterfølgende sider genbruger dette udsnit uden nye statusopslag.

Send en modtaget cursor uændret med samme pageSize og Accept-Language. Hver side kontrollerer aktiv manager, Tab og operationsrettigheder igen. Cursor er bundet til manager, tenant, adgangskodestempel, KID-adgang, RetentionDays og sprog og giver aldrig adgang i sig selv. Udsnittet udløber efter to minutter og kan fjernes tidligere. HTTP 400 betyder ugyldigt sideinput/cursor; 409 betyder ændret kontekst; 410 betyder udløbet/fjernet udsnit. Ved 409/410 kasseres cursor, og en frisk side hentes. 401/403/503 betyder det samme som ovenfor. Svar må ikke caches.

Opdater uden cursor hvert 30. sekund. Valgfri anchor=Offline:UNIT_KID starter ved den tilladte række; offset=50 (0–1999) er fallback, hvis rækken er væk, og begrænses til udsnittets størrelse. Kombinér ikke cursor med anchor/offset. Det holder den synlige position ved opdatering. Portalen beholder højst fem portioner på 25 rækker og henter tidligere/senere sider ved scroll. Ingen historisk eksport eller ubegrænset gennemløb.

const query = new URLSearchParams({ pageSize: '25' });
if (nextCursor) query.set('cursor', nextCursor);
const response = await fetch(apiBase + '/api/v1/tenant/status/page?' + query, {
  headers: { Authorization: 'Bearer ' + accessToken, 'Accept-Language': 'en-GB' }
});
if ([409, 410].includes(response.status)) { nextCursor = null; /* restart without cursor */ }
else {
  if (!response.ok) throw new Error('HTTP ' + response.status);
  const page = await response.json();
  console.table(page.status.items);
  nextCursor = page.nextCursor;
}

Vælg Public v1 (no login) i Swagger for status, statistik og købskort uden login. Hent kontrakten som Public OpenAPI JSON. Portal integrations v1 indeholder manager-login og manageroperationer. Opdelingen ændrer ikke ruter, operationnavne eller adgangskontrol. Klienter, der genererer offentlige kald fra det tidligere samlede v1-dokument, skal nu også importere Public-dokumentet.

Antal køb den seneste time

GET /api/v1/public/statistics/purchases kan kaldes uden login. Swagger-navnet er GetPublicPurchases, og JavaScript-klienten har client.getPurchases().

Tæller rækker i sitets A{tenant}.Log1Hour med UserId >= eUserId.Users AND UserId <= eUserId.Guest AND Text NOT LIKE '%E' AND Amount < 0. Det er køb, ikke unikke kunder. SQL-tabellens collation bestemmer sammenligningen af E. Brugerintervallet følger den fælles enum: aktuelt 1001–999999999 inklusive, også gæstekøb. Der tilføjes ikke bankfilter. Svaret indeholder currencies: [{currency, count, amount}] med én gruppe pr. valuta og amount = -SUM(Amount)/100 i hovedvalutaenheder inden for gruppen. Manglende valuta forbliver null i sin egen gruppe. Det overordnede count er summen af gruppernes antal. Uden køb returneres count: 0 og currencies: []. Der er intet samlet pengebeløb og ingen valutaomregning.

Log1Hour vedligeholdes af databasen med oprydning hvert minut. Optællingen bruger tabellens indhold ligesom den aftalte SQL, så tidsvinduet afhænger af denne oprydning. sinceUtc er den nominelle start én time før measuredAtUtc, og lookbackHours er 1. tenantKid angiver sitet, og count er antallet. Klienten kan ikke skifte tenant eller filtre.

Resultatet caches i ét minut pr. API-instans. Kortet opdaterer automatisk hvert minut, mens siden er åben. Samtidige kald deler én databaseforespørgsel. HTTP 503 med Retry-After: 60 betyder utilgængelig, ikke nul. Denne anonyme widget-læsning tillader GET fra alle origins uden credentials; beskyttede operationer beholder CORS-politikken for præcise origins.

const response = await fetch('https://api.team.kombine.technology/api/v1/public/statistics/purchases');
if (!response.ok) throw new Error('HTTP ' + response.status);
const purchases = await response.json();
console.log(purchases.count);
for (const group of purchases.currencies) {
  console.log(group.currency, group.count, group.amount);
}

Ikke frigivet kontraktændring: erstat læsning af de tidligere overordnede felter amount og currency med gennemløb af currencies. Se changelog. Klientpakkerne synkroniseres ved forberedelsen af næste betaudgivelse.

Købsgraf som SVG

GetPublicPurchasesChart — GET /api/v1/public/statistics/purchases/chart/{color}.svg returnerer image/svg+xml uden login. API'et danner en gennemsigtig linjegraf på 320 × 80 i kortets fulde indvendige bredde. Grafen viser kun en linje uden udfyldning, synlige tal, akseetiketter eller punktmarkører. Det sidste linjestykke afsluttes med et åbent pilehoved i linjestykkets retning. Hver flig er halvt så lang som det sidste linjestykke og har samme stregtykkelse og farve, uden udfyldning. Antal køb i tolv femminuttersintervaller fordeles jævnt fra venstre til højre kant, ældst først. Den lodrette skala følger de observerede minimums- og maksimumsværdier. En konstant serie, også en serie af nuller, vises som en vandret linje midt i grafen. De præcise antal bevares i SVG-beskrivelsen af hensyn til tilgængelighed. Intervallerne deler den præcise rullende UTC-time fra og med sinceUtc til, men ikke med, measuredAtUtc ud fra postens MS2000. De afrundes ikke til klokkeslættets femminuttersgrænser. Intervaller uden køb har værdien nul. Grafen tæller køb på tværs af valutaer, ikke pengebeløb, og bruger samme købsfiltre og snapshot cachet i ét minut som JSON-svaret. JSON-totalerne omfatter fortsat alle bevarede køb i Log1Hour, som opfylder filtrene. Deres antal kan derfor afvige fra grafens sum, hvis poster uden for grafens tidsinterval stadig findes i tabellen.

Det yderste svg-element har attributten data-counts: tolv kommaseparerede, ikke-negative heltal i invariant format uden tusindtalsseparatorer, ældst først. Det er de samme værdier, som bruges til linjen og SVG-beskrivelsen. Ved visning med img læses attributten fra den hentede SVG, og værdierne medtages i en lokaliseret alt-beskrivelse. Genbrug samme SVG-svar til billedet og teksten; der kræves intet ekstra datakald.

Den obligatoriske stiparameter color accepterer en RGB-hexværdi på tre eller seks tegn uden #, eksempelvis 258623, eller et understøttet SVG-farvenavn som green. Den ændrer kun udseendet. Tenant, datoer, intervallængde og købsfiltre kan ikke tilsidesættes. Svaret har Cache-Control: no-store og X-Content-Type-Options: nosniff; opdater én gang i minuttet. Ugyldig farve: HTTP 400 med ProblemDetails-koden invalid-purchase-chart-color. Utilgængelige data: HTTP 503 med koden purchase-statistics-unavailable og Retry-After: 60; behold den tidligere graf, eller vis utilgængelig, aldrig et opdigtet nul.

<img src="https://api.team.kombine.technology/api/v1/public/statistics/purchases/chart/258623.svg"
     width="320" height="80" alt="Antal køb pr. 5 minutter i den seneste time.">

SVG'en indeholder kun samlede antal. Ingen transaktionsdetaljer, kontoidentifikatorer eller managerrettigheder udstilles. Endpointet er ikke frigivet; genererede klienter synkroniseres ved betaforberedelsen.

Offentligt uden login

GET /api/v1/public/statistics/active-users — Antal aktive brugere for sitets tenant. Swagger: Public → GetPublicActiveUsers.

Tallet tæller unikke kombinationer af bank og bruger i tenantens aktuelle Log7 med bruger-id fra 1001 til og med 99999 eller gæste-id 999999999 (eUserId.Guest), bank-id mindst 1000 og MS2000 strengt nyere end 100 dage før opgørelsen i UTC. Flere rækker for samme bank/bruger tæller én gang; samme bruger-id i to banker tæller to gange. Det beskriver en nyere Log7-post, ikke nødvendigvis et login. Der filtreres ikke på brugerens Enabled eller Deleted.

Dette samlede tal er offentligt og uafhængigt af managerens adgang. Ingen personer eller enkelte bankers tal udleveres. Sitet bestemmer tenant; kaldet tager ingen KID, datoperiode eller andre filtre.

tenantKid identificerer sitets tenant, count er antallet, lookbackDays er 100, sinceUtc er den eksklusive tidsgrænse, og measuredAtUtc er opgørelsens tidspunkt. Svaret caches i op til 5 minutter pr. API-instans. Samtidige besøg deler én optælling, og der er ingen baggrundspolling. Ved databasefejl returneres HTTP 503 med Retry-After: 60; vis utilgængelig, ikke nul. Et vellykket svar med count 0 betyder faktisk nul.

const response = await fetch('https://api.team.kombine.technology/api/v1/public/statistics/active-users');
if (!response.ok) throw new Error('HTTP ' + response.status);
const statistics = await response.json();
console.log(statistics.count, statistics.measuredAtUtc);

JavaScript-klienten har også client.getActiveUsers(). Denne anonyme widget-læsning tillader GET fra eksterne origins uden credentials. Beskyttede operationer kræver fortsat en konfigureret, præcis origin.

Kombine-logoer — lokal SVG

Offentlige udgaver af de tre oprindelige Kombine-logoer, uafhængige af skrifttyper. Vælg Public v1 (no login) i Swagger. Ingen login, forretnings-KID, Tab, rettighed eller databaseadgang er nødvendig. Renderingen bruger .NET XML, symbolets oprindelige geometri og lokalt indlejrede bogstavkurver inklusive registreringsmærket. Ingen tredjeparts-grafikkomponenter, installerede skrifttyper, scripts eller eksterne assets.

LogoStipræfiksOperation IDProportioner
Symbol/api/v1/logos/kombineGetKombineLogo1:1
Navn alene/api/v1/logos/kombine-textGetKombineText590:111
Symbol og navn/api/v1/logos/kombine-logo-textGetKombineLogoText5:1

Hvert præfiks har tre former: /{color}.svg, /{color}/{width}.svg og /{color}/{background}/{width}.svg. De sidste to operationer har henholdsvis Sized og WithBackground tilføjet til operationens navn. Alle parametre står i stien uden querystring. Kun SVG, ingen automatisk konvertering til rasterformater.

Logo-dele: Brug /api/v1/logos/kombine/{parts}/{color}.svg, eventuelt med bredde eller baggrund/bredde før .svg. Operationer: GetKombineLogoParts, GetKombineLogoPartsSized og GetKombineLogoPartsWithBackground. Parts accepterer 1–4 bogstaver N/E/S/W uden forskel på store og små samt kombinationer som WE. N/S er lodrette streger; W/E indeholder hver en vandret streg og den tilhørende buede del. Den oprindelige kvadratiske viewBox bevares. Ugyldige dele giver 400 med invalid-logo-parameters. Alle parametre ligger i URL-stien. Standardruterne viser hele symbolet. Offentlig adgang, farver, størrelse og cache er uændret. Endnu ikke udgivet; klienter regenereres ved beta. Serverens diskcache bevarer URL-stien og SVG-filnavnet under en mappe for rendererens og grafikkens version. Efterfølgende kald læser den gemte fil, også efter genstart, indtil den udløber eller fjernes fra cachen.

GET /api/v1/logos/kombine/W/black.svg
GET /api/v1/logos/kombine/E/black.svg
GET /api/v1/logos/kombine/N/black.svg
GET /api/v1/logos/kombine/S/black.svg
GET /api/v1/logos/kombine/NS/black.svg
GET /api/v1/logos/kombine/WE/white/174d61/128.svg
GET /api/v1/logos/kombine/black.svg
GET /api/v1/logos/kombine-text/black/512.svg
GET /api/v1/logos/kombine-logo-text/white/174d61/512.svg

Kombine-symbol Kombine-navn Hvidt Kombine-logo på mørk baggrund

Farver accepterer 3/6-cifret RGB-hex uden #, præcise eColor-navne, almindelige farvenavne eller transparent, uafhængigt af store/små bogstaver. Ukendte navne afvises uden gæt. Uden bredde i URL'en har SVG'en ingen fast bredde/højde: viewBox og preserveAspectRatio="xMidYMid meet" tilpasser og centrerer hele logoet i det tilgængelige område uden beskæring eller stræk. En angivet bredde er et heltal fra 16–4096 pixel; højden følger de oprindelige proportioner og kan have decimaler. Brug en URL med bredde eller dimensioner på billedelementet, hvis størrelsen skal være fast. Baggrunden er transparent, medmindre den angives. En angivet baggrund tegnes direkte i SVG'en; i den gamle løsning blev baggrunden kun brugt til rasterbilleder. Tilføj alternativ tekst ved indsættelse.

Alle parametre beskriver et færdigt billede. Første kald renderer og gemmer atomisk på privat disk uden for wwwroot; gentagne kald genbruger filen, også efter genstart hvis disken bevares. Cachen indeholder højst 512 varianter/32 MiB i 24 timer. Nøglen adskiller logo, normaliserede farver, bredde, rendererversion og hash af det indlejrede asset. Fejl i diskcachen giver frisk rendering. Svar bruger image/svg+xml, offentlig browsercache i 600 sekunder og ETag/If-None-Match med 304.

Fejl: 400 ved forkert farve eller bredde, uden automatisk tilpasning. Validerede parametre giver code: invalid-logo-parameters; forkert heltalsformat bruger standardvalideringens ProblemDetails. 404 ved ukendte stier/formater såsom PNG. 429, når 16 logokald allerede behandles; vent før et nyt forsøg. Nye endpoints, ikke aliaser for KombineLogo1/KombineText1/KombineLogoText1. Genererede klienter og downloadpakker er synkroniseret i version 0.4.2.

curl --fail --output kombine.svg "$TENANT_API/api/v1/logos/kombine-logo-text/black/512.svg"

Lineære gradienter — SVG

GetLinearGradient og GetLinearGradientSized tegner en lineær gradient over hele canvas. Vælg Public v1 (no login), gruppen Gradients, i Swagger. Offentlig præsentation uden login, KID, rettighedskrav eller databaseadgang.

colors: 2–4 farver adskilt med bindestreger og jævnt fordelt; 3/6-cifret RGB-hex uden #, præcise eColor-navne, standardfarvenavne eller transparent (uden forskel på store og små bogstaver). angle: endelige grader med uret; 0 = venstre mod højre, 90 = top mod bund, 180 = højre mod venstre, 270 = bund mod top. Brug punktum til decimaler. Negative vinkler og hele omdrejninger normaliseres modulo 360. Størrelsen er som standard 200 × 200; begge mål kan være 16–4096 pixels. Vinklen bevares ved de ønskede mål, og gradienten spænder over hele rektanglet.

Kun SVG med alle parametre i stien; ingen scripts eller eksterne ressourcer. Angiv alternativ tekst ved indlejring. Privat diskcache: 24 timer, højst 512 varianter/32 MiB; ved I/O-fejl genereres billedet igen. HTTP: image/svg+xml, offentlig cache i 600 sekunder, ETag/If-None-Match med 304. Fejl: 400 ProblemDetails ved ugyldige input (invalid-gradient-parameters ved renderingsvalidering; fejlformede tal bruger standardvalidering), 404 ved ukendte formater/stier, 429 ved 16 samtidige gradientkald (prøv igen med stigende ventetid). Genererede klienter og pakker synkroniseres ved næste betaudgivelse.

GET /api/v1/gradients/linear/{colors}/{angle}.svg
GET /api/v1/gradients/linear/{colors}/{angle}/{width}x{height}.svg

GET /api/v1/gradients/linear/22aa88-ffcc33/45/800x400.svg
curl --fail --output gradient.svg "$TENANT_API/api/v1/gradients/linear/22aa88-ffcc33/45/800x400.svg"

Cirkler og fremdrift — SVG

Tre offentlige visninger erstatter tegnefunktionerne fra CircleGradient1, CircleProgress1 og CircleRunning1. Vælg Public v1 (no login) i Swagger. De tegner kun de angivne farver og procenttal, uden login, forretnings-KID, rettighedskrav eller databasekald. De læser ikke maskinstatus og beregner ikke fremdrift. Renderingen bruger .NET XML og egen geometri uden tredjeparts-grafikkomponenter, eksterne assets eller scripts.

VisningOperation IDMed størrelse
Farvegradient som baggrundGetCircleGradientGetCircleGradientSized
FremdriftscirkelGetCircleProgressGetCircleProgressSized
KøreindikatorGetCircleRunningGetCircleRunningSized
GET /api/v1/circles/gradient/{colors}.svg
GET /api/v1/circles/gradient/{colors}/{width}x{height}.svg
GET /api/v1/circles/progress/{background}/{colors}/{percent}.svg
GET /api/v1/circles/progress/{background}/{colors}/{percent}/{width}x{height}.svg
GET /api/v1/circles/running/{color}.svg
GET /api/v1/circles/running/{color}/{width}x{height}.svg

GET /api/v1/circles/gradient/22aa88-ffcc33-ee4444.svg
GET /api/v1/circles/progress/f0f4f3/22aa88-ffcc33-ee4444/65/200x200.svg
GET /api/v1/circles/running/22aa88.svg

Farvegradient omkring centrum Fremdrift: 65 procent Køreindikator

colors er 2–4 farver adskilt med bindestreg; køreindikatoren bruger én farve. Farver accepterer 3/6-cifret RGB-hex uden #, præcise eColor-navne, almindelige farvenavne eller transparent, uafhængigt af store/små bogstaver. Ingen numeriske enumindekser eller gæt ud fra dele af navnet. Standardstørrelsen er 200 × 200; bredde og højde kan hver være 16–4096 pixel. Kun SVG, med alle parametre i stien uden querystring.

Gradienten fylder det rektangulære billedfelt ligesom den gamle vinkelgradient: fra bunden med uret. Fremdriftscirklen bevarer sin runde form: heltallet percent fra 0–100 giver samme antal firkantede markeringer fra toppen med uret. Farverne fordeles over hele 100-procentskalaen. Nul viser ingen markeringer; 100 viser alle 100. Køreindikatorens halvcirkel roterer én gang hvert femte sekund med CSS og står stille, hvis brugeren foretrækker reduceret bevægelse. Angiv alternativ tekst, når billederne indsættes.

Billederne indeholder færdige, faste visningsdata, så alle tre kan caches på privat disk i 24 timer (højst 512 varianter/32 MiB). Nøgler omfatter normaliserede farver, type, procent, størrelse og rendererversion. Filer skrives atomisk uden for wwwroot og genbruges ved gentagne kald. Fejl i diskcachen giver frisk rendering. Svar bruger image/svg+xml, offentlig browsercache i 600 sekunder og ETag/If-None-Match med 304. Dokumentgrafer har deres egen cache og caches fortsat kun, når dokumentet er fuldendt.

Fejl: 400 ved forkert farveliste, ukendt farve, ikke-heltal eller procent/størrelse uden for intervallet; værdier tilpasses ikke automatisk. Validerede tegningsparametre giver code: invalid-circle-parameters; fejl i heltalsformat bruger standardvalideringens ProblemDetails. 404 ved ukendte stier/formater såsom PNG. 429, når 16 cirkelkald allerede behandles samtidigt; vent før et nyt forsøg. Nye operationer, ikke aliaser for gamle URL'er. Genererede klienter og downloadpakker er synkroniseret i version 0.4.2.

curl --fail --output progress.svg "$TENANT_API/api/v1/circles/progress/white/22aa88-ffcc33-ee4444/65.svg"

Icon — lokale ikonbilleder

IconKid beregnes i API’et

Ved lokationssvar med house udfylder API’et IconKid.Text med lokationens LocationId. g/house viser teksten centreret med sort inde i huset og tilpasser den til pladsen. Ikon-testsiden accepterer en tekst til forhåndsvisning; et rent house uden lokationskontekst har intet nummer.

Visningssvar bruger iconKid (C#: IconKid) i stedet for icon. De tilhørende felter hedder bankIconKid og unitIconKid. Brug værdien uændret og URL-kodet i /api/v1/icon/g/{kid}.svg. API’et danner KID’en: kun et ikon returneres som eIcon.ToString(); tekst, count, farve eller flere ikoner giver Kid.ToString(). Bank-, lokations- og enhedsnummer ligger allerede i Text. Calendar får dagens dag i måneden (1–31) i Europe/Copenhagen, før URL’en dannes. Nye metadata efter midnat giver et nyt filnavn. Billedcachen er uændret.

GetIconPresentation — GET /api/v1/icon/presentation?iconKid=calendar returnerer {"iconKid":"..."}. Offentlige visningsdata uden login, databaseopslag, rettigheder eller ændringer i data. Valgfrie text (højst 128 tegn uden kontroltegn), fortegnet Int64 count og RGB color (0–1073741823) erstatter de tilsvarende felter; øvrige felter bevares. Calendar bruger altid dagens dagstal som tekst. Ugyldige parametre giver 400. Ved netværksfejl/503 beholdes det tidligere billede; prøv igen senere. Metadata er no-store; billedets cacheregler ændres ikke.

Vælg tilladte indstillinger fra availableIcons og send fortsat enum-navnet ved ikonændringer (icon eller indstillingen Icon). Services returnerer også iconName til den valgte indstilling. GetActiveLocationCount returnerer både count og det færdige iconKid; klienten skal ikke opbygge badget. IconKid giver aldrig adgang. Se changelog; genererede klienter og downloadpakker er synkroniseret i version 0.4.2.

const response = await fetch(`${api}/api/v1/icon/presentation?iconKid=calendar`);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const { iconKid } = await response.json();
image.src = `${api}/api/v1/icon/g/${encodeURIComponent(iconKid)}.svg`;

GetIconAssetCatalog: GET /api/v1/icon/catalog/g returnerer et sorteret JSON-array af kanoniske eIcon-navne med en fil direkte i g (brug line for dette sæt). En ikonvælger skal tage fællesmængden med availableIcons i forretningssvaret og skjule manglende filer. Kataloget kræver hverken login eller rettigheder og giver ikke skriveadgang. Fallback fra andre sæt medtages ikke. Ukendte sæt giver 404; gentag 429/503 med stigende ventetid. Kataloget caches i ti minutter og afspejler de deployede filer.

curl --fail "$TENANT_API/api/v1/icon/catalog/g"

Åbn ikon-testsiden.

GetIconFromSet, GetIconImageFromSet og GetIconImageWithBackgroundFromSet renderer medfølgende lokale assets. Der kræves hverken login, Tab, handlingsrettighed, databaseopslag eller ekstern ikonserver. Andre felter i KID vælger ikke tenantdata og giver ingen rettigheder.

GET /api/v1/icon/{iconSet}/{kid}.{format}
GET /api/v1/icon/{iconSet}/{kid}/{size}.{format}
GET /api/v1/icon/{iconSet}/{kid}/{backColor}/{size}.{format}

GET /api/v1/icon/line/413132x7qE20i11Bi336699Ic.svg
GET /api/v1/icon/g/413132xE20i11Bi336699Ic/128.png
GET /api/v1/icon/line/house/white/128.jpg

kid er en kanonisk Kombine.Flex.Kid.ToString()-værdi. API’et læser Kid.Icons, Kid.Count (Int64), Kid.Color og Kid.Text. Eksempelvis bliver Kid.Icons = [house, check], Count = 7, Color = 0x336699, Text = "A12" til 413132x7qE20i11Bi336699Ic. Hvis kanonisk KID-parsing fejler, accepteres et præcist eIcon-navn uden forskel på store/små bogstaver med tæller 0, sort og tom tekst; både house og HOUSE virker. Numeriske enum-id’er, indeks og delstrenge er ikke fallback-navne. Ukendte navne, ugyldige/ikke-kanoniske KID’er og udefinerede ikoner giver 400. Et gyldigt KID uden eksplicit ikon bruger eIcon.none.

De separate count-, color-, text- og sub-segmenter og alle ruter uden iconSet er fjernet. Vælg line eller g. Kid.Color bruger de nederste 30 bit som uigennemsigtig RGB: 0 er sort, 0xFFFFFF er hvid, og øverste byte ignoreres som i Flex eColor (ARGB giver samme RGB). Kid.Text er UTF-8-tekst med forskel på store/små bogstaver, højst 128 tegn uden kontroltegn, og bruges kun i assets med tekstfelt. Det kanoniske KID må have op til 2048 tegn efter tekstkodning; URL-kod det som ét stisegment. Count = 0 skjuler antalsmærket. Andre antal vises som den fulde absolutte værdi: positive antal i en hvid kapsel med sorte tal og negative antal i en rød kapsel med hvide tal. Kapslen har runde ender og et rektangel imellem. Mærket bliver bredere med flere cifre uden at strække enderne eller ændre højde/skriftstørrelse; meget lange tal udvider SVG-fladen. SVG-titlen indeholder også hele antallet. Første element, Kid.Icons[0] (også tilgængeligt som Kid.Icon), vælger hovedikonet; Kid.Icons[1] vælger underikonet. Et manglende andet element eller eIcon.none udelader underikonet. En tom liste bruger eIcon.none som hovedikon. Yderligere elementer ignoreres ved rendering, men alle elementer skal være definerede eIcon-værdier. Øvrige parametre må højst have 128 tegn. Der bruges ingen queryparametre.

Formater: svg, png, jpg/jpeg, gif, bmp, tif/tiff, webp, ppm, tga, ico. Størrelsen begrænses til 16–4096 pixels (ICO højst 256). SVG beholder sit oprindelige koordinatområde; størrelse/baggrund gælder rasterbilleder. Udelad baggrundssegmentet for transparent rasterbaggrund. Ukendte formater returnerer SVG med image/svg+xml. Antalsmærker i rasterbilleder bruger den medfølgende Noto Sans Bold-font.

Første kald renderer og gemmer billedet på disk; samme variant læses derefter fra disk, også efter genstart hvis disken bevares. Gamle cacheposter kan fjernes, og nye instanser/deployments kan begynde med tom cache. Svar caches offentligt i browseren i ti minutter og understøtter ETag/Last-Modified med 304. Håndtér 400 ved ugyldige/for lange parametre, 404 ved manglende lokale assets, 429 ved for mange samtidige kald og 503 ved fejl i rendering/lager. Gentag midlertidige fejl med stigende ventetid. Ingen databasefallback eller offentlig cache-sletning.

C# · Kombine.Flex.Kid

Kombine.Flex.Kid kid = new Kombine.Flex.Kid();
kid.Icons.Add(Kombine.Flex.eIcon.house);
kid.Icons.Add(Kombine.Flex.eIcon.check);
kid.Count = 7;
kid.Color = 0x336699;
kid.Text = "A12";
System.String url = baseUrl + "/api/v1/icon/line/"
    + System.Uri.EscapeDataString(kid.ToString()) + ".svg";

VB.NET · Kombine.Flex.Kid

Dim kid As New Kombine.Flex.Kid()
kid.Icons.Add(Kombine.Flex.eIcon.house)
kid.Icons.Add(Kombine.Flex.eIcon.check)
kid.Count = 7
kid.Color = &H336699
kid.Text = "A12"
Dim url As System.String = baseUrl & "/api/v1/icon/line/" & System.Uri.EscapeDataString(kid.ToString()) & ".svg"
curl --fail --output house.svg "$TENANT_API/api/v1/icon/line/413132x7qE20i11Bi336699Ic.svg"

Ikonsæt: g bevarer de originale flerfarvede SVG-filer; line bruger sine palette-/tekstmarkeringer. Hovedikon og underikon søges hver for sig i det valgte sæt først og derefter i øvrige medfølgende sæt i ordinal alfabetisk rækkefølge med samme identitet. De kan komme fra forskellige sæt. Ukendte sæt eller assets, som mangler i alle lokale sæt, giver 404. Katalogerne viser kun direkte medlemskab, og diskcachens identitet omfatter det valgte sæt. Eksempelvis bruger /api/v1/icon/g/jaa_ckey.svg ikonet fra line, fordi g ikke indeholder jaa_ckey.

Migrering: tilføj hovedikon og eventuelt underikon til Kid.Icons i den rækkefølge, og sæt Count, Color og Text på KID, URL-kod ToString(), vælg sæt og fjern count/color/text/sub-segmenterne. Et præcist eIcon-navn virker fortsat til sorte ikoner uden tekst eller antalsmærke. Gamle stier er ikke kompatibilitetsaliaser; opdater gemte URL’er og følg den offentlige changelog. Genererede klienter og downloadpakker er synkroniseret i version 0.4.2.

Daglig oprydning fjerner som standard renderede varianter, der ikke har været brugt i 100 dage. API’et registrerer brug uafhængigt af filsystemets adgangstider. Pladsgrænser kan rydde op tidligere; næste kald danner billedet igen fra de originale medfølgende assets.

Services — faste serviceidentiteter

GetServices: GET /api/v1/services viser de konkrete serviceværdier i eUserId, også dem uden gemte indstillinger. Intervalslutmarkører er udeladt. Hvert element indeholder kanonisk kid, enum-navnet identity (ToString), name og iconKid. Kun Name og Icon hentes fra sitets A{TenantId:D4}.Log7, BankId 0. Den afgrænsede liste returneres samlet; nextCursor er null. filter søger bogstaveligt i identity eller name uden forskel på store/små bogstaver (højst 128 tegn); sort=identity|name, direction=asc|desc. Identity sorteres efter enum-værdien.

GetService: GET /api/v1/services/{serviceKid} returnerer service, hasApiKeyHash, apiKeyHash, canEdit, profileRevision og availableIcons. Service-KID bruger den eksisterende Manager-type, sitets tenant, bank 0 og et konkret service-id. Typen giver ikke rettigheder.

Rettigheder og redigering

Alle kald kræver en aktiv administratorsession, Services1 (60), selvstændig PermissionService2.Read og adgang til hele tenanten. Redigering kræver også Service Write. Læsere får null i apiKeyHash; kun redaktører får den gemte hash. Behandl den som følsom, og log den ikke. Hashen er aldrig med i listens svar.

Adgang for servicekonti

Servicekonti bruger ikke rettighedsmatrix, tildelte KID-afgrænsninger eller faner. Hvert endpoint afgør udtrykkeligt, hvilke godkendte eUserId-værdier det accepterer: synkronisering kræver eUserId.ServiceSync; status accepterer alle understøttede serviceidentiteter. Kontostatus, nøglens gyldighed og sitets tenant kontrolleres fortsat. Tidligere adgangsindstillinger bevares i databasen, men returneres og redigeres ikke. GetService returnerer ikke længere access. Ruter til servicens rettigheder, roller, faner og KID-tildeling er fjernet; stop med at kalde dem. Managerkontrollen ved visning og redigering af services er uændret. Se ændringsloggen. Klienter opdateres ved beta.

SetServiceProfileField: POST /api/v1/services/{serviceKid}/profile/{field} gemmer ét felt: Name eller Icon. Name tillader højst 200 tegn uden kontroltegn. Icon skal være et navn fra availableIcons (alle kendte positive eIcon-værdier); et sikkert ældre ikon vises først, men kan ikke tildeles igen. ApiKeyHash er skrivebeskyttet: forsøg på at ændre eller fjerne den giver HTTP 400 invalid-service-profile, også ved en tom værdi. Brug GenerateServiceApiKey nedenfor til at erstatte API-nøglen; send aldrig selve nøglen eller en manuelt beregnet hash. Eksisterende hashes bevares, indtil en ny nøgle genereres. Se Changelog på engelsk for vejledning om ændringen.

Ved skrivning kontrolleres den aktuelle konto, credential-stamp, tab, rettigheder og scope igen i en serialiserbar transaktion. Én indstilling tilføjes bank 0 Log7-historikken; uændrede værdier opretter ingen historik. Opdatér først UI efter et fuldt 200-svar, og brug den nye revision. Ingen automatiske skrivegenforsøg. Svar er no-store; kald har 12 sekunders tidsgrænse, og skrivekald tillader højst 4096 bytes.

const headers = { Authorization: `Bearer ${accessToken}`, Accept: 'application/json' };
const listResponse = await fetch(`${api}/api/v1/services?sort=name&direction=asc`, { headers });
if (!listResponse.ok) throw new Error(`GetServices: ${listResponse.status}`);
const { items } = await listResponse.json();
const serviceKid = items[0]?.kid;
if (!serviceKid) throw new Error('No services');
const detailResponse = await fetch(`${api}/api/v1/services/${serviceKid}`, { headers });
if (!detailResponse.ok) throw new Error(`GetService: ${detailResponse.status}`);
const detail = await detailResponse.json();
if (!detail.canEdit) throw new Error('Service Write is required');
const saved = await fetch(`${api}/api/v1/services/${serviceKid}/profile/Name`, {
  method: 'POST', headers: { ...headers, 'Content-Type': 'application/json' },
  body: JSON.stringify({ value: 'Scheduled integration', expectedRevision: detail.profileRevision })
});
if (!saved.ok) throw new Error(`SetServiceProfileField: ${saved.status}; reload before retry`);
const confirmed = await saved.json();

Fejl: 400 invalid-filter/invalid-sort/invalid-service-kid/invalid-service-profile — ret forespørgslen. 401 — log ind igen. 403 missing-services-tab/missing-services-read/missing-services-write/missing-tenant-access — få den manglende adgang. 409 service-profile-conflict — genindlæs og gennemgå ændringerne. 503 services-unavailable — genindlæs før manuelt genforsøg; en mistet kvittering kan give et usikkert udfald. Vis ikke en mislykket gemning som gennemført.

Generér en ny API-nøgle

GenerateServiceApiKey: POST /api/v1/services/{serviceKid}/api-key modtager {"expectedRevision":"<profileRevision fra GetService>"}. Kaldet kræver samme Service Read/Write, Services1 og adgang til hele tenanten som profilredigering, med ny adgangskontrol i transaktionen.

API'et genererer kt_ efterfulgt af 64 kryptografisk tilfældige ASCII-bogstaver og tal, altid med både store og små bogstaver. Nøgler skelner mellem store og små bogstaver. Kun hashen gemmes i eSetting.ApiKeyHash, med præcis samme funktion som administratorpasswords (kompatibel med HubManager.SHA512Salt). Den erstatter den tidligere hash. Et godkendt svar indeholder apiKey og details med den gemte hash og den nye revision. Selve nøglen returneres kun i dette svar og kan ikke hentes igen med GetService. Vis/kopiér den først efter bekræftet gemning; læg den ikke i logs, browserlagring, URL'er eller analyseværktøjer. Portalen fjerner den viste nøgle, når man forlader siden eller erstatter nøglen.

Service-nøglers hash gemmes i eSetting.ApiKeyHash, samme Log7-indstilling som administratorpasswords. eSetting.ApiKeyHash læses eller skrives ikke længere. Før en installation skifter til denne version, skal eksisterende service-hashes flyttes til Password via Log7-historikken uden at overskrive et eksisterende Password, eller nøglerne skal erstattes med nye i portalen. Eksisterende nøgler virker fortsat, hvis hashen flyttes uændret. Der er ingen automatisk migrering eller fallback. JSON-felterne apiKeyHash og hasApiKeyHash beholder deres navne og beskriver Password; manuel redigering af legitimationsoplysninger er fortsat afvist.

Samme 400/401/403/409/503-fejl, no-store, 12-sekunders grænse og højst 4096 bytes som profilredigering. Ved timeout eller mistet svar skal der genindlæses før manuel generering igen: det første kald kan have gemt. Gentag aldrig automatisk. Service-login aktiveres ikke. Genererede klienter og downloadpakker er synkroniseret i version 0.4.2.

const headers = { Authorization: `Bearer ${accessToken}`, Accept: 'application/json' };
const read = await fetch(`${api}/api/v1/services/${serviceKid}`, { headers });
if (!read.ok) throw new Error(`GetService: ${read.status}`);
const detail = await read.json();
const generated = await fetch(`${api}/api/v1/services/${serviceKid}/api-key`, {
  method: 'POST', headers: { ...headers, 'Content-Type': 'application/json' },
  body: JSON.stringify({ expectedRevision: detail.profileRevision })
});
if (!generated.ok) throw new Error(`GenerateServiceApiKey: ${generated.status}; reload before retry`);
const confirmed = await generated.json();
const display = document.createElement('code');
display.textContent = confirmed.apiKey;
document.body.append(display);
const copy = document.createElement('button');
copy.textContent = 'Copy API key';
copy.onclick = async () => {
  try { await navigator.clipboard.writeText(display.textContent); }
  catch { copy.textContent = 'Select and copy the key manually'; }
};
document.body.append(copy);
window.addEventListener('pagehide', () => { display.textContent = ''; copy.remove(); }, { once: true });

Vælg Downloads v1 under Select a definition i Swagger for beboer-CSV, konto-CSV/Excel, afregnings-ZIP og dokumenter i CSV/XLS. Brug samme manager-token og rettigheder. Den samlede OpenAPI-definition indeholder fortsat disse kald til klientværktøjer.

Flere managers må have samme e-mailadresse, hvis adgangskoderne er forskellige. Matcher flere konti både e-mail og adgangskode, beholder login den aktive, ikke-slettede manager med nyeste MS2000 på eSetting.Alive og nulstiller Password på de øvrige matchende managers i én transaktion. Aktivitet læses fra krumbens tidsstempel, aldrig dens Text-værdi. Manglende eller ugyldige tidsstempler kommer efter gyldige; ved ens tidsstempler, eller hvis alle er ukendte, bruges laveste UserId. Loginoplysninger og aktivitet læses igen under lås i transaktionen før oprydning. Konti med andre adgangskoder ændres ikke, og rettigheder lægges aldrig sammen. Uden en aktiv matchende konto sker ingen oprydning. Kald GetCurrentManager efter login for at se den valgte konto.

Et token udstedes først, når oprydningen er gemt. Utilgængelig database, fejl under oprydning eller mere end 100 matchende rækker giver HTTP 503. Gentag ikke automatisk et kald med usikkert resultat. Sessioner for konti, hvis adgangskode er ryddet, bliver ugyldige; andre API-instanser kan beholde deres eksisterende kontosnapshot i op til 60 sekunder. Felterne i request og tokensvar er uændrede.

Mislykkede loginforsøg kan gemmes som interne JSON-diagnoser. Klientens statuskoder og svar ændres ikke; interne årsager som dubleret kombination af e-mail og adgangskode udleveres ikke gennem login. Drift kan korrelere anmodningens trace-id med API-loggen. Adgangskoder og tokens må aldrig medsendes i fejlrapporter.

Login og managerindstillinger hentes fra sitets egen A{TenantId:D4}.Log7, BankId 0. D4 betyder tenant-id med mindst fire cifre: Team 166 bruger A0166.Log7. Tenant vælges i serverens PortalSite:TenantId; der er ingen fallback til en anden tenants managerdatabase.

Manageren skal have Enabled = 1. Mangler Deleted-rækken (eller er den SQL NULL), bruges 0: ikke slettet. En eksisterende ugyldig Deleted-værdi eller en positiv sletningsværdi blokerer stadig login.

1. Fra API-adresse til første login

API-adressen for dette tenant-site er https://api.team.kombine.technology. Brug API-adressen, ikke portalens adresse.

Eksemplerne bruger automatisk adressen på det API-site, hvor du læser vejledningen. Beta viser beta-adressen. Uden JavaScript vises Team-produktionsadressen; kontrollér adressen før brug. Du kan ikke skifte tenant med et ekstra felt, en query-parameter eller en header. Site og rettigheder bestemmes på serveren.

KaldFormålLogin?
GET /api/v1/public/statistics/purchasesAntal køb den seneste timeNej
GET /api/v1/public/statistics/active-usersAntal aktive brugere for sitets tenantNej
GET /api/v1/statusTjek, om API’et svarer. Tester ikke MySQL.Nej
POST /api/v1/session/loginByt managerens email og password til et midlertidigt adgangstoken.Email og password i JSON
GET /api/v1/session/meHent din egen profil og dine aktuelle rettigheder samlet.Adgangstoken

Prøv uden at skrive et program

  1. Åbn Swagger, og vælg Portal integrations v1. Swagger er en webside, hvor du kan læse om og afprøve API-kaldene.
  2. Åbn GetPortalStatus, vælg Try it out og derefter Execute. Et svar med HTTP 200 betyder, at kaldet lykkedes.
  3. Åbn LoginManager. Erstat eksemplets email og password med din managers oplysninger, og vælg Execute. Eksempeloplysningerne er opdigtede og kan ikke bruges til login.
  4. Kopiér kun værdien af accessToken fra svaret, uden anførselstegn. Klik Authorize, indsæt den under ManagerBearer, og godkend. Swagger tilføjer selv Bearer.
  5. Kør GetCurrentManager. Svaret indeholder de oplysninger, du kan bruge til profil og adgangskort i din portal.

En manager skal være aktiveret og ikke slettet. Manglende Tabs eller bankadgang forhindrer ikke selve login; forklar i din portal, hvilken adgang der mangler.

Sådan ser HTTP-kaldene ud

POST https://api.team.kombine.technology/api/v1/session/login
Content-Type: application/json
Accept: application/json

{"email":"manager@example.test","password":"<dit password>"}

Send det oprindelige password over HTTPS. API’et håndterer kontrollen; klienten skal ikke encode eller hashe passwordet.

{
  "accessToken": "<dit adgangstoken>",
  "expiresIn": 259200,
  "tokenType": "Bearer"
}
GET https://api.team.kombine.technology/api/v1/session/me
Authorization: Bearer <dit adgangstoken>
Accept: application/json

Tokenet gælder i tre dage (259.200 sekunder) fra login eller fornyelse. Behandl det som en hemmelig tekststreng; det er ikke en JWT. Læs expiresIn frem for at fastlåse levetiden. Før udløb kan RenewManagerSession erstatte tokenet efter brugeraktivitet. Almindelige API-kald forlænger det ikke, og et udløbet token kræver login. Der udstedes ikke et separat refresh-token og findes ikke individuel token-tilbagekaldelse. Logout fjerner klientens kopi; andre kopier beholder deres oprindelige udløb med fortsat kontrol af konto og password.

GetCurrentManager (GET /api/v1/session/me) indeholder den valgfrie gravatarUrl for den indloggede manager. Brug billedet i kontomenuen og iconKid, hvis værdien er null eller billedet fejler, herunder HTTP 404. URL'en bruger den normaliserede e-mails SHA-256-hash, 96 px, G-klassifikation og d=404; API'et kontakter ikke Gravatar. Direkte browserkald deler e-mailhash og klientens IP med Gravatar. Visningsfeltet giver ingen rettigheder og kræver ikke adgang til managerlisten. Ikke udgivet endnu.

RenewManagerSession

POST https://api.team.kombine.technology/api/v1/session/renew
Authorization: Bearer <nuværende gyldigt token>
Accept: application/json

HTTP 200
{"accessToken":"<nyt token>","expiresIn":259200,"tokenType":"Bearer"}

Kaldet har ingen request-body. Erstat først gemt token og udløbstid efter succes. API'et genkontrollerer samme site, aktiv konto og password-stempel via det afgrænsede snapshot (højst 60 sekunder). Der tildeles ingen nye Tab-, KID- eller operationsrettigheder. Lokale testlogin forbliver begrænset til Development og direkte loopback-forbindelser. HTTP 401 kræver login; 429 kræver ventetid ifølge Retry-After; 503 betyder utilgængelig kontolagring. Fejl forlænger ikke det gamle token. Undgå parallelle fornyelser og ignorer sene svar fra et tidligere login.

Portalen gemmer en vedvarende beskyttet cookie og fornyer både cookie og API-token ved mus, tastatur eller scrolling på en synlig side, højst én gang i minuttet. En fane, som blot står åben, offentlige statistikker og baggrundskald forlænger ikke sessionen. Logout fjerner browserens cookie. Eksisterende sessioner på én time beholder deres gamle udløb; log ind igen for at starte den nye session på tre dage. Genererede klienter og downloadpakker er synkroniseret i version 0.4.2. De pakkede klienter fornyer ikke automatisk.

Et helt eksempel i PowerShell 7

Kopiér blokken til PowerShell 7. Dialogen beder om managerens email og password. Eksemplet viser profilen og skriver hverken password eller token ud. Lokalt skal .NET’s udviklingscertifikat være betroet; brug dotnet dev-certs https --trust ved lokal udvikling.

$api = 'https://api.team.kombine.technology'
$credential = Get-Credential -Message 'Manager email and password'
$session = $null
$body = $null
$headers = $null
try {
    $body = @{
        email = $credential.UserName
        password = $credential.GetNetworkCredential().Password
    } | ConvertTo-Json -Compress

    $session = Invoke-RestMethod "$api/api/v1/session/login" `
        -Method Post -ContentType 'application/json' -Body $body -TimeoutSec 15

    $headers = @{ Authorization = "Bearer $($session.accessToken)" }
    $profile = Invoke-RestMethod "$api/api/v1/session/me" `
        -Headers $headers -TimeoutSec 15
    $profile | ConvertTo-Json -Depth 6
}
catch {
    if ($_.Exception.Response) {
        Write-Warning "HTTP $([int]$_.Exception.Response.StatusCode). See the error table."
    } else {
        Write-Warning 'Could not reach the API. Check the address, connection, and certificate.'
    }
}
finally {
    $body = $null
    $headers = $null
    $session = $null
    $credential = $null
}

Selvoprettelse — SignUp1

Ikke udgivet. RequestManagerSignUp (POST /api/v1/session/signup) kræver email, name, organisation, password og confirmPassword. Navn/organisation: 1–200 tegn uden kontroltegn; email: almindelig ASCII-internetadresse, højst 254 tegn. Ens adgangskoder: 12–128 synlige ASCII-tegn uden indledende/afsluttende mellemrum. Valgfrit language: præcis en, da eller es med små bogstaver. Udelad feltet, eller send null for engelsk. Tomme strenge, mellemrum, regionale sprogkoder, store bogstaver og andre værdier giver HTTP 400 ValidationProblemDetails med errors.Language, før kontoen oprettes eller mailen sættes i kø. Sitet vælger tenant; ukendte felter afvises.

POST /api/v1/session/signup
Content-Type: application/json

{"email":"person@arendt.dk","name":"Example Name","organisation":"Example Organisation","password":"YOUR_NEW_PASSWORD","confirmPassword":"YOUR_NEW_PASSWORD","language":"en"}

POST /api/v1/session/signup-confirmation
Content-Type: application/json

{"token":"TOKEN_FROM_EMAIL"}

API'en tildeler en aldrig brugt manageridentitet under den fælles tenant-lås og gemmer profil, password-hash, afventende markør, Enabled=0 og alle ni operationstilladelser=0 i Log7 i samme transaktion som mailen i Mails. Mailens UserId er managerens ID. Fejl i mailkøen ruller oprettelsen tilbage. Ingen tabs, områder eller roller tildeles; login er spærret indtil verificering. 202 accepted bekræfter behandling, ikke levering. Eksisterende adresser får samme svar uden afsløring. En urørt, afventende konto med korrekt adgangskode får et nyt link med samme manager-ID; andre eksisterende konti ændres ikke.

ConfirmManagerSignUp (POST /api/v1/session/signup-confirmation) accepterer kun token. Udtrykkelig POST verificerer emailen og aktiverer den eksisterende afventende konto; ingen ny konto eller ændring af adgangskode. Linket er bundet til tenant/miljø og aktuelle credential-/markør-/Enabled-revisioner, udløber efter 30 minutter og bruges én gang. 200 email-verified giver ingen session. Selvoprettelsen giver ikke i sig selv adgang til driftsdata eller funktioner. Autoriserede administratorer kan særskilt tildele eller fjerne adgang, mens emailverificeringen afventes. Bekræftelsen bevarer disse ændringer og tildeler ikke selv rettigheder. Log ind normalt bagefter.

400 invalid-profile/invalid-password: ret input. invalid-signup-token: ugyldig, udløbet, brugt eller ændret konto. Bed om nyt link med oprindelige credentials til en afventende konto. 429: vent efter Retry-After. 503 signup-unavailable: usikkert resultat; gentag ikke automatisk. Log aldrig bodies, tokens eller passwords. Højst to anmodninger pr. email pr. 15 minutter uden afsløring. Kun nortec.dk, kombinetech.com og arendt.dk modtager verificeringslinks; andre adresser giver 400 email-delivery-restricted uden konto/mail. Oprydning af efterladte afventende managers er endnu ikke implementeret. Klienter følger ved beta-forberedelse.

Managerens personlige indstillinger

GetMyManagerProfile og GetMyManagerTabs bruger sitets læseforbindelse og virker uden en konfigureret skriveforbindelse eller skriverettigheder i MySQL. canEdit beskriver managerens adgang, ikke databasekontoens rettigheder. Lagring forsøger SQL på tenantens særskilte Write-forbindelse eller den almindelige forbindelse, hvis en særskilt skriver ikke er konfigureret; MySQL afgør rettighederne uanset brugernavn; utilgængelige skrivninger returnerer 503 og må ikke forsøges igen automatisk.

Managerikonet åbner /my-settings i portalen. Eksterne klienter bruger de samme offentlige API-kald. Alle kald undtagen bekræftelse af et maillink kræver en aktiv, tenantbundet manager-session med aktuelt adgangskodestempel. De virker kun på sessionens egen manager. Profil, e-mail og password kræver ingen Tabs, Kids eller Managers Write. Ændring af egne tabs kræver udtrykkelig KID-adgang til alle banker som beskrevet nedenfor. KID-adgang, handlingsrettigheder, kontostatus og andre brugere ændres ikke. Låsen på administrative ændringer af ens eget administratorkort bevares.

Operation IDHTTP
GetMyManagerProfileGET /api/v1/session/me/profile
SetMyManagerProfileFieldPOST /api/v1/session/me/profile/{field}
GetMyManagerTabsGET /api/v1/session/me/tabs
SetMyManagerTabPOST /api/v1/session/me/tabs/{tabId}
RequestMyManagerEmailVerificationPOST /api/v1/session/me/email-verification
ConfirmMyManagerEmailPOST /api/v1/session/me/email-confirmation
ChangeMyManagerPasswordPOST /api/v1/session/me/password

Profil, ikon og tema

Profilen indeholder kid, name, organisation, iconKid, email, emailVerified, themeMode, iconSet, retentionDays, revision, availableIcons, men ingen adgangskodehash eller interne beviser. Ikonlisten indeholder personikoner; et eksisterende ikon uden for personlisten vises først. Kun felterne Name, Organisation, Icon, ThemeMode, IconSet og RetentionDays kan skrives generisk. Navn/organisation er strenge på højst 200 tegn uden kontroltegn. Nye ikoner skal være personikoner. Tema er numerisk System=0, Light=1, Dark=2. RetentionDays kræver et JSON-heltal fra 0 til 2147483647: antal dage, du kan se slettede poster, som du ellers har adgang til. 0 skjuler slettede poster. Det ændrer synlighed, ikke fysisk sletning; Tabs, Kids og handlingsrettigheder gælder fortsat. Svarfeltet er retentionDays; manglende gemte værdier giver 10 dage; ugyldige værdier giver 0. Et eksplicit gemt 0 bevares.

IconSet gemmer dit personlige ikonsæt som den præcise JSON-streng "g" eller "line". Både GetMyManagerProfile og GetCurrentManager returnerer iconSet; manglende eller ugyldige gemte værdier giver line; et udtrykkeligt gemt valg af g bevares. Portalen bruger valget i navigation, overskrifter, lister og gemte links i arbejdsområdet. Brug API’ets uændrede IconKid i /api/v1/icon/{iconSet}/{kid}.svg; den eksisterende fallback til andre sæt gælder stadig, hvis et ikon mangler. Indstillingen kræver kun din aktive egenkonto-session og giver ingen forretningsadgang.

POST /api/v1/session/me/profile/IconSet
Authorization: Bearer YOUR_MANAGER_TOKEN
Content-Type: application/json

{"revision":"REVISION_FROM_GET_MY_MANAGER_PROFILE","value":"line"}

Brug den returnerede revision ved næste gemning. Ugyldige værdier eller JSON-typer giver 400; en forældet revision giver 409: genindlæs før et manuelt forsøg. Inaktive sessioner giver 401. Ved 503 eller netværksfejl beholdes senest bekræftede valg; genindlæs før en usikker skrivning gentages. Valget ændrer ikke ikonidentiteter eller billedcachen.

Send senest bekræftede revision sammen med én value. Succes returnerer den gemte profil og ny revision. Vis først valget som gemt efter svaret. 409 profile-conflict kræver genindlæsning og brugerens stillingtagen før overskrivning. SetCurrentManagerTheme er fortsat tilgængelig.

curl -H "Authorization: Bearer ACCESS_TOKEN" \
  "https://api.team.kombine.technology/api/v1/session/me/profile"

curl -X POST -H "Authorization: Bearer ACCESS_TOKEN" -H "Content-Type: application/json" \
  "https://api.team.kombine.technology/api/v1/session/me/profile/Name" \
  -d '{"revision":"REVISION_FROM_PROFILE","value":"Alex Jensen"}'

curl -X POST -H "Authorization: Bearer ACCESS_TOKEN" -H "Content-Type: application/json" \
  "https://api.team.kombine.technology/api/v1/session/me/profile/RetentionDays" \
  -d '{"revision":"REVISION_FROM_LAST_RESPONSE","value":30}'

Vælg dine egne tabs

GetMyManagerTabs returnerer kid, tabs, availableTabs, revision, canEdit; hvert tab har stabilt numerisk id og enum-navnet name. Alle aktive managers kan læse deres egne valg. canEdit kræver udtrykkelig adgang til hele sidens tenant (alle banker og lokationer). Adgang til enkelte banker/lokationer er ikke nok, heller ikke hvis de dækker alle nuværende banker. Hverken Managers-tab eller Managers Write kræves. Tabs giver ikke handlingsrettigheder, og tabs uden implementering skjules fortsat i arbejdsområdet.

Vælg et tabId fra availableTabs, og send kun boolesk enabled og seneste tab-revision (64 hexadecimale tegn). Revisionen er adskilt fra profilrevisionen. Sessionen bestemmer manageren; man kan ikke vælge manager eller tenant i requesten. Aktuelt login og adgang til alle banker kontrolleres igen i Log7-transaktionen. Ukendte numeriske tabs bevares, og uændrede valg opretter ingen historik. Alle egne tabs, også Managers, kan fjernes og tilføjes igen, så længe adgangen til alle banker bevares.

GET /api/v1/session/me/tabs
Authorization: Bearer ACCESS_TOKEN

POST /api/v1/session/me/tabs/60
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json

{"revision":"REVISION_FROM_GET_MY_MANAGER_TABS","enabled":true}

HTTP 200 returnerer det fulde, bekræftede tabsvar. HTTP 400 betyder ugyldig request/tab; 401 ugyldig eller tilbagekaldt session; 403 missing-tenant-access manglende adgang til alle banker; 409 tabs-conflict eller invalid-stored-tabs kræver genindlæsning og brugerens stillingtagen. Netværksfejl eller 503 kan have et usikkert udfald: gentag aldrig automatisk. Denne instans rydder straks sin rettighedscache; andre instanser opdaterer inden for 60 sekunder. Portalen opdaterer tabs i arbejdsområdet straks efter den bekræftede gemning, også under åbne banker, uden at erstatte igangværende personlige indtastninger. Hvis arbejdsområdet ikke kan opdateres, bevares det gemte valg, og et link til genindlæsning vises.

Verificering af e-mail

Send ny adresse og nuværende adgangskode, også ved lokalt bruger-id-login. Valgfrit language er en (standard), da eller es. HTTP 202 email-verification-queued betyder lagt i kø, ikke leveret. Loginadressen ændres først ved en udtrykkelig bekræftelses-POST. Samme forløb kan verificere den eksisterende adresse.

POST /api/v1/session/me/email-verification
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json

{"email":"alex@nortec.dk","currentPassword":"CURRENT_PASSWORD","language":"en"}

POST /api/v1/session/me/email-confirmation
Content-Type: application/json

{"token":"TOKEN_FROM_EMAIL_FRAGMENT"}

Det betroede portallink åbner /verify-email#token=…, kan bruges én gang og udløber efter 30 minutter. Klienten kan ikke angive retur-URL eller målbruger. Portalen fjerner fragmentet og sender token via en CSRF-beskyttet formular. GET ændrer aldrig kontoen. Bekræftelse kræver ikke bearer: token giver kun ret til netop denne adresseændring. Token er bundet til tenant, miljø og logversionerne for e-mail, adgangskode og bekræftelse. Ændringer af disse ugyldiggør linket, også hvis en gammel værdi senere gendannes. Første gennemførte bekræftelse ugyldiggør andre udestående links.

200 email-verified gemmer adressen, det versionsbundne bevis og en notifikation til den gamle gyldige adresse i samme transaktion. emailVerified bliver falsk, hvis en administrator senere ændrer adressen. Gamle/manuelle flag tæller ikke som dette bevis. Sessionernes udløb og rettigheder bevares; brug den verificerede adresse ved næste login.

Midlertidig mailbegrænsning: kun præcise domæner nortec.dk, kombinetech.com og arendt.dk kan modtage verificeringslinks. Andre adresser giver 400 email-delivery-restricted uden ændring eller verificeringsmail. En videresendt udviklingsmail kan ikke bevise ejerskab af en anden adresse. Notifikationer følger fortsat den centrale lås: øvrige modtagere erstattes med jens@arendt.dk; Cc/Bcc fjernes.

Skift adgangskode

POST /api/v1/session/me/password
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json

{"currentPassword":"CURRENT_PASSWORD","password":"NEW_PASSWORD","confirmPassword":"NEW_PASSWORD","language":"en"}

Send den nuværende adgangskode og to ens nye indtastninger. Den nye kode skal være forskellig fra den gamle: 12–128 printbare ASCII-tegn uden mellemrum først/sidst, af hensyn til det fælles eksisterende login. 200 password-changed gemmer adgangskodehistorik og notifikation sammen. Kassér gammel bearer, og log ind igen. Portalen logger ud efter succes; andre API-instanser kan cache gamle legitimationsoplysninger i op til 60 sekunder. Dette er genbekræftelse af adgangskoden, ikke to-faktor-login.

Fejl og begrænsninger

400-koder: invalid-profile, invalid-email, email-already-verified, current-password-invalid, invalid-password, password-unchanged og invalid-email-token. Sidstnævnte dækker udløbet, brugt, ændret eller fremmed token samt inaktiv konto. Modelvalidering bruger ValidationProblemDetails; ukendte requestfelter afvises. 401 kræver nyt login, 409 genindlæsning, 429 ventetid og 503 account-unavailable betyder utilgængelig eller ubekræftet lagring.

Mailanmodninger og adgangskodeskift har hver to forsøg pr. konto pr. 15 minutter pr. API-instans samt fælles recovery-IP-grænse på 10/minut. Bekræftelse bruger IP-grænsen. Kontoen genvalideres inde i skrivetransaktionen, med 12 sekunders frist. Svar er no-store. Log aldrig adgangskoder, komplette requestbodies eller tokens. Gentag ikke automatisk en ubekræftet skrivning; læs profilen eller prøv normalt login først. Mail sendes asynkront af eksisterende worker. Genererede klienter og downloadpakker er synkroniseret i version 0.4.2. Dette er nye endpoints; eksisterende kontrakter er ikke omdøbt.

Glemt adgangskode

RequestManagerPasswordReset og ResetManagerPassword er anonyme HTTPS-kald. API-værten bestemmer tenant. Nulstilling kræver én entydig, aktiv administratorkonto og ændrer ingen Tabs, Kids eller rettigheder.

Midlertidig udviklingsbegrænsning: en enkelt, almindelig mailadresse på præcis nortec.dk, kombinetech.com eller arendt.dk modtager direkte. Alle andre modtagere erstattes med jens@arendt.dk. Domænet kontrolleres uden forskel på store og små bogstaver; underdomæner og modtagerlister slipper ikke igennem. Cc og Bcc tømmes altid. Den indtastede mailadresse bruges stadig til at finde den oprindelige administrator. Begrænsningen gælder i alle miljøer og kan ikke slås fra i konfigurationen.

POST /api/v1/session/forgot-password
Content-Type: application/json

{"email":"manager@example.com","language":"da"}

HTTP 202 {"code":"accepted"} er ens for kendte, ukendte, tvetydige, deaktiverede, slettede og adressebegrænsede konti. Svaret indeholder intet token. Mailens engangslink udløber efter 30 minutter. Sprog: en (standard), da eller es. Mailen sendes asynkront; 202 er ikke en leveringskvittering.

POST /api/v1/session/reset-password
Content-Type: application/json

{"token":"TOKEN_FROM_EMAIL","password":"A new example password!","confirmPassword":"A new example password!"}

Brug 12–128 ASCII-tegn uden mellemrum først eller sidst og en anden adgangskode end den nuværende. Begrænsningen forhindrer tegn i at blive ændret af den eksisterende fælles hashfunktion. HTTP 200 {"code":"password-reset"} bekræfter, at adgangskodehistorik og bekræftelsesmail er gemt samlet. Log ind normalt bagefter. Andre API-instanser kan beholde en tidligere gyldig sessionskopi i op til ét minut.

HTTP 400 invalid-password: adgangskoden eller gentagelsen er ugyldig. invalid-reset: linket er udløbet, brugt, fra en anden tenant eller ugyldigt, kontoen er ændret/inaktiv, eller adgangskoden er uændret. Bed eventuelt om et nyt link. Ugyldige JSON-felter giver også 400 med valideringsoplysninger. HTTP 429: følg Retry-After. HTTP 503: konfiguration eller lager er utilgængeligt. Gentag ikke automatisk en gemning med ukendt resultat; prøv login eller et nyt link. Log aldrig tokens eller request bodies.

Portalen flytter tokenet fra URL-fragmentet til en CSRF-beskyttet POST-formular og fjerner fragmentet fra adresselinjen. Siden bruger no-store og no-referrer. Genererede klienter og downloadpakker er synkroniseret i version 0.4.2.

Invitation af administratorer

InviteManager inviterer en eksisterende administrator til at vælge en adgangskode. Det kræver en aktiv manager-bearersession med Managers1 (28), Administratorer Læs og Skriv samt adgang til hele tenanten. Låsen på eget kort gælder også: du skal være den eneste aktive administrator med adgang til hele tenanten for at invitere dig selv. API'et genkontrollerer aktuelle loginoplysninger, rettigheder og modtagerens synlighed i transaktionen.

POST /api/v1/managers/{managerKid}/invitation
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json

{"expectedRevision":"PROFILE_REVISION_FROM_GET_MANAGER","language":"da"}

Erstat expectedRevision med de 64 tegn i profileRevision fra GetManager eller den seneste bekræftede gemning. Modtageren skal være aktiveret, ikke slettet og have en gyldig, gemt mailadresse. Kaldet kan ikke vælge en anden modtager eller portaladresse. Sproget er en (standard), da eller es.

HTTP 202 {"code":"invitation-queued"} bekræfter, at mailen er lagt i køen, ikke at den er leveret. Samme modtagerbegrænsning gælder invitationer: kun nortec.dk, kombinetech.com og arendt.dk modtager direkte; alle andre modtagere erstattes med jens@arendt.dk, uden Cc/Bcc. Afsendelsen ændrer ikke adgangskode, kontostatus, Tabs, Kids eller rettigheder. Linket åbner /accept-invitation, udløber efter 30 minutter og bruger ResetManagerPassword med adgangskodekravene ovenfor. Når adgangskoden er valgt, kan linket ikke bruges igen. Brugeren logger derefter ind normalt.

HTTP 400 betyder ugyldigt KID/kald; 401 kræver nyt login; 403 betyder manglende rettigheder eller låst eget kort; 404 omfatter administratorer skjult af retention. HTTP 409 profile-conflict kræver genindlæsning; manager-invitation-invalid kræver rettelse af den gemte mailadresse eller kontostatus. HTTP 429 invitation-rate begrænser invitationer til to pr. modtager pr. 15 minutter pr. API-instans; følg Retry-After. HTTP 503 manager-invitation-unavailable angiver en konfigurations-/lagerfejl. Gentag ikke automatisk ved uklart svar: mailen kan allerede være lagt i kø. Genererede klienter og downloadpakker er synkroniseret i version 0.4.2.

Klient-IP ved serverbaseret login

API'et registrerer forbindelsens IP-adresse ved afviste loginforsøg. En portal, der kalder API'et fra sin egen server, kan desuden sende browserens IPv4-/IPv6-adresse i den valgfrie header X-Portal-Login-Client-IP:

X-Portal-Login-Client-IP: 192.0.4.20

Send kun en adresse, som portalserveren selv har observeret. API'et behandler headeren som en oplysning fra klienten og gemmer den særskilt fra den IP, API'et selv har observeret. Den giver ingen rettigheder og ændrer hverken tenant, loginbegrænsninger eller rate limits. Ugyldige eller flere adresser ignoreres uden at ændre login-svaret. Headeren er ikke nødvendig for direkte API-klienter. Login-svar og fejlhåndtering er uændrede; der udleveres ingen diagnostik i svaret.

2. Forstå managerens oplysninger og adgang

Dette er et opdigtet svar. Brug altid de faktiske værdier fra API’et.

{
  "kid": "3E7Q46o3B9ACA01h",
  "retentionDays": 30,
  "themeMode": 0,
  "databaseAccess": {"canWrite": false, "checkedAtUtc": "2026-09-25T12:00:00Z"},
  "organisation": "Example organisation",
  "name": "Eksempelmanager",
  "iconKid": "house",
  "tabs": [4, 12],
  "hasBankAccess": true,
  "tabDetails": [{"id": 4, "name": "Bank1"}, {"id": 12, "name": "Dashboard1"}],
  "resourceGrants": [{"kid": "3E7Q14o2Ab", "scope": "Bank"}],
  "navigationBanks": [{"kid": "3E7Q14o2Ab", "name": "Example bank", "iconKid": "house"}],
  "operationPermissions": [
    {"resource": "Managers", "level": "Read", "flags": 1, "canRead": true, "canWrite": false, "canCreate": false, "canDelete": false, "canRenameExternalId": false, "canRename": false},
    {"resource": "Bank", "level": "Read", "flags": 1, "canRead": true, "canWrite": false, "canCreate": false},
    {"resource": "Location", "level": "3", "flags": 3, "canRead": true, "canWrite": true, "canCreate": false},
    {"resource": "Unit", "level": "7", "flags": 7, "canRead": true, "canWrite": true, "canCreate": true},
    {"resource": "User", "level": null, "flags": null, "canRead": false, "canWrite": false, "canCreate": false},
    {"resource": "Installer", "level": "Read", "flags": 1, "canRead": true, "canWrite": false, "canCreate": false, "canDelete": false, "canRenameExternalId": false, "canRename": false},
    {"resource": "Service", "level": "Read", "flags": 1, "canRead": true, "canWrite": false, "canCreate": false, "canDelete": false, "canRenameExternalId": false, "canRename": false}
  ]
}
FeltBetydning og anvendelse
organisationValgfri organisation fra managerens eSetting.Organisation i A{TenantId:D4}.Log7, BankId 0. Tom streng, når den mangler. Portalen viser den under navnet, når den er udfyldt. Kun visning; giver ingen rettigheder. Hentes i samme opslag og følger managerens cache på højst 60 sekunder.
retentionDaysAntal dage efter sletningen, hvor manageren må se en slettet bank, lokation, enhed, bruger eller reservation. Hentes fra eSetting.RetentionDays i A{TenantId:D4}.Log7, BankId 0. Manglende værdier giver 10 dage. Negative eller ugyldige værdier giver 0, som skjuler slettede objekter. Et eksplicit gemt 0 bevares. Feltet ændrer ikke Kids-, Tab- eller handlingsrettigheder og styrer ikke fysisk sletning. Caches sammen med manageren i højst 60 sekunder. GetBankUsers håndhæver grænsen. Kommende detaljer, optællinger og søgninger skal også håndhæve den.
kidManagerens ID som tekststreng. Gem og send værdien uændret. me returnerer kun den indloggede manager. Et ID giver ikke adgang i sig selv.
name, iconKidVisningsnavn og ikonidentifikator. Navn kan være tomt. house kan vises fra /api/v1/icon/g/house.svg. Brug et kendt, gyldigt ikonnavn, og ellers et standardikon; indsæt ikke rå HTML eller en vilkårlig URL fra feltet.
tabs, tabDetailsDe tilladte sider, som vi kalder Tabs. ID’erne svarer til eTab; navnene er stabile enum-navne, ikke oversatte sidetitler. Brug ID’et som nøgle og oversæt visningen i din egen portal. Ukendte ID’er må ikke antages at give en kendt rettighed.
hasBankAccessOm der er mindst én bank- eller lokationsadgang på dette site. Det er ikke en generel tilladelse til alle data.
resourceGrantsDe konkrete områder, manageren må have adgang til. Se reglerne nedenfor. Arrayet indeholder ikke banknavne eller selve bankdata.
operationPermissionsFire uafhængige handlingsrettigheder: Bank, Location, Unit og User. Brug de beregnede canRead, canWrite og canCreate til at vise relevante handlinger.

Portalens databaseadgang

GetCurrentManager medtager databaseAccess i samme profilkald. Genbrug managerens bearer-session; ingen tenant- eller bankparameter kan sendes. Feltet er til visning, fx nederst i arbejdsområdet. canWrite=true betyder, at kontrollen af læse-/tilføjelsesrettigheder på tenantens fælles Log7 og bank-nul Log7 lykkedes. false betyder, at læsning virker, men SQL-rettighedskontrollen afvises eller serveren er sat til read-only. null eller et manglende felt betyder ukendt, aldrig bekræftet læseadgang.

const access = profile.databaseAccess;
const accessText = access?.canWrite === true ? 'Database: Læse- og skriveadgang'
  : access?.canWrite === false ? 'Database: Kun læseadgang'
  : 'Databaseadgang: Ukendt';

checkedAtUtc angiver kontrollens starttid i UTC. Kendte resultater deles i 10 minutter pr. tenant/API-instans; fejl i ét minut. Der foretages ingen testskrivninger eller baggrundspolling. Fejl i denne kontrol ændrer ikke et ellers gyldigt profilsvar til en loginfejl. Sessionens eksisterende 401/503-regler gælder fortsat. Dette er en vejledende kontrol: rettigheder kan variere mellem banker, og triggere/transaktioner kontrolleres ikke. Administratorers eventuelle undtagelse fra read-only antages ikke. Feltet giver aldrig rettigheder; alle handlinger kræver fortsat managerens konto-, Tab-, KID- og handlingsadgang.

Gem dit tema

GetCurrentManager returnerer themeMode som en numerisk eThemeMode: System=0, Light=1, Dark=2. Manglende eller ugyldige gemte værdier giver System. Brug værdien ved login, også i en anden browser eller på en anden enhed. System følger enhedens udseende; gem ikke den aktuelt beregnede lyse/mørke farve som selve valget.

SetCurrentManagerTheme gemmer kun den indloggede managers egen indstilling. Det kræver en aktiv manager-session på sitet, inklusive konto- og adgangskodestatus. Det kræver ingen bank-Tabs, KID-adgange eller skriverettighed til beboere og giver ingen adgang til forretningsdata. Kaldet accepterer ikke en anden tenant, bank, manager eller KID.

POST https://api.team.kombine.technology/api/v1/session/me/theme
Authorization: Bearer <accessToken>
Content-Type: application/json

{"themeMode":2}

Succes er HTTP 200 med {"themeMode":2}. Gentagelse af samme gemte værdi opretter ingen ny historikpost. Værdien følger med næste profilopslag; API-instansen, der gemmer, rydder sin profilcache. Andre instanser kan vise en ældre profil i op til 60 sekunder. Svarene er no-store.

Manglende/ukendte værdier eller ekstra requestfelter giver 400; udløbne/tilbagekaldte sessions giver 401. Utilgængelig lagring, herunder en manglende skriveforbindelse, giver 503. Bevar det senest bekræftede valg og forklar en fejl; et lokalt vist tema må ikke præsenteres som gemt. Ved et usikkert netværkssvar bør profilen hentes igen før et nyt forsøg. Der er ingen baggrundspolling eller push mellem enheder; et senere login henter den gemte indstilling. Officielle portaler, eksterne portaler og autoriserede agenter bruger samme HTTPS/JSON-kontrakt og bearer-session.

Hvilke banker og lokationer?

Et objekt i resourceGrants gælder kun den angivne tenant. Flere objekter giver adgang til flere områder.

  • scope = "Tenant": KID’en beskriver hele sitets tenant. Vis “Alle banker” og “Alle lokationer”.
  • scope = "Bank": KID’en beskriver én bank med alle dens lokationer.
  • scope = "Location": KID’en beskriver én bestemt lokation i en bank.

scope beskriver adgangens omfang. Brug ID-strengen uændret; udled ikke rettigheder eller objektnavne fra den.

Sådan bruger du KID-strenge

En KID er et ID fra API-svarets kid-felt. Behandl det som en almindelig tekststreng i alle programmeringssprog. Gem og send hele værdien uændret, inklusive store og små bogstaver. Du skal ikke afkode, opdele eller selv konstruere den. Intet Kombine-bibliotek er nødvendigt.

Brug bankens kid fra navigationBanks til bankkald, lokationens kid fra lokationslisten til lokationskald og brugerens kid fra brugerlisten til brugerkald. Brug encodeURIComponent(kid) i JavaScript, når et ID indsættes i en URL. Brug felter som name til visning og scope til adgangens omfang.

Et ID er hverken et login-token eller en tilladelse. Serveren kontrollerer sitet og managerens rettigheder ved hvert beskyttet kald. Et ID kan ikke skifte tenant eller give flere rettigheder.

Denne version af kontrakten erstatter de tidligere separate felter userId, tenantId, bankId og locationId med KID-strenge. Opdater klienter, der brugte de gamle felter. De eksisterende login- og profilruter er uændrede.

Hvad må manageren gøre?

Installer kommer fra eSetting.PermissionInstaller2 (3016) i den aktuelle Log7 for bank 0. Kategorien returneres af GetCurrentManager, GetManagers og GetManager. GetInstallers kræver Installer Read, Installers1 og adgang til hele tenanten; flagene giver ikke rettigheder til andre områder. Find rettigheder efter resource, ikke efter placering i listen eller et fast antal kategorier.

De syv kategorier Managers, Bank, Location, Unit, User, Installer og Service bruger uafhængige ePermission2-bitflag fra PermissionManagers2/Bank2/Location2/Unit2/User2/Installer2/Service2. flags er bitmasken: Read=1, Write=2, Create=4, Delete=8, RenameExtrenatId=16, Rename=32. Fx giver 5 læsning og oprettelse, men ikke skrivning eller sletning. Ingen flag giver automatisk andre flag.

Manglende/tomme værdier giver Read (1); eksplicit 0 giver ingen adgang. Ugyldige værdier eller ukendte bits giver null og ingen adgang. Brug canRead, canWrite, canCreate, canDelete, canRenameExternalId og canRename til visning. Det eksisterende level-felt er enumtekst og kan være et tal ved kombinationer. Gamle Permission-indstillinger anvendes ikke. Tabs, KID-scope og aktiv konto kontrolleres stadig; manglende rettigheder giver HTTP 403. Managers-flag tilføjer ikke nye manager-operationer.

Vis “Du har endnu ikke adgang til nogen banker”, hvis hasBankAccess er false. Vis “Du har endnu ikke adgang til nogen Tabs”, hvis tabs er tomt. Begge beskeder kan være relevante samtidig. En netværksfejl eller HTTP 503 skal vises som en fejl ved hentning, ikke som manglende rettigheder.

Klientens knapper er hjælp til brugeren. API’et skal kontrollere konto, site, Tab, objektets område og handling, før det returnerer eller ændrer forretningsdata. En klient må ikke kunne give sig selv rettigheder ved at ændre JSON eller et link.

3. Fejl og hvad klienten skal gøre

Kontrollér HTTP-status først. Login kan ved 403 give et stabilt code-felt, eksempelvis {"code":"disabled"}. Andre fejl bruger normalt Problem Details med status, title og eventuelt traceId; valideringsfejl kan også have errors. En proxy eller webserver kan sende en tom fejl eller HTML, så kræv ikke JSON for at håndtere en fejl.

Status / kodeHvad betyder det?Vis eller gør
400Ugyldigt JSON, email eller manglende felter.Ret input. Email højst 254 tegn; password højst 1.024 tegn.
401 ved loginEmail/password passer ikke til en gyldig manager.“Email eller password er forkert.” Undgå automatisk gentagelse.
401 ved meToken mangler, er ugyldigt/udløbet, eller konto/password er ændret.Fjern token og vis login igen. Svaret afslører ikke den konkrete kontotilstand.
403 / disabledManageren er deaktiveret.“Din manager er deaktiveret. Kontakt administratoren.”
403 / deletedManageren er slettet.“Din manager er slettet. Kontakt administratoren.”
403 / account-settingsKontoens aktiverings-/sletteindstillinger mangler eller er ugyldige.“Din manager er ikke korrekt opsat. Kontakt administratoren.”
413 / 415For stor login-forespørgsel / forkert indholdstype.Send kun email og password som application/json. Grænsen for login-body er 8.192 bytes.
429For mange loginforsøg.Vent mindst Retry-After sekunder (aktuelt 60). Brug 60 sekunder, hvis headeren mangler.
503API’et kan ikke hente de nødvendige data.Vis midlertidig driftsfejl. Tilbyd et nyt forsøg efter en pause.

403-årsagen ved login gives først efter korrekt password. Oversæt koderne i din egen brugerflade. Byg ikke logik på de engelske fejltekster eller et bestemt traceId.

4. Din egen portal

Komplet JavaScript-eksempel

Åbn det fungerende JavaScript-eksempel. Det har felter til API-adresse, email og password samt knapper til status, login, opdatering af profil og logout. Eksemplet bruger almindelig fetch og kræver ingen npm-pakker eller framework.

Vil du bruge det i din egen portal, så kopier portal-api.mjs til din JavaScript-mappe. Filen er et lille, valgfrit eksempel; du kan også bruge direkte fetch-kald som vist nedenfor. Brug ét klientobjekt pr. bruger og API-site:

import { createPortalClient } from './portal-api.mjs';

const portal = createPortalClient('https://api.team.kombine.technology');
// email og password kommer fra din loginformular.
await portal.login(email, password);
const manager = await portal.getProfile();
// Vis manager.name, manager.tabDetails og manager.operationPermissions.
// Kald portal.getProfile() ved manuel opdatering og portal.logout() ved logout.

Indlæs din egen scriptfil med <script type="module" src="./app.mjs"></script>. Gem eksempelfilerne på din egen webserver; åbn dem ikke via file://, og importér ikke JavaScript-filen direkte fra et andet API-domæne. Det samme klientmodul kan bruges i Node.js med indbygget fetch. JavaScript, der kører på en server, behøver ikke CORS.

Klienten gemmer kun tokenet i hukommelsen. Den genbruger tokenet, rydder det ved HTTP 401 og giver fejl med status, code og ved HTTP 429 retryAfter. Netværks-, certifikat-, timeout- og CORS-fejl har ikke nødvendigvis HTTP-status. Der er ingen automatisk gentagelse eller polling. På en Node.js-server må klientobjektet ikke deles mellem brugere.

Portal med egen server

Lad din server kalde API’et og opbevare adgangstokenet i den enkelte brugers beskyttede session. Det er også modellen i den officielle Blazor-portal. Del aldrig én managers session mellem flere brugere. Browseren bruger din portals eget login/cookie, mens serveren sender bearer-tokenet til API’et. Det kræver ikke CORS.

Browseren kalder API’et direkte

Det er også muligt. Hvis din portal ligger på en anden adresse end API’et, skal API-administratoren tilføje dens præcise origin under Cors:AllowedOrigins. En origin er protokol, værtsnavn og eventuel port, uden sti eller afsluttende skråstreg. Listen er tom som standard.

{
  "Cors": {
    "AllowedOrigins": ["https://min-portal.example", "https://localhost:5173"]
  }
}

Dette er serverkonfiguration og kræver genstart. Miljøvariablen for første adresse er Cors__AllowedOrigins__0. Kun tilføjede origins kan læse svar fra integrationskaldene gennem browseren. Login og rettighedskontrol gælder stadig. CORS er en browserregel, ikke adgangskontrol for serverprogrammer.

Hvis din udviklingsside kører på http://localhost:5173, skal netop den adresse tilføjes; https://localhost:5173 er en anden origin. Brug altid API’ets HTTPS-adresse. Browseren sender selv OPTIONS før relevante kald; API’et håndterer dette. Brug ikke mode: 'no-cors': så kan din kode ikke læse JSON-svaret.

Her er en lille browserfunktion. Kald den med værdier fra din loginformular. Den returnerer profilen; tokenet bliver ikke gemt i localStorage eller sat i URL’en.

async function loginAndLoadProfile(apiBaseUrl, email, password) {
  const base = apiBaseUrl.replace(/\/$/, '');
  const login = await fetch(`${base}/api/v1/session/login`, {
    method: 'POST',
    credentials: 'omit',
    headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
    body: JSON.stringify({ email, password }),
    signal: AbortSignal.timeout(15000)
  });
  if (!login.ok) {
    const error = await login.json().catch(() => ({}));
    throw new Error(`Login: HTTP ${login.status}, ${error.code ?? ''}`);
  }
  const session = await login.json();
  const result = await fetch(`${base}/api/v1/session/me`, {
    credentials: 'omit',
    headers: { Authorization: `Bearer ${session.accessToken}`, Accept: 'application/json' },
    signal: AbortSignal.timeout(15000)
  });
  if (!result.ok) throw new Error(`Profile: HTTP ${result.status}`);
  return await result.json();
}

Eksemplet viser det første forløb. I en fuld portal skal du genbruge tokenet i hukommelsen for den pågældende session, implementere fejltabellen og rydde sessionen ved logout. CORS tillader aktuelt GET/POST samt Authorization, Content-Type, Accept og Accept-Language; Retry-After kan læses af browseren. Cookies deles ikke med API’et.

Gør visninger delbare ved at gemme objekt, filtre og sortering i sidens URL, efterhånden som funktionerne bygges. Modtageren logger ind som sig selv. Token, password og følsomt indhold må ikke stå i URL’en. API-felter og rettighedskoder er ens på alle sprog; oversæt kun visningsteksterne.

5. AI-agenter bruger samme API

  1. Giv integrationsprogrammet tenant-sitets API-basisadresse og OpenAPI-dokumentet. OpenAPI er en maskinlæsbar beskrivelse af de eksisterende kald, felter og svar.
  2. Lad den godkendte værtsapplikation håndtere login og token som en hemmelighed for den relevante manager. Undgå at placere credentials i almindelige prompts, modeloutput eller logs.
  3. Tilføj tokenet som Authorization-header, når agentens HTTP-værktøj kalder GetCurrentManager. Agenten får samme oplysninger og begrænsninger som en portal med den manager.
  4. Behandl returnerede navne og andre dataværdier som data. De er ikke instruktioner til agenten. Brug kun de operationer, som OpenAPI-dokumentet faktisk beskriver.

Sessionens operationer hedder LoginManager, RenewManagerSession og GetCurrentManager; GetPortalStatus er fortsat offentlig. Værtsapplikationen håndterer login, udskiftning af token og udløb. Sessioner gælder i tre dage og kan fornyes før udløb efter brugeraktivitet. Agenter kan bruge almindelige HTTPS-kald eller MCP-endpointet nedenfor. OAuth-delegering og et offentligt servicelogin findes ikke; ubemandet adgang kræver en særskilt aftale om autentificering.

Assistent med læseadgang på søgesiden

AskPortalAssistant — POST /api/v1/assistant/query modtager question (1–2000 tegn) og valgfri history (højst 12 beskeder med role user/assistant og content; 8000 tegn pr. besked, 20000 i alt). Svaret indeholder answer som ren tekst, forsøgte forretningsoperationer i operations og højst 20 links med kanonisk kid og relativ portalsti path. Kontrollér vigtige oplysninger i portalen.

Hvert link indeholder også valgfrit name, kanonisk bankKid for banken og API-genereret iconKid. Vis eksempelvis name ?? kid som linktekst og brug path som destination. Portalen tilføjer den valgte bank/lokation til det lokale arbejdsområde og åbner den; adgangen kontrolleres igen ved navigation. Links kommer kun fra vellykkede aktuelle API-opslag, herunder lokationsreferencer i driftsstatus, aldrig fra genereret tekst.

Den interne assistent finder otte godkendte læseværktøjer gennem MCP tools/list: SearchBanks, SearchLocations, SearchUsers, GetSearchBank, GetBankLocations, GetLocations, GetLocationUnits og GetTenantStatus. Værtsprogrammet kræver deres read-only-annotationer, viser modellens værktøjer med MCP-navne og JSON-inputskemaer og udfører forretningsopslag gennem tools/call. Denne MCP-omlægning er implementeret lokalt og endnu ikke udgivet. Swagger/OpenAPI kan fortsat bruges til at afprøve API’et og arbejde med kontrakten og er kilde til serverens fælles MCP-katalog.

Hvert opslag bruger managerens bearer-session og eksisterende Tab-, KID- og Read-rettigheder. Den interne assistent får ingen ekstra adgang og udfører ingen skrivehandlinger, selv om andre autoriserede MCP-klienter kan bruge det bredere katalog til læsning og ændring. Portalens server håndterer MCP-forbindelsen og beholder tokenet i HTTP-headers; modeludbyderen modtager almindelige Responses-funktionsdefinitioner og autoriserede resultater. Spørgsmål, medsendt historik og relevante autoriserede resultater sendes til OpenAI; legitimationsoplysninger sendes aldrig til modellen. Når live-diagnostik er aktiveret, gemmes det aktuelle spørgsmål én gang i den autoriserede, proceslokale logbuffer efter sessionskontrol og før modellen kaldes. Bufferen udløber efter 15 minutter eller ved genstart. Historik og svar indgår ikke i hændelsen. Der gemmes ingen chathistorik på serveren; store=false slår Responses-lagring af applikationstilstand fra, men garanterer ikke, at udbyderen slet ikke opbevarer data.

Assistenten bruger gennemgåede instruktioner pakket med API og kan indlæse tre skills: offline-installations, find-location og explain-balance. Skills giver ingen ekstra operationer eller rettigheder. Saldo-skillen forklarer begrænsningen: chatkataloget indeholder endnu ingen læsning af saldi eller kontoposteringer. Indlæsning af skills tæller med i de syv modelrunder, men ikke i de otte datalæsninger.

curl "$BASE/api/v1/assistant/query" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" -H "Accept-Language: da-DK" \
  --data '{"question":"Hvilke af mine anlæg er offline?","history":[]}'

Grænser: otte forretningsopslag, syv modelrunder, 120 sekunder, højst 50 rækker ved sideinddeling og 48000 bytes pr. værktøjsresultat. Oversigter uden sideinddeling beholder deres eksisterende grænser. For store resultater tilbageholdes som afkortede; fejlede kilder og hasMore må ikke tolkes som fuldstændige eller fejlfrie data. 400: ugyldigt spørgsmål/historik. 401/403: session utilgængelig; log ind igen eller kontrollér rettigheder. 429: vent i Retry-After (seks spørgsmål/minut pr. manager, fire samtidige forespørgsler pr. proces). 503 med code=assistant-not-configured: administratoren skal aktivere funktionen; andre 503-fejl betyder udbyder-/netværksfejl eller timeout. Prøv igen manuelt. Operationen er til serverkald uden browser-CORS. De eksisterende regler for session, fejl og sprog gælder. Genererede klienter og downloadpakker er synkroniseret i version 0.4.2.

6. Opdateringer, belastning og drift

Hent samlet og genbrug svaret. Ét kald til me er nok til profil, navigation og alle tre adgangskort. Login bør ske ved sessionens start, ikke før hvert kald. Managerindstillinger caches i op til 60 sekunder pr. API-instans. Samtidige opslag for samme manager deler opdateringen. Der køres ingen automatisk databasepolling.

Ændringer i rettigheder, Kids, Tabs, Enabled og Deleted kan derfor slå igennem op til ét minut senere ved næste kald. En side, der allerede står åben, bliver ikke automatisk opdateret; genindlæs den eller tilbyd “Opdatér”. Ved fejl efter cacheudløb bruges gamle rettigheder ikke som reserve. Undgå hurtige retry-løkker; ved midlertidige fejl kan du fx vente 5, 15 og 30 sekunder og derefter vise fejlen.

Login har aktuelt en grænse på 10 forsøg pr. normaliseret email pr. minut og 120 pr. kilde-IP pr. minut, pr. API-instans. Serverportaler kan dele kilde-IP, så login skal ikke gentages unødigt. Bruger- eller hostnavne og databasedetaljer skal ikke bruges som credentials.

Til den, der driver API’et

Publicér API’et over HTTPS med det korrekte tenant-site, hostnavne, databaseforbindelse og beskyttede, vedvarende Data Protection-nøgler. Web og API skal bruge samme tenant. Eksterne klienter får API-adressen og deres manageradgang; databasepassword bliver på serveren. Ved flere replikaer skal nøgler for samme site deles sikkert, og loginbegrænsning koordineres. Cache er lokal pr. instans. Konfigurér kun forwarded headers fra kendte reverse proxies.

Swagger, OpenAPI og vejledningerne følger med API-publiceringen og styres af ApiDocumentation:Enabled (standard: true). Engelsk er hovedsprog på /docs (også /docs/en), med dansk på /docs/da og spansk på /docs/es. Klientpakker har engelsk README.md samt README.da.md og README.es.md. Denne ændring publicerer ikke i sig selv API’et på internettet.

GET /api/v1/database/status er kun til drift. Det ligger i et separat diagnostikdokument og kræver en anden slags token: en JWT med scope portal.diagnostics. Managerens token virker ikke her. Brug /api/v1/status eller /health til almindeligt svartjek uden MySQL-kald.

Når API’et udvides

Nye portaloplysninger og handlinger skal implementeres i det fælles API, før Web tager dem i brug, og dokumenteres her og i OpenAPI. Eksterne klienter skal tolerere ekstra JSON-felter og ukendte fremtidige enum-værdier uden at give flere rettigheder. Eksisterende v1-felters betydning må ikke ændres skjult; uforenelige kontraktændringer kræver en ny version. Store lister skal have serverfiltrering og sideopdeling, når de tilføjes.

Banknavne i navigationen

Det eksisterende GET /api/v1/session/me (GetCurrentManager) indeholder navigationBanks. Genbrug dit bearer-token og læs profile.navigationBanks i JavaScript. Hvert objekt har kid, name og iconKid; der kræves ingen numeriske id'er eller ekstra kald. Brug KID som objektets identitet, og kopiér den præcise streng ved deling.

Navnene kræver en aktiv konto, mindst én tilladt Tab, Bank Read og bank- eller lokationsadgang på dette site. En lokationsadgang giver kun bankens navn og ikon til navigationen, aldrig adgang til hele bankens data. Arrayet er tomt ved adgang til alle banker (denne liste kommer senere), manglende Tabs, manglende adgange eller ugyldig Bank-rettighed. Handlinger under hver Tab kræver fortsat selvstændig kontrol af rettigheder.

Navn og ikon hentes fra sitets Log24, EntryType Settings (2), LocationId 0 og UnitId 0, med eSetting.Name (99) og Icon (37). Manglende værdier er tomme strenge: vis en tekst for bank uden navn og et lokalt reserveikon. Vis tekst som tekst, aldrig HTML. Et sikkert ikonnavn som house kan bruges til /api/v1/icon/g/house.svg. Portalen viser bankens KID ved mouseover og tilbyder kopiering ved højreklik.

Navne og ikoner caches ét minut pr. bank pr. API-instans og genbruges mellem managers. Manglende cacheværdier hentes samlet i grupper på højst 64 banker. Rettigheder kontrolleres uafhængigt med managerens tidsbegrænsede snapshot. Der søges ikke efter banker ved adgang til hele tenanten. Databasefejl giver 503, som ikke må fortolkes som en tom rettighedsliste. Prøv senere; ugyldig eller udløbet session giver 401. Navigationsnavnene er ikke en bankdetaljevisning eller en liste over slettede objekter.

tabDetails[].icon kommer fra AttributeMetaIcon på den tilsvarende eTab, fx bank_building. Brug samme sikre ikonadresse som for banker. En tom streng betyder intet ikon; vis et lokalt reserveikon. Ikonerne læses fra den fælles enum uden databaseopslag.

userKid vælger én præcis beboer i banken: await client.getBankUsers(bankKid, { userKid: residentKid }). Brug en kanonisk User-KID fra et tidligere svar. KID skal tilhøre sitets tenant og den angivne bank og må ikke kombineres med filter eller cursor (400). Samme manager-, Tab-, User Read-, lokations- og RetentionDays-regler gælder. Svaret har nul eller én række og ingen fortsættelsescursors. En manglende eller ikke-synlig beboer giver en tom liste; det afslører ikke, om beboeren findes. Portalens delbare visning bruger /banks/{pageKid}, hvor sidens bank-KID indeholder både Tab=Users2 og beboerens UserId. Ældre ?resident={userKid}-links viderestilles; browserens arbejdsområde gemmer kun lokale genveje og giver ingen rettigheder.

Wildcards i filter: * matcher nul eller flere tegn, og ? matcher ét tegn. Eksempel: filter=1568-*002* eller filter=Anna?*. Der søges fortsat efter indhold i Nummer ELLER Navn, også uden wildcards. Andre tegn, herunder SQL-tegnene % og _, behandles bogstaveligt. URL-kod filteret med URLSearchParams eller encodeURIComponent. Matchning sker i API-hukommelsen på det eksisterende cachede grundlag; der genereres ingen wildcard-SQL.

filter søger efter en deltekst i number ELLER name, uden forskel på store/små bogstaver (sprogneutral sammenligning). Højst 200 tegn; mellemrum i begyndelsen og slutningen fjernes. Tomt filter viser hele den autoriserede liste. Filtrering sker før sideopdeling og bruger det fælles cachede sorteringsgrundlag, også med identity. Bevar filteret sammen med cursor; ændret filter kræver en ny forespørgsel uden cursor, ellers returneres 400. Rettigheder og RetentionDays gælder stadig. Eksempel: await client.getBankUsers(bankKid, { sort: 'name', direction: 'asc', filter: 'anna', pageSize: 25 }). URL-kod filtertekst. Et delt link indeholder søgeteksten.

iconKid indeholder brugerens eSetting.Icon (37) som et valideret eIcon-navn. Manglende, tomme, ukendte værdier og none giver user. Både lagrede enum-navne og numeriske enum-værdier understøttes. Vis ikonet fra /api/v1/icon/g/{iconKid}.svg. Ikonet hentes med sidens øvrige indstillinger og følger samme adgangskontrol og cache; der laves ikke et separat databaseopslag pr. bruger.

Vælg sort=number|name|location|deleted og direction=asc|desc. Sorteringen gælder hele den liste, manageren må se, også ved løbende indlæsning. Udeladt sort bruger fortsat den tidligere identity-rækkefølge (kun asc). Numeriske numre sorteres numerisk før tekstnumre; navne og tekstnumre sammenlignes uden forskel på store/små bogstaver med en sprogneutral ordinal rækkefølge. Location er det laveste synlige lokationsnummer med Access eller NoAccess. Tomme værdier og ikke-slettede brugere står først i ASC og sidst i DESC. Slettede sorteres på slettetidspunkt. Brugerens identitet afgør rækkefølgen ved ens værdier.

const page = await client.getBankUsers(bankKid, {
  pageSize: 25, sort: 'name', direction: 'asc'
});
const next = page.nextCursor
  ? await client.getBankUsers(bankKid, {
      pageSize: 25, sort: 'name', direction: 'asc', cursor: page.nextCursor
    })
  : null;

Bevar sort, direction og pageSize under indlæsning. Når sorteringen ændres, start uden cursor. En cursor til en anden sortering eller retning giver 400. Sorterede cursors er positioner i den aktuelle, autoriserede liste; ændrede data eller rettigheder kan flytte positioner mellem kald. Et delt link giver aldrig afsenderens rettigheder.

Til sortering læses et fælles grundlag med fire indstillinger én gang og genbruges i op til 60 sekunder på tværs af managers og sorteringsvalg. API'et filtrerer rettigheder og sorterer i hukommelsen; kun den valgte portion får hentet øvrige oplysninger. Der udføres ingen COUNT, SQL OFFSET eller opslag pr. bruger. Kold cache kræver gennemlæsning af bankens almindelige brugere; meget store banker kan ramme timeout og give 503. Gentag ikke straks automatisk. Identity-tilstand bruger fortsat den begrænsede gennemgang på højst 1.000 kandidater og scanLimitReached.

Den officielle portal bruger GetBankUsers til løbende indlæsning ved scrolling. Eksterne portaler kan gøre det samme: hent én nextCursor ad gangen, genbrug svaret og stop ved null. Ved fejl stoppes automatisk indlæsning; vis fejl og tilbyd et nyt forsøg. Autorisation gælder for hvert kald, også når cursoren kommer fra en kollegas link.

Brugere i en bank (Users2)

GET /api/v1/banks/{bankKid}/users?pageSize=25 · operation GetBankUsers. Brug bankens KID fra navigationBanks eller et bank-scope i resourceGrants. Send managerens bearer-token. Ingen separate tenant-, bank- eller bruger-id'er sendes.

const page = await client.getBankUsers(bankKid, { pageSize: 25 });
for (const user of page.items) console.log(user.kid, user.name, user.number);
if (page.nextCursor) {
  const next = await client.getBankUsers(bankKid, {
    pageSize: 25, cursor: page.nextCursor
  });
}

Svaret har items, previousCursor, nextCursor og scanLimitReached. Hver bruger har kid, name (99), number (1824), email (2800), deletedAt (1996, UTC eller null), locations (1809, KID og Access/NoAccess), tags (2977, KID og eTagState) samt attributes (2978, eUserAttribute-navn og value; negativ værdi betyder ingen talværdi). Manglende tekst og lister er tomme; manglende Deleted betyder ikke slettet. E-mail læses fra den aktuelle Log7-værdi for eSetting.Email; både JSON-strenge og ældre ren tekst afkodes. Feltet er kun til læsning og kan ikke ændres via profile.

sms er beboerens aktuelle telefonnummer fra Log7 eSetting.SMS, afkodet fra JSON-streng eller ældre tekst uden ændring af formatet. Manglende værdi er tom; ældre data kan bruge 0 for intet nummer. Feltet læses sammen med de øvrige detaljer og er underlagt samme tab-, læse-, ressource- og opbevaringsregler. Det er kun til læsning og sender ingen SMS eller opkald. Portalen bruger mailto: og tel: til gyldige kontaktværdier; ugyldige værdier vises som tekst. Håndtering af links afhænger af enhedens mail- og telefonapps.

{"email":"resident@example.invalid","sms":"+1 202-555-0123"}

API'et kræver aktiv manager, Users2 (53), User Read og et passende KID-scope. Bank-/tenantadgang omfatter alle bankens almindelige brugere. Ved lokationsadgang vises kun brugere tilknyttet mindst én tilladt lokation med Access eller NoAccess; andre lokationer fjernes fra svaret. Navn, nummer, e-mail, brikker og attributter er brugerens fælles bankdata. RetentionDays begrænser slettede brugere; ugyldig sletteværdi skjuler brugeren. eUserId.Users til og med UsersLast medtages, ikke manager- eller servicekonti.

pageSize er 1–200 (standard 25). Standardtilstanden identity bruger stigende brugeridentitet. Send en returneret cursor uændret; null betyder ingen fortsættelse i den retning. Cursoren kan deles med en kollega, men giver aldrig rettigheder. Kollegaens egne rettigheder gælder, så indholdet kan være anderledes. Listen er ikke et fastlåst øjebliksbillede, hvis data ændres mellem sider.

I identity-tilstand caches portioner i højst 60 sekunder; der bruges hverken fuld optælling eller OFFSET. Højst 1.000 kandidater undersøges pr. kald. scanLimitReached=true betyder, at siden kan være kort eller tom: fortsæt med cursoren. Poll ikke alle sider automatisk.

400: ugyldig KID, cursor eller sidestørrelse (åbn første side igen). 401: log ind igen. 403: manglende Tab, scope eller User Read. 503: midlertidig datafejl; vis en fejl og prøv senere, ikke en tom liste. Bearer-token bliver aldrig lagt i portalens delbare URL. JavaScript kræver en tilladt CORS-origin som beskrevet ovenfor.

Users2: Hver post i locations indeholder også iconKid, lokationens eIcon-navn fra eSetting.Icon i Log24. Manglende eller ugyldigt ikon giver house. Ikoner caches i op til ét minut. state er fortsat Access eller NoAccess. Portalen viser ikonet med state som data-parameter og lokationsnummer/state/KID ved mouseover.

Lokationer på bankoverblik

GET /api/v1/banks/{bankKid}/locations (operationId: GetBankLocations) returnerer et array af {kid,name,iconKid,enabled,deleted,deletedAt}. Brug bankens KID uden Tab og managerens bearer-token.

const response = await fetch(`${api}/api/v1/banks/${encodeURIComponent(bankKid)}/locations`, {
  headers: { Authorization: `Bearer ${token}` }
});
if (!response.ok) throw new Error(`Location request failed: ${response.status}`);
const locations = await response.json();

Kræver aktiv manager, mindst én tildelt Tab, Location Read (også inkluderet i Write/Create) og adgang til det aktuelle site, bank eller lokation. Lokationsadgang giver kun de tildelte lokationer. En udtrykkelig adgang til alle banker viser alle lokationsstatusser, også inaktive lokationer og sletninger uden for RetentionDays. Andre adgange viser kun Enabled præcis 1 og ikke-slettede lokationer eller sletninger inden for RetentionDays. Nul dage skjuler alle slettede lokationer; ugyldige og fremtidige sletteværdier skjules ved begrænset adgang. 400: ugyldig KID eller forkert site; 401: log ind igen; 403: manglende adgang; 503: prøv senere. Data caches i op til 60 sekunder, mens adgang kontrolleres ved hvert kald. Listen sorteres efter lokationsnummer uden paginering. Kun lokationer med Name, Icon, Deleted eller Enabled i Log24 kan findes. Manglende eller ugyldigt ikon giver house. Brug kid som lokationens ID og name som visningsnavn.

Statusfelter: enabled er kun true, når den gemte Enabled er præcis 1; manglende eller ugyldige værdier giver false. deleted er true ved en positiv Deleted-værdi i MS2000, false ved nul (også manglende) eller null ved ugyldige værdier. deletedAt er slettetidspunktet i UTC eller null ved nul eller et tidspunkt uden for det understøttede interval. Vis inaktiv, når enabled er false; ellers slettet, når deleted er true; ellers aktiv. Status giver aldrig adgang; API filtrerer synligheden efter managerens aktuelle adgang og RetentionDays. Felterne svarer til GetLocations og hentes i samme cachede Log24-læsning.

Lokationsoverblik og units

GET /api/v1/locations/{locationKid}/units, operationId GetLocationUnits. Svaret er {location:{kid,name,iconKid},items:[{kid,name,iconKid,cycle,cycleText,unitType,unitTypeName,unitTypeSource}]}. Brug en kanonisk lokations-KID fra bankens lokationsliste eller en beboers locations; sidstnævnte indeholder nu også lokationens name.

const response = await fetch(`${api}/api/v1/locations/${encodeURIComponent(locationKid)}/units`, {
  headers: { Authorization: `Bearer ${token}` }
});
if (!response.ok) throw new Error(`Unit request failed: ${response.status}`);
const { location, items } = await response.json();

Kræver aktiv manager, mindst én Tab, Location Read og Unit Read (Read skal være sat særskilt), samt adgang til sitet og banken eller netop denne lokation. Adgang genkontrolleres ved hvert kald. Lokationens og unitternes Deleted følger RetentionDays; manglende Deleted betyder ikke slettet. 400: forkert KID/site; 401: log ind igen; 403: manglende rettighed; 404: lokationen mangler eller er ikke længere synlig; 503: prøv senere. Der returneres højst 255 units, sorteret efter unitnummer, uden paginering. Tom items er et gyldigt resultat.

Enhedsdata fra Log24 caches i op til 10 sekunder. Enheder med Name, Icon, Deleted, Cycle, UnitType eller UnitType2 kan findes. Manglende navn er tomt; manglende/ugyldigt unit-ikon giver object_cube. Gyldige valgte ikoner bevares. API’et lægger unitnummeret i IconKid.Text; g/object_cube og line/object_cube viser teksten på kassens forside. Samme standard gælder enhedsoverblik, dokumenter, reservationer og forbrugsposter. Web bruger /locations/{locationKid} som delbart link. Genveje i arbejdsområdet gemmes kun pr. manager og browserfane og giver aldrig rettigheder; krydset fjerner kun genvejen.

Vejr ved lokationen (ikke udgivet)

GetLocationWeather, GET /api/v1/locations/{locationKid}/weather, henter aktuelle Open-Meteo-data gennem det fælles Portal API. Brug den eksisterende manager-bearer-session og et kanonisk lokations-KID fra GetBankLocations. Tenant bestemmes af sitet; klienten indsender hverken vejrnøgle, adresse eller koordinater.

Kræver aktiv manager, mindst én tildelt Tab, Bank Read, Location Read, adgang til sitet/banken/lokationen og synlighed efter RetentionDays. Lokationen og dens bank skal også opfylde synligheden fra GetObjectAddress. Unit Read og Write kræves ikke. Hvert kald kontrollerer adgang og læser lokationens egne Log-indstillinger før cacheopslag.

Gyldige eSetting.Latitude/eSetting.Longitude (heltals-milliontedele af grader) har førsteprioritet. Ellers slår API’et den tilgængelige Address og Zip op via sitets eksisterende geokoder. Ufuldstændige adresser og postområder er omtrentlige. Manglende værdier bliver aldrig nul eller arvet fra andre objekter. Kaldet gemmer ingen koordinater og ændrer ingen indstillinger.

const response = await fetch(apiBase + '/api/v1/locations/' + encodeURIComponent(locationKid) + '/weather', {
  headers: { Authorization: 'Bearer ' + accessToken }
});
if (!response.ok) throw new Error('HTTP ' + response.status);
const weather = await response.json();
if (weather.status === 'Available') console.log(weather.current.temperatureCelsius);

Svaret indeholder locationKid, status, address, zip, positionSource og current. Status er Available, LocationUnknown (position kunne ikke findes) eller Unavailable (tjeneste/konfiguration utilgængelig). current er kun udfyldt ved Available. positionSource er Coordinates, Address eller null. Address og Zip kan være tomme. current indeholder UTC-tiderne observedAtUtc/retrievedAtUtc, temperatureCelsius og de valgfrie apparentTemperatureCelsius, windSpeedMetresPerSecond, precipitationMillimetres, relativeHumidityPercent, WMO weatherCode og isDay. Null betyder ukendt, ikke nul.

400: ugyldigt/forkert tenants KID; 401: log ind igen; 403: manglende Tab/adgang/læserettighed; 404: manglende eller skjult lokation/bank; 503 med location-weather-unavailable: lagerfejl eller tidsfrist. Bevar resten af siden og prøv igen ved senere navigation. Vejret caches ti minutter pr. tenant/lokation/adresserevision; negative svar ét minut. HTTP-svar er no-store. Højst fire samtidige tjenesteopslag pr. proces, tolv sekunders samlet frist og ingen automatiske genforsøg. Kortet opdateres ved navigation.

Data er modelberegnede, ikke målt på stedet. Vis kildehenvisning til Open-Meteo og CC BY 4.0. Se Open-Meteos dokumentation for felter og WMO-koder. Det nye endpoint er ikke udgivet; klientpakker opdateres ved beta-forberedelse.

Lokationens åbningstider

GET /api/v1/locations/{locationKid}/opening-hours, operationId GetLocationOpeningHours, returnerer de effektive åbningstider for lokationens synlige enheder, grupperet efter ens planer. Brug et kanonisk lokations-KID fra GetBankLocations og den eksisterende manager-bearer-session. Kræver aktiv manager, tildelt Tab, Location Read, Unit Read, adgang til sitet/banken/lokationen og synlighed efter RetentionDays. Åbningstider giver aldrig adgang.

const response = await fetch(apiBase + '/api/v1/locations/' + encodeURIComponent(locationKid) + '/opening-hours', {
  headers: { Authorization: 'Bearer ' + token, 'Accept-Language': 'da-DK' }
});
if (!response.ok) throw new Error('Åbningstider utilgængelige: ' + response.status);
const hours = await response.json();
for (const group of hours.groups) {
  console.log(group.units, group.weekly, group.exceptions, group.isOpenNow, group.nextChange);
}

Svaret indeholder locationKid, timeZone, calculatedAt og groups. Hver gruppe har units:[{kid,name}], weekly, exceptions, nullable isOpenNow og nextChange. Planrækker har label,status,opens,closes,closesNextDay,daysOfWeek,date. Status er de stabile strenge Open, Closed, AllDay og Unknown. Kun Open har HH:mm-tider; closesNextDay markerer lukning ved midnat/næste dag. Ugerækker har ISO-ugedage 1–7 (mandag–søndag) og null date; undtagelser har YYYY-MM-DD-dato og tomt ugedagsarray. Accept-Language styrer navne og etiketter. Tomme groups er gyldige.

Åbne- og lukketid arver hver for sig fra tidligere ugedage i den autoriserede Alive-hovedterminals plan. Enhedens egne åbningstider og undtagelser tilsidesætter ikke hovedterminalen. En kendt controller uden ugebegrænsninger er AllDay. Manglende, skjulte eller cirkulære ejere giver Unknown; null isOpenNow betyder hverken åbent eller lukket. Ens eksplicitte tider betyder Closed; korte intervaller bevares. Prioritet: Custom1, Custom2, Custom3, indstillede helligdage, første onsdag, ugeplan. Der returneres én undtagelse pr. dato fra i dag til og med to kalendermåneder frem, også over årsskiftet. Skuddag gælder kun i skudår. Indstillede regler for 1. maj, grundlovsdag og den historiske store bededag understøttes. En datoundtagelse erstatter også forrige dags åbning over midnat.

Tider følger lokationens TimeZoneId/ældre TimeZone, derefter bankens, med Europe/Copenhagen som standard ved manglende værdi. Ikke-eksisterende tider ved sommertid flyttes frem til første gyldige minut; gentagne tider bruger første åbning og sidste lukning. nextChange har UTC-offset og er null ved ukendt status eller ingen fundet ændring inden 369 dage. calculatedAt angiver beregningstidspunktet; genindlæs for aktuel status. Tiderne beskriver planlagt åbning, ikke driftsklarhed eller garanteret adgang. Værdier læses pr. kald; forespørg højst én gang i minuttet og hold pause på skjulte sider.

400: ugyldigt/forkert sites KID; 401: log ind igen; 403: manglende adgang/Tab/læserettighed (reason kan være missing-location-read eller missing-unit-read); 404: manglende eller skjult lokation; 503: utilgængelige, ugyldige eller for store data. Vis 503 som utilgængelig med manuelt genforsøg. Højst 255 synlige enheder; ingen skrivninger. Med i klientpakker 0.4.2 til denne betaudgivelse.

Åbningstider kommer fra den autoriserede hovedterminal via tenantens Alive.MainId i samme bank og lokation. Åbningsindstillingerne læses fra denne terminals Log24. Maskinens egne åbningstider og eState.MasterUnitId bruges ikke til dette opslag. Manglende, skjult eller ugyldig hovedterminal samt kæder giver ukendt åbningstid.

Manglende, tomme eller nulstillede åbningstider betyder ingen tidsbegrænsning (AllDay, Altid åbent). En valgfri datoundtagelse uden både måned og dag ignoreres. Ugyldige datoundtagelser ignoreres. Ugyldige ugetider og uafklarede hovedterminaler forbliver ukendte.

Lokationens reservationer (ikke udgivet)

GetLocationReservations: GET /api/v1/locations/{locationKid}/reservations?from=2026-10-19&days=7. Genbrug administratorens bearer-token. Kræver Bookings1, Location/Unit Read, lokationsadgang og RetentionDays-synlighed. User Read kræves også. Hver post indeholder userNumber og userName fra beboerens seneste indstillinger i bankens Log7; manglende værdier er tomme strenge. Manglende adgang giver 403 før reservationsdata læses. Hver post har også kanonisk userKid (null ved ukendt beboer). Vis userKid som reserve, hvis userNumber er tomt; userName forbliver tomt, når navnet mangler.

curl -H "Authorization: Bearer TOKEN" "https://API/api/v1/locations/LOCATION_KID/reservations?from=2026-10-19&days=7"

Svaret indeholder locationKid, from, days og items med unitKid, startMinute og durationMinutes. Start er lokale vægurminutter siden 2000-01-01, ikke UTC; varigheden er positiv og uafhængig af kalenderens ture. Vis præcise tider, også ved overlap og midnat. Læser tenantens aktuelle Log5; ingen historik fra bankens Log5. Annullerede og AUT/isy-rækker udelades. Ugepositioner under 10080 har ingen konkret dato og udelades. Fra år 2001, 1–31 dage, højst 5000 rækker. 400 ugyldigt input, 401 udløbet session, 403 manglende adgang, 404 skjult lokation, 503 utilgængelige eller for mange data. Vis fejl som utilgængelig; tomme felter garanterer ikke ledige tider. Ingen skrivninger.

Lokationens reservationskalendere

GET /api/v1/locations/{locationKid}/booking-calendars, operationId GetLocationBookingCalendars (datobaseret kontrakt ikke udgivet), læser kalendere med åbningstider for synlige enheder. Genbrug managerens bearer-token og et kanonisk lokations-KID fra GetBankLocations. Kræver aktiv manager, Bookings1, Location Read, Unit Read, adgang til sitet/banken/lokationen og synlighed efter RetentionDays. User Read kræves ikke; svaret indeholder ingen beboere eller reservationer. Adgang kontrolleres før læsning af forretningsdata.

const response = await fetch(apiBase + '/api/v1/locations/' + encodeURIComponent(locationKid) + '/booking-calendars?from=2026-10-20&days=14', {
  headers: { Authorization: 'Bearer ' + accessToken, 'Accept-Language': 'da-DK' }
});
if (!response.ok) throw new Error('Kalenderkald mislykkedes: ' + response.status);
const { groups } = await response.json();
for (const group of groups) {
  if (group.status !== 'Configured') continue; // Vis en advarsel om ugyldig kalender.
  for (const day of group.days)
    for (const slot of day.slots)
      console.log(day.date, slot.startMinute, slot.durationMinutes, slot.type);
}

Datobaserede kalendere (forberedt til beta)

Kalender-API’et samler nu ugens ture med lokationens åbningstider, nedarvning, helligdage og datoundtagelser. Valgfrie parametre: from=2026-10-20 (ISO-dato; som standard i dag i lokationens tidszone), days=14 (1–31). Ugyldige intervaller giver HTTP 400. Svaret tilføjer timeZone, from og days; hver gruppedag har date og dayOfWeek. Configured-grupper indeholder de ønskede datoer frem for en gentaget uge. Perioder afkortes ved midnat og åbningstid: startMinute er 0–1439, durationMinutes er positiv, og summen overstiger aldrig 1440. Type 0=reservation, 1=chance, 2=lukket, 3=ukendt åbningstid. Ukendt må aldrig vises som ledigt. Grupper skelner også mellem effektive åbningstider. Vis datoerne direkte og hent et nyt interval ved navigation; klienten skal hverken gentage ugen eller flette åbningstider ind. Svaret beskriver opsætning, ikke belægning eller garanti for reservation. Klientpakker 0.13.0 indeholder denne kontrakt. Frontendens datonavigation og livscyklus er testet; betaudgivelse afventer.

Configured-grupper har udfoldede dage. Ugyldige og manglende/tomme kalendere samt det ældre formats værdi for ingen kalender udelades; en ugyldig indstilling på én enhed skjuler ikke gyldige kalendere på andre enheder. Tomme grupper betyder ingen synlige opsatte kalendere, ikke fri anvendelse. Højst 255 enheder, 8192 indstillingsrækker og otte sekunders frist for indstillinger; genindlæs ved navigation. HTTP 400: ugyldigt/fremmed KID; 401: log ind igen; 403: manglende tab/adgang/læserettighed; 404: manglende eller retention-skjult lokation; 503: utilgængelige, tvetydige eller for store data. Vis aldrig en fejl som en tom kalender. Kaldet opretter ingen reservationer og ændrer ingen indstillinger.

Ugyldige datoundtagelser (ugyldig eller ufuldstændig dato/tid) ignoreres enkeltvis. Ugeplanen og øvrige gyldige undtagelser bevares, også en gyldig undtagelse med lavere prioritet på samme dato. En gyldig undtagelse med lukket bevares.

Åbningstider kommer fra den autoriserede hovedterminal via tenantens Alive.MainId i samme bank og lokation. Åbningsindstillingerne læses fra denne terminals Log24. Maskinens egne åbningstider og eState.MasterUnitId bruges ikke til dette opslag. Manglende, skjult eller ugyldig hovedterminal samt kæder giver ukendt åbningstid.

Lokationens reservationsregler

GET /api/v1/locations/{locationKid}/booking-rules, operationId GetLocationBookingRules, læser de registrerede regler for synlige enheder. Genbrug manager-bearer-sessionen og et kanonisk lokations-KID fra GetBankLocations. Kræver aktiv manager, tildelt Tab, Location Read, Unit Read, adgang til sitet/banken/lokationen og synlighed efter RetentionDays. Regler giver aldrig adgang.

const response = await fetch(apiBase + '/api/v1/locations/' + encodeURIComponent(locationKid) + '/booking-rules', {
  headers: { Authorization: 'Bearer ' + token, 'Accept-Language': 'da-DK' }
});
if (!response.ok) throw new Error('Reservationsregler utilgængelige: ' + response.status);
const policy = await response.json();
for (const group of policy.groups) {
  console.log(group.name, group.units);
  for (const rule of group.rules) console.log(rule.code, rule.text, rule.warning);
}

Arrayet groups er den kompakte visning beregnet af APIet. Hvert afsnit indeholder name, units, rules, common og valgfri lokaliseret help. Ens regler samles. Et fælles afsnit indeholder fælles regler, efterfulgt af afsnit med forskellene. Vis afsnittene i rækkefølge, og brug enhedsnavnene, når name er null. Reservationsgrænser gælder fortsat separat for hver reservationsgruppe. Der er intet separat displayGroups-felt. Alt er ren tekst.

Svaret indeholder locationKid, calculatedAt (UTC) og groups:[{name,units,rules}]. Hver gruppe har et valgfrit registreret name, units:[{kid,name}] og rules:[{code,text,warning}]. Enheds-KID’er er kanoniske og autoriserede. Navne og tekster følger Accept-Language og skal vises som ren tekst, aldrig HTML. Stabile koder: Method, ReservationLimit, BookingHorizon, ReservationPrice, NoShowRelease, NoShowFee, BeforeStart, EarlyRelease, CurrentTurn, OutsideTurns, Dependency, DryingRoom, SettingsConflict, InvalidCalendar, UnknownSettings. Vis også tekst og advarselsflag for fremtidige koder.

Til valgfri fremhævning af værdier leverer hver regel også parts:[{text,isValue}]. Sammenkæd tekstdelene i rækkefølge uden ekstra mellemrum for at genskabe rule.text. Antal, beløb, minutter og andre værdier har isValue:true; øvrig tekst har false. Placeringen følger sproget, også ved gentagne værdier. API’et leverer ingen HTML- eller Markdown-formatering. Alle dele er ren tekst, også gemte navne med HTML-lignende tegn. De eksisterende felter text, code og warning er uændrede; ældre klienter kan ignorere parts. Brug text, hvis parts mangler eller er tom. Dansk eksempel:

{
  "code": "NoShowRelease",
  "text": "Ved manglende fremmøde frigives reservationen 30 minutter efter turens start.",
  "warning": false,
  "parts": [
    { "text": "Ved manglende fremmøde frigives reservationen ", "isValue": false },
    { "text": "30", "isValue": true },
    { "text": " minutter efter turens start.", "isValue": false }
  ]
}

Webklienten kan selv oprette fremhævning med textContent. Brug aldrig innerHTML til nogen af repræsentationerne:

for (const part of rule.parts?.length ? rule.parts : [{ text: rule.text, isValue: false }]) {
  const node = document.createElement(part.isValue ? 'strong' : 'span');
  node.textContent = part.text;
  row.append(node);
}

Kun enheder med samme kalender og samtlige regelindstillinger samles. SettingsConflict betyder, at en kalender har forskellige regler; opdeling i visningen opretter ikke nye kvoter. Antalsgrænsen gælder kommende reservationer pr. beboer i kalendergruppen eller globalt. Positive ugegrænser inkluderer indeværende kalenderuge; nul bruger den gamle horisont på 400 dage. Manglende før-/efter-/tillægstid bruger 15 minutter, og tørrerumssøgning bruger 4320 minutter. Ugyldige værdier giver advarsler frem for ubegrænset adgang. Kalenderformat v3 understøttes; manglende kalendere udelades bortset fra straksreservation. Tomme grupper er gyldige.

Valuta kommer fra enhedens, en synlig controllers, lokationens eller bankens registrerede indstillinger, aldrig sproget. Ukendt valuta vises udtrykkeligt med warning=true. Navne på skjulte afhængige enheder returneres ikke. Reglerne er et øjebliksbillede af opsætningen, ikke driftsstatus, beboerens resterende kvote eller en reservationsvalidering. Beboerreservationer indlæses ikke, og der foretages ingen skrivning. Opdater ved navigation; undgå polling oftere end én gang i minuttet.

400: ugyldigt KID/forkert site; 401: log ind igen; 403: manglende scope/Tab/læserettighed; 404: manglende eller retention-skjult lokation; 503: travlt, utilgængelige, ugyldige eller for store data. Vis 503 som utilgængelig med manuelt genforsøg, aldrig som fri reservation. Højst 255 synlige enheder, 8192 Log24-værdier, otte sekunders frist for regeldata og to samtidige regellæsninger. Med i klientpakker 0.4.2 til denne betaudgivelse.

Terminalniveau i arbejdsområdet

Controllerens navn er den første units eState.ComputerName, ligesom i GetTerminals. Manglende navn er tomt; eSetting.Name bruges ikke som erstatning.

terminal.kid er en kanonisk Controller-KID med MainId og nul UnitId, adskilt fra den første units KID. Hierarkiet er bank → lokation → controller → unit; den første unit er også et selvstændigt barn. GetControllerOverview — GET /api/v1/controllers/{controllerKid} og GetControllerGroup — GET /api/v1/controllers/{controllerKid}/groups/{kind}/Core genbruger rettigheds- og retentionkontrollen fra GetUnitOverview/GetUnitGroup. Data ligger i den første units Log24. Svarets unit.kid identificerer denne unit; unit.terminal.kid identificerer controlleren. Begge gruppelister bruger foreløbig Core. Settings bruger eksisterende typemetadata; states er tomme, indtil Core-statemetadata findes. Udled ikke felter. Brug den returnerede unit-KID og eksisterende revisionskontrol ved autoriseret redigering/historik. Ugyldig controller-KID giver 400, manglende adgang 401/403, ukendt/skjult controller eller gruppe 404 og utilgængeligt lager 503. Genbrug managerens bearer-token:

curl "$BASE/api/v1/controllers/$CONTROLLER_KID/groups/settings/Core" -H "Authorization: Bearer $TOKEN"

Enheder fra GetLocationUnits, GetUnitOverview, GetUnitGroup, GetUnits og GetTerminals indeholder det nullable felt terminal: {kid,name,iconKid}. Brug terminalens kanoniske KID til at placere underenheden under hovedterminalen og terminalen under lokationen. Navnet kommer fra eState.ComputerName, og det uigennemsigtige ikon-KID indeholder hovedenhedens nummer; send iconKid uændret til GetIconFromSet.

API'et læser kun afgrænsede Alive.MainId-relationer i den betroede tenant efter de eksisterende kontroltrin for manager, Tab, operation, ressourcer og retention. Hovedterminalen skal være en tilladt, RetentionDays-synlig Log24-enhed i samme bank/lokation. En underenheds positive MainId kan identificere terminalen, selv om dens egen Alive-række mangler. En eksisterende hovedenhedsrække, der peger på en anden hovedenhed eller har en ugyldig værdi, afvises. Feltet giver ingen ekstra rettigheder, og identiteter udledes ikke fra navne, ikoner eller Offline.

Manglende, ugyldigt, forældreløst, skjult, kædet eller utilgængeligt hierarki giver terminal:null. Vis i så fald en gruppe for ukendt terminal; antag ikke, at enheden er sin egen terminal. Fejl i det valgfrie hierarki fjerner ikke et ellers tilgængeligt overblik, en gruppe eller en katalogside. Eksisterende HTTP-fejl og opdateringsgrænser gælder. GetUnits indeholder fortsat både hoved- og underenheder; klassifikation og paginering i GetTerminals er uændret. Lokalt implementeret, ikke udgivet; pakkede klienter opdateres ved betaudgivelsen.

const parent = unit.terminal;
if (parent) {
  addUnitUnderTerminal(unit, parent.kid, parent.name, parent.iconKid);
} else {
  addUnitUnderUnknownTerminal(unit);
}

På Nortec vises fluebenet som underikon i line-sættet med tenantens grønne farve (#4AC337). Hovedikonets farve og offline-krydset er uændrede; tenant vælges fra betroet sitekonfiguration.

Enhedens fremdrift

Hver enhed fra GetLocationUnits, GetUnitOverview og GetUnitGroup indeholder progress: {status, percent, remainingSeconds, calculatedAtUtc}. API'et beregner estimatet fra samme afgrænsede Log24-snapshot uden ekstra rettigheder eller endpoint. De eksisterende krav til manager, Tab, Location Read, Unit Read, ressourceadgang og retention gælder stadig. Hent højst hvert tiende sekund og sæt skjulte sider på pause.

const response = await fetch(apiBase + '/api/v1/units/' + encodeURIComponent(unitKid), {
  headers: { Authorization: 'Bearer ' + token }
});
if (!response.ok) throw new Error('Enhedsopslag fejlede: ' + response.status);
const { unit } = await response.json();
const progress = unit.progress;
const percent = progress?.percent; // null betyder ukendt/ikke relevant, aldrig nul procent

Fremdrift bruger kun clamp((nu - Started) / (Done - Started) * 100, 0, 100), rundet ned til et heltal. Gyldige tider giver Estimated, også ved 100 på og efter forventet sluttid, og remainingSeconds bliver aldrig negativ. Cycle, Enabled, OutOfOrder, Connected og forløbs-id påvirker ikke beregningen. Manglende, ikke-positive eller ugyldige tidsstempler og Done før eller lig med Started giver UnknownEndTime med null i percent og remainingSeconds.

Værdier beregnes ved kaldet ud fra cachede data; calculatedAtUtc bekræfter ikke frisk kontakt til enheden. Procenten måler forløbet forventet tid, ikke fysisk afslutning. Ved 401: log ind igen; 403/404: utilgængelig; 503: prøv senere.

Enhedsikoner og offline-status ved behov

GET /api/v1/units/icons?kid={unitKid}&kid={andenUnitKid}, operationId GetUnitIcons, tager 1–32 kanoniske enheds-KID’er fra dette site. Hent kun synlige ikoner og saml unikke KID’er i ét kald. Kræver aktiv manager, en tildelt Tab, Location Read, Unit Read, bank-/lokationsadgang samt enheder og lokationer, der er synlige efter RetentionDays. Adgang kontrolleres før læsning af Alive.

const query = new URLSearchParams();
visibleUnitKids.forEach(kid => query.append('kid', kid));
const response = await fetch(apiBase + '/api/v1/units/icons?' + query, {
  headers: { Authorization: 'Bearer ' + token }
});
if (response.status === 401) throw new Error('Log ind igen');
if (!response.ok) throw new Error('Ikonopslag fejlede');
const { items } = await response.json();
// Ved status === 200 bruges iconKid uændret i /api/v1/icon/{iconSet}/{kid}.svg.

Hvert element har kid, iconKid, offline og status. Kun Alive.Offline = 1 tilføjer eIcon.close som Kid.Icons[1]. Hovedikon, enhedsnummer og øvrige præsentationsfelter bevares. Alive.Offline = 0 sætter Kid.Icons[1] til eIcon.check i stedet for offline-markeringen. Manglende Alive-række, null eller andre Offline-værdier giver offline: null uden statusikon. Der udledes ikke status fra MainId eller Cycle.

400: ugyldige/fremmede KID’er eller for mange; 401: log ind igen. Status pr. element: 403 manglende adgang, 404 manglende/skjult efter retention, 503 midlertidigt utilgængelig. Disse har null iconKid/offline og betyder ikke online. Bevar senest bekræftede ikon ved midlertidige fejl. Opslag afgrænses til tenant/bank/lokation og caches i op til 10 sekunder; HTTP-svaret er no-store. Genkontrollér højst hvert 10. sekund, stop på skjulte sider og undgå overlappende kald. Det offentlige billedendpoint slår ikke op i Alive. Ingen skrivning eller hardwarekommandoer.

Bankikoner og samlet enhedsstatus

GET /api/v1/banks/icons?kid={bankKid} · operationId GetBankIcons. Tager 1–8 kanoniske bank-KID’er fra sitet; gentag kid. Kræver aktiv manager, tildelt Tab, Bank Read, Location Read, Unit Read og passende adgang. Lokationsbegrænset adgang medregner kun disse lokationer. Deaktiverede lokationer kræver adgang til alle banker; både lokationens og enhedens RetentionDays-synlighed gælder.

curl -G "$API_BASE/api/v1/banks/icons" -H "Authorization: Bearer $TOKEN" --data-urlencode "kid=$BANK_KID"
{"items":[{"kid":"…","iconKid":"…","offline":true,"status":200}]}

En medregnet enhed med Alive.Offline=1 giver eIcon.close som andet Kid.Icons-element. En ikke-tom samling, hvor alle er kendt Offline=0, giver eIcon.check. Tomme/ufuldstændige data giver offline=null uden tilføjet status, medmindre en enhed er bekræftet offline. Alive-rækker uden tilsvarende Log24-enhed medregnes ikke. Brug iconKid uændret i ikonets URL; GetIconPresentation tilføjer antal/farve og bevarer status.

400: ugyldigt/fremmed KID eller for mange input. 401: log ind igen. 503: sessionslager utilgængeligt. Pr. element betyder 403 manglende adgang og 503 utilgængelige/tvetydige/for omfattende data med null i iconKid/offline. Fejl betyder aldrig online. Hent kun synlige banker, fjern dubletter, opdatér højst hvert tiende sekund og pausér skjulte sider. Status caches ti sekunder pr. tilladt lokationssamling; navne/lokationer tres sekunder. HTTP no-store. Kun læsning af Log24/Alive; højst 65.536 enheder pr. bank og 128 lokationer pr. SQL-batch. Adgang kontrolleres på hvert kald.

Lokationsikoner og samlet enhedsstatus

GET /api/v1/locations/icons?kid={locationKid}&kid={andenLocationKid}, operationId GetLocationIcons, tager 1–32 kanoniske lokations-KID’er fra sitet. Kræver aktiv manager, en tildelt Tab, Location Read, Unit Read, bank-/lokationsadgang og synlighed efter RetentionDays, ligesom GetLocationUnits. Adgang kontrolleres før hvert statusopslag.

const query = new URLSearchParams();
[...new Set(visibleLocationKids)].slice(0, 32).forEach(kid => query.append('kid', kid));
const response = await fetch(apiBase + '/api/v1/locations/icons?' + query, {
  headers: { Authorization: 'Bearer ' + token }
});
if (response.status === 401) throw new Error('Sign in again');
if (!response.ok) throw new Error('Location icon lookup failed');
const { items } = await response.json();
// For status === 200, use iconKid unchanged in /api/v1/icon/{iconSet}/{kid}.svg.

Hvert element har kid, iconKid, nullable offline og status. Hvis blot én enhed i den autoriserede oversigt har Alive.Offline=1, fås offline:true og eIcon.close i Kid.Icons[1]. Kun et ikke-tomt sæt, hvor alle enheder har Alive.Offline=0, giver offline:false og eIcon.check. Ellers er status null uden tilføjet statusikon. Bekræftet offline har forrang over manglende data. Tomme lokationer, manglende/ugyldige Alive-værdier, skjulte enheder og forældreløse Alive-rækker er ikke bevis for online. Kun enheder fra GetLocationUnits indgår. Hovedikon og LocationId-tekst bevares.

400: ugyldige/fremmede KID’er eller forkert antal; 401: log ind igen. Status pr. element: 403 manglende adgang, 404 manglende/skjult efter retention, 503 midlertidigt utilgængelig, med null iconKid/offline. Fejl betyder aldrig online. Bevar senest bekræftede ikon ved midlertidige fejl. Hent kun synlige ikoner, saml dubletter, stop på skjulte sider og genkontrollér højst hvert 10. sekund uden overlappende kald. Lokations- og enhedsopslag deler den afgrænsede Alive-cache i op til 10 sekunder. Svaret er no-store; uændrede billedadresser kan genbruges uden at nulstille billedet. Ingen databaseskrivning eller hardwarekommandoer; offentlig billedrendering læser ikke Alive.

Enhedstype og grupper

GetLocationUnits indeholder nu unitType (tal), unitTypeName (eUnitType-navn, hvis defineret) og unitTypeSource. Typen læses fra Log24 sammen med de øvrige kolonner.

GET /api/v1/units/{unitKid}, operationId GetUnitOverview, returnerer {location,unit,descriptorAvailable,settingGroups,stateGroups}. Brug en kanonisk enheds-KID fra lokationslisten. Kræver aktiv manager, mindst én Tab, særskilt Location Read og Unit Read samt adgang til sitet og banken eller lokationen. Både enhed og lokation skal være synlige efter RetentionDays.

const response = await fetch(`${api}/api/v1/units/${encodeURIComponent(unitKid)}`, {
  headers: { Authorization: `Bearer ${token}`, "Accept-Language": "da-DK" }
});
if (!response.ok) throw new Error(`Enhedsopslag fejlede: ${response.status}`);
const { unit, descriptorAvailable, settingGroups, stateGroups } = await response.json();
  • Kun den oprindelige pakkede UnitType bestemmer typen efter ottebit-reglen (value >> 1) & 255. UnitType2 ignoreres og bruges heller ikke som reserve. Manglende/ugyldig type giver null, aldrig en opdigtet Type000; ukendte tal bevares.
  • Grupperne kommer fra udgivelsens SettingsAndStates.json-katalog: entydige eSettingGroup/eStateGroup-navne sorteret efter enumværdi. Manglende eller ukendt type giver descriptorAvailable=false og tomme grupper. Kun metadata returneres, ingen indstillings-/statusværdier, skriverettigheder eller adgang til udstyret.
  • Enhedsdata caches højst 10 sekunder; lokationsnavne og managerrettigheder højst 60 sekunder. Adgang kontrolleres ved hvert kald. Accept-Language oversætter navne/status, ikke gruppeidentifikatorer. Poll højst hvert 10. sekund, og pausér skjulte sider.
  • 400: ugyldig/fremmed KID; 401: log ind igen; 403: manglende Tab/adgang/læserettighed; 404: enhed/lokation mangler eller skjules af RetentionDays; 503: prøv senere. Efter adgangskontrollen kan 403 have årsagen missing-location-read eller missing-unit-read.

Portalen bruger dette kald på /units/{unitKid}. Klik på en række tilføjer enheden under lokationen i arbejdsområdet og viser typens grupper. Fjernelse af genvejen sletter aldrig enheden.

Læs indstillinger eller status i en enhedsgruppe

GET /api/v1/units/{unitKid}/groups/{kind}/{group}, operationId GetUnitGroup. kind er settings eller states; group er en præcis identifikator fra GetUnitOverview. Svaret er {location,unit,kind,group,items:[{name,valueType,scope,valueStatus,value,ms2000}]}. API'et vælger felter efter enhedstypen. Samme kontrol af aktiv manager, Tab, Location Read, Unit Read, site/adgang og RetentionDays udføres før værdier læses.

const response = await fetch(`${api}/api/v1/units/${encodeURIComponent(unitKid)}/groups/states/Widget`, {
  headers: { Authorization: `Bearer ${token}`, "Accept-Language": "da-DK" }
});
if (!response.ok) throw new Error(`Gruppeopslag fejlede: ${response.status}`);
const page = await response.json();
for (const field of page.items) console.log(field.name, field.valueStatus, field.value);

Ét afgrænset Log24-opslag henter nyeste MS2000 for hvert deklareret felt på den aktuelle enhed. Manglende række har valueStatus=missing; gemt null/tom tekst forbliver stored. Ingen standardværdi eller ældre værdi indsættes. MainUnit og felter på andre objekter får other-scope uden værdi; ejende KID gættes ikke. Skjulte indstillinger udelades; legitimationsfelter maskeres uden at læse værdierne. Felter og grupper beholder stabile enumnavne. Kaldet læser kun; ingen udstyrskommandoer eller redigering.

Værdier caches ikke; type/lokation/manager beholder eksisterende grænser på 10/60 sekunder. Højst 512 felter og 16.384 tegn pr. værdi; tvetydige/for store svar giver 503 for hele kaldet. Poll settings højst hvert 5. sekund og states hvert 10. sekund, og pausér skjulte sider. 400: ugyldig kind, syntaks eller KID/site; 401: log ind igen; 403: manglende rettighed; 404: skjult enhed/lokation eller gruppe ikke deklareret for typen; 503: prøv senere. Efter adgangskontrol kan 403 have missing-location-read eller missing-unit-read. Portalen henter først gruppen, når den åbnes, og gemmer genvejen under enheden i arbejdsområdet. Fjernelse ændrer aldrig udstyrsdata.

Prisfelter med AttributeMetaRegEx(RegExs.Price) får valueFormat: "Price" og pattern i GetUnitGroup (og GetControllerGroup). GetUnitSettingHistory returnerer samme valueFormat på sideniveau. Metadata giver ingen skriverettigheder. Vis value / 100 med to decimaler, og omregn det redigerede beløb med præcis decimalregning (12,50 → "1250"). Kontrollér pattern (.NET RegExs.Price) og minimum/maximum mod den rå værdi efter omregning, aldrig mod den viste tekst. Den aktuelle prisregex tillader 1–6 cifre: rå 0–999999 svarer til 0,00–9999,99. SetUnitSetting modtager og returnerer fortsat rå invariant tekst og validerer på serveren; "12,50" giver 400. Tomme valgfrie værdier forbliver tomme. Historik og gendannelseskald bruger rå værdier; omregn ikke historikken to gange. Ukendte/null-formater bruger den almindelige editor. Formatet angiver ingen valuta. Tilføjelsen er endnu ikke udgivet.

Live sync og indstillingshistorik

hasHistory er true, når indstillingen har mindst én historikpost i bankens Log2. Vis kun historieknappen, når både canReadHistory og hasHistory er true; flaget giver ingen rettigheder. Historikken hentes fortsat først ved åbning.

GetUnitGroup tilføjer sync, changedBy:{kid,kind,name,iconKid} og canReadHistory på indstillinger. Sync læses fra den præcise Log2-post i banken, som den viste værdi stammer fra: kun 1 er kvitteret; andre værdier afventer, og null betyder ukendt. Ændring af sync alene ændrer hverken MS2000 eller værdirevisionen. Poll højst hvert femte sekund for settings og hvert tiende for states, uden overlap og med pause i skjulte faner.

GetUnitSettingHistory kræver samme aktive manager, tildelte Tab, lokationsadgang, Location Read, Unit Read og RetentionDays som læsekaldet. Historikkens brugernavne giver ikke adgang til brugernes konti eller lister. Kun indstillinger på den aktuelle enhed kan læses; skjulte felter, legitimationsoplysninger og andre scopes er udelukket.

curl "$API/api/v1/units/$UNIT_KID/groups/settings/Core/Name/history?limit=25" \
  -H "Authorization: Bearer $TOKEN"

Svaret er {unitKid,group,setting,items:[{value,ms2000,sync,changedBy}],nextBeforeMs2000}, nyeste først. Hent næste side med &beforeMs2000=NEXT_CURSOR; null betyder slut. Limit er 1–50, standard 25. Hent først historik, når den åbnes. Navne og ikoner er aktuelle Log7-oplysninger, ikke historiske kopier. Manager/service bruger bank nul, installatører tenantens bank og beboere enhedens bank; ukendte brugere kan mangle KID/navn. changedBy er null, når ingen bruger er registreret (UserId nul); vis da hverken brugerikon eller brugernavn. Ukendte brugere med et andet UserId bevares.

400: ugyldigt input/site; 401: log ind igen; 403: manglende læseadgang; 404: utilgængelig enhed/gruppe/indstilling; 503: lagerfejl eller tvetydige/for store værdier. Ingen automatisk gentagelsesløkke. Hvert kald har otte sekunders lagerfrist og højst 16.384 tegn pr. værdi. Ingen databaseskrivning, optælling eller historikcache.

Rediger en enhedsindstilling

SetUnitSetting: POST /api/v1/units/{unitKid}/groups/settings/{group}/{setting}. GetUnitGroup returnerer også canEdit, revision, required, minimum, maximum og valgbare options. Redigering kræver Unit Write samt læsekaldets konto-, Tab-, adgangs- og retentionsrettigheder. API'et kontrollerer manager og enhedstype igen under skrivetransaktionen.

Send invariant tekst i value, højst 4096 tegn, samt feltets expectedRevision. Type-, påkrævet-, interval-, mønster- og valgmulighedsregler fra enum-metadata og SettingsAndStates.json gælder. Bool normaliseres til 0/1. Standardværdier indsættes ikke. Status er altid skrivebeskyttet. Skjulte, enhedsstyrede, skrivebeskyttede, ORM-styrede og hemmelige felter kan ikke redigeres. MainUnit/andre objekters felter og dynamiske valgmuligheder understøttes endnu ikke til redigering.

const headers = { Authorization: `Bearer ${token}`, "Content-Type": "application/json" };
const path = `${api}/api/v1/units/${encodeURIComponent(unitKid)}/groups/settings/Core`;
const read = await fetch(path, { headers });
if (!read.ok) throw new Error(`HTTP ${read.status}`);
const field = (await read.json()).items.find(item => item.name === "Name");
if (!field?.canEdit) throw new Error("This setting cannot be edited");
const saved = await fetch(`${path}/Name`, {
  method: "POST", headers,
  body: JSON.stringify({ value: "Washer 1", expectedRevision: field.revision })
});
if (!saved.ok) throw new Error(`HTTP ${saved.status}; reload before retrying`);
const confirmed = await saved.json(); // value, ms2000, revision

Et vellykket svar bekræfter lagring, ikke levering til enheden: canonical unit KID, gruppe, indstilling, værdi, MS2000 og ny revision returneres. Ændringen tilføjes bankens Log2 med Sync=0 og editorens UserId; Log24 kontrolleres før commit. 400: ugyldig værdi; 401: log ind igen; 403: skrivning afvist; 404: enhed/gruppe/felt utilgængeligt; 409: værdi eller type ændret; 503: lagerfejl. Efter konflikt eller mistet svar skal værdierne læses igen. Gentag aldrig skrivning automatisk.

I portalen tilføjer klik på en indstillingsgruppe alle enhedens indstillingsgrupper til arbejdsområdet. Klik på en statusgruppe tilføjer alle statusgrupper. Indstillinger gemmes, når feltet forlades; valg gemmes straks. Den bekræftede værdi vises først efter API-succes. Automatisk opdatering holder pause under redigering, gemning og uafklarede fejl.

Sprog i unitnavne

Send Accept-Language: da-DK på hvert API-kald. Sproget bindes ikke til login eller token. Unitnavne som [455] 1 får den kendte eLocalization-pladsholder erstattet af den fælles oversættelse; øvrig tekst bevares. Ukendte id-numre forbliver uændrede. Content-Language angiver det valgte sprog. Samme ti sprog som portalen understøttes; regionale varianter accepteres, no/nn bruger norsk bokmål og pt-BR bruger pt-PT. Manglende, ugyldigt eller ikke-understøttet sprog giver en-GB. Prioriteter q respekteres, q=0 fravælges. Rå data caches før oversættelse, så sprog ikke blandes mellem brugere og ikke giver ekstra SQL.

const response = await fetch(`${api}/api/v1/locations/${locationKid}/units`, { headers: { Authorization: `Bearer ${token}`, "Accept-Language": "da-DK" } });
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const overview = await response.json();

Aktuel eCycle

GetLocationUnits returnerer nu cycle (stabilt eCycle-navn) og cycleText (Accept-Language) på hver enhed. Begge er null ved manglende eller ugyldig værdi. Nyeste MS2000 fra eSetting.Cycle (1619, Settings) og eState.Cycle (20, States) vælges; ved samme tid vinder States. En ugyldig nyeste værdi erstattes aldrig af en ældre. Enhedsdata caches nu højst 10 sekunder; lokationsnavne og managerrettigheder stadig højst ét minut. SQL er afgrænset til banken og lokationen for begge grene. Portalen opdaterer hvert 10. sekund, pauser i skjulte faner, undgår samtidige kald og skjuler gamle detaljer ved fejl. Eksterne klienter kan gentage samme GET med samme bearer-token og Accept-Language hvert 10. sekund.

Afregning: visning, historik og downloads

Alle kald kræver Authorization: Bearer TOKEN, fanen Settlement2, læserettighed til bank og brugere samt adgang til hele banken. Adgang til enkelte lokationer giver ikke adgang til bankens samlede afregning. API'et kontrollerer aktuelle rettigheder ved hvert kald; manageroplysninger caches højst ét minut. Afregning bliver aldrig afsluttet, fortrudt eller ændret af disse GET-kald.

OperationGET-sti
GetBankSettlements/api/v1/banks/{bankKid}/settlements?beforePeriod=123
GetBankSettlementPeriod/api/v1/banks/{bankKid}/settlements/{period}
DownloadBankSettlement/api/v1/banks/{bankKid}/settlements/{period}/download?format=XLS

Udelad beforePeriod ved første historikkald. Svaret indeholder op til 25 afsluttede perioder, nextSettlement og nextBeforePeriod. Send næste markør uændret; null betyder slut. Ukendte datoer og værdier er null. Datoer er UTC. Periode 0 er den igangværende, foreløbige periode og findes kun via detalje- og downloadkald. Den kan ændre sig frem til afregning.

Detaljer indeholder sourceEntries, includedEntries, groups og formats. Hver gruppe har group, currency, entries og amountMinor. Summen er fortegnet fra databasen i mindsteenheder, ikke et formatteret valutabeløb. Beløb i forskellige valutaer lægges aldrig sammen. Historikkens registrerede sum kan afvige fra eksporten.

Elforbrug med ChargePoint_58/TimeNew udelades; kontantbanker udelader også Month og Transfer. Grupper vælges i rækkefølgen ETest, EInstaller, EGuest, bankens nummermasker (U), EDate og LR. Nummermasker bruger % for flere tegn og _ for ét tegn. Gyldige ældre posteringer med typen Unknown medtages med deres registrerede beløb. Brugernes aktuelle nummer, navn, brikker og attributter anvendes, så en genhentet historisk fil er ikke nødvendigvis identisk med den oprindelige. Afregning er undtaget fra RetentionDays: slettede brugere indgår også i periodens detaljer og downloads uanset sletningens alder. De almindelige manager-, fane-, bank- og læserettigheder kontrolleres stadig.

Download er en ZIP-fil med én fil per gruppe og valuta samt manifest.json til afstemning. En tom periode indeholder kun manifestet. Tekstformater kan udelade grupper uden eksportérbare beløb; gruppernes summer fremgår stadig af manifestet. XLS giver rigtige .xlsx-filer med Number, Amount og UserId efter eksportformatets konvention; UserId i dette kompatibilitetsformat er numerisk, mens HTTP-kontrakten bruger KID'er. De øvrige formater er UTF-8-tekst med CRLF. Et Excel-beløb beholder databasens fortegn; eksempelvis NAVISION vender fortegnet.

Tilgængelige formater: XLS, ATB, BL, DEAS, FRUEHØJGAARD, HEIMSTADEN, LEJERBO, MD90_1, MD90_3, MD90_3_minus, MD90_3_plus, MD90_3_AABKBH og NAVISION. MD90_3 er kun defineret for bank 1001 og 1068. NIRAS og ROBERT er obsolete. HUMAN, KMD, LYKKEBO og MD90 tilbydes ikke, da den undersøgte fælles eksportkode ikke har en implementeret eksport for dem. Formatnavne er case-sensitive; brug listen fra detaljesvaret.

const headers = { Authorization: `Bearer ${token}` };
const base = `/api/v1/banks/${encodeURIComponent(bankKid)}/settlements`;
const details = await fetch(`${base}/12`, { headers });
if (!details.ok) throw new Error(`HTTP ${details.status}`);
const period = await details.json();
const download = await fetch(`${base}/12/download?format=XLS`, { headers });
if (!download.ok) throw new Error(`HTTP ${download.status}`);
const url = URL.createObjectURL(await download.blob());
const link = document.createElement('a');
link.href = url; link.download = 'afregning-12.zip'; link.click();
setTimeout(() => URL.revokeObjectURL(url), 60000);

Eksemplet forudsætter samme origin; eksterne browserportaler bruger API'ets fulde baseadresse og en tilladt CORS-origin. 400 betyder ugyldig KID/periode/format, 401 kræver nyt login, 403 betyder manglende rettigheder, 404 betyder ukendt afsluttet periode, 422 betyder data, der ikke kan eksporteres sikkert, og 503 betyder midlertidigt utilgængelige data. Ved 422 kan årsagen være ugyldig transaktionskode, ugyldigt nummer/valuta, flere brugere med samme eksportnummer, feltoverskridelse eller mere end 100.000 posteringer/10.000 brugere. Ingen delvise filer leveres. Ret data/format frem for at gentage 422. Ved 503 vent før nyt forsøg.

Historik og periodedata caches højst ét minut med samling af samtidige opslag. Detaljer hentes først, når en periode vælges. Der udføres ingen baggrundsforespørgsler og ingen MySQL-skrivninger. Den gamle sides afregningsparathed og automatiske jobs er ikke flyttet; dette API påstår ikke, at en bank er klar til at blive afsluttet.

Det ekstra heatmap-array indeholder {latitude, longitude, amount} for alle køb i den vedligeholdte Log1Hour-tabel, grupperet efter koordinater. Ingen rækkegrænse eller ekstra tidsfilter: databasen vedligeholder den seneste time. Poster uden gyldige koordinater udelades; de eksisterende købs- og overførselsfiltre bevares. Beløb summeres i valutaens hovedenhed uden valutaomregning. Kun de animerede items begrænses til de nyeste køb. Portalen viser hele heatmappet straks ved hver opdatering hvert tiende sekund, uafhængigt af mønternes animation. Ingen navne eller bruger-id medtages.

Map1: Offentligt kort med køb

GET /api/v1/public/displays/Map1 · operation GetPublicDisp73 i Swagger-definitionen Public. Kaldet kræver ikke login. Kun sitets tenant vælges af serveren; query-parametre kan ikke ændre tenant, bank eller tidsgrænse. ?limit=100 er valgfri (standard 100), tillader 1–200 og giver HTTP 400 ved ugyldige værdier. Grænsen gælder kun de animerede items; heatmappet indeholder alle køb. Alle limits deler ét cachet snapshot; kun de animerede items afgrænses til det ønskede limit.

Brug /api/v1/public/displays/Map1. Den gamle disp73-adresse er fjernet og giver 404. Operation-ID er fortsat GetPublicDisp73 (JavaScript: getMap1()).

const response = await fetch('https://api.team.kombine.technology/api/v1/public/displays/Map1');
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const sample = await response.json();
for (const p of sample.items) {
  // Ignorér kendte p.kid. Fordel nye mønter jævnt over næste 10 sekunder.
  queueCoin(p.kid, p.latitude, p.longitude, p.timestampUtc, p.amount);
}

Svaret er {measuredAtUtc, refreshAfterSeconds: 10, items: [...]}. refreshAfterSeconds er altid 10. Listen items indeholder {kid, latitude, longitude, timestampUtc, amount}, nyeste først. Der returneres højst de 200 nyeste køb fra den vedligeholdte Log1Hour-tabel uden tidsfilter. Poster uden gyldige koordinater udelades, så listen kan være kortere. Overførsler og poster, hvor Text ender på E, udelades.

timestampUtc er et ISO 8601 UTC-tidspunkt: 2000-01-01T00:00:00Z + oprindelig MS2000 (uden tillæg). kid er posteringens kanoniske KID med oprindelig tid og tenant/bank/lokation/enhed; det giver ingen adgangsrettigheder. Bruger-ID, navne, brikker og posteringstekst medsendes ikke. Beløbet er positivt i hovedenheder (minus Amount / 100); der foretages ingen valutaomregning, og valuta medsendes ikke.

Hent hvert 10. sekund uden overlappende kald. Ignorér eksisterende KID'er, sammenlæg med beholdningen og fjern de ældste, hvis der er over 100. Nye mønter fordeles jævnt over de næste 10 sekunder og falder én gang; timestampUtc bruges kun til sortering og fjernelse af de ældste poster. Skjulte faner stopper hentning og animation; de genoptages ved tilbagekomst. Reduceret bevægelse slår faldanimationen fra. API-cachen deles i 10 sekunder. Tom liste betyder ingen visbare poster. HTTP 503 betyder utilgængelige data: behold eksisterende mønter og vent 30 sekunder (Retry-After). Eksterne browserklienter skal have en tilladt CORS-origin. KID'er fra dette offentlige display giver ikke adgang til beskyttede bankkald.

Users2 bruger udelukkende Log-tabeller som datagrundlag. Beboer-id, nummerkontrol, brikejerskab og ændringer bygger på Log7; synkronisering anmodes via Log2. Der læses eller skrives ikke i de obsolete Users- eller Settings-tabeller. API-ruter og kommandoer er uændrede.

Users2: beboere, redigering og eksport

GetBankUsers understøtter locationKid samt deleted=all|active|deleted|no-access. Lokationen skal tilhøre samme bank og være omfattet af managerens adgang. no-access viser synlige beboere uden registreret Access på nogen lokation, som manageren må se. Det omfatter tomme lokationslister og lister med kun NoAccess. Ved bankadgang vurderes hele banken; skjulte lokationer påvirker ikke resultatet for en lokationsbegrænset manager. Beboere uden tilknytning til en tilladt lokation bliver fortsat skjult for lokationsbegrænsede managers. Retention gælder også her, så synlige slettede beboere kan indgå. Et valgt locationKid begrænser yderligere til beboere tilknyttet den lokation. Filtrering sker før paginering og gælder også CSV-eksport. Behold filtrene ved brug af cursors; ændringer kræver en ny hentning uden cursor. Ukendt filterværdi giver 400, manglende rettigheder 403 og utilgængelige data 503.

const page = await client.getBankUsers(bankKid, {
  sort: 'number', deleted: 'no-access', pageSize: 25
});
// Ved næste side: behold indstillingerne og tilføj cursor: page.nextCursor.

GET /api/v1/banks/{bankKid}/users/{userKid}/workspace (GetBankUserWorkspace) henter aktuelle redigeringsfelter og en uigennemsigtig revision. Kræver Users2, User Read og bank-/tenantdækkende adgang. Lokationsbegrænsede managers kan fortsat læse deres liste og eksport, men ikke redigere en delt beboer via disse handlinger.

POST /api/v1/banks/{bankKid}/users (CreateBankUser) tager {"action":"create","name":"Ny beboer","number":"001"} og returnerer HTTP 201 med den nye beboers KID. Kræver User Create. Lokationsadgang og brikker tildeles separat.

POST /api/v1/banks/{bankKid}/users/{userKid}/commands (ExecuteBankUserCommand) kræver den aktuelle revision. Alle handlinger kræver User Read. Attributes kræver Write; tag/location kræver Create; delete/restore kræver Delete; replace kræver Create og Delete. Profile kræver Rename ved ændret navn og RenameExtrenatId ved ændret nummer. Alle kræver Users2 samt bankdækkende adgang. Manageridentitet og tenant kommer udelukkende fra session og sitekonfiguration.

// baseUrl og token fra login-eksemplet ovenfor; bankKid/userKid er API-returnerede KID'er.
const path = baseUrl + '/api/v1/banks/' + encodeURIComponent(bankKid) + '/users/' + encodeURIComponent(userKid);
const headers = { Authorization: 'Bearer ' + token, 'Content-Type': 'application/json' };
const read = await fetch(path + '/workspace', { headers });
if (!read.ok) throw new Error('HTTP ' + read.status);
const current = await read.json();
const saved = await fetch(path + '/commands', { method: 'POST', headers,
  body: JSON.stringify({ action: 'profile', revision: current.revision, name: 'Nyt navn', number: current.number }) });
if (saved.status === 409) throw new Error('Genindlæs og gennemgå de aktuelle oplysninger før et nyt forsøg');
if (!saved.ok) throw new Error('HTTP ' + saved.status);
const updated = await saved.json();

Beboerikoner: GetBankUserWorkspace og kommandosvar indeholder iconKid, availableIcons og canEditIcon. Skift ikon med ExecuteBankUserCommand: action: "icon", den aktuelle revision og et præcist eIcon-navn med eIconSubject.Person-metadata. Det kræver Users2, User Read, User Write og bankdækkende adgang. Et nuværende ikon uden Person-metadata vises først, men må ikke vælges som en ny værdi. Ikoner hentes lokalt via /api/v1/icon/g/{kid}.svg.

{"action":"icon","revision":"<revision fra GetBankUserWorkspace>","iconKid":"user"}

Ikonskift kræver eller ændrer ikke beboerens navn eller nummer. Vis først det returnerede ikon efter HTTP 200, og brug den nye revision ved næste ændring. Send ændringer, der deler revision, efter hinanden. Ugyldigt ikon giver 400, manglende rettighed 403, gammel revision eller slettet beboer 409 og lagerfejl 503. Ved 409 eller et usikkert netværksresultat skal oplysningerne genindlæses og gennemgås før et nyt forsøg. Klik på det allerede valgte ikon skal ikke sende en ændring.

  • profile: name og number. Nummerformat og eksisterende aktive numre kontrolleres.
  • attributes: hele attributes-listen med kanoniske eUserAttribute-navne og value (-1 = uden talværdi).
  • tag: tagKid og state (Unlocked, Locked eller Deleted). KID skal være kanonisk og høre til banken. En brik med en anden ejer afvises; historiske brikker ændres ikke.
  • location: locationKid og state (Access eller NoAccess). Lokationen skal være aktiv.
  • delete: valgfrit deleteAtUtc. Tomt/fortid sletter nu og deaktiverer brikker. En fremtidig dato planlægger sletning, højst 366 dage frem.
  • restore: genåbner eller annullerer planlagt sletning. Brikker genaktiveres ikke automatisk. Retention begrænser genåbning.
  • replace: name, number og valgfrit deleteAtUtc. Sletning af den gamle og oprettelse af den nye beboer sker i én transaktion. Ingen overførsel af saldo, brikker eller lokationsadgang. Ved fremtidig sletning kan nummeret ikke genbruges endnu.

HTTP 400: ugyldige felter/KID/nummerformat. 401: sessionen er ikke gyldig. 403: utilstrækkelige rettigheder. 404: ikke tilgængelig inden for retention. 409: ændret revision, nummerkonflikt eller brik allerede i brug. 503: skriveforbindelse eller lager utilgængeligt. ProblemDetails har om muligt et stabilt code, fx conflict, number-exists, tag-in-use eller writes-unavailable. Ved netværksfejl efter POST: læs igen og kontrollér resultatet før genforsøg; antag ikke at ændringen blev rullet tilbage.

GET /api/v1/banks/{bankKid}/users/export (ExportBankUsers) returnerer UTF-8 CSV med semikolon og stabile kolonnenavne. Samme filter/sort/direction/locationKid/deleted og rettigheder som listen. Højst 10.000 beboere og 4 MiB tekst; HTTP 422 kræver smallere filtre og giver ingen delvis fil. Hver side genautoriseres; data er ikke et transaktionelt øjebliksbillede. Ingen aktiveringskoder eller saldo indgår i CSV.

GET /api/v1/banks/{bankKid}/users/{userKid}/activation (GetBankUserActivation) returnerer et aktiveringsbrevgrundlag med activationCode. Kræver User Create og bankdækkende adgang; slettede beboere afvises. Koden er en legitimationsoplysning og bør ikke logges eller caches.

qrCodeDataV1 er QR-kodens komplette data, ikke et billede. Formatet svarer til FlexORMs GetQRCodeString(): tenantens URL, # og et FlexCipherLongs-fragment med bankkode, brugerkode og UTC-sekunder siden 2000-01-01. Dan QR-koden lokalt fra den uændrede tekst med fri kant; send aldrig aktiveringsoplysninger til en ekstern QR-tjeneste. En tom streng betyder, at tenanten mangler en aktiverings-URL. Hent igen for et nyt tidspunkt; gyldigheden afgøres af den eksisterende aktiveringsmodtager. Kræver en aktiv administrator, Users2, User Read, User Create og bankdækkende adgang. Ugyldige KID'er giver 400, ugyldig session 401, manglende rettigheder 403, manglende/slettet beboer 404 og utilgængeligt datalager 503. Svaret må ikke caches.

QR-version 2: qrCodeDataV2 indeholder fem FlexCipherLongs-tal i rækkefølgen bankCode, userCode, seconds, noise, checksum. De første tre er identiske med version 1 i samme svar. Noise genereres på ny med en kryptografisk tilfældighedsgenerator for hvert payload, jævnt fordelt fra 0 til 1073741823 (30 bit); gentagelser kan forekomme. Checksummen er (((bankCode * 31 + userCode) * 31 + seconds) * 31 + noise) % 1073741824. Undgå overflow ved at starte med c = bankCode % M og gentage c = (c * 31 + value % M) % M for userCode, seconds og noise, hvor M = 1073741824 og mellemregninger bruger UInt64. Afkod med FlexCipherLongs.Parse(5, fragment), kontrollér gyldig afkodning, at både noise og checksum højst er 1073741823, og sammenlign det femte tal med beregningen. Begge er logiske 30-bit-værdier kodet som tal, ikke felter med fast bytelængde. Noise giver ikke autentifikation eller beskyttelse mod genafspilning; checksum opdager kun fejl. Opdatér læsere af det tidligere uudgivne version 2-format med fire tal, og regenerér QR-koderne; der er ingen fallback til ældre version 2-formater. Version 1 bruger fortsat tre tal. Begge felter er tomme, hvis tenant mangler aktiverings-URL. Generér QR-billedet lokalt uden at ændre data.

curl --fail-with-body -H "Authorization: Bearer $TOKEN" "$API/api/v1/banks/$BANK_KID/users/$USER_KID/activation"
# Dan QR-koden lokalt fra response.qrCodeDataV1; log ikke svaret.

Ændringer gemmes med managerens ID og anmoder den eksisterende backend om synkronisering. Et vellykket HTTP-svar bekræfter lagring, ikke levering til fysisk udstyr. Automatisk synkronisering og planlagt sletning forudsætter den eksisterende backend. EVaskeri/CP-autologin, abonnementshåndtering og test-/informationsbeskeder er ikke migreret. Lokale bekvemmelighedslogin giver ingen yderligere rettigheder.

Reservationer · Bookings1

GetBankBookings: GET /api/v1/banks/{bankKid}/bookings. Kræver fanen Bookings1 (8), Location/Unit/User Read samt adgang til banken eller de konkrete lokationer. Lokationsadgang begrænser både poster og filtermuligheder. Alle objekt-id'er er kanoniske KID'er på sitets tenant.

const page = await fetch(`${base}/api/v1/banks/${encodeURIComponent(bankKid)}/bookings?from=2026-09-01&through=2026-09-30&status=all&limit=50`, {
  headers: { Authorization: `Bearer ${accessToken}`, 'Accept-Language': 'da-DK' }
}).then(async response => {
  if (!response.ok) throw await response.json();
  return response.json();
});

Filtre: from/through inklusive lokale startdatoer; standard UTC-dagens dato minus 7 til plus 90 dage, højst 367 datoer. locationKid, unitKid, userKid, search (navn/nummer, højst 100 tegn) og status=all|active|cancelled. limit er 1–200, standard 50. offset er 0–100000; øg med limit, når hasMore er true. Sider er ikke et låst øjebliksbillede; opdater ved ændringer.

items indeholder den nyeste aktuelle hændelse pr. lokation/enhed/beboer/start. Supersederede Log5-poster (Period forskellig fra 0) og koderne AUT/isy udelades. startLocal/endLocal er lokale tider uden UTC-offset; tilføj aldrig Z. recordedAtUtc er registreringstid i UTC. weeklyMinute er i stedet position 0–10079 for ugereservationer, som altid medtages uafhængigt af datofilteret. durationMinutes er positiv; cancelled fortæller, om reservationen er aflyst. source bevarer systemkoden, fx ARR/ArR (fremmøde), FE0 (udeblivelse) og FE1 (udeblivelse med gebyr).

ExecuteBankBookingCommand: POST til /api/v1/banks/{bankKid}/bookings/{bookingKid}/commands med {"action":"cancel"} eller {"action":"restore"}. Brug KID'et fra den viste post; det identificerer også revisionen. Ud over læserettigheder kræves Unit Write og adgang til reservationens lokation. UI-felterne canCancel/canRestore er vejledende; API'et kontrollerer altid rettigheder og aktuel version igen.

const booking = page.items.find(item => item.canCancel);
if (!booking) throw new Error('Ingen reservation på siden kan aflyses.');
const response = await fetch(`${base}/api/v1/banks/${encodeURIComponent(bankKid)}/bookings/${encodeURIComponent(booking.kid)}/commands`, {
  method: 'POST', headers: { Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({ action: 'cancel' })
});
if (!response.ok) throw await response.json();
const changed = await response.json();

En ændring tilføjer en Log5-post med modsat fortegn på varigheden, Currency SER og Sync 0. Historik bevares. Et 200-svar bekræfter lagring; synced=false betyder, at synkronisering via den eksisterende backend afventes. Ingen direkte enhedskommando sendes. 400: ugyldigt filter/KID/handling; 401: ugyldig session; 403: manglende rettighed; 409 conflict: ændret post, eller time-occupied: tiden er optaget; 422 weekly-restore-unavailable: ugereservation kan ikke gendannes; 503: lager eller skriveforbindelse utilgængelig. Efter netværksfejl skal listen læses igen før et eventuelt nyt forsøg. POST gentages aldrig automatisk.

Begrænsninger: ingen oprettelse, serieudvidelse, eksport eller gebyrændring. Ugereservationers rå ugeposition vises uden at gætte første ugedag; gendannelse af dem understøttes endnu ikke. En aktiv ugereservation spærrer konservativt for gendannelse på den enhed. Gendannelse kontrollerer overlap, men genberegner ikke controllerens bookingregler. Skrivning kræver InnoDB og højst 20.000 aktuelle hændelser på enheden; filterlisten er begrænset til 4.096 navne. Intet cachelag eller automatisk polling anvendes.

Posteringer · Account2

Bilagsgrupper og afkodning fra FlexOrm

Beskrivelser bruger den afkodede tekst fra Kombine.Flex.FlexDto.KrumbValue.TransactionLine.ToString(), herunder program, varighed, sæbe og overførsler. API'et opløser numeriske pladsholdere til lokaliseret tekst. Ældre beskrivelser med ren tekst understøttes fortsat, når de kan vises sikkert; manglende DTO-tekst erstattes af en sikker beskrivelse. Rå JSON og betalings-id'er udleveres ikke. Hver post har desuden documentKey (en uigennemsigtig nøgle inden for banken), documentId (positivt DocId eller null), isAnonymized (boolesk) og paymentKind (Credit, ReserveRefund, Managed eller tom streng). Brug ikke beskrivelsen til at afgøre betalingshandlingen; brug paymentKind som metadata.

documents er en ekstra liste med {key, docId, lines, totals} for denne side. Linjer med samme DocId samles inden for samme oprindelige beboer, lokation og periode, på tværs af enheder. Manglende/ugyldigt DocId og betalingsstyrede poster forbliver særskilte. Bilag sorteres efter deres tidligste indlæste linje, nyeste bilag først; linjerne står kronologisk. Bilagssummer gælder kun de indlæste linjer og beregnes pr. valuta. Filtre og sidegrænser kan afgrænse et bilag: summerne er ikke nødvendigvis hele fakturaens sum. Den flade items-liste, offset/limit, hasMore og summer for hele søgningen beholder deres betydning. Saml flere sider efter documentKey, fjern dubletter efter postens kid, og kræv ens revisioner. Udvid aldrig de godkendte filtre for at hente et helt bilag.

for (const document of page.documents) {
  console.log(document.key, document.docId, document.totals);
  for (const line of document.lines) console.log(line.kid, line.description);
}

Opbevaring og betalingsstyrede poster

Tenantens Log24 LawAccountingYears gælder alle poster; LawSurveillanceDays gælder desuden nulposter. En positiv værdi i managerens Log7 erstatter den tilsvarende tenantværdi; et år regnes som 365 dage. Manglende, ugyldige eller nulværdier betyder ingen opbevaring af identitet. Poster før en gældende frist vises på bank/lokation/enhed med bankens GDPR-bruger-KID (UserId 1000), tomme userName/userNumber, transaktionstypen som sikker beskrivelse, isAnonymized=true og canReverse=false. Beløb indgår stadig i summerne. Ved eksplicit userKid fjernes udløbne poster før sideinddeling og summering. CSV/XLSX følger samme regler og beholder de eksisterende kolonner.

Revision omfatter ændret synlighed som følge af fristerne. Cachen er adskilt pr. godkendt manager; samme managers identiske, godkendte søgning deler fortsat en cache i højst 30 sekunder. Klienten kan ikke angive manageren. Betalingsstyrede poster beholder deres sikre beskrivelse og udleverer paymentKind som metadata; de kan ikke tilbageføres gennem den almindelige tilbageførsel, selv om den lagrede type ligner forbrug. Tilbageførsel kontrollerer også fristerne i skrivetransaktionen. Ikke-tilladte poster giver 422 reversal-unavailable. Eksisterende håndtering af 400/401/403/409/503 gælder fortsat. Gentag aldrig automatisk en usikker økonomisk skrivning.

Listefunktionaliteten bruger ingen FlexOrm-runtime, gamle forretningstabeller, eksterne betalingsrefusioner eller opslag i betalingsmetadata uden for Log. Genererede klienter og downloadpakker er synkroniseret i version 0.4.2.

GetBankAccount: GET /api/v1/banks/{bankKid}/account. Kræver Account2 (2), Bank/Location/Unit/User Read samt adgang til banken eller konkrete lokationer. Managerens konto, credential stamp og sitets tenant kontrolleres inden opslag. Lokationsadgang begrænser poster, summer og filtermuligheder. Genbrug den almindelige manager bearer-session.

const filters = new URLSearchParams({from: '2026-09-01', through: '2026-09-30',
  timeZone: 'Europe/Copenhagen', kind: 'all', limit: '50'});
const accountUrl = `${base}/api/v1/banks/${encodeURIComponent(bankKid)}/account`;
const response = await fetch(`${accountUrl}?${filters}`, {
  headers: {Authorization: `Bearer ${accessToken}`, 'Accept-Language': 'da-DK'}
});
if (!response.ok) throw new Error(`Account HTTP ${response.status}`);
const page = await response.json();
for (const row of page.items) console.log(row.kid, row.recordedAtUtc, row.amountMinor, row.currency);

Datoer er inklusive i timeZone, som standard i dag i Europe/Copenhagen. Sommertid håndteres; højst 367 datoer. En ikke-negativ period erstatter datointervallet; nul betyder igangværende periode. De seneste 100 tilgængelige periodenumre medsendes. Valgfrie locationKid, unitKid og userKid skal være kanoniske KID'er i samme bank. kind er all, debit (negative) eller credit (positive). includeZero, includeBookings og includeMonthly er som standard true. Limit er 1–200 (standard 50), offset 0–100000. Nyeste poster først, derefter lokation/enhed. Nye posteringer kan forskyde sider mellem kald.

items indeholder posteringens uforanderlige KID, scope-KID'er, recordedAtUtc, fortegnet amountMinor, valuta, beskrivelse, posteringstype, periode, aktuelle navne, reversed, valgfrit reversalOfKid og canReverse. totals summerer hele det filtrerede udvalg pr. valuta. Det er bevægelser, ikke beboerens saldo; der foretages ingen valutaomregning. Portalen viser de gamle hundrededele som amountMinor / 100. Navne er aktuelle værdier fra Log7/Log24, ikke historiske snapshots. Både moderne JSON, gammel tekst og JSON indlejret i det gamle tekstformat understøttes.

recordedAtUtc / MS2000 er hændelsestid, ikke indsættelsestid. Posteringer kan ankomme minutter eller dage senere. Brug aldrig højeste MS2000 som markør for alle nye databaseposter. Hver side har en revision for hele det filtrerede sæt: identiteter, perioder og beløb pr. valuta. Den er uafhængig af offset/limit og er hverken cursor eller adgangsbevis.

GetBankAccountRevision: GET /account/revision med samme filtre og samme adgangskrav. Returnerer kun {"revision":"..."}. Revisionsopslag deles i højst 30 sekunder mellem ens autoriserede udsnit pr. API-instans; adgang kontrolleres ved hvert kald. Kontrollér højst hvert 30. sekund, og stop mens visningen er skjult. Ved ændring hentes den indlæste del igen fra offset 0. Kombinér kun sider med samme revision, og erstat først den gamle liste, når alle nødvendige sider er hentet. Ved ændring under hentningen kasseres det ufærdige resultat og forsøges senere med pause.

const check = await fetch(`${accountUrl}/revision?${filters}`, {
  headers: {Authorization: `Bearer ${accessToken}`}, cache: 'no-store'
});
if (check.status === 401 || check.status === 403) {
  clearAccountView(); // fjern data og håndtér login/adgang
} else if (!check.ok) {
  showRetryLater(); // ved 503: vent og øg pausen ved gentagne fejl
} else if ((await check.json()).revision !== page.revision) {
  await reloadLoadedAccountPages(); // offset 0; kræv ens revision på alle sider
}

Opdatering er ikke øjeblikkelig: polling plus delt cache kan forsinke registrering omtrent ét minut. Revisionskontrollen undersøger det filtrerede Log1-sæt; den bygger ikke på en antaget maksimal forsinkelse. Navne og tilbageførselsmarkeringer kan ændres uafhængigt af revisionen; genindlæs ved behov, og kontrollér altid handlinger på serveren. Ved vedvarende ændringer eller timeout bevares den gamle visning med mulighed for at prøve igen. CSV/XLSX er fortsat eksport i ét database-snapshot.

ExportBankAccount: GET /account/export med samme filtre og format=csv eller format=xlsx. Sideinddeling ignoreres. Hele udvalget hentes i ét snapshot, højst 10000 rækker og 4 mio. tegn i beskrivelser/navne. For store eksporter afvises uden skjult afkortning. Beløb er fortegnede hundrededele, datoer UTC og kolonne-/objekt-id'er stabile. CSV beskytter mod regnearksformler; XLSX bruger tekstceller til ikke-betroede værdier.

const exportResponse = await fetch(`${accountUrl}/export?${filters}&format=xlsx`, {
  headers: {Authorization: `Bearer ${accessToken}`}
});
if (!exportResponse.ok) throw new Error(`Export HTTP ${exportResponse.status}`);
const workbook = await exportResponse.blob();

ReverseBankAccountEntry: POST /account/{transactionKid}/reversal uden body. Kræver desuden adgang til hele banken og Bank/User Write. Kun genkendte negative forbrugsposteringer for beboere med interne fysiske brikker/aktiveringsbrikker kan tilbageføres. API'et låser og kontrollerer originalen, tilføjer det modsatte beløb i igangværende periode, bevarer dokument-/programdata og bestiller saldo-/adgangssynkronisering. Originalen bevares. Betalingsudbyderoverførsler, anonyme/systembrugere, krediteringer, månedsoverførsler, ukendte posteringstyper og allerede tilbageførte poster understøttes ikke. canReverse er vejledende; API'et kontrollerer igen ved handlingen.

// Først efter at din brugerflade/bruger har gennemset og bekræftet netop denne postering.
const reversed = await fetch(`${accountUrl}/${encodeURIComponent(entry.kid)}/reversal`, {
  method: 'POST', headers: {Authorization: `Bearer ${accessToken}`}
});
if (!reversed.ok) {
  const problem = await reversed.json();
  throw new Error(problem.code || `HTTP ${reversed.status}`);
}

Fejl: 400 ugyldigt filter/KID/tidszone; 401 udløbet, ændret eller inaktiv session; 403 manglende fane-/scope-/operationsrettighed; 404 postering mangler; 409 already-reversed; 413 data-limit; 422 reversal-unavailable; 503 lager/skriveforbindelse utilgængelig eller ikke-transaktionelt lager. Framework-valideringsfejl bruger standard ProblemDetails med errors. Gentag aldrig automatisk en finansiel skrivning, heller ikke efter timeout: genindlæs først listen og kontrollér resultatet.

Begrænsninger: kun Log-tabeller; ingen skemaændringer, udbyderrefusioner, bankoverførsler, saldonulstilling eller direkte hardwarelevering. Skrivning kræver InnoDB; bestilt synkronisering bekræfter ikke levering. Højst 4096 filternavne og 100 valutagrupper. Ingen caching eller polling. Portalens udskriv/PDF-funktion udskriver kun den indlæste side; brug eksport til hele udvalget.

SearchBanks søger også i eSetting.SettlementEmails fra Log24. Mail kræver adgang til hele banken (eller tenant); lokationsadgang giver kun søgning på banknavn. Svaret indeholder matchedSetting (Name eller SettlementEmails) og matchedValue med navnet eller den første matchende mailadresse fra den semikolonseparerede liste. Match på navn har forrang. Vis feltet som tekst, aldrig HTML. Eksempel: GET /api/v1/search/banks?q=bogholder%40example.dk. Samme cache, grænse og fejlstatusser gælder; der udføres ikke et nyt SQL-opslag for hvert tastetryk.

Lokationssøgning

GET /api/v1/search/locations?q=0123 (SearchLocations) kører uafhængigt af SearchBanks. Der søges i Log24: Name, Bank (alternativt banknavn), Zip, Address, VismaCustNo og TeltonikaSMS. Kræver manager-bearer, mindst én aktuel Tab, Location Read og adgang til lokationen. RetentionDays gælder. 2–128 tegn, bogstavelige delstrenge uden forskel på store/små bogstaver, højst 50 resultater plus hasMore. Resultater har kind=Location, kanonisk kid, name, icon, zip og matchedSetting/matchedValue. Feltprioritet følger listen ovenfor. Vis som tekst. Brug fx fetch(`${api}/api/v1/search/locations?q=${encodeURIComponent("0123")}`, { headers: { Authorization: `Bearer ${token}` } }) eller almindelig HTTPS GET med Authorization: Bearer.

GET /api/v1/search/location-activation?q=... (SearchLocationActivation) afkoder med Kombine.Flex.Activation og henter navn/ikon fra samme lokationscache. Lokationskoden indeholder bank/lokation, ikke tenant; kun det konfigurerede site bruges. Ugyldige, manglende eller utilgængelige lokationer giver tom liste. Bank- og lokationskoder deler 5 kald pr. 10 minutter og 20 pr. time pr. manager. Alle kodekald tæller; 429 giver Retry-After. Browseren vælger én kodeprovider pr. input. 400: input; 401: log ind igen; 403: adgang; 503: midlertidig fejl. Behold andre delresultater ved fejl. Cache er 60 sekunder og samler samtidige opslag; ingen SQL pr. lokation. Rategrænsen er pr. proces og kræver fælles lager ved flere replikaer.

Alle bank- og lokationssøgninger, inklusive kodeopslag og GetSearchBank, omfatter kun BankId >= 1000. Specialbanker under 1000 returneres ikke.

Beboersøgning

Beboerresultater indeholder også number fra eSetting.Number. Portalen viser Nummer · Navn og udelader tomme eller identiske dele. name bevarer sin eksisterende betydning; number er null for andre resultattyper.

Beboersøgning og beboeraktiveringskoder returnerer også tilhørende banker med isContext=true, når Bank Read er givet. Højst 50 beboerfund plus unikke banker; hasMore gælder beboerne. Bankerne flettes efter kid med øvrige resultater og vises også, når direkte banksøgning er slået fra. Kontaktoplysninger medsendes ikke for kontekstbanker.

En fuld e-mailadresse søges som præcist match uden forskel på store/små bogstaver, via Log7-tekstindekset. Kun navn og TagId understøtter delvis søgning.

GET /api/v1/search/users?q=anna (SearchUsers) kører parallelt med banker/lokationer. Brug Authorization: Bearer. Søger i aktuel Log7: Number, Name, Email, SMS og Tags. Tekst er bogstavelige delstrenge uden forskel på store/små bogstaver. TagId matches som del af et positivt decimalt ID; mellemrum accepteres, hex understøttes ikke. Nummer og e-mail kræver præcist match; navn tillader delstrenge. Navn/TagId har 10 sekunders SQL-timeout og 12 sekunder inklusive ventetid. Tags afkodes med samme JSON/legacy-parser som beboerlisten.

Kræver Users2, User Read og tenant/bankadgang eller beboerens Access/NoAccess-tilknytning til en tilladt lokation. Kun almindelige beboere, BankId >= 1000 og RetentionDays. Højst 50 synlige fund; op til 501 kandidater undersøges. hasMore beder om præcisering, også ved kandidatgrænsen, så brede søgninger kan udelade fund. Resultater: kind=User, kid, name (nummer som reserve), icon og matchedSetting/matchedValue. Ingen øvrige kontaktfelter eller briklister. Feltprioritet: Number, Name, Email, SMS, Tags.

GET /api/v1/search/user-activation?q=... (SearchUserActivation) afkoder med Kombine.Flex.Activation og henter navn/ikon. Koden indeholder bank/user, ikke tenant; kun det konfigurerede site bruges. Alle tre kodeproviders deler 5 kald/10 minutter og 20/time pr. manager. Ugyldige/utilgængelige koder giver tom liste. 400 input; 401 login; 403 adgang; 429 vent Retry-After; 503 midlertidig fejl. Behold øvrige delresultater. Cache 60 sekunder pr. søgetekst/bankområde samler samtidige kald; ingen SQL pr. beboer. Rategrænse fortsat pr. proces.

fetch(`${api}/api/v1/search/users?q=${encodeURIComponent("anna@example.dk")}`, { headers: { Authorization: `Bearer ${token}` } })

Banker i lokationsresultater

SearchLocations og SearchLocationActivation returnerer også de fundne lokationers banker, når manageren har Bank Read. Bankerne har isContext=true og indeholder navn og ikon, ikke kontaktoplysninger. Højst 50 lokationer samt deres unikke banker; hasMore gælder lokationerne. Flet efter kid på tværs af svar, placér banken før dens lokationer, og lad et direkte bankfund erstatte kontekstforklaringen. Bankerne genbruger samme 60-sekunders cache som banksøgning. De medfølger også, hvis kun lokationssøgning er valgt.

for (const item of response.items) {
  if (!byKid.has(item.kid) || !item.isContext) byKid.set(item.kid, item);
}

SearchUserSms

GET /api/v1/search/user-sms?q=51573605 (SearchUserSms) er et separat hurtigt delopslag med samme rettigheder, sletningsregler og bankkontekst som SearchUsers. Det bruger præcise opslag på Log7.Text-indekset, understøtter rå og JSON-kodede værdier og normaliserer mellemrum, bindestreger, parenteser, + og internationalt 00. Otte danske cifre matches både med og uden 45. 8–15 cifre accepteres; ugyldigt format giver tom liste. Svaret flettes efter kid. Egen 60-sekunders cache og samtidighedslås gør, at en langsom delstrengs-/TagId-søgning ikke blokerer SMS-fund. SearchUsers bevarer delstrengs- og TagId-søgning og kan stadig time ud; behold SMS-delresultater ved 503. 400/401/403/503 som SearchUsers; ingen aktiveringskvote bruges.

fetch(`${api}/api/v1/search/user-sms?q=51573605`, { headers: { Authorization: `Bearer ${token}` } })

Forbrug: manglende læserettighed

Forbrugsendpoints bevarer HTTP 403 og code forbidden. Efter kontrol af tab og bankadgang kan svaret desuden indeholde reason: missing-bank-read, missing-location-read, missing-unit-read eller missing-user-read. Vis en lokaliseret forklaring, og behold generel afvisning for ukendte årsager. Ingen forbrugsdata hentes ved afvisning.

{"status":403,"code":"forbidden","reason":"missing-unit-read"}

Reservation: HTTP 403 beholder code forbidden. Efter tab- og bankadgangskontrol kan reason være missing-location-read, missing-unit-read eller missing-user-read. Vis årsagen lokaliseret; ukendte årsager vises som generel afvisning. Ingen reservationsdata læses ved afvisning.

{"status":403,"code":"forbidden","reason":"missing-unit-read"}

GetCurrentManager returnerer Tabs og TabDetails sorteret efter eTab AttributeMetaSortOrder og derefter numerisk tab-ID. Manglende metadata har værdien 0. Klienter kan vise fanerne i den returnerede rækkefølge; rettighederne er uændrede.

SearchBanks, SearchLocations og SearchUsers accepterer komplette kompakte Kids samt ToBankId (166.2000), ToLocationId (166.2000.4) og ToUserId (166.2000-1100). Kid matcher præcist og giver matchedSetting=Kid og den læsbare identitet i matchedValue. Samme site-, tab-, læse-, ressource- og opbevaringsregler gælder. Forkert tenant/type eller manglende adgang giver ingen resultater. Beboeropslaget afgrænses direkte til bank og beboer. Lokationer/beboere medtager fortsat tilhørende bank, når tilladt. Ingen aktiveringsafkodning eller ekstra aktiveringskvote bruges. HTTP-fejl og øvrige tekstsøgninger er uændrede.

GET /api/v1/search/users?q=166.2000-1100

SearchUsers understøtter kidOnly=true til direkte beboer-Kid-opslag, også når almindelig beboersøgning er slået fra. Ikke-Kid-input returnerer tomt svar uden forretningsopslag. Samme rettigheder gælder; knapvalget ændres ikke.

GET /api/v1/search/users?q=166.2000-1100&kidOnly=true

Kid-søgning accepterer også bank, lokation og beboer uden tenant-ID: 2000, 2000.4 og 2000-1100. Kombine.Flex.Kid læser identiteten; manglende tenant udfyldes udelukkende fra API-sitets konfiguration. Angivet tenant bevares og kontrolleres. kidOnly=true understøtter også beboerformatet uden tenant. Alle adgangsregler er uændrede.

GetLocationUnits bevarer HTTP 403. Efter kontrol af faner og ressourceadgang kan reason være missing-location-read eller missing-unit-read. Klienter kan vise en lokaliseret forklaring og bruge generel afvisning ved ukendt årsag. Ingen lokations- eller enhedsdata læses ved afvisningen.

{"status":403,"reason":"missing-unit-read"}

Forbrug returnerer HTTP 503 med code periods-timeout, når periodernes SQL-opslag overskrider 10 sekunder, storage-timeout ved andre SQL-timeouts og storage-text-comparison ved uforenelige tekstsammenligninger. Vis en lokaliseret forklaring; undgå gentagne automatiske forsøg. Rå SQL og databasefejl udleveres ikke. Periodetimeout påvirkes ikke af datofilteret.

Forbrugs periodeliste hentes fra bankens LogA (LocationId=0, UnitId=0, Period>0), højst 100 nyeste perioder. Periode 0 er fortsat valgfri aktuel periode. Ved bankadgang bruges intet Log1-opslag til listen. Ved lokationsbegrænset adgang kontrolleres eksistensen af en Log1-postering i en tilladt lokation pr. periode. Posteringer, summer og revision bruger fortsat Log1 med uændret adgangskontrol.

Saldoer for flere beboere

GetBankUserBalances: POST /api/v1/banks/{bankKid}/users/balances er et rent læsekald. Send 1–50 kanoniske beboer-Kids fra samme bank. Brug de Kids, som GetBankUsers returnerer; numeriske ID’er og læsbare aliaser accepteres ikke. Dubletter returneres én gang i den først angivne rækkefølge.

const response = await fetch(`${apiBase}/api/v1/banks/${bankKid}/users/balances`, {
  method: 'POST',
  headers: { Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({ userKids: visibleResidents.map(resident => resident.kid) })
});
if (!response.ok) throw new Error(`Saldoopslag fejlede: ${response.status}`);
const { items } = await response.json();
// Brug item.balances: én saldo pr. valuta. Læg aldrig forskellige valutaer sammen.

Kræver aktiv manager, eTab.Users2, User Read og ressourceadgang til hele banken. En lokationsrettighed giver ikke adgang til en saldo på tværs af alle lokationer. Rettigheder kontrolleres ved hvert kald. Slettede beboere følger managerens opbevaringsregel. Ukendte eller skjulte beboere returneres som not-found med null, aldrig som saldo nul.

Brug balances, som indeholder en række pr. valuta med currency, currentBalanceMinor, previousBalanceMinor, previousPeriod og previousPeriodIsProvisional. Beløb er fortegnede 64-bit heltal i systemets eksisterende mindste valutaenhed (fx øre for DKK). Ingen valutaomregning udføres, og forskellige valutaer må ikke summeres. Valutakoder kommer fra Log1, afkodes, trimmes og skrives med store bogstaver. Manglende/tom valutakode samles i en særskilt række med currency:null; der gættes ingen valuta. Rækker sorteres efter valutakode.

Beboerrabatten gælder kun DKK. Den tilføjes én gang i DKK-rækken, som også oprettes, hvis beboeren kun har posteringer i andre valutaer. En eksisterende beboer uden posteringer og uden rabat får balances:[]. En ukendt/skjult beboer har også en tom liste, men status not-found.

Valutasaldoerne følger periode 0 og FlexOrms korrektion for forsinket afregning. Bilag grupperes efter DocId, og deres afregningsgrænse afgøres, før beløbene opdeles efter valuta. Tenantens lovbestemte historikgrænser med positive manageroverrides anvendes på korrektionshistorikken. Rabatloftet for en foreløbig periode beregnes kun på DKK-beløbet. Saldoerne er ikke et betalingskrav.

Kun de valgte beboere læses i fælles opslag. Bankindstillinger genbruges inden for kaldet; der bruges ingen delt saldocache. Højst to saldokald kører samtidig pr. API-proces, med højst ét sekund i kø og 20 sekunders frist for databasearbejdet. Korrektionshistorikken er begrænset til 50.000 linjer pr. kald. Beboerlisten i portalen viser først rækkerne og henter derefter den nuværende saldo i grupper på højst 10 beboere, én gruppe ad gangen. Nye rækker ved scroll hentes tilsvarende. Saldoer genindlæses ved filter-, sorterings- og sideskift; listen poller ikke saldoerne. API-grænsen er fortsat 50 beboere pr. kald.

400 betyder ugyldig liste/Kid; 401 udløbet session; 403 manglende adgang; 413 for stor request. 503 med storage-busy, storage-timeout, balance-batch-too-large eller storage-unavailable giver ingen delvise saldoer. Ved stor portion: reducer antallet. Ved travlhed/timeout: vent før et begrænset nyt forsøg, og vis fejl frem for nul. Ingen databaseændringer foretages.

De eksisterende enkeltfelter på beboerresultatet, request-formatet og operation-ID'et bevares af hensyn til kompatibilitet. Enkeltfelterne følger fortsat den gamle beregning på tværs af valutaer og må ikke bruges som en bestemt valutas saldo; brug balances. Begge perioder og alle valutaer hentes i samme Log1-opslag, grupperet pr. beboer, periode og valuta, uden ekstra opslag pr. valuta. Højst 100 valutagrupper tillades pr. beboer.

Alle valutagrupper bruger beboerens samme seneste positive periode, også hvis en valuta senest forekom i en ældre periode. Ingen postering i den valgte periode for en valuta betyder saldo 0 i den periode. Ingen tidligere periode betyder fortsat null. Af hensyn til korrekt visning bør en klient ikke falde tilbage til de gamle blandede beløb, hvis valutalisten mangler.

Sidste periode er beboerens højeste positive periodenummer, også hvis banken er nået længere. En gemt periodes saldo er dens sum uden at tillægge rabat igen. Findes der ingen tidligere periode, er både saldo og periodenummer null, og foreløbig-markeringen er false. Det gælder også en beboer med kun periode 0 eller uden posteringer. En tidligere periode med saldo nul har derimod beløbet 0 og et periodenummer.

Ved forsinket afregning følger den foreløbige tidligere saldo FlexOrms bilagsgruppering og begrænsede rabat. Det gælder også kontantbanker, hvor den aktuelle saldo bevares. previousPeriodIsProvisional=true betyder, at perioden alene er beregnet i hukommelsen; den er ikke gemt eller afregnet. Periodenummeret er bankens højeste Log1-periode plus én og må ikke bruges som et eksisterende afregnings-/download-ID. Det højeste nummer hentes med ét indekseret MAX-opslag for hele portionen, kun når korrektion kan være nødvendig. En foreløbig periode kan have saldo nul.

{
  "kid": "<beboer-Kid fra GetBankUsers>",
  "status": "ok",
  "currentBalanceMinor": -12345,
  "previousBalanceMinor": -25000,
  "previousPeriod": 23,
  "previousPeriodIsProvisional": false,
  "balances": [
    { "currency": "DKK", "currentBalanceMinor": -10000, "previousBalanceMinor": -20000, "previousPeriod": 23, "previousPeriodIsProvisional": false },
    { "currency": "EUR", "currentBalanceMinor": -2345, "previousBalanceMinor": -5000, "previousPeriod": 23, "previousPeriodIsProvisional": false }
  ]
}

GetBankUserBalances returnerer også latestPostingMs2000 (UTC-millisekunder siden 2000-01-01; nul betyder ingen Log1-posteringer) og hasActiveSubscription (aktivt kort-/SEPA-abonnement). Begge felter er null for manglende/skjulte brugere og bruger saldoens rettigheder og databaseøjebliksbillede. Aktiv status følger Orders: CardSubscription eller SepaSubscription, Flags > 0, CR2000 > 0 og ActionCode OK/AUTHORIZE. Det er ikke en betalingsbekræftelse. Ved HTTP 503 vises utilgængelig, og kaldet kan gentages; antag aldrig inaktiv eller nul. Med i alle klientvarianter 0.4.2 til denne betaudgivelse.

Installatører — GetInstallers

GET /api/v1/installers?includeActivationCode=true tilføjer items[].activationCode (brugerens aktiveringskode) og kræver også Installer Create. Standard false udelader koder; manglende Create giver 403 missing-installers-create. Kun autoriserede, synlige poster får koder. Undlad at logge eller cache koderne; adgangskoder returneres aldrig. Aktiveringskodekolonnen kan ikke bruges til sortering.

GetUserCount — GET /api/v1/users/count returnerer {count, iconKid} for aktive beboere inden for UserFinder1, User Read og sitets aktuelle bank-/lokationsadgang. Det omtrentlige ikontal caches i hukommelsen i op til 24 timer pr. scope; genstart eller udløb kan udløse ny optælling. Slettede beboere medtages ikke, og listens filter påvirker ikke tallet. Adgang kontrolleres ved hvert kald. 401/403 betyder afvist adgang; 503 betyder, at optællingen fejlede. Vis ukendt, og prøv senere—ikke nul. Eksempel: curl -fsS "$API/api/v1/users/count" -H "Authorization: Bearer $TOKEN".

Installatører bruger som standard eIcon.worker, når det gemte ikon mangler, er tomt eller ugyldigt. Et gyldigt, eksplicit gemt ikon bevares.

GET /api/v1/installers kræver en aktiv administrator, Installers1 (68), selvstændig PermissionInstaller2.Read og et KID med adgang til hele tenanten. Bank- eller lokationsadgang er ikke tilstrækkelig. Konto, credential-stempel, tab, rettighed og scope kontrolleres på hver side via administratorens tidsbegrænsede snapshot. Ikonet kan ændres med SetInstallerIcon som beskrevet nedenfor.

curl -fsS "$API/api/v1/installers?pageSize=50&sort=lastActive&direction=desc" \
  -H "Authorization: Bearer $TOKEN"

Svaret er {items: [...], nextCursor: ...}. Hver post indeholder kid, name, icon, email, locations, tags, deleted, deletedAt, enabled, lastActiveAt. Alle objekt-id'er er kanoniske KIDs; udled det viste numeriske UserId fra installatørens KID. Lokationer og brikker indeholder kid og enum-navnet state. NoAccess og låste/slettede/historiske brikker bevares: oplysningerne beskriver installatøren og giver ikke den kaldende administrator rettigheder. Lokationsnavne slås ikke op.

Der læses fra sitets A{TenantId:D4}.Log7 med BankId=TenantId og eUserId.Installeres..InstalleresLast (1–999). Administratorens rettighedsindstillinger ligger fortsat på bank nul. Credentials udvælges ikke. Deaktiverede installatører medtages. Slettede poster følger den kaldende administrators RetentionDays; ugyldige/fremtidige sletningstidspunkter skjules. Manglende Deleted betyder nul. Manglende/ugyldig Enabled er null. Alive bruger den aktuelle krumbs MS2000, aldrig Text; manglende, ikke-positive eller ikke-repræsenterbare tider er null. Vis UTC-tider i brugerens tidszone. Læsning ændrer ikke Alive.

  • pageSize: 1–250, standard 50. Følg nextCursor indtil null, også efter tomme sider. Cursoren er bundet til administrator, tenant, filter, sortering og sidestørrelse.
  • filter: bogstavelig deltekst i Navn/E-mail uden forskel på store/små bogstaver, trimmet, højst 128 tegn uden kontroltegn. URL-kod parametre. SQL-jokertegn behandles bogstaveligt.
  • sort: identity (standard), name, email, locations, tags, deleted, enabled, lastActive; direction: asc (standard) eller desc. Sortering sker før sideinddeling. Navn/e-mail sammenlignes ordinalt uden forskel på store/små bogstaver. Lokationer/brikker sammenlignes leksikografisk som sorterede numeriske id/status-lister; deleted efter tidsstempel; enabled efter null/false/true; aktivitet kronologisk. Identiteten afgør ligheder i samme retning.
  • Identity stigende bruger afgrænsede keyset-opslag. Andre sorteringer bruger et tenant-lokalt indeks med højst 999 poster, cachet i 60 sekunder, efterfulgt af nye sidedetaljer med fornyet retentionkontrol. Null står først stigende. Listen er ikke et fastfrosset snapshot; start forfra hvis cursorens anker forsvinder.
let cursor = null;
do {
  const query = new URLSearchParams({pageSize: '50', sort: 'name', direction: 'asc'});
  if (cursor) query.set('cursor', cursor);
  const response = await fetch(`${api}/api/v1/installers?${query}`, {
    headers: {Authorization: `Bearer ${token}`}
  });
  if (!response.ok) throw new Error(`GetInstallers: ${response.status}`);
  const page = await response.json();
  renderInstallers(page.items);
  cursor = page.nextCursor;
} while (cursor);

Fejl: 400 invalid-page/invalid-filter/invalid-sort/invalid-cursor (ret input eller start forfra); 401 (log ind igen); 403 missing-installers-tab/missing-installers-read/missing-tenant-access (få tildelt den manglende adgang); 503 installers-unavailable (databasefejl eller 12-sekunders grænse, prøv igen manuelt). Svar er no-store. Ingen delvise sorterede resultater, automatisk genforsøgsløkke eller skrivehandlinger.

Installatøroplysninger og ikon — GetInstaller / SetInstallerIcon

GET /api/v1/installers/{installerKid} kræver samme læseadgang som listen. Svaret indeholder installer, canEditIcon, iconRevision og availableIcons. KID skal være en kanonisk Installer-KID for sitets tenant, med BankId lig TenantId og et UserId i installatørintervallet. Manglende eller historikskjulte installatører giver 404.

Ikonlisten bruger samme metadata som administratorens ikonvælger: alle eIcon med eIconSubject.Person. Et aktuelt, gyldigt ikon uden Person-metadata vises først, men kan ikke tildeles igen. POST /api/v1/installers/{installerKid}/icon kræver både Installer Read og Write, tab 68 samt adgang til hele tenanten. canEditIcon er kun en visningshjælp; API'et kontrollerer den aktuelle konto, rettigheder og historikadgang igen ved gemning.

const detailResponse = await fetch(`${api}/api/v1/installers/${installerKid}`, {
  headers: {Authorization: `Bearer ${token}`}
});
if (!detailResponse.ok) throw new Error(`GetInstaller: ${detailResponse.status}`);
const detail = await detailResponse.json();
const response = await fetch(`${api}/api/v1/installers/${installerKid}/icon`, {
  method: 'POST',
  headers: {Authorization: `Bearer ${token}`, 'Content-Type': 'application/json'},
  body: JSON.stringify({icon: selectedPersonIcon, expectedRevision: detail.iconRevision})
});
if (!response.ok) throw new Error(`SetInstallerIcon: ${response.status}`);
const confirmed = await response.json();
renderInstallerIcon(confirmed.icon); // Apply only after a successful response.

Gemningen ændrer kun eSetting.Icon og returnerer kid, icon, iconRevision, availableIcons. Et uændret ikon opretter ingen historikpost. Andre felter, lokationsadgang og brikker kan endnu ikke redigeres her.

400: invalid-installer-kid eller invalid-installer-icon. 401 kræver nyt login. 403: missing-installers-tab, missing-installers-read, missing-installers-write eller missing-tenant-access. 404: installer-not-found. 409: installer-icon-conflict; genindlæs før et nyt valg. 503: installer-icon-unavailable. Bevar den hidtidige markering ved fejl; genindlæs efter en usikker netværksfejl, og gentag aldrig en skrivning automatisk.

Administratorer — GetManagers

Listeelementer og GetManager indeholder nu operationPermissions og retentionDays for den viste administrator. De syv områder Managers, Bank, Location, Unit, User, Installer og Service afspejler de aktuelle PermissionManagers2, PermissionBank2, PermissionLocation2, PermissionUnit2, PermissionUser2 PermissionInstaller2 og PermissionService2 i Log7. Formatet med resource, level, flags og seks can*-værdier er det samme som i GetCurrentManager. Bittene er uafhængige: Write giver ikke Read; manglende eller tom indstilling giver Read; ugyldige værdier giver null flags og ingen handlinger. Rettighederne skal altid kombineres med kontostatus, tabs og Kids.

retentionDays er administratorens antal dage med adgang til ellers tilladte slettede poster. Manglende, ugyldige og negative værdier vises som 0. Det planlægger ingen fysisk sletning. Den indloggede kalder bruger fortsat sine egne rettigheder og RetentionDays til at læse administratoren; visningen giver ingen af den viste kontos rettigheder. Data hentes sammen med de øvrige detaljer i samme Log7-opslag; sorteringsindekset er uændret. RetentionDays kan fortsat kun læses; rettighedsændringer er beskrevet nedenfor. Læsekaldets statuskoder er uændrede. Brug fortsat det kanoniske manager-Kid i curl-eksemplet nedenfor.

{"retentionDays":30,"operationPermissions":[{"resource":"User","level":"RenameExtrenatId, Rename","flags":48,"canRead":false,"canWrite":false,"canCreate":false,"canDelete":false,"canRenameExternalId":true,"canRename":true}]}

GetManager: GET /api/v1/managers/{managerKid} henter én administrator med præcis samme felter og adgangskrav som et listeelement. Brug det kanoniske kid fra listen. KID fra en anden tenant eller uden for managerintervallet giver 400 invalid-manager-kid. Manglende administrator eller en post uden for historikadgangen giver 404 manager-not-found; den næste administrator returneres aldrig som erstatning. 401, 403 og 503 håndteres som for listen. Der hentes højst én kandidat fra Log7, og ingen databaseværdier ændres.

MANAGER_KID='indsæt kid fra GetManagers'
curl "$API_BASE/api/v1/managers/$MANAGER_KID" \
  -H "Authorization: Bearer $TOKEN"

Portalen bruger dette opslag til genveje under Administratorer i arbejdsområdet. Genveje er lokale for den aktuelle browsersession, tenant og indloggede manager. De giver aldrig adgang i sig selv; hvert besøg kontrolleres igen af API’et. Administratorvisningen er foreløbig kun læsende.

filter søger i hele administratorlisten efter en del af Name eller Email, uden forskel på store og små bogstaver. Højst 128 tegn; yderste mellemrum fjernes. Tomt filter viser hele den tilladte liste. Der filtreres før portionsgrænsen, og %, _ og ! søges som almindelige tegn. Ved nyt filter skal du starte uden cursor; behold samme filter på efterfølgende sider. Kontroltegn eller for langt filter giver 400 invalid-filter.

curl --get "$API_BASE/api/v1/managers" \
  --data-urlencode "filter=admin@example.test" \
  --data-urlencode "pageSize=50" \
  --data-urlencode "sort=name" --data-urlencode "direction=asc" \
  -H "Authorization: Bearer $TOKEN"

Hvert resultat indeholder også organisation, enabled, deleted og deletedAt. Organisation er en tom streng, når den mangler. Enabled er true ved 1, false ved 0 og null ved manglende/ugyldig værdi. Deleted er true ved et positivt slettetidspunkt; deletedAt er da UTC-tidspunktet, ellers null. De felter alene beviser ikke, at kontoen kan logge ind. Vis ukendt Enabled som ukendt, og anvend fortsat de dokumenterede rettigheder. Slettede poster vises kun inden for kalderens historikadgang.

GET /api/v1/managers?pageSize=50 henter administratorer fra tenantens aktuelle Log7, bank 0, i det inklusive interval eUserId.Managers–eUserId.ManagersLast. Root-, beboer- og servicekonti er uden for intervallet. Resultatet indeholder kid, navn, ikon, e-mail, resourceGrants og tabs. Adgangskoder returneres aldrig.

Kaldet kræver en aktiv managersession, eTab.Managers1 (28), PermissionManagers2 Read og en udtrykkelig Kid-adgang til hele tenanten. Bank- og lokationsadgang er ikke tilstrækkelig. Write giver ikke Read; en manglende permissionsværdi giver Read. API’et kontrollerer alle krav på hver side via det fælles sessionssnapshot, der højst er 60 sekunder gammelt.

curl "$API_BASE/api/v1/managers?pageSize=50" \
  -H "Authorization: Bearer $TOKEN"
let cursor = null;
do {
  const url = new URL('/api/v1/managers', API_BASE);
  url.searchParams.set('pageSize', '50');
  if (cursor) url.searchParams.set('cursor', cursor);
  const response = await fetch(url, { headers: { Authorization: `Bearer ${token}` } });
  if (!response.ok) throw new Error(`GetManagers: ${response.status}`);
  const page = await response.json();
  renderAdministrators(page.items); // Brug textContent, ikke HTML fra data.
  cursor = page.nextCursor;
} while (cursor);

I brugerflader hentes næste side, når brugeren nærmer sig bunden. pageSize er 1–250, standard 50, og holdes uændret. nextCursor er uigennemsigtig og bundet til tenant, manager, portionsstørrelse, filter, sortering og retning. Følg den, selv ved en tom eller kort side, indtil den er null. Start uden cursor ved ændring af parametrene. Siderne er ikke et fastfrosset snapshot af samtidige ændringer; mangler fortsættelsens manager i et opdateret indeks, returneres 400 invalid-cursor, og listen skal hentes forfra.

lastActiveAt i både GetManagers og GetManager er senest registrerede aktivitet: MS2000 på den aktuelle eSetting.Alive-post i Log7, BankId 0, omregnet fra millisekunder siden 1. januar 2000 UTC. Det er postens tidsstempel, ikke dens Text-værdi eller tidspunktet for andre profilændringer. Manglende, ikke-positive eller ikke-repræsenterbare tidsstempler giver null; vis dem som ukendt. Vis gyldige UTC-tider i brugerens lokale tidszone. Læsning ændrer ikke Alive, og værdien beviser ikke, at administratoren er online. sort=lastActive sorterer kronologisk; ukendte tider står først i asc og sidst i desc. Sorteringsindekset kan være op til 60 sekunder gammelt, mens sidens detaljer læses på ny.

curl --get "$API_BASE/api/v1/managers" \
  --data-urlencode "sort=lastActive" --data-urlencode "direction=desc" \
  --data-urlencode "pageSize=50" -H "Authorization: Bearer $TOKEN"

sort=identity|name|email|organisation|kids|deleted|enabled|lastActive og direction=asc|desc sorterer hele det tilladte resultat før portionsgrænsen. Standard i API’et er fortsat identity/asc; portalen bruger name/asc. Navn, e-mail og organisation sammenlignes sprogneutralt uden forskel på store og små bogstaver. Kids sammenlignes som numerisk sorterede bank-/lokationsnumre for det aktuelle site, uafhængigt af rækkefølgen i Log7. Tomme scopes kommer først. Enabled sorteres ukendt, false, true; Deleted efter slettetidspunkt med ikke-slettede først. Manageridentiteten afgør lighed; desc vender hele rækkefølgen.

Valgfri sortering bruger et fælles indeks med syv Log7-indstillinger, cachet i højst 60 sekunder pr. filter. Kun den valgte portions detaljer hentes på ny; adgang og slettehistorik kontrolleres hver gang. Højst 20.000 matchende identiteter må indgå i indekset, og den samlede cache er begrænset til 50.000 vægtede poster (minimum 100 pr. filter). Samtidige indeksopslag samles; en datafejl giver fem sekunders pause før nyt indeksopslag. Kold cache kan kræve et ekstra opslag; den samlede kaldsfrist er 12 sekunder. Identity/asc bruger fortsat ét afgrænset opslag pr. side. Der bruges ingen samlet optælling eller opslag pr. manager.

Deaktiverede administratorer medtages. Slettede administratorer følger kalderens RetentionDays; ugyldig slettestatus skjules. Kids uden tenant opløses til API-sitet, og kun dette sites gyldige grants vises. Tabs følger AttributeMetaSortOrder og derefter nummer. Dette er tildelte sider og scopes, ikke dokumentation for effektive handlingsrettigheder.

400 (invalid-page/invalid-cursor/invalid-sort): start forfra med gyldige parametre. 401: log ind igen. 403: missing-managers-tab, missing-managers-read eller missing-tenant-access forklarer det manglende krav; gentag ikke automatisk. 503 (managers-unavailable): vis fejl og tilbyd et begrænset nyt forsøg med samme cursor. 503 manager-directory-too-large: indsnævr søgefilteret og start forfra; API’et returnerer aldrig en lydløst afkortet sorteret liste. Listekaldet er rent læsende. Rettighedsændringer er beskrevet nedenfor.

Service kommer fra eSetting.PermissionService2 (3017) i aktuel bank 0 Log7 og returneres af GetCurrentManager, GetManagers og GetManager. Kategorien bruger de samme seks uafhængige flag og regler for manglende/ugyldige værdier. Den aktiverer ikke service-login og giver ikke rettigheder til andre områder. Brug SetManagerPermission med resource Service; de eksisterende regler for adgang, samtidige ændringer og fejl gælder.

Ændr administratorrettigheder

SetManagerPermission: POST /api/v1/managers/{managerKid}/permissions/{resource}. Kræver aktiv session, tab 28, adgang til hele tenanten samt både PermissionManagers2 Read og Write. Dit eget rettighedskort kan kun ændres, hvis du er den eneste aktive, ikke-slettede administrator med adgang til hele tenanten. Alle de almindelige adgangskrav gælder stadig. GetManager og listeposter indeholder isCurrentManager og canEditPermissions til visningen; API’et kontrollerer altid den aktuelle konto, credential stamp, tab og rettigheder i skrivetransaktionen.

Undtagelsen undersøger andre administratorers Kids, Enabled og Deleted i tenantens bank 0 Log7. Kun aktiv konto med et eksplicit grant til hele tenanten tæller, uanset tabs og handlingsrettigheder. Adgang til enkelte banker eller lokationer tæller ikke. Kontrollen bruger hverken listefilter, slettehistorik eller cachet optælling. Ved gemning låses de relevante poster og intervaller i samme serialiserbare transaktion som ændringen; en tidligere tilladelse fra visningen er ikke nok. Et læsekald med eget kort og Managers Write kræver ét ekstra afgrænset opslag til denne visningsoplysning. Højst 20.000 andre scopeposter undersøges; kan fraværet af en anden administrator ikke bekræftes inden grænsen eller tidsfristen, afvises kaldet med 503.

Hent først GetManager. Vælg resource: Managers, Installer, Service, Bank, Location, Unit eller User. Send ét flag (Read=1, Write=2, Create=4, Delete=8, RenameExtrenatId=16, Rename=32), den ønskede enabled-værdi og kategoriens senest viste expectedFlags. Feltet er obligatorisk, også når værdien er null ved ugyldige gemte rettigheder. Kun den valgte bit ændres; Write giver ikke automatisk Read.

curl -X POST "$API_BASE/api/v1/managers/$MANAGER_KID/permissions/Bank" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  --data '{"flag":2,"enabled":true,"expectedFlags":1}'

200 returnerer den gemte rettighedskategori. Opdater først fluebenet efter et gyldigt OK-svar. Lagringen følger temaskift: en ny historikpost i bank 0 Log7 med kalderen som aktør; triggeren til aktuel Log7 verificeres før commit. Uændrede værdier giver ingen ny historik. Tenant kommer udelukkende fra serverkonfigurationen. Målets lokale sessionscache ryddes; andre API-instanser kan vise et læsesnapshot i op til 60 sekunder. Rettighedsændringer bruger altid frisk autorisation. Deaktiverede administratorer kan redigeres; slettede følger kalderens RetentionDays.

  • 400: ugyldig KID, kategori, flag eller input.
  • 401: log ind igen.
  • 403: own-manager-permissions, når en anden aktiv administrator også har tenant-adgang, eller manglende tab, Read, Write eller tenant-adgang. Opdater kortets visning; genforsøg ikke automatisk.
  • 404 manager-not-found: administratoren findes ikke eller er skjult af slettehistorikken.
  • 409 permission-conflict: hent og gennemgå de aktuelle rettigheder før et nyt forsøg.
  • 503 manager-permissions-unavailable eller timeout: behold det viste flueben, og vis en fejl. Et mistet svar kan følge efter en gennemført commit; genhent før et manuelt nyt forsøg. Ingen automatisk gentagelse.

Der kræves en konfigureret skriveforbindelse og transaktionelle Log7-tabeller. Kaldet ændrer kun de syv Permission*2-indstillinger; navn, Kids, tabs og øvrige administratorfelter kan ikke ændres her.

Rollen Combine (ikke udgivet)

SetManagerPermissionRole — POST /api/v1/managers/{managerKid}/permission-role. Rollen combine giver alle seks flag (63) i alle ni rettighedskategorier, vælger alle aktuelle tilgængelige faner og tilføjer sitets Tenant-KID for alle banker i én atomisk Log7-transaktion. Send expectedTabsRevision og expectedKidsRevision fra GetManager sammen med alle ni expectedFlags. Kravene om aktiv konto, Managers1, adgang til hele tenant’en, Managers/Tabs/Kids Læs+Skriv og kontrol af egne ændringer gælder fortsat. Snævrere tildelinger på sitet erstattes; fremmede gemte tildelinger bevares uden adgang til andre tenants. Fremtidige faner tilføjes ikke automatisk. Svaret tilføjer resourceGrants, kidsRevision og canEditKids; valider hele svaret før visningen opdateres. Manglende revisioner giver 400 invalid-permission-role; samtidige ændringer giver 409 permission-conflict, tabs-conflict eller kids-conflict; ugyldige gemte værdier giver invalid-stored-tabs eller invalid-stored-kids. Intet gemmes delvist. Ved 409 skal der genlæses og gennemgås; ved 503 eller usikkert svar skal der genlæses før et manuelt genforsøg. Ingen automatiske genforsøg. Kontostatus og profilfelter ændres ikke.

curl "$API_BASE/api/v1/managers/$MANAGER_KID/permission-role" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" --data @- <<JSON
{"role":"combine","expectedTabsRevision":"$TABS_REVISION","expectedKidsRevision":"$KIDS_REVISION","expectedFlags":{"Managers":1,"Installer":1,"Service":1,"Bank":1,"Location":1,"Unit":1,"User":1,"Tabs":1,"Kids":1}}
JSON

Rettighedsmatricen har nu ni kategorier: Managers, Bank, Location, Unit, User, Installer, Service, Tabs og Kids. SetManagerPermissionRole kræver alle ni expectedFlags, inklusive de sidst viste værdier for Tabs og Kids (eksplicit null ved ugyldige værdier). Manglende lagrede værdier giver Læs (1); ugyldige værdier giver ingen rettigheder. Operator, technical-support og tenant-accounting sætter alle ni til 63; accounting og caretaker sætter Tabs/Kids til Læs (1). SetManagerTab og SetManagerKid kræver desuden kalderens Læs og Skriv for henholdsvis Tabs eller Kids, kontrolleret igen i transaktionen. Alle rolleskift kræver både Tabs og Kids Læs/Skriv, kontrolleret igen i transaktionen, fordi alle roller erstatter begge rettighedsmasker. Accounting og combine erstatter fanetildelingerne; de øvrige roller bevarer dem. Alle hidtidige adgangskrav gælder fortsat. Afvisning giver 403 missing-tabs-read, missing-tabs-write, missing-kids-read eller missing-kids-write. Brug canEditTabs og canEditKids uafhængigt af canEditPermissions/canEdit. Felterne styrer redigering af tildelinger, ikke kalderens eksisterende navigation eller KID-afgrænsning; Læs skjuler ikke oversigtsmetadata. Create/Delete/Rename lagres selvstændigt og erstatter ikke Skriv ved tildeling. Eksisterende databaseværdier migreres ikke. Ændringen er endnu ikke udgivet.

Rollen tenant-installer ({Tenant} - Montør) giver alle seks rettigheder (63) i alle ni kategorier, ligesom technical-support. SetManagerPermissionRole kræver alle ni expectedFlags og bevarer valgte faner og KID-tildelinger. Tilføjelsen er endnu ikke udgivet.

De nye roller technical-support ({Tenant} - Teknisk support) og tenant-accounting ({Tenant} - Regnskab) giver indtil videre alle seks rettigheder (63) i alle ni kategorier og bevarer fanerne, ligesom operator. SetManagerPermissionRole accepterer disse værdier med alle ni expectedFlags. Endnu ikke udgivet.

Send alle ni kategorier i expectedFlags, inklusive Service. Manglende eller ekstra kategorier giver 400 invalid-permission-role uden skrivninger.

Foruddefinerede administratorroller

SetManagerPermissionRole sætter en komplet rolle i ét kald: POST /api/v1/managers/{managerKid}/permission-role. De samme krav til aktiv konto, Managers1, Managers Read+Write, tenant-adgang og undtagelsen for eget kort gælder som ved SetManagerPermission. API’et definerer rettighederne; klienten sender rollen, de aktuelt viste forventede værdier og den påkrævede tabrevision.

roleBankLocation, Unit, UserManagers, Installer, Service
accounting (Regnskab)Læs (1)Læs (1)Ingen (0)
caretaker (Varmemester)Læs (1)Læs + skriv (3)Ingen (0)
operator (Operatør)Alle seks flag (63)Alle seks flag (63)Alle seks flag (63)

Ved accounting skal den seneste tabsRevision fra GetManager også sendes som expectedTabsRevision. Eksisterende klienter skal tilføje dette nye felt for at anvende Regnskab. Manglende eller ugyldig revision giver 400 invalid-permission-role. Transaktionen kontrollerer både rettigheder og tabs før første skrivning. En gammel tabrevision giver 409 tabs-conflict; ugyldige gemte tabs giver 409 invalid-stored-tabs. Ingen del af rollen gemmes ved nogen af konflikterne. Genhent og gennemgå før et nyt forsøg. Sæt TABS_REVISION nedenfor til den læste revision.

Hent først GetManager, og kopier alle ni operationPermissions[].flags til expectedFlags med resource som nøgle. Medtag eksplicit null ved en ugyldig gemt værdi. Eksemplet forudsætter, at alle aktuelt viste værdier er 1:

curl -X POST "$API_BASE/api/v1/managers/$MANAGER_KID/permission-role" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  --data "{\"role\":\"accounting\",\"expectedTabsRevision\":\"$TABS_REVISION\",\"expectedFlags\":{\"Managers\":1,\"Installer\":1,\"Service\":1,\"Bank\":1,\"Location\":1,\"Unit\":1,\"User\":1,\"Tabs\":1,\"Kids\":1}}"

Kaldet erstatter alle ni kategorier, også ved at fjerne ekstra flueben, i én serialiserbar Log7-transaktion. Alle forventede værdier kontrolleres før første skrivning; hver historikpost angiver kalderen, og triggeren til aktuel Log7 verificeres før commit. Uændrede kategorier giver ingen ny historik. Svaret indeholder role, alle ni operationPermissions, tabs, tabsRevision, canEditTabs og canEditPermissions. Kontroller hele OK-svaret, før rettigheder og tabs opdateres samlet. Brug svarets tabsRevision ved næste ændring. Regnskab eller Varmemester anvendt på eget kort fjerner Managers-adgang og returnerer canEditPermissions=false; lås derefter redigeringen. Operatør sætter alle 54 flueben, også for Administratorer, Installatører og Services, og bevarer Managers-adgang. Regnskab erstatter hele tabvalget med præcis Users2 (53), Account2 (2) og Settlement2 (40) og fjerner alle øvrige ID-numre, også ukendte gemte ID-numre. De øvrige roller undtagen combine bevarer tabs og Kids. Kontostatus og profilfelter bevares. Rollen er et engangsvalg af rettigheder, ikke et gemt medlemskab eller en løbende automatisk regel.

400 invalid-permission-role betyder ukendt rolle eller manglende, ekstra eller ugyldige forventede værdier; ugyldig tenant/KID bruger invalid-manager-kid. 401 kræver login; 403 bruger de samme adgangskoder som enkeltændringer, herunder own-manager-permissions; 404 er manager-not-found. Uoverensstemmelse i en kategori giver 409 permission-conflict uden delvis gemning: genhent og gennemgå. Ved 503 manager-permissions-unavailable eller usikkert timeout beholdes visningen; genhent før et manuelt nyt forsøg. Brug aldrig automatisk gentagelse eller en række enkeltkald som erstatning for det samlede rollekald. Den eksisterende frist på 12 sekunder og grænsen for kontrol af eneste manager gælder fortsat.

Ændr administratorens tabs

GetManager returnerer desuden availableTabs: alle kendte numeriske eTabs undtagen None og Length, sorteret efter AttributeMetaSortOrder og derefter ID, uden dubletter fra aliaser. Hver post indeholder id, enum-afledt name og iconKid. Kataloget medtager også sider, der endnu ikke er implementeret. Marker de ID’er, som findes i tabs. Listekaldet udelader kataloget; begge læsekald returnerer canEditTabs og den uigennemsigtige tabsRevision.

SetManagerTab: POST /api/v1/managers/{managerKid}/tabs/{tabId}. Kræver aktiv konto, Managers1 (28), både Managers Read og Write samt adgang til hele tenanten. Eget kort følger samme undtagelse for den eneste aktive manager med tenant-adgang som rettighedsændringer. Visningsoplysninger giver aldrig i sig selv lov til at skrive: API’et genkontrollerer låst, aktuel konto, tab, scope og rettigheder i transaktionen.

Hent først administratoren, kopier den præcise tabsRevision, og vælg et ID fra availableTabs. Eksemplet giver adgang til tab 8:

TABS_REVISION='kopier tabsRevision fra GetManager'
curl -X POST "$API_BASE/api/v1/managers/$MANAGER_KID/tabs/8" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  --data "{\"enabled\":true,\"expectedRevision\":\"$TABS_REVISION\"}"

200 returnerer gemte tabs, næste tabsRevision, canEditTabs og canEditPermissions. Opdater først markeringen efter et valideret OK-svar, og brug svarets revision ved næste klik. Fjerner du din egen Managers1-adgang, returneres begge redigeringstilladelser som false: lås hele redigeringen. Tabadgang er kun ét adgangskrav; den giver ikke Kids, kontoadgang eller handlingsrettigheder.

Kun den valgte tab ændres. Ukendte heltals-ID’er og øvrige gemte poster bevares. Gemningen bruger den fælles serialiserbare Log7-historiktransaktion i bank 0 med kalderen som aktør og verificeret trigger til aktuel Log7; uændret værdi giver ingen historikpost. Kalderens og målets sessionscache ryddes også ved usikkert udfald. Andre instanser kan vise læsesnapshots i op til 60 sekunder; alle skrivninger genkontrollerer aktuel adgang. Fristen på 12 sekunder og grænsen for kontrol af eneste manager gælder stadig.

  • 400 invalid-manager-kid eller invalid-tab-change: ugyldig/fremmed KID, utilgængelig tab, manglende boolesk værdi eller ugyldig revision.
  • 401: log ind igen. 403: samme adgangskoder som rettighedsændringer, herunder own-manager-permissions. 404: manager-not-found.
  • 409 tabs-conflict: genhent og gennemgå det seneste valg. invalid-stored-tabs: gemt JSON er ikke en liste af heltal; ingen data blev overskrevet.
  • 503 manager-tabs-unavailable, timeout eller mistet svar: behold markeringen, og genhent før et manuelt nyt forsøg. Gentag aldrig skrivninger automatisk.

Administratorens Kids

GetManager og GetManagers returnerer resourceGrants, kidsRevision og canEditKids. Brug SearchBanks og SearchLocations til bank- og lokationsvalg; deres normale læse-, scope- og historikregler gælder fortsat. Beboere og enheder er ikke gyldige adgangsvalg.

SetManagerKid: POST /api/v1/managers/{managerKid}/kids. Kræver aktiv manager, Managers1 (28), både Managers Read og Write samt adgang til hele tenanten. Eget kort kan kun ændres af den eneste aktive manager med tenant-adgang. API’et genkontrollerer de aktuelle Log7-oplysninger i transaktionen.

  • En bank-KID giver adgang til hele banken og erstatter bankens enkelte lokationer.
  • En lokations-KID giver kun adgang til netop den lokation.
  • Site-tenantens KID betyder Alle banker og erstatter de enkelte bank- og lokationsvalg. Fjernes den igen, gendannes de tidligere valg ikke.
  • enabled: false fjerner kun det præcise valg. Tilføjelse af allerede dækket adgang ændrer intet.
curl "$API/api/v1/managers/$MANAGER_KID/kids" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" --data @- <<JSON
{"resourceKid":"$RESOURCE_KID","enabled":true,"expectedRevision":"$KIDS_REVISION"}
JSON

Et bekræftet 200-svar indeholder de samlede resourceGrants, ny kidsRevision og canEditKids. Opdater først markeringer derefter. Fjernes egen tenant-adgang, er canEditKids=false; lås alle redigeringsfelter. KIDs skal være kanoniske og tilhøre sitets tenant; klienten kan ikke vælge tenant. Tilføjede banker/lokationer skal findes som Settings-scope i Log24. Fjernelse af gamle valg er tilladt.

Kun Kids ændres via den eksisterende, auditerede bank-zero Log7-transaktion. Tabs og øvrige rettigheder bevares. Gemte site-relative valg tolkes på dette site; fremmede gemte valg bevares uden at give adgang her. Højst 1.000 Kids ved udvidelse; fjernelse og samling under en hel bank er fortsat mulig. Sessions- og listecache ugyldiggøres; læsecache på andre API-instanser kan leve op til 60 sekunder. Alle skrivninger genautoriseres.

  • 400 invalid-manager-kid/invalid-kid-change: ugyldig/fremmed identitet, forkert scope eller manglende boolean/revision.
  • 401: log ind. 403: sædvanlige rettighedskoder, herunder own-manager-permissions. 404 manager-not-found inkluderer historikbegrænsning; resource-not-found betyder, at den valgte bank/lokation ikke findes.
  • 409 kids-conflict: genhent og gennemgå. invalid-stored-kids: ugyldige gemte Kids overskrives ikke. kids-limit: for mange valg.
  • 503 manager-kids-unavailable, timeout eller mistet svar: behold visningen, genhent før manuelt nyt forsøg, og gentag aldrig automatisk. Skrivningen har en frist på 12 sekunder.

Kontrollen af slettede poster ved skrivning og det bekræftede canEditProfile bruger samme databaseur som slettetidspunktet. En administrator med adgang kan derfor straks gendanne en anden manager inden for sin gældende retention, selv om API- og databaseuret afviger lidt. Send Deleted=false med den seneste profileRevision, og skift først knappen efter et gyldigt 200-svar.

Ændr administratoroplysninger

SetManagerProfileField: POST /api/v1/managers/{managerKid}/profile/{field}. Kræver aktiv manager, Managers1 (28), Managers Read og Write samt adgang til hele tenanten. Eget kort følger samme undtagelse for eneste aktive manager med tenant-adgang som rettigheder og tabs. De syv felter kræver Managers Write uden yderligere Delete- eller Rename-flag. Hver skrivning genkontrollerer låst, aktuel autorisation og målets synlighed.

Hent først GetManager. canEditProfile er kun en visningsoplysning; kopier profileRevision præcist. Brug følgende feltnavne med store/små bogstaver som vist:

fieldJSON-værdiGemt betydning
Name / OrganisationTekst, højst 200 tegn, uden kontroltegnUnicode/mellemrum bevares; tom tekst rydder feltet.
Enabledtrue / false1 / 0
Deletedtrue / falseServerens sletningstid i MS2000 / 0
RetentionDaysHeltal 0–2147483647Dages synlighed for slettede poster, man ellers har adgang til.
IconPræcist eIcon-navn som tekstKun ikoner med eIconSubject.Person i metadata kan vælges på ny.
EmailÉn ikke-tom e-mailadresse, højst 254 tegnLoginadresse. Uden mellemrum, kontroltegn eller visningsnavn. Store/små bogstaver bevares.

E-mailformat valideres før ethvert databaseopslag: almindelig ASCII-adresse med højst 64 tegn før @ og et fuldt domæne som firma.dk. Domænets dele er 1–63 bogstaver, cifre eller bindestreger; ingen indledende/afsluttende bindestreg. Internationale domæner angives som punycode. Tom adresse, navn@firma, gentagne/indledende/afsluttende punktummer, mellemrum, citeret lokal del og adresse-litteraler afvises med 400 invalid-profile-change. Den gemte adresse og revision bevares. Portalen viser en feltfejl uden at sende et gemmekald; en rettet adresse gemmes ved næste blur. Eksisterende ældre e-mailværdier omskrives ikke ved andre profilændringer. Kontrollen undersøger format, ikke om postkassen findes.

E-mailen gemmes med samme rettigheder og revisionskontrol. Svaret indeholder den bekræftede email. Adgangskoden og eksisterende sessioner bevares. Adressen verificeres ikke, og der sendes ingen mail eller invitation. Login følger fortsat de eksisterende regler for dubletter: e-mail og adgangskode skal tilsammen identificere én konto. Portalens Invitér-knap er foreløbig deaktiveret og har ingen API-handling.

PROFILE_REVISION='kopier seneste profileRevision fra GetManager'
curl "$API_BASE/api/v1/managers/$MANAGER_KID/profile/Email" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" --data @- <<JSON
{"value":"manager@example.test","expectedRevision":"$PROFILE_REVISION"}
JSON

GetManager.availableIcons indeholder alle personikoner i numerisk enum-rækkefølge. Markér iconKid. Er det nuværende ikon uden for personkataloget, står det først, men det kan ikke vælges som en ny tildeling. Manglende/usikre filnavne vises som user uden at ændre databasen. Kataloget sendes ikke med den paginerede liste. Efter gemning returneres iconKid og opdaterede availableIcons. Markeringen ændres først efter et gyldigt 200-svar; behold den ved fejl. Ikonændringer deler revision og rettighedskontrol med de øvrige profilfelter.

PROFILE_REVISION='kopier seneste profileRevision fra GetManager'
curl "$API_BASE/api/v1/managers/$MANAGER_KID/profile/Icon" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" --data @- <<JSON
{"value":"angel","expectedRevision":"$PROFILE_REVISION"}
JSON
PROFILE_REVISION='kopier profileRevision fra GetManager'
curl -X POST "$API_BASE/api/v1/managers/$MANAGER_KID/profile/Name" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  --data "{\"value\":\"Nyt navn\",\"expectedRevision\":\"$PROFILE_REVISION\"}"

Ved sletning sendes {"value":true,"expectedRevision":"..."} til /profile/Deleted. Gendan med false og seneste revision. Klienten sender aldrig selve sletningstidspunktet. Databaseserveren sætter millisekunder siden 2000-01-01 UTC; dette er ikke Unix-tid. Gentaget true bevarer det oprindelige sletningstidspunkt. Gendannelse gemmer 0, aldrig en boolesk tekst. Kun det valgte felt ændres.

200 returnerer field, de syv profilværdier, deletedMs2000 (0 eller tidspunkt), deletedAt (UTC eller null), en ny profileRevision og canEditProfile. Valider hele svaret, før visningen ændres. False i redigeringstilladelsen låser profil, rettigheder, roller og tabs. Det sker ved deaktivering/sletning af egen konto eller når sletning af en anden administrator skjuler denne efter din RetentionDays. Gendannelse kræver en kalder, hvis eksisterende historikadgang omfatter den slettede administrator; skrivningen omgår aldrig historikreglen.

Den fælles serialiserbare Log7-skrivning i bank 0 tilføjer kun ændrede indstillinger, angiver kalderen og verificerer triggeren til aktuel værdi før commit. Ingen legacy-tabeller eller adgangskoder ændres. Sessions- og listecache på instansen ryddes også ved usikkert udfald; andre instanser kan beholde læsesnapshots i op til 60 sekunder. Deaktiverede/slettede konti mister adgang ved næste friske autorisationskontrol; skrivninger genkontrollerer altid straks. Fristen på 12 sekunder og grænsen for kontrol af eneste manager gælder fortsat.

  • 400 invalid-manager-kid eller invalid-profile-change: forkert identitet, ukendt felt, ugyldig type/interval eller manglende revision/værdi.
  • 401: log ind. 403: sædvanlig afvisning af rettigheder/eget kort. 404 manager-not-found: findes ikke eller er uden for kalderens historik.
  • 409 profile-conflict: et af de syv rå profilfelter er ændret; genhent og gennemgå.
  • 503 manager-profile-unavailable eller timeout: behold seneste tilstand og ugemte indtastninger. Et mistet svar kan følge efter commit; genhent før et manuelt nyt forsøg. Ingen automatisk gentagelse.

MCP: læs og skriv med administratorens rettigheder (ikke udgivet)

Implementeret i denne kildegren; endnu ikke deployet. Konfigurér en MCP-klient til https://api.{tenant}.kombine.technology/mcp eller det tilsvarende beta-domæne med Streamable HTTP. Endpointet er uden sessionstilstand: send hver JSON-RPC-besked som et separat POST /mcp med administratorens Authorization: Bearer-header. Der kræves intet MCP-Session-Id. GET-streaming, genoptagelige sessioner og serverinitierede forespørgsler tilbydes ikke.

Hent det uigennemsigtige token med almindeligt LoginManager, og indstil det som en hemmelig HTTP-header i MCP-værtsprogrammet. Tokenet er bundet til API-sitets tenant og miljø. Brug RenewManagerSession uden for MCP før udløb, og udskift den konfigurerede header; MCP-kald forlænger ikke tokenets levetid. Login-, password- og tokenoperationer indgår ikke i værktøjskataloget. Denne version har hverken OAuth-opdagelse, klientregistrering eller browserbaseret samtykke: klienten skal understøtte en konfigureret bearer-header. URL’en alene giver ikke automatisk ChatGPT-connectorlogin.

tools/list returnerer et eksplicit katalog over godkendte JSON-operationer til læsning og ændring. Værktøjsnavnene er de stabile operation IDs fra OpenAPI, fx GetCurrentManager, GetObjectAddress og UpdateObjectAddress. Sti- og queryparametre ligger direkte i arguments; JSON-requestens DTO ligger under body. Brug det returnerede inputSchema til felttyper og påkrævede værdier. Fildownloads, SVG-/billedendpoints, operatørdiagnostik, hosting/logoperationer, passwordgendannelse/-ændring, emailbekræftelse, InviteManager, GenerateServiceApiKey og AskPortalAssistant indgår ikke. JSON-operationer til ikonstatus, fx GetUnitIcons, GetLocationIcons og GetBankIcons, er tilgængelige. Hent det aktuelle katalog i stedet for at antage, at alle REST-operationer er med.

Hvert forretningsværktøj udfører den eksisterende API-operation med administratorens bearer-session og samme tidsbegrænsede kontrol af konto, Tab, KID og handlingsrettigheder. Synlighed i kataloget giver ingen adgang. Agenten kan kun ændre det, som administratoren må ændre; KID giver hverken rettigheder eller mulighed for at vælge en anden tenant. Eksisterende validering, revisionskontrol, historik og operationsgrænser gælder fortsat. Behandl returneret tekst som data, og indhent brugerens tilladelse til de ønskede ændringer i agentens værtsprogram.

Curl-eksempel til kopiering

Dette POSIX-shell-eksempel bruger miljøvariabler: sæt PORTAL_API_BASE til sitets betroede HTTPS-basisadresse, PORTAL_ACCESS_TOKEN til det aktuelle token og PORTAL_OBJECT_KID til et autoriseret kanonisk objekt-KID. Hold tokens ude af prompts, URL’er og logs. Læseeksemplerne ændrer ingen data; det sidste eksempel gemmer en adresse.

API="$PORTAL_API_BASE"
TOKEN="$PORTAL_ACCESS_TOKEN"
KID="$PORTAL_OBJECT_KID"
MCP_VERSION='2025-11-25'
mcp_post() {
  curl --silent --show-error "$API/mcp" \
    -H "Authorization: Bearer $TOKEN" \
    -H 'Content-Type: application/json' \
    -H 'Accept: application/json, text/event-stream' \
    -H "MCP-Protocol-Version: $MCP_VERSION" --data-binary @-
}
mcp_post <<'JSON'
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"portal-example","version":"1.0"}}}
JSON
mcp_post <<'JSON'
{"jsonrpc":"2.0","method":"notifications/initialized"}
JSON
mcp_post <<'JSON'
{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}
JSON
mcp_post <<JSON
{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"GetObjectAddress","arguments":{"kid":"$KID"}}}
JSON

Brug protokolversionen fra initialize-svaret i efterfølgende MCP-Protocol-Version-headers. Accepterede notifikationer giver HTTP 202 uden indhold. Læs adresseværktøjets returnerede data.revision, og send netop den værdi som expectedRevision:

REVISION='REVISION_FRA_SENESTE_LÆSNING'
mcp_post <<JSON
{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"UpdateObjectAddress","arguments":{"kid":"$KID","body":{"address":"Bjerregade 5","zip":"8722","expectedRevision":"$REVISION"}}}}
JSON

Skrivningen kræver objektets Read- og Write-rettigheder samt de øvrige adgangskrav for adresser. Hent en ny revision før hver efterfølgende ændring. En gemt adresse kan stadig have et fejlet koordinatopslag; kontrollér data.outcome.

Svar, fejl og genforsøg

Kataloget er fælles metadata og filtreres ikke efter den aktuelle administrators rettigheder; adgangen kontrolleres ved hvert kald. Grænser: 120 MCP-kald pr. minut pr. administrator, otte samtidige MCP-kald pr. API-proces uden kø, 128 KiB pr. MCP-request og 256 KiB pr. forretningssvar. Den underliggende operations lavere requestgrænser og øvrige begrænsninger gælder fortsat. En grænse er et maksimum, ikke en anbefalet pollingfrekvens.

Et forretningssvar indeholder {status, data, truncated}: status er den underliggende API-operations HTTP-status, og data er dens JSON-svar eller ProblemDetails. Kontrollér både MCP-værktøjssvarets isError og dette objekt; vellykket MCP-transport betyder ikke, at forretningsoperationen lykkedes. HTTP-fejl fra operationen og tilbageholdte for store svar sætter isError=true. truncated=true betyder, at det fulde svar er tilbageholdt; indsnævr søgningen eller brug mindre sider. Almindeligt hasMore kræver stadig sideopdeling.

HTTP 401 på selve MCP-kaldet betyder manglende gyldig bearer-session: forny før udløb, eller log ind igen. En forretningsfejl med 403 står i værktøjssvaret og betyder manglende rettighed. Forretningsfejl 409 kræver en ny læsning og gennemgang af den samtidige ændring. HTTP 429 betyder begrænsning; følg Retry-After, hvis den findes, og sænk frekvensen. Ugyldige værktøjsnavne eller argumenter giver MCP-fejl. Gentag aldrig skrivning automatisk efter timeout, annullering, mistet svar eller 503: data kan allerede være gemt. Læs tilbage først, og vurder derefter, om en ny skrivning er nødvendig. Annullering ruller ikke en allerede gemt operation tilbage.

Klienternes kildekode, tests og generatorer er offentligt tilgængelige på GitHub.

Changelog for API-kontrakten · Se ændringer i request/response, før du opdaterer din integration.

.NET-klient uden Kombine-afhængigheder

Download NuGet-pakke (.nupkg) · 0.13.0 NuGet.org NuGet.org: produktionsudgaver

Kombine.Flex.Portal.Client giver typede C#-metoder til alle 178 offentlige integrationsoperationer, inklusive offentlige kald uden login. Samme NuGet-pakke indeholder .NET Standard 2.0, .NET 8 og .NET 10. Den har ingen afhængigheder til andre Kombine-pakker eller projekter og indeholder ikke databaseadgang, KID-beregning eller forretningslogik. Udgivelse på NuGet.org styres særskilt af det offentlige SDK-repository. Denne beta tilbyder den gennemgåede 0.13.0-pakke direkte; brug lokal installation nedenfor, hvis versionen endnu ikke findes på NuGet.org.

Platforme og afhængigheder

Kundens platformKlientudgave valgt af NuGetAfhængigheder
.NET Framework 4.7.2 / 4.8 / 4.8.1.NET Standard 2.0Microsoft System.Text.Json 10.0.12 og dens Microsoft-afhængigheder
.NET 8 / 9.NET 8Ingen ekstra pakker
.NET 10.NET 10Ingen ekstra pakker

Kontrollerne dækker builds mod Framework 4.7.2 og 4.8 med kørsel på installeret Framework 4.8.1 samt kørsel på .NET 8, 9 og 10. En oprindelig 4.7.2-installation og ældre .NET Core/.NET-versioner er ikke afprøvet. Microsoft anbefaler Framework 4.7.2 eller nyere til .NET Standard. Til kunder uden NuGet findes fortsat de separate DLL-pakker til 2.0, 4.5 og CE/Mobile.

Vælg først tenantens API-URL, derefter e-mail og adgangskode. Klienten bruger de eksisterende API-kald; API'ets funktionalitet og rettighedskontrol er uændret.

Installation

Udgivelse på NuGet.org styres særskilt af det offentlige SDK-repository. Denne beta tilbyder den gennemgåede 0.13.0-pakke direkte; brug lokal installation nedenfor, hvis versionen endnu ikke findes på NuGet.org.

dotnet add package Kombine.Flex.Portal.Client --version 0.4.2 --source https://api.nuget.org/v3/index.json

Læg den leverede Kombine.Flex.Portal.Client.0.13.0.nupkg i mappen packages ved dit projekt, og installér fra den lokale mappe:

dotnet nuget add source ./packages --name flex-local
dotnet add package Kombine.Flex.Portal.Client --version 0.13.0

Behold nuget.org eller jeres godkendte spejl som pakkekilde til Microsoft-afhængigheder. I Visual Studio kan samme lokale mappe tilføjes som NuGet-kilde. Framework-programmer bør have automatiske binding redirects aktiveret ved versionskonflikter; ved brug af egen HttpClient tilføjes også frameworkreferencen System.Net.Http.

Login og tabs

using Kombine.Flex.Portal.Client;
// I en async-metode; tenantApiUrl slutter med /
using (var api = new PortalApiClient(new Uri(tenantApiUrl)))
{
    await api.LoginAsync(email, password, cancellationToken);
    var manager = await api.GetCurrentManagerAsync(cancellationToken);
    if (manager.TabDetails != null)
        foreach (var tab in manager.TabDetails)
            Console.WriteLine($"{tab.Id}: {tab.Name}");
    api.ClearSession();
}

Metoderne følger OperationIdAsync og har typede request/response-modeller samt IntelliSense. Eksempler: GetBankUsersAsync, GetBankUserBalancesAsync og GetInstallersAsync. Canoniske KIDs og cursors skal sendes uændret. Klienten giver ingen ekstra rettigheder og udfører ikke automatiske retries.

PortalApiException.StatusCode og Code beskriver API-fejl: 401 kræver nyt login, 403 er manglende adgang, 409 kræver genindlæsning af revision, 429 kræver respekt for Retry-After, og 503 betyder midlertidigt utilgængelig. Netværksfejl er HttpRequestException. Log aldrig adgangskoder, tokens eller hele svar ukritisk.

ClearSession fjerner klientens token; pakkede klienter fornyer ikke automatisk, og API'et har ikke server-side logout. Ved tenant-skift oprettes en ny klient og et separat login. De separate konsol- og Windows-eksempler bruger udelukkende denne klient til API-adgang. Windows-eksemplet viser de tilladte tabs og gemmer kun token/udløb i Windows Credential Locker.

Changelog for API-kontrakten · Se ændringer i request/response, før du opdaterer din integration.

Python

Python 3.11+ · pip / wheel · 151 API-kald

kombine-flex-portal-client er en selvstændig pakke til Python 3.11+. Den bruger kun standardbiblioteket og kræver hverken .NET, NuGet eller andre Kombine-pakker.

Installation

Installér den leverede wheel-fil lokalt. Pakken er endnu ikke publiceret på PyPI.

python -m pip install ./kombine_flex_portal_client-0.13.0-py3-none-any.whl

Login og tabs

Vælg først tenantens API-URL, afsluttet med /, og derefter e-mail og adgangskode. Opret en ny klient ved tenantskift. API’et kontrollerer konto, tabs, Kids og handlingsrettigheder ved hvert kald. Se adgangsreglerne og fejlkoderne.

# Python: tenant_api_url, email og password kommer fra din loginformular.
from kombine_flex_portal import PortalClient, PortalApiError
with PortalClient(tenant_api_url) as api:
    try:
        api.login(email, password)
        manager = api.get_current_manager()
        for tab in manager.get("tabDetails") or []:
            print(tab.get("id"), tab.get("name"))
    except PortalApiError as error:
        print(error.status, error.code)

login() gemmer token i hukommelsen. Det rå login_manager(body)-kald gemmer ikke sessionen. clear_session() og close() fjerner det lokale token; fornyelse kræver et eksplicit RenewManagerSession-kald; individuel token-tilbagekaldelse findes ikke. Offentlige kald sender aldrig token. Adgangskoden gemmes ikke af klienten.

Kald og datatyper

Alle 178 operationer har metoder i snake_case, fx get_bank_user_balances og get_installers. Request- og response-modeller er TypedDict i kombine_flex_portal.models. JSON-feltnavne, KIDs, cursors og revisioner bevares. Int64 er eksakte Python-heltal; ISO-datoer er strenge. Null er ikke nul.

page = api.get_bank_users(bank_kid, page_size=25, sort="number", direction="asc")
balances = api.get_bank_user_balances(bank_kid, {"userKids": user_kids})

Downloads, fejl og timeout

with api.export_bank_users(bank_kid) as download:
    with open("residents.csv", "wb") as target:
        for chunk in download.iter_bytes():
            target.write(chunk)

PortalApiError indeholder status, code, headers og response. Sidstnævnte kan indeholde persondata. Netværksfejl bruger URLError/OSError/TimeoutError; ugyldig eller for stor JSON giver PortalProtocolError. Ingen automatiske retries eller pagination.

Klienten er synkron. Standard timeout=30 er socket-I/O-timeout, ikke en samlet deadline for et langt download. JSON er begrænset til 16 MiB som standard. Brug en arbejdstråd i async-/GUI-applikationer. HTTPS bruger normal certifikatkontrol; redirects afvises.

Changelog for API-kontrakten · Se ændringer i request/response, før du opdaterer din integration.

JavaScript / TypeScript

Node.js 22+ / moderne browsere · npm / ESM · 151 API-kald

@kombine/flex-portal-client er én ESM-pakke med JavaScript og TypeScript-typer. Den kan bruges i Node.js 22+ og moderne browsere med fetch, BigInt og Web Streams. Der er ingen runtimeafhængigheder eller andre Kombine-pakker.

Installation

Installér den leverede tarball. Pakken er endnu ikke publiceret på npm. TypeScript er valgfrit; pakken indeholder allerede kompileret JavaScript.

npm install ./kombine-flex-portal-client-0.13.0.tgz

Login og tabs

Vælg først tenantens API-URL, afsluttet med /, og derefter e-mail og adgangskode. Opret en ny klient ved tenantskift. API’et kontrollerer konto, tabs, Kids og handlingsrettigheder ved hvert kald. Se adgangsreglerne og fejlkoderne.

import { PortalClient, PortalApiError } from '@kombine/flex-portal-client';
const api = new PortalClient(tenantApiUrl);
try {
  await api.login(email, password);
  const manager = await api.getCurrentManager();
  for (const tab of manager.tabDetails ?? []) console.log(tab.id, tab.name);
} catch (error) {
  if (error instanceof PortalApiError) console.error(error.status, error.code);
  else console.error('API-kaldet kunne ikke gennemføres.');
} finally { api.clearSession(); }

login() gemmer token i hukommelsen. Det rå loginManager(body)-kald gemmer ikke sessionen. clearSession() og close() fjerner det lokale token; fornyelse kræver et eksplicit RenewManagerSession-kald; individuel token-tilbagekaldelse findes ikke. Offentlige kald sender aldrig token. Adgangskoden gemmes ikke af klienten.

Kald og datatyper

Alle 178 operationer har metoder i camelCase og eksporterede TypeScript-interfaces. Int64-felter bruger bigint, også expiresIn, så store saldobeløb ikke afrundes. Int32 er number; datoer er ISO-strenge. KIDs, cursors, revisioner og null bevares.

Brug value.toString() til at vise bigint. JavaScripts JSON.stringify kan ikke direkte håndtere bigint; ved eget JSON-output skal du eksplicit vælge fx strenge. Klientens egen serializer sender int64 korrekt som JSON-tal. Konvertér ikke store beløb ukritisk til Number.

const page = await api.getBankUsers(bankKid, { pageSize: 25, sort: 'number', direction: 'asc' });
const balances = await api.getBankUserBalances(bankKid, { userKids });

Downloads, fejl og timeout

PortalApiError indeholder status, code, headers og response; hele svar kan indeholde persondata. Netværks-/CORS-fejl bruger fetch-fejl, timeout/afbrydelse typisk TimeoutError/AbortError, og ugyldig JSON giver PortalProtocolError. Ingen automatiske retries eller pagination.

Standard timeoutMs: 30_000 gælder hele svaret, også stream-læsning. Hvert kald accepterer AbortSignal. JSON har en standardgrænse på 16 MiB; fil-downloads streames og skal lukkes. HTTPS bruger normal certifikatkontrol, og redirects afvises.

Browser / CORS

Brug en bundler, eller læg hele pakkens dist-mappe på din webserver og importér ./dist/index.js fra et <script type="module">. API’ets Cors:AllowedOrigins skal tillade sidens præcise origin. Kun CORS-eksponerede headers kan læses i browseren. Node.js kræver ikke browser-CORS.

Se CORS-opsætning og den lille JavaScript-demo. Demoens portal-api.mjs er et separat, mindre eksempel med sin egen metodeoverflade.

Changelog for API-kontrakten · Se ændringer i request/response, før du opdaterer din integration.

.NET Framework 2.0 og Visual Studio 2008 uden NuGet

Kunder med gamle .NET Framework 2.0-applikationer kan bruge Kombine.Flex.Portal.Client.Net20.dll via Add Reference → Browse. ZIP-pakken indeholder DLL, XML-dokumentation til IntelliSense, kildekode, Kombine.Flex.Portal.Client.2008.sln og en selvstændig testapplikation. Der kræves ingen NuGet- eller Kombine-pakker; kun mscorlib 2.0 og System 2.0. Dette er .NET Framework 2.0, ikke .NET Standard 2.0.

Installation

Udpak ZIP-pakken, og tilføj klientens DLL med Add Reference → Browse. Læg den medfølgende XML-fil ved siden af DLL’en for IntelliSense. NuGet er ikke nødvendigt.

Login og tabs

Placér using/Imports øverst i filen og resten af koden inde i en metode. Inputvariabler (tenantApiUrl, loginoplysninger og KIDs) kommer fra din applikation. Begge sprog bruger den samme DLL.

C#

using System;
using Kombine.Flex.Portal.Client.Net20;

using (PortalApiClient api = new PortalApiClient(new Uri(tenantApiUrl)))
{
    ManagerSessionResponse session = api.Login(email, password);
    ManagerProfileResponse manager = api.GetCurrentManager();
    Console.WriteLine(manager.Name);
    if (manager.TabDetails != null)
    {
        foreach (ManagerTabResponse tab in manager.TabDetails)
            Console.WriteLine("{0}: {1}", tab.Id, tab.Name);
    }
    api.ClearSession();
}

VB.NET

Imports System
Imports Kombine.Flex.Portal.Client.Net20

Using api As New PortalApiClient(New Uri(tenantApiUrl))
    Dim session As ManagerSessionResponse = api.Login(email, password)
    Dim manager As ManagerProfileResponse = api.GetCurrentManager()
    Console.WriteLine(manager.Name)
    If manager.TabDetails IsNot Nothing Then
        For Each tab As ManagerTabResponse In manager.TabDetails
            Console.WriteLine("{0}: {1}", tab.Id, tab.Name)
        Next
    End If
    api.ClearSession()
End Using

Vælg tenantens API-URL først. Alle 120 offentlige integrationskald findes som synkrone metoder uden Async-suffiks. Valgfrie filtre ligger i OperationOptions-klasser, eksempelvis GetBankUsersOptions. Datoer/tidsstempler er ISO 8601-strenge med bevaret offset; penge i mindste valutaenhed er nullable Int64. KIDs/cursors/revisioner sendes uændret. Biblioteket indeholder ingen databaseadgang eller forretningsregler.

Fejl giver PortalApiException med StatusCode, Code og Headers; følg samme 400/401/403/404/409/429/503-regler som ovenfor. Netværk/TLS/timeout giver WebException. Der er ingen automatiske retries. Kald blokerer og bør køres på en baggrundstråd i en desktop-UI. ClearSession fjerner tokenet lokalt; brug RenewManagerSession eksplicit til fornyelse; individuel token-tilbagekaldelse findes ikke.

HTTPS-krav: .NET 2.0-kompatibel kode kræver stadig en opdateret Windows-/CLR 2.0-installation med TLS 1.2 og betroede certifikater. Biblioteket ændrer ikke maskinens indstillinger. Den eksplicitte helper PortalApiClient.EnableTls12() vælger TLS 1.2 for hele værtsprocessen og fejler, hvis den ikke understøttes. Se Microsofts TLS-vejledning. Ingen fallback deaktiverer certifikatkontrol eller sender kundedata med usikker HTTP.

Changelog for API-kontrakten · Se ændringer i request/response, før du opdaterer din integration.

.NET Framework 4.5 / Visual Studio 2012

Kombine.Flex.Portal.Client.Net45.dll indeholder de samme 178 typede, synkrone API-kald. ZIP'en indeholder DLL/XML, kildekode, Kombine.Flex.Portal.Client.2012.sln og en selvstændig testapplikation. Kun frameworkbiblioteker kræves; ingen NuGet- eller Kombine-afhængigheder. VS2008 understøtter op til .NET Framework 3.5; 4.5 kræver VS2012 eller kompatible buildværktøjer. Se Microsofts versionsoversigt.

Installation

Udpak ZIP-pakken, og tilføj klientens DLL med Add Reference → Browse. Læg den medfølgende XML-fil ved siden af DLL’en for IntelliSense. NuGet er ikke nødvendigt.

Login og tabs

Placér using/Imports øverst i filen og resten af koden inde i en metode. Inputvariabler (tenantApiUrl, loginoplysninger og KIDs) kommer fra din applikation. Begge sprog bruger den samme DLL.

C#

using System;
using Kombine.Flex.Portal.Client.Net45;

using (PortalApiClient api = new PortalApiClient(new Uri(tenantApiUrl)))
{
    ManagerSessionResponse session = api.Login(email, password);
    ManagerProfileResponse manager = api.GetCurrentManager();
    Console.WriteLine(manager.Name);
    if (manager.TabDetails != null)
    {
        foreach (ManagerTabResponse tab in manager.TabDetails)
            Console.WriteLine("{0}: {1}", tab.Id, tab.Name);
    }
    api.ClearSession();
}

VB.NET

Imports System
Imports Kombine.Flex.Portal.Client.Net45

Using api As New PortalApiClient(New Uri(tenantApiUrl))
    Dim session As ManagerSessionResponse = api.Login(email, password)
    Dim manager As ManagerProfileResponse = api.GetCurrentManager()
    Console.WriteLine(manager.Name)
    If manager.TabDetails IsNot Nothing Then
        For Each tab As ManagerTabResponse In manager.TabDetails
            Console.WriteLine("{0}: {1}", tab.Id, tab.Name)
        Next
    End If
    api.ClearSession()
End Using

Vælg tenantens API-URL før login. Samme konto-/Tab-/KID-/operationsrettigheder og fejlkoder gælder. PortalApiException indeholder HTTP-status, kode og headers; netværk/TLS/timeout giver WebException eller I/O-fejl. Ugyldig/for stor JSON giver InvalidDataException. Der er ingen automatiske retries eller token-refresh. JSON-grænsen er 16 MiB; downloads streames og skal lukkes. Datoer bevares som ISO 8601-strenge og saldi som nullable Int64. Den eksplicitte TLS-helper ændrer processens protokolvalg, ikke certifikatkontrol eller registry. Build er kontrolleret mod 4.5-referencebiblioteker; runtime-test er udført på en nyere installeret CLR 4, ikke en oprindelig 4.5-installation.

Changelog for API-kontrakten · Se ændringer i request/response, før du opdaterer din integration.

Windows CE / Windows Mobile: Compact Framework 2.0

Brug Kombine.Flex.Portal.Client.Compact20.dll til Smart Device-projekter. Den separate Kombine.Flex.Portal.Client.Compact2008.sln og ZIP-pakken indeholder alle 178 typede, synkrone kald, kildekode og et testprogram til enheden. Kun Compact Frameworks egne biblioteker kræves; ingen NuGet eller Kombine-afhængigheder. Desktop-klientens DLL kan ikke bruges i stedet.

Installation

Udpak ZIP-pakken, og tilføj klientens DLL med Add Reference → Browse. Læg den medfølgende XML-fil ved siden af DLL’en for IntelliSense. NuGet er ikke nødvendigt.

Login og tabs

Placér using/Imports øverst i filen og resten af koden inde i en metode. Inputvariabler (tenantApiUrl, loginoplysninger og KIDs) kommer fra din applikation. Begge sprog bruger den samme DLL.

C#

using System;
using Kombine.Flex.Portal.Client.Compact20;

using (PortalApiClient api = new PortalApiClient(new Uri(tenantApiUrl)))
{
    api.TimeoutMilliseconds = 30000;
    ApiStatusResponse status = api.GetPortalStatus();
    ManagerSessionResponse session = api.Login(email, password);
    ManagerProfileResponse manager = api.GetCurrentManager();
    Console.WriteLine(manager.Name);
    if (manager.TabDetails != null)
    {
        foreach (ManagerTabResponse tab in manager.TabDetails)
            Console.WriteLine("{0}: {1}", tab.Id, tab.Name);
    }
    api.ClearSession();
}

VB.NET

Imports System
Imports Kombine.Flex.Portal.Client.Compact20

Using api As New PortalApiClient(New Uri(tenantApiUrl))
    api.TimeoutMilliseconds = 30000
    Dim status As ApiStatusResponse = api.GetPortalStatus()
    Dim session As ManagerSessionResponse = api.Login(email, password)
    Dim manager As ManagerProfileResponse = api.GetCurrentManager()
    Console.WriteLine(manager.Name)
    If manager.TabDetails IsNot Nothing Then
        For Each tab As ManagerTabResponse In manager.TabDetails
            Console.WriteLine("{0}: {1}", tab.Id, tab.Name)
        Next
    End If
    api.ClearSession()
End Using

Vælg tenantens HTTPS-API-adresse først. Login, tabs, KIDs og operationsrettigheder følger samme regler som ovenfor. Fejl returneres som PortalApiException; netværksfejl som WebException eller I/O-fejl. Ugyldige/for store JSON-data giver PortalProtocolException. JSON-grænsen er 2 MiB; brug små sider. Downloads streames. Den samlede HTTP-deadline er som standard 30 sekunder og kan justeres. Der er ingen automatiske retries eller token-refresh.

Enhedskrav: CF 2.0-kompatibilitet garanterer ikke moderne HTTPS. TLS, cipher suites og certifikatunderstøttelse afhænger af enhedens OS/OEM-image. Der findes ingen EnableTls12() i Compact-klienten, og den opgraderer ikke netværksstacken. Afprøv GetPortalStatus() på den konkrete enhed før login. Ingen usikker fallback eller ændring af API'ets sikkerhed. Bibliotek og enhedstests er bygget mod CF 2.0; runtime/HTTPS på en fysisk CE/Mobile-enhed eller emulator er endnu ikke verificeret.

Dart / Flutter · Portal API

Kildepakken 0.2.1 indeholder 178 operationer fra den medfølgende kontrakt, eksempler, syntetiske tests og vejledninger på dansk, engelsk og spansk. Kræver Dart 3.6 eller nyere (under 4.0). Pak ud ved siden af din applikation og tilføj denne stiafhængighed:

dependencies:
  kombine_flex_portal_client:
    path: ../Kombine.Flex.Portal.Client.Dart
dart pub get
# Flutter: flutter pub get
import 'package:kombine_flex_portal_client/kombine_flex_portal_client.dart';

final client = PortalClient(tenantApiUrl);
try {
  await client.login({'email': email, 'password': password});
  final banks = await client.getBanks();
} finally {
  client.close();
}

Brug tenantens API-adresse og manageroplysninger fra din applikation. Objektmodeller bruger wire-map-felter; int64-værdier bruger BigInt. HTTPS og normale API-rettigheder gælder. Ingen automatiske gentagelser. Flutter på enheder er ikke afprøvet. Pakken er ikke udgivet på pub.dev.

SHA-256 · API-kontraktens ændringslog

PHP · Portal API

64-bit PHP 8.2+, ext-curl og ext-json. Ingen ekstra PHP-biblioteker. Version 0.13.0 er med i API-downloads. Den er ikke udgivet på Packagist.

Version 0.3.1 opdaterer dokumentationen for GetBankUserBalances til den nye databasefrist på 20 sekunder. Felter i kald og svar er uændrede. Giv ekstra tid til transport og adgangskontrol; HTTP 503 returnerer fortsat ingen delvise saldoer. Version 0.2.5 tilføjer de valgfrie felter latestPostingMs2000 og hasActiveSubscription til GetBankUserBalances. Posteringstidspunktet er et 64-bit antal UTC-millisekunder siden 2000-01-01; nul betyder ingen posteringer. Null eller et manglende felt betyder ukendt, og manglende eller skjulte beboere giver null. Abonnementsstatus bekræfter ikke en betaling. Bevar eksisterende saldohåndtering og rettigheder; se /docs#user-balances. Version 0.2.5 tilføjer GetLocationOpeningHours og GetLocationBookingRules. Begge kræver Location Read, Unit Read og adgang til lokationen. Reservationsregler indeholder ren tekst samt ordnede parts med text/isValue til valgfri fremhævning; vis aldrig strengene som HTML. Brug text som fallback for ældre svar. Se /docs#location-opening-hours og /docs#location-booking-rules for rettigheder, eksempler og grænser. Version 0.2.5 tilføjer GetUserReceipts og GetHostingMetrics til API-releases, som tilbyder disse operationer. Indlæs kvitteringer efter behov fra offset 0. Fortsæt med nextOffset og samme revision; ved HTTP 409 (receipts-changed) skal tidligere sider kasseres, og indlæsningen genstartes ved offset 0. Hold valutaer adskilt og beløb som 64-bit heltal i mindste valutaenhed. Se /docs#user-receipts og /docs#hosting for rettigheder og grænser.

Hent PHP-ZIP · PHP-kontrolsummer · API-kontraktens changelog

Læg ZIP-filen i applikationens packages/-mappe, og installér med Composer (ext-zip kræves under installation):

composer config repositories.kombine artifact ./packages
composer require kombine/flex-portal-client:0.13.0

Uden Composer: udpak i flex-portal-client/, og indlæs dens autoload.php i stedet. Behold hele src/-mappen. ZIP-filen indeholder engelske, danske og spanske README-filer, operationer, modeller og den offentlige OpenAPI-kontrakt.

Brug en administrators mail og adgangskode til tenantens Portal API. Hent tenantUrl og loginvariabler fra beskyttet konfiguration eller en loginformular. API-adressen skal slutte med /. Behold bearer-tokenen på PHP-serveren.

<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';

use Kombine\Flex\Portal\PortalClient;
use Kombine\Flex\Portal\ApiException;
use Kombine\Flex\Portal\ProtocolException;
use Kombine\Flex\Portal\TransportException;

$api = new PortalClient($tenantUrl, timeout: 30);
try {
    $session = $api->login($email, $password);
    $result = $api->getCurrentManager();
} catch (ApiException $error) {
    $status = $error->status;
    $code = $error->apiCode;
    $retryAfter = $error->headers['retry-after'] ?? null;
    // Håndter fejlen efter tabellen nedenfor; gentag ikke skrivninger blindt.
} catch (TransportException | ProtocolException $error) {
    // Timeout, netværksfejl, ugyldigt svar eller overskredet svargrænse.
    // En skrivning kan være gennemført: kontrollér tilstanden før gentagelse.
} finally {
    $api->close();
}

renew() kalder eksplicit RenewManagerSession og gemmer den nye token. Den rå renewManagerSession() returnerer kun svaret. Forny før udløb ud fra brugeraktivitet; efter udløb eller tilbagekaldelse kræves nyt login. Der er ingen særskilt refresh-token.

Hvert forretningskald kontrollerer fortsat administratorens kontostatus, tilladte Tab, KID-område og operationsrettighed. Et KID giver ikke i sig selv adgang.

Sideinddeling er eksplicit: genbrug den returnerede cursor, og fortsæt, til den mangler, også efter en tom side. Behold revisionstokens til skrivninger. Download til en midlertidig fil, og omdøb den først efter succes.

$page = $api->getBankUsers($bankKid, ['pageSize' => 25, 'sort' => 'number']);
$balances = $api->getBankUserBalances($bankKid, ['userKids' => $userKids]);
$receipts = $api->getUserReceipts($userKid, ['offset' => 0]);
// Request the next page only when needed, using nextOffset and the same revision.
$stream = fopen($temporaryPath, 'w+b');
try {
    $download = $api->exportBankUsers($bankKid, $stream);
} finally {
    fclose($stream);
}
rename($temporaryPath, $completedPath);

ApiException giver status, apiCode og headers med små bogstaver. 400: ret input; 401: log ind; 403: kontrollér rettigheder; 404/409: genindlæs og løs konflikten; 429: respektér Retry-After; 5xx: håndter midlertidig fejl. TransportException og ProtocolException dækker netværk/tidsfrist og ugyldige/for store data. En fejlet skrivning kan være gennemført: kontrollér tilstanden før gentagelse. Log aldrig loginoplysninger, tokens eller fulde payloads.

Svar er associative arrays; lister er indekserede arrays. Brug 64-bit int til heltalsfelter, og bevar manglende/null-værdier. Klienten kontrollerer TLS, afviser redirects, udelader tokens ved anonyme kald og sletter sessionen ved HTTP 401. Standardgrænser: 30 sekunder i alt, 16 MiB JSON, 64 KiB headers og 1 GiB pr. download. Downloads skriver til en stream, som applikationen ejer; kassér delvise filer ved fejl. Ingen automatisk gentagelse, sideinddeling, fornyelse eller kvittering. Serverbaseret PHP kræver ikke browser-CORS.

Enhedsliste (Units1)

Kørende enheder (lokalt implementeret, ikke udgivet): GET /api/v1/units?runningOnly=true&fields=progress&sort=progress&direction=asc filtrerer før sortering og sideinddeling. En kørende enhed har gyldige positive Log24 Started/Done-tider med start <= beregningstid < forventet slut. Manglende, ugyldige, fremtidige og afsluttede tidsforløb udelades. Standard er false; progress-kolonnen er valgfri. Kan kombineres med enabledOnly, søgning og autoriseret lokations-/controllerafgrænsning. Der filtreres ikke yderligere på driftstilstand eller maskintype. Beregningstiden fastholdes gennem nextCursor-sider i højst 15 minutter; genindlæs første side for aktuel status. Gentag runningOnly ved hver cursor; ændringer giver 400 invalid-cursor. GetTerminals afviser runningOnly=true med 400 invalid-filter. Eksisterende Units1, Bank/Location/Unit Read, scope, slettefrist og 401/403/503 er uændrede.

Valgfri progress (lokalt implementeret, ikke udgivet): GET /api/v1/units?fields=progress tilføjer items[].progress: {status,percent,remainingSeconds,calculatedAtUtc} med præcis samme beregning som overblikskortene. Driftsdata hentes i én afgrænset Log24-forespørgsel for den autoriserede side og beregnes ved kaldet. Procenten bruger kun starttid og forventet sluttid, begrænset til 0–100. Manglende eller ugyldige tider giver null i percent. Uden tilvalg er progress null. GetTerminals udfylder ikke feltet. Brug sort=progress med direction=asc/desc til numerisk sortering før sideinddeling (kun GetUnits). Ukendte værdier står først ved stigende og sidst ved faldende sortering. Beregningstidspunktet fastholdes gennem en cursor-kæde i højst 15 minutter, så tidens gang alene ikke flytter enheder mellem sider; nye enhedsdata kan stadig flytte rækker. Rettigheder og slettefrist kontrolleres med aktuel tid. Progress kan ikke søges; bevar fields med nextCursor. Eksisterende Units1-, Bank/Location/Unit Read-, scope- og RetentionDays-kontroller samt 400/401/403/503-svar gælder. Værdien er et øjebliksbillede og opdateres ikke løbende.

GetUnits accepterer også terminalKid: GET /api/v1/units?terminalKid={controllerKid}. Parameteren er en kanonisk Controller-KID med MainId (UnitId=0). Den første unit og underenheder med samme lokations Alive.MainId til controlleren returneres. Forælderen skal være en autoriseret Log24-enhed, som er synlig efter RetentionDays. Modstridende relationer, kæder og cykler afvises. Forælderens Alive-række må mangle; manglende relationer for underenheder giver aldrig medlemskab. Eksisterende Units1- og læserettigheder gælder. Afgrænsning sker før sideinddeling. Gentag terminalKid med hver cursor; ændringer giver 400 invalid-cursor. Ugyldig KID, forkert tenant eller terminalKid på GetTerminals giver 400 invalid-terminal. Udelad den for den almindelige liste. Lokalt implementeret, ikke udgivet.

Valgfri locationKid på GetTerminals og GetUnits afgrænser listen til én kanonisk lokations-KID i sidens tenant. Den begrænser eksisterende rettigheder før søgning, sortering og sideinddeling; adgangskrav og terminaludvælgelse er uændrede. Eksempel: GET /api/v1/terminals?locationKid={locationKid}&enabledOnly=true. Gentag den med hver cursor; ændret afgrænsning giver 400 invalid-cursor. Ugyldig KID eller forkert tenant giver 400 invalid-location. Udelad den for listen på tværs af lokationer. Lokalt implementeret, ikke udgivet.

Ikke udgivet: sort=kid sorterer GetUnits og GetTerminals numerisk efter bank, lokation og enhed inden for sitets tenant. Brug direction=asc eller desc; behold begge parametre uændret ved fortsættelse med cursor.

GetUnits — GET /api/v1/units

Kræver en aktiv administrator med Units1 (50), Bank Read, Location Read, Unit Read og adgang til de relevante ressourcer. Viser hoved- og underenheder fra sitets Log24. Begrænset adgang kræver en aktiveret lokation og bank/lokation/enhed inden for slettefristen. Adgang til hele sitet kan vise alle tilstande. enabledOnly=true viser kun enheder med Enabled præcis 1.

fields=locationName,bankName,unitType,washDocId,outOfOrder vælger ekstra kolonner. Kanoniske kid, locationKid, bankKid, navne, iconKids og enabled/deleted/deletedAt returneres altid. Ekstra enhedsfelter er null, hvis de ikke er valgt, og tomme, hvis de mangler. Typen kommer kun fra oprindelig pakket UnitType med (value >> 1) & 255; UnitType2 ignoreres og bruges heller ikke som reserve. includeCoordinates=true returnerer lokationens latitude/longitude i decimalgrader; manglende/ugyldige koordinater er null.

filter søger i gemte navne, enhedsnumre/-typer, WashDocId og OutOfOrder eller et komplet kanonisk/site-relativt bank-, lokations- eller enheds-KID. sort: kid, name, bankName, locationName, unitId, unitType, washDocId eller outOfOrder; direction=asc/desc. Enhedsnummer/type og WashDocId sorteres numerisk; tekst bruger utf8mb4_general_ci. Navne kan oversættes til visning, mens søgning og sortering bruger gemte navne.

pageSize er 1–250 (standard 50). Gentag samme parametre og bearer-session med nextCursor, indtil den er null. Cursoren udløber efter 15 minutter og er bundet til tenant, administrator, adgang, slettefrist og parametre. Start forfra uden cursor, når parametrene ændres. Samtidige ændringer kan flytte rækker.

400 invalid-page/filter/sort/fields/cursor: ret input eller genstart listen. 401: log ind igen. 403 missing-units-tab/missing-bank-read/missing-location-read/missing-unit-read/missing-resource-access: kontrollér rettigheder. 503 units-unavailable: prøv igen manuelt. No-store. Med i denne version og klientpakker 0.13.0.

GET /api/v1/units?pageSize=50&fields=locationName,bankName,unitType&enabledOnly=true&sort=name&direction=asc
Authorization: Bearer <session-token>

Antal aktive enheder

GetActiveUnitCount — GET /api/v1/units/active-count returnerer {count,iconKid} til tælleren i Enheder/Units1-navigationen. Genbrug managerens bearer-session. Kræver samme aktive administrator, Units1 (50), Bank Read, Location Read, Unit Read og site/bank/lokationsadgang som GetUnits. Tæller forskellige hoved- og underenheder fundet i Log24 med Enabled præcis 1 og Deleted nul eller manglende; ugyldige sletteværdier og slettede enheder udelades, også ved adgang til hele sitet. Begrænset adgang kræver stadig en aktiveret lokation samt bank/lokation med synlig sletningstilstand efter RetentionDays; adgang til hele sitet tilsidesætter kun disse regler for de overordnede ressourcers tilstand. Den betroede sitekonfiguration fastlægger tenant.

Antallet er uafhængigt af søgning, valgte kolonner, sortering og sideinddeling; der er ikke brug for listens parametre. iconKid er den færdige ikonidentitet med tælleren: vis den uændret via ikon-API'et i stedet for selv at sammensætte en badgeidentitet. Den giver ingen rettigheder. Antallet læses på ny; rettighedsdata kan være op til 60 sekunder gamle. Svar er no-store. 401: log ind igen. 403 missing-units-tab, missing-bank-read, missing-location-read, missing-unit-read eller missing-resource-access: kontrollér rettigheder. 503 units-unavailable betyder utilgængelig database eller overskredet frist på 12 sekunder: lad brugeren prøve igen manuelt, og vis aldrig fejlen som nul. Portalen henter antallet i baggrunden som på Lokationer og viser hele antallet på ikonet og i den lokaliserede hjælpetekst. Implementeret lokalt, endnu ikke udgivet; genererede klienter og downloadpakker synkroniseres ved beta-deployment.

curl "$BASE/api/v1/units/active-count" -H "Authorization: Bearer $TOKEN"

Terminalliste (Terminals1)

Terminaloversigt (ikke udgivet): GetTerminals (GET /api/v1/terminals) henter name fra eState.ComputerName, ikke eSetting.Name. Navnefilter og sortering bruger ComputerName; manglende navne er tomme. Vælg tekstkolonner med ?fields=versionMinor,bootReason,booted,firmware,storageCardSerialNumber,page,backLight. Booted bruger eSetting.Booted; de øvrige bruger eState. Fravalgte felter er null; valgte manglende værdier er tomme. Felterne understøtter tekstsortering. Bevar fields og sort ved nextCursor. Terminals1, Bank/Location/Unit Read og afgrænsede ressourcescopes kræves fortsat. Ugyldige felter/sortering giver 400, manglende rettigheder 403. GetUnits er uændret.

Hver rækkes iconKid bruger det faste terminalikon uafhængigt af eSetting.Icon med UnitId i feltet Text. Vis det uændret; erstat det ikke med svaret fra indlæsning af enhedsikoner.

GetTerminals — GET /api/v1/terminals returnerer samme UnitDirectoryResponse som GetUnits: {items,nextCursor,hasAllBanksAccess,fields}. Kræver aktiv administrator med Terminals1 (66), Bank Read, Location Read, Unit Read og adgang til relevant site/bank/lokation. Terminals1 kontrolleres særskilt; Units1 er ikke påkrævet. Rettigheder og RetentionDays kontrolleres på hver side. Begrænset adgang kræver en aktiveret lokation og bank/lokation/enhed inden for slettefristen; eksplicit adgang til hele sitet kan vise alle tilstande.

Kun enheder fundet i Log24, hvis UnitId svarer til en positiv Alive.MainId i samme bank og lokation, returneres. Flere Alive-rækker kan pege på samme hovedterminal; den vises én gang. En Alive-række for en underenhed kan identificere dens hovedterminal, men underenheden bliver ikke selv et terminalresultat. Manglende/nul MainId, forældreløse Alive-identiteter og enheder uden adgang udelades. Dette er en skrivebeskyttet klassifikation fra den betroede tenants Alive-tabel; den finder ikke ekstra udstyr, viser ikke online-status og giver ingen rettigheder. GetUnits viser fortsat både hoved- og underenheder.

Brug samme parametre som GetUnits: fields=locationName,bankName,unitType,washDocId,outOfOrder, filter, sort=kid|name|bankName|locationName|unitId|unitType|washDocId|outOfOrder, direction=asc|desc, enabledOnly, includeCoordinates, pageSize og cursor. Kanoniske KID’er, navne, iconKids og enabled/deleted/deletedAt returneres altid; ikke-valgte ekstra enhedsfelter er null, og valgte manglende værdier er tomme. Typen kommer kun fra oprindelig pakket UnitType med (value >> 1) & 255; UnitType2 ignoreres og bruges heller ikke som reserve. Et filter på højst 128 tegn søger i gemte navne, enhedsnumre/-typer, WashDocId og OutOfOrder eller et præcist kanonisk/site-relativt bank-, lokations- eller enheds-KID. Navne kan oversættes til visning, mens søgning/sortering bruger gemte værdier. Numerisk enhedsnummer/type/WashDocId og tekstsammenligning følger GetUnits. enabledOnly=true kræver Enabled præcis 1. includeCoordinates=true returnerer lokationens nullable koordinater i decimalgrader til kort; hent alle sider for det fulde autoriserede kort.

pageSize er 1–250, standard 50. Genbrug managerens bearer-session og samme parametre med nextCursor som cursor, indtil den er null. Keyset-cursorer udløber efter 15 minutter og er bundet til listen, tenant, administrator, adgang, slettefrist og parametre. Start forfra uden cursor, når de ændres; en GetUnits-cursor kan ikke bruges til GetTerminals. Samtidige ændringer i enheder eller Alive kan flytte rækker.

Erstat HTTPS-adressen med tenantens Portal API, og genbrug et gyldigt manager-token:

curl --get 'https://YOUR-PORTAL-API/api/v1/terminals' \
  -H "Authorization: Bearer $TOKEN" \
  --data-urlencode 'fields=locationName,bankName,unitType' \
  --data-urlencode 'enabledOnly=true' \
  --data-urlencode 'includeCoordinates=true' \
  --data-urlencode 'sort=name' \
  --data-urlencode 'direction=asc' \
  --data-urlencode 'pageSize=50'

400 invalid-page, invalid-filter, invalid-sort, invalid-fields eller invalid-cursor: ret input eller genstart listen. 401: log ind igen. 403 missing-terminals-tab, missing-bank-read, missing-location-read, missing-unit-read eller missing-resource-access: kontrollér rettigheder. 503 terminals-unavailable: vis fejlen, og lad brugeren prøve igen manuelt; vis den ikke som en tom liste. Svar er no-store. Implementeret lokalt, endnu ikke udgivet; genererede klienter og downloadpakker synkroniseres ved beta-deployment.

Antal aktive terminaler

GetActiveTerminalCount — GET /api/v1/terminals/active-count returnerer {count,iconKid} til tælleren i Terminaler/Terminals1-navigationen. Kræver samme aktive administrator, Terminals1 (66), Bank Read, Location Read, Unit Read og site/bank/lokationsadgang som GetTerminals; Units1 er ikke påkrævet. Reglerne for aktiv tilstand og synlige overordnede ressourcer følger GetActiveUnitCount. Kun autoriserede enheder fundet i Log24, hvis UnitId svarer til en positiv tenantbundet Alive.MainId i samme bank og lokation, tælles. Hver terminalidentitet tælles én gang, selv når flere Alive-rækker for underenheder identificerer den; underenhederne bliver ikke ekstra terminaler.

Genbrug managerens bearer-session; der er ikke brug for søge-, kolonne-, sorterings- eller sideinddelingsparametre. Vis det returnerede iconKid uændret via ikon-API'et; det giver ingen rettigheder. Antallet læses på ny med no-store-svar og uafhængigt af listens filtre. 401: log ind igen. 403 missing-terminals-tab, missing-bank-read, missing-location-read, missing-unit-read eller missing-resource-access: kontrollér rettigheder. 503 terminals-unavailable betyder utilgængelig database eller overskredet frist på 12 sekunder: lad brugeren prøve igen manuelt, og vis aldrig fejlen som nul. Portalen henter tælleren i baggrunden som på Lokationer. Implementeret lokalt, endnu ikke udgivet; genererede klienter og downloadpakker synkroniseres ved beta-deployment.

curl "$BASE/api/v1/terminals/active-count" -H "Authorization: Bearer $TOKEN"

Changelog

Beta 2026-10-26 · aa51399c · Portal clients 0.13.0 / Dart 0.3.0 · production pending

More1 tools move from bank context to the tenant workspace

ConvertToolTime — POST /api/v1/tenant/tools/time and InspectToolKid — POST /api/v1/tenant/tools/kid replace POST /api/v1/banks/{bankKid}/tools/time and POST /api/v1/banks/{bankKid}/tools/kid. The old routes and required bankKid path parameter are removed. JSON request/response fields and operation IDs are unchanged. The trusted site selects the tenant; no tenant or bank selector is required. An active manager, More1 (29), Bank Read and at least one resource grant in the site's tenant remain required. Bank- and location-restricted grants qualify without selecting a context bank. The obsolete 400 invalid-bank error is removed; credential, permission, conversion and availability errors retain their meanings.

Migration: change the request URL to /api/v1/tenant/tools/time or /api/v1/tenant/tools/kid, remove the bank path parameter and reuse the existing manager bearer token and JSON body. Open More1 from the tenant workspace. KID inspection remains a syntax-only operation that grants no access to the inspected identity. Portal clients 0.13.0 and Dart 0.3.0 include the new signatures; rebuild callers without bankKid. Verified on all five beta apps on 2026-10-26 at commit aa51399c. Production is pending.

Beta 2026-10-23 · 3c2a88b · clients synchronized · production pending

Unit document lists use one date and include earlier history

GetUnitDocuments — GET /api/v1/units/{unitKid}/documents: request parameters from/through are replaced by date, a UTC calendar date in yyyy-MM-dd format, defaulting to today. Previously, the default interval covered 24 hours and all searches were limited to 31 days. The list now includes matching document identities from 2000-01-01 through the entire selected day. Response from is the epoch and through is the date at 23:59:59.999Z; their types and the remaining page fields are unchanged. Supplying the removed request parameters returns 400 invalid-document-search. Bank-wide search and full-document measurement limits are unchanged.

Migration: send one UTC date and reset offset when it or sorting changes. Fetch pages lazily, preserving that date. For a total, call GetUnitDocumentsCount — GET /api/v1/units/{unitKid}/documents/count with the same date independently of page loading; it returns {count, from, through}. Both operations retain the same tenant, manager, WashDoc3, Read, scope and retention checks. Verified on beta 2026-10-23 at commit 3c2a88b; clients are synchronized. Production pending.

Beta 2026-10-23 · 3c2a88b · clients synchronized · production pending

WashDoc measurements and signals follow the original unit type

GetUnitDocumentGraph — GET /api/v1/documents/{documentKid}/graph: for Alliance dryers 19/29/30, a historical Level series previously appeared as water level with an unknown unit; it now has group=temperature2, Temperature2 label/localization and °C for bare numeric values. Its stable source name=Level and values are unchanged. LevelTarget/LevelUnfiltered use group=legacyLevel and unknown units. Bare Humidity on those three types uses %. Their signals now accept flags 0–127, interpret heat as bits 32|64, expose nullable airflow from bit 16, and return null inlet/outlet instead of interpreting dryer switches as water valves. Other types retain existing signal semantics. Bare Spin on QW washer types 15/16/17/25/31 no longer claims rpm: series/point units are null and values 0–3 use step=true. Explicit rotation-scale JSON keeps its recorded units.

The added presentation object contains trusted original unitType, kind and preferred groupOrder. Additional actual groups remain present. Migration: use each returned semantic group, label, unit and step flag instead of inferring measurement meaning from its source name. Treat null water signals as inapplicable/unknown, never off, and handle the new airflow signal. Apply preferred ordering only to groups present in the document and retain unlisted groups. PNG, SVG and public widget rendering share these type rules. Stored data, request parameters and authorization are unchanged. Verified on beta 2026-10-23 at commit 3c2a88b; clients are synchronized. Production pending.

Beta 2026-10-23 · 3c2a88b · clients synchronized · production pending

Document tables and downloads omit columns without data

GetUnitDocumentTable — GET /api/v1/documents/{documentKid}/table, DownloadUnitDocumentCsv — GET /api/v1/documents/{documentKid}/table.csv, DownloadUnitDocumentXls — GET /api/v1/documents/{documentKid}/table.xls and GetUnitDocumentHtml — GET /api/v1/documents/{documentKid}/table.html: selected fields previously produced columns even when they contained no values. Now columns omits a field when every cell in the complete authorized document has no numeric value and null, empty or whitespace-only text. Numeric zero, false and nonempty text remain data. The same positions are removed from each rows[].cells array and download headers/cells. Remaining column order, rows, timestamps and values are preserved; no populated field is dropped because its data appears late. A document without populated fields returns empty columns and cell arrays, while downloads retain their timestamp columns.

GetUnitDocumentGraph — GET /api/v1/documents/{documentKid}/graph shares the filtered data: a signal column containing only null/empty values now contributes no signals entries. Absence remains unknown, never false/off. Stored null transitions in a column with actual data are retained.

Migration: construct column mappings from the returned kind/name metadata on every response; do not assume every requested field is present or reuse positional indexes from another document. Treat an omitted field as having no values in this document. Authorization, field-selection limits and storage are unchanged. Verified on beta 2026-10-23 at commit 3c2a88b; clients are synchronized. Production pending.

Beta 2026-10-23 · 3c2a88b · clients synchronized · production pending

Machine types use only the original packed UnitType

GetLocationUnits — GET /api/v1/locations/{locationKid}/units, GetUnitOverview — GET /api/v1/units/{unitKid}, GetUnitGroup — GET /api/v1/units/{unitKid}/groups/{kind}/{group}, GetControllerOverview — GET /api/v1/controllers/{controllerKid}, GetControllerGroup — GET /api/v1/controllers/{controllerKid}/groups/{kind}/{group}, GetUnits — GET /api/v1/units, GetTerminals — GET /api/v1/terminals, GetTenantStatus — GET /api/v1/tenant/status and GetTenantStatusPage — GET /api/v1/tenant/status/page: type interpretation now uses only original eSetting.UnitType, decoded as (value >> 1) & 255. Previously a present direct UnitType2 took precedence. UnitType2 is now ignored and never supplies a fallback. Missing, malformed or out-of-Int64-range UnitType remains unknown; no default type is invented. Stored settings are unchanged.

Affected results include unitType, unitTypeName, unitTypeSource (UnitType when present, null when absent), available setting/state groups, directory type search/filter/sort, SMS exclusion in AutoOutOfOrder, and type-derived progress/widget presentation. Field types and authorization are unchanged, but values and available groups can change. Migration: discard cached type/group metadata, use the returned original type rather than interpreting UnitType2, restart directory cursors and refresh field revisions before editing. Handle unknown types and groups that are no longer available. This supersedes the source-precedence rule recorded in the 2026-10-12 entry. Verified on beta 2026-10-23 at commit 3c2a88b; clients are synchronized. Production pending.

Beta 2026-10-23 · 3c2a88b · clients synchronized · production pending

Unit and terminal cursor options support adaptive page sizes

GetUnits — GET /api/v1/units and GetTerminals — GET /api/v1/terminals: the opaque cursor option binding no longer includes pageSize. Previously, pageSize was fixed by the cursor and limited to 100; it now accepts 1–250 and may change between pages. Cursors issued before this update fail with HTTP 400 invalid-cursor. Migration: discard an old cursor and restart the same filtered query without it. Keep all other query options unchanged, treat cursors as opaque, and continue until nextCursor is null. Tenant, manager, scope, retention and operation checks remain enforced. Other private directory endpoints also accept pageSize 1–250 and permit size changes without changing their filter/scope binding. Response fields and UserBalance are unchanged.

Beta 2026-10-23 · 3c2a88b · clients synchronized · production pending

Reservation cards include resident display details and require User Read

GetLocationReservations — GET /api/v1/locations/{locationKid}/reservations and GetLocationReservationHistory — GET /api/v1/locations/{locationKid}/reservation-history now require User Read alongside Bookings1, Location/Unit Read and existing resource/retention checks. Previously these operations returned occupancy/history without resident metadata or User Read. Authorized items add userNumber and userName strings from the latest resident bank Log7 settings; missing values are empty strings. Items also add nullable canonical userKid, used as the display fallback for a missing number (null when no resident is recorded). Existing time, unit and revision fields are unchanged. Migration: grant User Read to managers who may inspect these details, handle 403 for managers lacking that permission, and render the new strings as plain text. Do not treat failed reads as availability.

Beta 2026-10-23 · 3c2a88b · clients synchronized · production pending

Invalid stored calendars are omitted

GetLocationBookingCalendars — GET /api/v1/locations/{locationKid}/booking-calendars: malformed stored calendars are now omitted from groups, alongside missing calendars. Previously they returned status: Invalid with empty days. Valid calendars on other authorized units remain visible. Migration: treat omitted units as having no valid configured calendar, not unrestricted booking availability; remove expectations of Invalid groups. Storage and authorization failures still return their documented errors.

Beta 2026-10-23 · 3c2a88b · clients synchronized · production pending

Booking calendars return opening-aware dates instead of repeating weeks

GetLocationBookingCalendars — GET /api/v1/locations/{locationKid}/booking-calendars adds optional from (ISO date) and days (1–31, default 14). Invalid date ranges return 400. The response adds timeZone, from, days and per-day date. Previously group days were a seven-day recurring template with potentially negative starts and multi-day durations. They now represent the requested dates with slots clipped to midnight and effective opening hours; type 3 means unknown opening hours. Groups also distinguish different opening schedules.

Migration: render returned dates directly, fetch additional ranges when navigating, remove client-side weekly repetition and opening-hour merging, and display unknown periods as unavailable. This remains configuration rather than booking availability. The current Portal client packages include the dated contract. Verified on all five beta apps on 2026-10-23 at commit 3c2a88b; production pending.

Beta 2026-10-17 · 59db3ea · Portal clients 0.8.5 · production pending

New camera captures use GUID filenames

Endpoints: GetCameraCaptures — GET /api/v1/camera and GetCameraCaptureContent — GET /api/v1/camera/{id}/content. New captures return a GUID-based fileName and download filename instead of the original filename stem, FRA/AMS label and capture identity. Related files retain suffixes such as .thumbnail.jpg and .json. Existing stored captures remain readable under their existing names. Original device names and origin regions remain in the metadata JSON.

Migration: treat returned filenames as opaque; do not parse region, device filename or capture identity from them. Continue using API capture identifiers and content links. The filename GUID is not the capture ID. Request fields, response field types and authorization are unchanged. Prepared for beta deployment; production pending.

Beta 2026-10-14 · 747c9e9 · Portal clients 0.7.2 · production pending

Negative icon counts display an absolute-value notification badge

Endpoints: GetIconFromSet — GET /api/v1/icon/{iconSet}/{kid}.{format}, GetIconImageFromSet — GET /api/v1/icon/{iconSet}/{kid}/{size}.{format} and GetIconImageWithBackgroundFromSet — GET /api/v1/icon/{iconSet}/{kid}/{backColor}/{size}.{format}. Affected: rendered notification badge for the signed count encoded in the icon KID.

Previously, zero and negative counts hid the badge, and positive counts used a red background with white digits. Zero still hides it; negative counts now show the full absolute value with a red background and white digits. Positive counts now use a white background and black digits. All signed Int64 values, including Int64.MinValue, remain accepted. KID encoding, response media types and operation IDs are unchanged.

Customer action

If negative counts previously suppressed a badge in your integration, encode count zero instead. To retain the former red badge for a positive count, encode its negative value; otherwise retain the positive count for the white badge. Do not compute the absolute value using signed Int64 arithmetic for its minimum value. Portal clients 0.7.2 include the current contract documentation. Verified on beta on 2026-10-14 at commit 747c9e9; production pending.

Beta 2026-10-13 · 353b2ae · clients 0.7.1 · production pending

Packed unit types retain all eight type bits

GetLocationUnits — GET /api/v1/locations/{locationKid}/units, GetUnitOverview — GET /api/v1/units/{unitKid}, GetUnitGroup — GET /api/v1/units/{unitKid}/groups/{kind}/{group}, GetUnits — GET /api/v1/units, GetTerminals — GET /api/v1/terminals and controller overview/group equivalents now decode legacy UnitType as (value >> 1) & 255, previously & 63. For example, raw 132 now identifies type 66 instead of type 2. Response field types are unchanged, but unitType, unitTypeName, available setting/state groups and directory type filtering/sorting can change. A present direct UnitType2 still takes precedence, including malformed values returning null without fallback.

Migration: use the returned type and group lists, discard cached type metadata and restart directory pagination. Refresh setting revisions before writing: edits and history are governed by the corrected type, and previously accessible groups may return 404. Verified on all five beta apps on 2026-10-13 at commit 353b2ae; packaged Portal clients 0.7.1 are synchronized. Production pending.

Beta 2026-10-13 · 353b2ae · clients 0.7.1 · production pending

Unit progress uses only elapsed expected time, clamped to 0–100%

GetLocationUnits — GET /api/v1/locations/{locationKid}/units, GetUnitOverview — GET /api/v1/units/{unitKid}, GetUnitGroup — GET /api/v1/units/{unitKid}/groups/{kind}/{group}: progress.percent now uses floor(clamp((now − Started)/(Done − Started) × 100, 0, 100)). Previously lifecycle, connection and sequence state could suppress the percentage, estimated progress stopped at 99, and 100 required DONE. Valid timing now returns status Estimated and 0–100 regardless of those states; at/after the expected end percent is 100 and remainingSeconds is 0. Before start, percent is 0. Missing/invalid times return UnknownEndTime and null percent/remainingSeconds. Field types are unchanged. The same calculation is available through the new optional fields=progress on GetUnits — GET /api/v1/units.

Migration: accept Estimated with 100 percent; do not infer physical completion from progress status or percent. Use cycle for device state. Remove assumptions that elapsed estimates return EstimateExpired or that disabled/disconnected units always have null percentages. Verified on all five beta apps on 2026-10-13 at commit 353b2ae; packaged Portal clients 0.7.1 are synchronized. Production pending.

Beta 2026-10-13 · 353b2ae · clients 0.7.1 · production pending

Controller identities use MainId and retain the first unit as a child

GetLocationUnits — GET /api/v1/locations/{locationKid}/units, GetUnitOverview — GET /api/v1/units/{unitKid}, GetUnitGroup — GET /api/v1/units/{unitKid}/groups/{kind}/{group}, GetUnits — GET /api/v1/units and GetTerminals — GET /api/v1/terminals: optional terminal.kid previously identified the main Unit (UnitId). It now identifies a Controller (MainId, UnitId=0). The terminal.name field now uses the main unit’s eState.ComputerName, matching GetTerminals, instead of eSetting.Name; absent ComputerName returns an empty string. Nullability and icon fields are unchanged; each row's own unit KID is unchanged. GetUnits query terminalKid now requires this Controller KID instead of a Unit KID, and includes the first unit as well as mapped children instead of excluding it. Old Unit KIDs return 400 invalid-terminal.

Migration: refresh cached names and hierarchy metadata, display terminal.name as reported computer text (handle empty names), group by the returned terminal.kid, and pass it unchanged as terminalKid. Keep the first unit as its own child; do not compare its KID to the controller KID. Restart directory cursors and discard stale cached parent identities. Controller overview/group routes use the Controller KID; their unit.kid remains the underlying storage unit. Verified on all five beta apps on 2026-10-13 at commit 353b2ae; packaged Portal clients 0.7.1 are synchronized. Production pending.

Beta 2026-10-13 · 353b2ae · clients 0.7.1 · production pending

Self-registration rejects unsupported email languages

RequestManagerSignUp — POST /api/v1/session/signup: optional nullable language now accepts only the exact lowercase strings en, da and es when a non-null value is supplied. Previously any string was accepted, with values other than exact da/es silently using English. Empty strings, whitespace, regional tags, uppercase and unknown values now return HTTP 400 ValidationProblemDetails with errors.Language before account creation or mail queueing. Omitting the field or sending null still uses English; valid requests retain the existing response contract.

Migration: map browser or application language preferences to one of the three supported lowercase codes. For English, send en, omit the field or send null; do not send an empty string or an unprocessed regional language code. Handle language validation errors before resubmitting. Verified on all five beta apps on 2026-10-13 at commit 353b2ae; packaged Portal clients 0.7.1 are synchronized. Production pending.

Beta 2026-10-13 · 353b2ae · clients 0.7.1 · production pending

Public purchase amounts are grouped by currency

GetPublicPurchases — GET /api/v1/public/statistics/purchases: the top-level decimal amount and nullable string currency are removed. Previously all qualifying purchase amounts were combined and labelled with MAX(Currency). The response now contains a non-null currencies array of {currency, count, amount} objects: one group per currency, with a nullable string currency, integer purchase count and decimal amount in major currency units, calculated as -SUM(Amount)/100 within that group. Missing currency remains null in its own group. No combined monetary total or conversion is returned.

The top-level count remains the total number of purchases and equals the sum of group counts. With no purchases, the former amount: 0, currency: null are replaced by currencies: []; count remains zero. tenantKid, lookbackHours, sinceUtc, measuredAtUtc, the request, filters and access rules are unchanged.

Migration: iterate over currencies and display each group's amount with its own currency; use the top-level count for the overall purchase count. Handle an empty array and a null currency without assuming a known currency. Stop reading the removed top-level fields and do not sum amounts across currencies. Verified on all five beta apps on 2026-10-13 at commit 353b2ae; packaged Portal clients 0.7.1 are synchronized. Production pending.

Beta 2026-10-13 · 353b2ae · clients 0.7.1 · production pending

Self-registration creates a pending manager before email delivery

RequestManagerSignUp — POST /api/v1/session/signup: required request fields password and confirmPassword added. Previously email/name/organisation queued proof without creating an account; now the password is chosen immediately and a disabled manager plus manager-bound verification mail are committed atomically. Existing-address processing remains non-disclosing 202 accepted.

ConfirmManagerSignUp — POST /api/v1/session/signup-confirmation: password and confirmPassword removed; request contains only token. Previously confirmation created a manager and returned 201 manager-created; now it activates the existing pending manager and returns 200 email-verified. No business access is granted.

Migration: collect matching 12–128 printable ASCII passwords on the initial form, send them with signup, send only the verification token on confirmation, and expect 200/email-verified. Tokens from the previous local flow are invalid; request a new link. Verified on all five beta apps on 2026-10-13 at commit 353b2ae; packaged Portal clients 0.7.1 are synchronized. Production pending.

Release 2026-10-10 · available on this host · clients 0.6.1

Terminal directory names use the reported computer name

GetTerminals — GET /api/v1/terminals: items[].name now reads eState.ComputerName from tenant-bound Log24 instead of eSetting.Name. Missing computer names return an empty string; there is no settings-name fallback. Name filtering and sorting use the new source. Optional string fields/sorts added: versionMinor, bootReason, booted, firmware, storageCardSerialNumber, page, backLight. They are null unless requested through fields; selected missing values are empty strings. Booted uses eSetting.Booted, the other added values use eState.

Migration: treat name as the reported computer name, handle empty names, and restart pagination after release. Existing stored names are preserved. GetUnits and UserBalance name behavior is unchanged.

Release 2026-10-10 · available on this host · clients 0.6.1

Bank and location directory tab assignments corrected

GetLocations — GET /api/v1/locations and GetActiveLocationCount — GET /api/v1/locations/active-count now require Locations1 (65), previously Banks2 (5). Without tab 65 these operations return 403. The new unreleased GetBanks — GET /api/v1/banks and GetActiveBankCount — GET /api/v1/banks/active-count require Banks2 (5), previously Locations1 (65).

Migration: review and explicitly assign tab 65 for location directory access and tab 5 for bank directory access. Stored assignments and numeric enum IDs are not rewritten. Resource scopes and operation permissions still apply. Request and response fields are unchanged; active-count iconKid values use the corresponding tab metadata. UserBalance is unchanged.

Release 2026-10-10 · available on this host · clients 0.6.1

Tab 66 is named Terminals1

GetCurrentManager — GET /api/v1/session/me, GetManager — GET /api/v1/managers/{managerKid}, SetManagerTab — POST /api/v1/managers/{managerKid}/tabs/{tabId} and SetManagerPermissionRole — POST /api/v1/managers/{managerKid}/permission-role: tab objects with id:66 now use name:"Terminals1" instead of name:"Disp66", including the available-tab catalog. The numeric identity and stored tab assignments remain unchanged.

Migration: identify tabs by their stable numeric ID, update any name comparisons for tab 66, and treat its terminal-directory access as tenant-bound. This does not automatically assign tab 66 or grant resource/operation permissions. Clients 0.6.1 include this contract. UserBalance is unchanged.

Release 2026-10-10 · available on this host · clients 0.6.1

Role replacement requires Tabs and Kids permissions

SetManagerPermissionRole — POST /api/v1/managers/{managerKid}/permission-role: every role now requires the caller's Read and Write permissions on both Tabs and Kids, checked from the fresh authorization snapshot inside the transaction before any write. Previously only accounting checked Tabs and no role checked Kids, although all roles replaced both permission masks. Newly rejected requests return 403 with missing-tabs-read, missing-tabs-write, missing-kids-read or missing-kids-write. Request and success-response fields are unchanged.

Migration: require both categories' Read/Write permissions before offering role replacement, handle these 403 codes and obtain the missing permissions through an authorized administrator. Existing manager, tenant and own-edit checks remain. UserBalance is unchanged.

Release 2026-10-07 · available on this host · clients 0.5.2

Default manager retention is 10 days

GetCurrentManager — GET /api/v1/session/me, GetMyManagerProfile — GET /api/v1/session/me/profile, GetManagers — GET /api/v1/managers and GetManager — GET /api/v1/managers/{managerKid}: retentionDays now defaults to 10 when the stored setting is absent or SQL NULL, previously 0. Explicit values, including 0, are preserved; malformed or negative values still resolve to 0. This also changes the default visibility window for otherwise authorized deleted records; no records are physically deleted or migrated. Integrations should use the returned value; save an explicit 0 if deleted records must stay hidden.

Changes to existing endpoints’ request or response contracts that may require changes in your integration. Internal fixes and improvements that keep the contract compatible are not listed.

Each entry identifies the endpoint, the previous and new contract, the customer action and the release status. Release labels describe the version served by this host. Beta and production roll out independently; check the documentation on your target host before migrating.

Tracking starts on 27 September 2026. Earlier releases have not been backfilled.

Release 2026-10-07 · available on this host · clients 0.5.2

Remove service permission, KID and tab assignments

GetService — GET /api/v1/services/{serviceKid}, SetServiceProfileField — POST /api/v1/services/{serviceKid}/profile/{field} and GenerateServiceApiKey — POST /api/v1/services/{serviceKid}/api-key: remove the previously unreleased access object (nested under details for key generation), including resourceGrants, tabs, availableTabs, operationPermissions, kidsRevision, tabsRevision, canEdit, canEditTabs and canEditKids.

Removed operations: SetServicePermission — POST /api/v1/services/{serviceKid}/permissions/{resource}, SetServicePermissionRole — POST /api/v1/services/{serviceKid}/permission-role, SetServiceTab — POST /api/v1/services/{serviceKid}/tabs/{tabId} and SetServiceKid — POST /api/v1/services/{serviceKid}/kids. Their request bodies and response contracts are no longer supported.

Migration: remove service access editors and stop sending assignment requests or reading access fields. Each business endpoint explicitly authorizes the authenticated eUserId; synchronization requires ServiceSync. Service authentication, account state, key revocation, tenant isolation and manager authorization for service administration remain enforced. Existing stored access settings are preserved but ignored for service authorization. The Tabs/Kids entry below describes the final manager-only contract. UserBalance is unchanged. Clients 0.5.2 include this contract.

Release 2026-10-07 · available on this host · clients 0.5.2

Logo parts use URL path segments

GetKombineLogo, GetKombineLogoSized and GetKombineLogoWithBackground — GET /api/v1/logos/kombine/{color}.svg, /{color}/{width}.svg and /{color}/{background}/{width}.svg: the earlier local, unreleased optional parts query parameter has been removed. These routes return the complete symbol.

Migration: replace /api/v1/logos/kombine/black.svg?parts=W with /api/v1/logos/kombine/W/black.svg (GetKombineLogoParts). The intermediate unreleased /kombine/parts/{parts}/... routes have also been replaced by /kombine/{parts}/...; remove the literal parts/ segment. Combinations use the same segment, for example /api/v1/logos/kombine/NS/black.svg. Width and background/width variants use GetKombineLogoPartsSized and GetKombineLogoPartsWithBackground. All parameters precede the final .svg. Parts accepts 1–4 letters N/E/S/W, case-insensitively; invalid selections return 400 with invalid-logo-parameters. SVG geometry and existing full-symbol contracts are unchanged. Clients 0.5.2 include this contract.

Release 2026-10-07 · available on this host · clients 0.5.2

Independent Tabs and Kids permissions; nine-category role requests

SetManagerPermissionRole — POST /api/v1/managers/{managerKid}/permission-role: expectedFlags previously required seven categories; now exactly nine are required, adding Tabs and Kids. Incomplete requests return 400 invalid-permission-role. Role responses and operationPermissions projections add these two resources; match by name rather than position. Full support presets set both to 63; accounting/caretaker set both to Read (1). Existing stored values are preserved; absent values default to Read, malformed values grant nothing.

SetManagerTab — POST /api/v1/managers/{managerKid}/tabs/{tabId} and SetManagerKid — POST /api/v1/managers/{managerKid}/kids now additionally require the caller's respective Tabs/Kids Read and Write. Applying accounting also requires Tabs Read/Write. Previously these operations relied on the parent Managers edit permission and existing scope checks. New 403 codes: missing-tabs-read, missing-tabs-write, missing-kids-read, missing-kids-write. Existing parent, tenant and own-edit checks remain.

Migration: reread the object and send all nine expectedFlags, with explicit null for invalid masks. Handle independent manager canEditTabs/canEditKids hints. Role canEditTabs may differ from canEditPermissions. Grant the new write flags explicitly through an authorized permission editor before changing assignments; there is no automatic data migration. UserBalance is unchanged. Clients 0.5.2 include this manager contract.

Release 2026-10-05 · available on this host · clients 0.4.1, PHP 0.4.2

Location directory optional fields must be requested

GetLocations — GET /api/v1/locations: previously vismaCustNo was always a string and both activation-code fields were populated whenever authorized. They now default to null unless explicitly selected with the new comma-separated fields query parameter. Select vismaCustNo,bankActivationCode,locationActivationCode to retain the previous values (code permissions still apply). New optional fields are address, zip, longitude and latitude. Status, canonical KIDs, names and icons remain present. The page adds a normalized fields array.

Migration: request every optional field your client consumes and accept null for unselected values. Send the same selection on every page; restart without the old cursor after changing it. Unknown fields return 400 invalid-fields, and changing a cursor's selection returns 400 invalid-cursor. Packaged clients 0.4.1 include this contract.

Release 2026-10-05 · available on this host · clients 0.4.1, PHP 0.4.2

Version 2 QR noise and checksum expanded to 30 bits

GetBankUserActivation — GET /api/v1/banks/{bankKid}/users/{userKid}/activation: in qrCodeDataV2, noise and checksum (the fourth and fifth decoded values) change from unsigned 24-bit values (0–16777215) to unsigned 30-bit values (0–1073741823). The checksum modulus changes from 16777216 to 1073741824: (((bankCode * 31 + userCode) * 31 + seconds) * 31 + noise) % 1073741824. Noise is freshly generated with a cryptographic random generator. The five-value order and JSON string representation are unchanged; version 1 is unchanged.

Migration: continue decoding five values, expand range validation for both noise and checksum to 30 bits, and use the new modulus at each checksum step with UInt64 intermediates. Regenerate QR codes from the earlier unreleased version 2 format; no old-modulus fallback. Packaged clients 0.4.1 and PHP 0.4.2 include this contract.

Development revision · superseded before beta release

Version 2 QR payload adds random noise before the checksum

GetBankUserActivation — GET /api/v1/banks/{bankKid}/users/{userKid}/activation: qrCodeDataV2 changes from four encoded numbers (bankCode, userCode, seconds, checksum) to five (bankCode, userCode, seconds, noise, checksum). Noise is a fresh cryptographically generated unsigned 24-bit value, 0–16777215. The checksum remains 24-bit but now includes noise: (((bankCode * 31 + userCode) * 31 + seconds) * 31 + noise) % 16777216. The JSON field remains a non-null string; version 1 is unchanged.

Migration: decode five values with FlexCipherLongs.Parse(5, fragment), validate the fourth and fifth values as 24-bit, and include noise when verifying the fifth value. Reduce after each arithmetic step using UInt64 intermediates. Regenerate earlier unreleased version 2 QR codes; no compatibility fallback. Noise may repeat and is not authentication or replay protection. Packaged clients will be synchronized at beta preparation.

Development revision · superseded before beta release

Version 2 QR checksum changed to weighted 24-bit arithmetic

GetBankUserActivation — GET /api/v1/banks/{bankKid}/users/{userKid}/activation: the fourth decoded value in qrCodeDataV2 previously used the unsigned 16-bit sum (bankCode + userCode + seconds) % 65536. It now uses the unsigned 24-bit value ((bankCode * 31 + userCode) * 31 + seconds) % 16777216, in the range 0–16777215. The JSON field remains a non-null string, and FlexCipherLongs still encodes four numeric values. This is a logical three-byte checksum, not a separate fixed-width byte field. Version 1 and permission requirements are unchanged.

Migration: update version 2 readers to validate the new range and formula. Reduce each input modulo 16777216 before multiplication/addition and use wide intermediate integers to avoid overflow. Regenerate QR codes from the earlier unreleased version 2 format; the old checksum is not accepted as a fallback. This checksum is error detection, not authentication. Packaged clients will be synchronized during beta release preparation.

Release 2026-10-05 · available on this host · clients 0.4.1, PHP 0.4.2

Explicit version 1 name for resident QR data

GetBankUserActivation — GET /api/v1/banks/{bankKid}/users/{userKid}/activation: the response field qrCodeData is renamed to qrCodeDataV1. The old field is removed without an alias. The value remains a non-null JSON string with the unchanged version 1 FlexCipherLongs payload, or an empty string when no tenant activation URL exists. qrCodeDataV2 and authorization requirements are unchanged.

Migration: rename the response property in your model and read qrCodeDataV1 when rendering version 1. No QR payload or decoding changes are needed. Packaged clients will be synchronized during beta release preparation.

Release 2026-10-02 · available on this host · clients 0.3.1

Compact reservation rules, complete role requests and canonical map route

GetLocationBookingRules — GET /api/v1/locations/{locationKid}/booking-rules: previously each groups entry contained a complete policy for units sharing calendar/settings. Now groups contain the compact presentation: identical rules are combined; shared rules appear once in a section with common:true, localized name and optional help, followed by differences. Each section identifies its applicable units. Render groups in order and combine applicable common and specific rules when evaluating the displayed policy for a unit. Do not treat a differences section as a complete policy or combine reservation quotas. The development-only displayGroups field is removed; use groups.

SetManagerPermissionRole — POST /api/v1/managers/{managerKid}/permission-role: expectedFlags must include all seven categories. Previously omitting only Service preserved its stored value; now incomplete requests return 400 invalid-permission-role without writes. Read the current matrix and send Managers, Installer, Service, Bank, Location, Unit and User.

GetPublicDisp73 — GET /api/v1/public/displays/Map1: the old /api/v1/public/displays/disp73 alias is removed and returns 404. Update stored URLs to Map1. The operation ID and canonical route response are unchanged. UserBalance contracts are unchanged.

Release 2026-10-01 · available on this host

Presentation response fields renamed to IconKid

Breaking response change: icon, bankIcon and unitIcon become iconKid, bankIconKid and unitIconKid, including nested objects, navigation, tabs, audit editors and document columns. They remain JSON strings. Previously an enum name; now an API-computed icon identity. Only Kid.Icon returns eIcon.ToString(); extra text/count/colour/icons return canonical Kid.ToString(). Empty/unavailable metadata remains an empty string. Object numbers are embedded in Text; calendar Text is the current day of month in Europe/Copenhagen. Rendering an existing filename does not consult the clock.

Stable operation IDUnchanged method/path
GetCurrentManagerGET /api/v1/session/me
GetMyManagerProfileGET /api/v1/session/me/profile
SetMyManagerProfileFieldPOST /api/v1/session/me/profile/{field}
GetManagersGET /api/v1/managers
GetManagerGET /api/v1/managers/{managerKid}
SetManagerProfileFieldPOST /api/v1/managers/{managerKid}/profile/{field}
SetManagerTabPOST /api/v1/managers/{managerKid}/tabs/{tabId}
SetManagerPermissionRolePOST /api/v1/managers/{managerKid}/permission-role
GetInstallersGET /api/v1/installers
GetInstallerGET /api/v1/installers/{installerKid}
SetInstallerIconPOST /api/v1/installers/{installerKid}/icon
GetServicesGET /api/v1/services
GetServiceGET /api/v1/services/{serviceKid}
SetServiceProfileFieldPOST /api/v1/services/{serviceKid}/profile/{field}
GenerateServiceApiKeyPOST /api/v1/services/{serviceKid}/api-key
GetLocationsGET /api/v1/locations
GetBankLocationsGET /api/v1/banks/{bankKid}/locations
GetLocationUnitsGET /api/v1/locations/{locationKid}/units
GetUnitOverviewGET /api/v1/units/{unitKid}
GetUnitGroupGET /api/v1/units/{unitKid}/groups/{kind}/{group}
SetUnitSettingPOST /api/v1/units/{unitKid}/groups/settings/{group}/{setting}
GetUnitSettingHistoryGET /api/v1/units/{unitKid}/groups/settings/{group}/{setting}/history
GetBankUsersGET /api/v1/banks/{bankKid}/users
GetBankUserWorkspaceGET /api/v1/banks/{bankKid}/users/{userKid}/workspace
CreateBankUserPOST /api/v1/banks/{bankKid}/users
ExecuteBankUserCommandPOST /api/v1/banks/{bankKid}/users/{userKid}/commands
GetBankBookingsGET /api/v1/banks/{bankKid}/bookings
ExecuteBankBookingCommandPOST /api/v1/banks/{bankKid}/bookings/{bookingKid}/commands
GetBankAccountGET /api/v1/banks/{bankKid}/account
GetBankDocumentsGET /api/v1/banks/{bankKid}/documents
GetUnitDocumentTableGET /api/v1/documents/{documentKid}/table
GetTenantStatusGET /api/v1/tenant/status
SearchBanksGET /api/v1/search/banks
SearchBankActivationGET /api/v1/search/bank-activation
GetSearchBankGET /api/v1/search/banks/{bankKid}
SearchLocationsGET /api/v1/search/locations
SearchLocationActivationGET /api/v1/search/location-activation
SearchUsersGET /api/v1/search/users
SearchUserSmsGET /api/v1/search/user-sms
SearchUserActivationGET /api/v1/search/user-activation

Migration: rename response model properties and pass the supplied string unchanged, URL-encoded, to /api/v1/icon/{iconSet}/{kid}.svg. Stop interpreting every value as an enum name or composing KIDs in the portal. Use public GetIconPresentation — GET /api/v1/icon/presentation for presentation options and a fresh calendar identity. Icon setting writes and availableIcons still use enum names; service objects also expose iconName for selection. GetActiveLocationCount (GET /api/v1/locations/active-count) additionally returns a ready-to-render iconKid with its badge. Permissions and tenant binding are unchanged; these values grant no access.

Release: 2026-10-01, client version 0.2.1. Generated clients, OpenAPI snapshots and downloadable packages are synchronized with this contract. This release label applies to the version served by this host; beta and production are promoted independently. Existing image paths and image caching are unchanged.

Release 2026-10-01 · available on this host

Icon images · Canonical KID with icons, count, color and text; required asset set

Affected requests: kid previously accepted an eIcon name, numeric value/index or substring. It now first parses a canonical Kombine.Flex.Kid.ToString() and reads Kid.Icons, Kid.Count (Int64), Kid.Color and Kid.Text. Separate count/color/text/sub path fields are removed. Color uses the low 24 bits as opaque RGB (0 = black; the high byte is ignored, as for Flex eColor). Text is UTF-8, case-sensitive and limited to 128 characters without controls. Encoded canonical KIDs are limited to 2048 characters. If that fails, an exact case-insensitive eIcon name is accepted with count zero, black and empty text. Numeric/index/substring enum lookup is no longer a fallback; invalid input returns 400. The first list entry is the main icon and the second is the under-icon. A missing/none second entry means no under-icon; later entries do not affect rendering. Every list entry must be a defined eIcon value. An empty list uses eIcon.none. Other KID fields cause no business lookup or permission change. Count ≤ 0 hides the badge. Positive counts display in full in a red capsule whose straight middle widens between circular ends. Very long labels widen the SVG canvas; no count is abbreviated.

Operation IDPrevious method/pathNew method/path
GetIconFromSetGET /api/v1/icon/{iconSet}/{kid}/{color}/{count}/{text}/{sub}.{format}GET /api/v1/icon/{iconSet}/{kid}.{format}
GetIconImageFromSetGET /api/v1/icon/{iconSet}/{kid}/{color}/{count}/{text}/{sub}/{size}.{format}GET /api/v1/icon/{iconSet}/{kid}/{size}.{format}
GetIconImageWithBackgroundFromSetGET /api/v1/icon/{iconSet}/{kid}/{color}/{count}/{text}/{sub}/{backColor}/{size}.{format}GET /api/v1/icon/{iconSet}/{kid}/{backColor}/{size}.{format}
GetIcon2Svg (removed)GET /api/v1/icon/{kid}/{color}/{count}/{text}/{sub}.{format}Use GetIconFromSet with explicit line or g.
GetIcon2Image (removed)GET /api/v1/icon/{kid}/{color}/{count}/{text}/{sub}/{size}.{format}Use GetIconImageFromSet with explicit line or g.
GetIcon2ImageWithBackground (removed)GET /api/v1/icon/{kid}/{color}/{count}/{text}/{sub}/{backColor}/{size}.{format}Use GetIconImageWithBackgroundFromSet with explicit line or g.

Customer action

The interim local route /api/v1/icon/{iconSet}/{kid}/{sub}.{format} is also replaced by /api/v1/icon/{iconSet}/{kid}.{format}. Append the former sub value as the second entry of Kid.Icons and remove its path segment; the size/background forms remove that segment in the same way.

Add the main icon and optional under-icon to Kid.Icons in that order, set Count, Color and Text on a Kid, URL-encode ToString(), remove the count/color/text/sub segments and select the set explicitly (line preserves the former default). For example, Kid.Icons = [house, check], Count = 7, Color = 0x336699, Text = "A12" produces 413132x7qE20i11Bi336699Ic: use /api/v1/icon/line/413132x7qE20i11Bi336699Ic.svg. For count zero, black and empty text, /api/v1/icon/line/house.svg also works. Update stored links/builders; old shapes are not aliases and overlapping paths can be interpreted as different requests. Rendering formats, set fallback, cache/304 behavior and the public asset catalog remain unchanged. Unknown sets/missing assets return 404; invalid parameters return 400.

Release: 2026-10-01, client version 0.2.1. Generated clients, OpenAPI snapshots and downloadable packages are synchronized with this contract. Upgrade the client and migrate the removed routes as described above. This release label applies to the version served by this host; beta and production are promoted independently.

Release 2026-09-28 · available on this host

Account2 · Retention-aware identities, decoded descriptions and reversal eligibility

Operation IDMethod/path
GetBankAccountGET /api/v1/banks/{bankKid}/account
ExportBankAccountGET /api/v1/banks/{bankKid}/account/export
GetBankAccountRevisionGET /api/v1/banks/{bankKid}/account/revision
ReverseBankAccountEntryPOST /api/v1/banks/{bankKid}/account/{transactionKid}/reversal

Previous: listing/export did not apply tenant/manager retention settings. userKid, userName and userNumber could identify expired entries; descriptions could contain partial legacy text or internal payment markers. Ordinary reversal eligibility did not explicitly reject expired or payment-managed consumption.

New: tenant LawAccountingYears and LawSurveillanceDays, with positive manager overrides, mask expired identities in general views: userKid becomes the bank's GDPR user KID (UserId 1000), userName/userNumber become empty strings, description contains only the transaction type, isAnonymized is true and canReverse is false. Zero/missing/invalid retention values retain no identity. With an explicit userKid, expired entries are excluded from rows and totals. CSV/XLSX applies the same rules with unchanged columns. Descriptions use the full shared decoder; internal payment IDs are removed. Payment-managed and expired entries return 422 reversal-unavailable on ordinary reversal. Revision values now include retention visibility and caches are separated by manager. Amount representation and request fields remain unchanged.

Migration: display an anonymous label when isAnonymized is true and never link it to a resident profile. Do not interpret description text as a payment identifier; use the additive paymentKind field (Credit, ReserveRefund, Managed or empty). Honor canReverse and handle 422 without retrying. Do not merge pages with different revisions. The additive documentKey/documentId and documents fields group only filtered lines on each page; merge groups by key across pages, not DocId alone. Existing flat items and row pagination remain supported.

Release: 2026-09-28. The clients and downloadable packages have been synchronized with this contract; package version 0.1.0 is retained and no external registry publication is claimed.

Release 2026-09-28 · available on this host

Icon images · Missing assets are searched in other local sets

Affected responses: an icon missing from the selected set previously returned HTTP 404 even when another local set contained it. Rendering now searches for the same eIcon identity in the selected set first, then other packaged sets in ordinal alphabetical order. Each main/under-icon resolves independently. A matching asset returns HTTP 200 in the requested format, or 304 for a matching conditional request. Unknown sets and icons absent from every local set still return 404. Request fields, format encodings and existing images in the selected set are unchanged. There is no external-server or database fallback. Included in release 2026-09-28.

Operation IDMethod/path
GetIcon2SvgGET /api/v1/icon/{kid}/{color}/{count}/{text}/{sub}.{format}
GetIcon2ImageGET /api/v1/icon/{kid}/{color}/{count}/{text}/{sub}/{size}.{format}
GetIcon2ImageWithBackgroundGET /api/v1/icon/{kid}/{color}/{count}/{text}/{sub}/{backColor}/{size}.{format}
GetIconFromSetGET /api/v1/icon/{iconSet}/{kid}/{color}/{count}/{text}/{sub}.{format}
GetIconImageFromSetGET /api/v1/icon/{iconSet}/{kid}/{color}/{count}/{text}/{sub}/{size}.{format}
GetIconImageWithBackgroundFromSetGET /api/v1/icon/{iconSet}/{kid}/{color}/{count}/{text}/{sub}/{backColor}/{size}.{format}

Customer action

Do not interpret a successful render as proof that the asset belongs to the requested set. Use the documentation set catalogs to inspect actual membership. For example, /api/v1/icon/g/house/black/0/0/none.svg now renders the house asset from line. Paths without a set still prefer line. Clients that implement their own 404-based set search can rely on server-side lookup instead. Refresh rendered images or allow the existing ten-minute browser cache to expire; renderer build changes invalidate disk-cache entries.

Release 2026-09-28 · available on this host

GetKombineLogo / GetKombineText / GetKombineLogoText · Responsive SVG dimensions

Endpoints: GET /api/v1/logos/kombine/{color}.svg, GET /api/v1/logos/kombine-text/{color}.svg and GET /api/v1/logos/kombine-logo-text/{color}.svg. The SVG response root previously had width="256" and a proportional numeric height (256, 48.162712 or 51.2). These attributes are now omitted. The unchanged viewBox and preserveAspectRatio="xMidYMid meet" let the complete artwork fit and center in its viewport without cropping or stretching. Routes with an explicit width retain their pixel dimensions. Colors, geometry, content type, operation IDs and status codes are unchanged. Included in release 2026-09-28.

Customer action

If your layout or SVG parser requires fixed dimensions, use the corresponding /{color}/256.svg route, or specify dimensions on the embedding element. Do not assume the unsized response contains numeric width/height attributes. Renderer cache keys have changed; clients can refresh or wait for the existing 600-second browser cache to expire.

Release 2026-09-28 · available on this host

LoginManager · Duplicate credentials now select one active manager

Endpoint: POST /api/v1/session/login. Affected: the status and account selection when multiple managers match both email and password. Previously this case returned HTTP 401. It now returns HTTP 200 for the active, non-deleted manager with the newest eSetting.Alive krumb MS2000, after atomically clearing Password on the other matching managers. Activity uses the krumb timestamp, not Text; missing or invalid timestamps rank last. Equal timestamps, including all unknown, are resolved by the lowest UserId. The selection uses a fresh locked read in the cleanup transaction. Different passwords sharing an email are unchanged. No active match means no cleanup and the lowest matching account determines the existing HTTP 403 account-state error. Cleanup/storage failures or more than 100 matching rows return HTTP 503. Request fields and the token response representation are unchanged. Included in release 2026-09-28.

Customer action

Do not rely on duplicate credentials returning 401. Call GetCurrentManager after login and use its identity and permissions; grants from duplicate accounts are not combined. Sessions of accounts whose passwords are cleared become invalid, subject to the existing maximum 60-second cache on other API instances. To keep distinct accounts usable, give them distinct credentials before this change is released. Do not automatically retry an uncertain login/cleanup response.

Release 2026-09-28 · available on this host

GetIconFromSet / GetIconImageFromSet / GetIconImageWithBackgroundFromSet · Shorter icon-set paths

Affected requests: remove the literal sets segment after /api/v1/icon/. The previous set-specific paths are removed. The operation IDs, parameter names, rendering, response formats and cache behavior are unchanged. Included in release 2026-09-28.

Operation IDPrevious method/pathNew method/path
GetIconFromSetGET /api/v1/icon/sets/{iconSet}/{kid}/{color}/{count}/{text}/{sub}.{format}GET /api/v1/icon/{iconSet}/{kid}/{color}/{count}/{text}/{sub}.{format}
GetIconImageFromSetGET /api/v1/icon/sets/{iconSet}/{kid}/{color}/{count}/{text}/{sub}/{size}.{format}GET /api/v1/icon/{iconSet}/{kid}/{color}/{count}/{text}/{sub}/{size}.{format}
GetIconImageWithBackgroundFromSetGET /api/v1/icon/sets/{iconSet}/{kid}/{color}/{count}/{text}/{sub}/{backColor}/{size}.{format}GET /api/v1/icon/{iconSet}/{kid}/{color}/{count}/{text}/{sub}/{backColor}/{size}.{format}

Customer action

Remove sets/ from set-specific image URL builders and stored links. For example, use /api/v1/icon/line/house/000000/7/A12/none/128.svg. Keep the set name and all other path values. Existing icon-first routes without a set name still use line; known set names take precedence where route shapes overlap. The rebuilt clients in release 2026-09-28 use these paths.

Release 2026-09-28 · available on this host

GetIcon2Svg / GetIcon2Image / GetIcon2ImageWithBackground · Icon paths and format parameter

Affected requests: the URL prefix for all three GET image operations changes from /Icon2 to /api/v1/icon. The former routes are removed. Operation IDs are retained. The extension parameter is now named format instead of fileType on the sized routes; it is a required path parameter. The short route replaces its fixed .svg suffix with required .{format} and also accepts raster formats, using 128 × 128 pixels when no size is supplied. Existing SVG and sized-image rendering semantics, content types and conditional caching are unchanged. Included in release 2026-09-28.

Operation IDPrevious method/pathNew method/path
GetIcon2SvgGET /Icon2/{kid}/{color}/{count}/{text}/{sub}.svgGET /api/v1/icon/{kid}/{color}/{count}/{text}/{sub}.{format}
GetIcon2ImageGET /Icon2/{kid}/{color}/{count}/{text}/{sub}/{size}.{fileType}GET /api/v1/icon/{kid}/{color}/{count}/{text}/{sub}/{size}.{format}
GetIcon2ImageWithBackgroundGET /Icon2/{kid}/{color}/{count}/{text}/{sub}/{backColor}/{size}.{fileType}GET /api/v1/icon/{kid}/{color}/{count}/{text}/{sub}/{backColor}/{size}.{format}

Customer action

Update image URL builders and stored links to use /api/v1/icon/. Use format when binding parameters by their OpenAPI names, and pass svg explicitly for the former SVG-only operation. Keep presentation values in their existing path segments; no query parameters are required. For example, use /api/v1/icon/house/000000/7/A12/none.svg or /api/v1/icon/house/000000/7/A12/none/128.png. Continue URL-encoding individual path values. The rebuilt clients in release 2026-09-28 use these paths.

Release 2026-09-28 · available on this host

GenerateServiceApiKey · Generated keys now start with kt_

Endpoint: POST /api/v1/services/{serviceKid}/api-key

Affected: the response field apiKey. Previously, generated keys started with k followed by 64 random ASCII letters and digits (65 characters total). New keys start with kt_ followed by the same 64-character random payload (67 characters total). The payload still includes both uppercase and lowercase letters. Keys remain case-sensitive. The request and all other response fields are unchanged. Included in release 2026-09-28.

Customer action

Allow the underscore and the new total length in fields and validators that consume generated keys. Preserve the exact value returned by the API, including the prefix. Prefer treating keys as opaque strings. Do not add or replace a prefix on existing keys: their stored hashes and login behavior are unchanged. Both existing keys and new kt_ keys remain usable with Equipment service login until replaced or revoked. Client packages were rebuilt for release 2026-09-28.

Release 2026-09-28 · available on this host

SetServiceProfileField · ApiKeyHash no longer accepts manual writes

Endpoint: POST /api/v1/services/{serviceKid}/profile/{field}

Affected: the field path parameter and the response to manual hash writes. Previously, ApiKeyHash accepted a caller-supplied hash, or an empty value to clear it, and returned HTTP 200 on success. Only Name and Icon are now accepted. Requests with field=ApiKeyHash return HTTP 400 with problem code invalid-service-profile, regardless of the supplied value. No hash is written or cleared. Included in release 2026-09-28.

Customer action

Remove manual hash editing and clearing. To replace a service key, read its latest profileRevision with GetService, then call GenerateServiceApiKey at POST /api/v1/services/{serviceKid}/api-key:

{"expectedRevision":"<profileRevision from GetService>"}

Use the returned apiKey only after a successful HTTP 200 response and keep details.profileRevision for later edits. Generation replaces the existing key; the plaintext key is returned once. Do not retry automatically after an uncertain response. Manual key import and clearing are no longer supported. Existing stored hashes and response shapes are unchanged. Client packages were rebuilt for release 2026-09-28.

Locations1 lokationsliste

Én bank: Både GetLocations og GetActiveLocationCount accepterer valgfri bankKid, en kanonisk bank-KID i sidens tenant. Den afgrænser eksisterende rettigheder før søgning, sortering og sideinddeling og giver aldrig ekstra adgang. Eksempel: GET /api/v1/locations?bankKid={bankKid}&fields=address&enabledOnly=true. Gentag samme bankKid med hver cursor; ændringer giver 400 invalid-cursor. Ugyldig KID eller forkert tenant giver 400 invalid-bank. Udelad den for at vise lokationer på tværs af banker. Lokalt implementeret, ikke udgivet.

Antal filtrerede lokationer (ikke udgivet)

GetLocationCount — GET /api/v1/locations/count returnerer {"count":123} for hele det filtrerede resultat. Brug samme filter, fields, enabledOnly, bankKid, sort og direction som GetLocations. Valgte felter indgår i søgningen. Samme Locations1, Bank Read, Location Read, tenant/ressourceadgang, retention, KID/kodesøgning og kodeadgang kontrolleres. Antallet omfatter alle matchende tilstande, som reglerne tillader; det er ikke navigationens aktive tæller. Ingen cursor eller pageSize.

const query = new URLSearchParams({ filter: 'q3', fields: 'soapEnd', enabledOnly: 'true', sort: 'soapEnd', direction: 'desc' });
const response = await fetch(apiBase + '/api/v1/locations/count?' + query, {
  headers: { Authorization: 'Bearer ' + accessToken }
});
if (!response.ok) throw new Error('Antallet kunne ikke hentes: ' + response.status);
const { count } = await response.json();

Start optællingen parallelt med datahentningen. Vis ukendt fremdrift, indtil antallet er klar; regn aldrig fejl som nul. HTTP 400: ugyldige valg; 401: log ind igen; 403: manglende adgang/rettigheder; 503 locations-unavailable: databasefejl eller overskredet frist på 12 sekunder. No-store. Antallet er præcist ved opslaget, ikke et snapshot af efterfølgende sider. Samtidige ændringer i data eller rettigheder kan ændre det endelige antal; vis ikke en falsk 100%. Afbryd et uafsluttet count-kald, når datahentningen er færdig. Lokalt implementeret, ikke udgivet; klientgenerering og pakker udskydes til betaforberedelsen.

Filtrerede eksportantal for alle kataloger

Excel-eksport starter med 100 rækker og tilpasser derefter hver side mellem 10 og 250 rækker efter den målte API-svartid med et mål på 2,5 sekunder. Hvert datakald har en annulleringstimeout på 3 sekunder. Ved timeout gentages samme cursor med halvt så mange rækker; fejler minimumsstørrelsen også, afbrydes eksporten uden en delvis fil. Private katalogers pageSize må ændres med cursor; filtre, sortering, valgte felter og scope skal bevares. Det separate count-kald beholder sin egen tidsgrænse. Normale listesidestørrelser og balance-batches er uændrede. Tidligere enheds-/terminalcursors kræver genstart uden cursor (400 invalid-cursor). Ikke udgivet.

Disse endpoints returnerer {"count":123} for hele det autoriserede eksportudvalg. Send samme filter, sort, direction som listen. Banker og enheder/terminaler accepterer også enabledOnly, fields; banker bankType; enheder/terminaler locationKid, terminalKid, runningOnly (enheder også autoOutOfOrderOnly); installatører includeActivationCode; beboersøgning enabledOnly; bankens beboere locationKid, deleted. Send ikke cursor eller sidestørrelse.

Operation IDGET
GetBankExportCount/api/v1/banks/export-count
GetTerminalExportCount/api/v1/terminals/export-count
GetUnitExportCount/api/v1/units/export-count
GetManagerExportCount/api/v1/managers/export-count
GetInstallerExportCount/api/v1/installers/export-count
GetUserExportCount/api/v1/users/export-count
GetBankUserExportCount/api/v1/banks/{bankKid}/users/export-count
GetServiceExportCount/api/v1/services/export-count
curl "$BASE/api/v1/units/export-count?filter=Laundry&enabledOnly=true&sort=name&direction=asc" -H "Authorization: Bearer $TOKEN"

Hver optælling genkontrollerer listens adgang, scope, retention og filtre. Banker, lokationer og enheder/terminaler bruger direkte SQL-optællinger. Beboersøgningen tæller hele banker i SQL; ved lokationsadgang læses kun identitet/adgang gennem den fælles dekoder for ældre Locations og JSON, uden detaljer eller sidegennemgang. Administratorer, installatører og bankens beboere tælles fra ét minimalt filtreret indeks (eksisterende indeks kan være op til 60 sekunder gamle); services bruger deres faste katalog. Eksisterende frister og grænser giver stadig 503 frem for delvise antal. Start count parallelt med data, vis rækker mens det afventes/fejler, og skift derefter til procent. HTTP 400: ugyldige valg; 401: login; 403: manglende adgang. No-store. Der er intet snapshot på tværs af senere sider, så samtidige ændringer kan gøre antallet ugyldigt. Navigationens antal bevarer deres egen betydning. Lokalt implementeret, ikke udgivet; klienter/pakker følger ved beta-forberedelsen.

Antal aktive lokationer

GetActiveLocationCount — GET /api/v1/locations/active-count. Returnerer {"count":123} til Lokationer/Locations1-ikonet. Kræver samme aktive administrator, Locations1, Bank Read, Location Read og tenant/bank/lokationsadgang som GetLocations. Tæller kun forskellige lokationer med Enabled præcis 1 og Deleted=0, også ved adgang til alle banker. Manglende Deleted regnes som nul; manglende/ugyldig Enabled og ugyldig Deleted udelades. Ved begrænset adgang skal den overordnede bank stadig være synlig efter RetentionDays. Uafhængigt af søgning og sideinddeling; banker under 1000 udelades. Antallet læses på ny ved hvert kald; rettighedsdata er højst 60 sekunder gamle. No-store. 401 kræver login; 403 betyder manglende fane, læserettigheder eller ressourceadgang; 503 locations-unavailable betyder utilgængelig database eller en overskredet frist på 12 sekunder—prøv igen manuelt, og vis aldrig fejlen som nul. Portalen henter antallet i baggrunden, efter navigationen er vist, uden at forsinke siden. Der foretages ét kald pr. navigationselement uden løbende polling. Ved fejl vises ingen tæller; ikonet og hjælpeteksten viser hele antallet; den hvide kapsel bliver bredere efter antal cifre.

curl "$BASE/api/v1/locations/active-count" -H "Authorization: Bearer $TOKEN"

GetLocations — GET /api/v1/locations viser tilgængelige lokationer på tværs af banker. Kræver aktiv administrator, Locations1 (65), Bank Read, Location Read og adgang til relevant tenant/bank/lokation, kontrolleret på hver side. Lokationsadgang giver aldrig andre lokationer. Med eksplicit adgang til hele tenanten vises alle lokationstilstande, også deaktiverede og gamle sletninger; bankens sletning skjuler dem ikke. Med begrænset adgang vises kun lokationer med Enabled præcis 1, og både bankens og lokationens sletning følger RetentionDays. Manglende Enabled er ikke aktiveret. Deleted=0 eller manglende værdi er synlig; positive tidsstempler skal ligge fra nu minus RetentionDays til nu, inklusive grænserne. Nul dages historik skjuler alle slettede; ugyldige eller fremtidige sletteværdier skjules ved begrænset adgang. Kun sitets Log24 læses; banker under 1000 udelades. Lokationen skal have Name, Icon, VismaCustNo, Enabled eller Deleted for at blive fundet.

curl "$BASE/api/v1/locations?pageSize=50&sort=name&direction=asc&filter=Vaskeri" -H "Authorization: Bearer $TOKEN"

enabledOnly=true udelader lokationer, hvor Enabled ikke er præcis 1, før sideinddeling. Standard er false. Filteret indsnævrer kun de tilladte resultater: eksisterende regler for sletning/RetentionDays og adgang til aktiveringskoder gælder stadig. Aktiverede, slettede lokationer vises fortsat, når adgangen tillader det. Brug samme værdi ved alle fortsættelser; ved ændring skal du starte uden cursor (ellers 400 invalid-cursor). Eksempel: GET /api/v1/locations?enabledOnly=true&sort=name&direction=asc&pageSize=50.

Valgte kolonner: fields er en kommasepareret liste med bankName,vismaCustNo,bankActivationCode,locationActivationCode,address,zip,longitude,latitude,teltonikaSms,alternativeBankName,mask,timeZone,online,lastContactAt. Udeladt eller tom betyder kun identifikatorer, status, navne og ikoner; fravalgte egenskaber er null. Svarets fields-array indeholder det normaliserede valg. Lokationens egne address og zip er tekst (tom ved manglende værdi), uden arv fra banken. longitude og latitude er decimale grader omregnet fra gemte mikrograder; manglende, ugyldige eller værdier uden for ±180/±90 bliver null. Kun valgte indstillinger tilknyttes i opslaget; eksternt ID kan stadig læses internt til søgning/sortering. Kodevalg omgår aldrig krav om tenantadgang og Create. Eksempel: GET /api/v1/locations?fields=vismaCustNo,address,zip,longitude,latitude&enabledOnly=true&pageSize=50. Behold samme valg ved fortsættelse; rækkefølge og dubletter er uden betydning. Ukendte felter giver 400 invalid-fields; ændret valg giver 400 invalid-cursor, så start uden cursor. Portalen genindlæser straks listen, når et kolonneflueben ændres i sidemenuen, og husker valget i browseren og sidens URL. KID bevares som API-identitet, men vises ikke som listekolonne.

Kortkoordinater: includeCoordinates=true returnerer også latitude og longitude uden at føje dem til tekstsøgningen eller ændre svarets fields-valg. Standard: false. Eksempel: GET /api/v1/locations?fields=address&filter=vej&includeCoordinates=true. Samme lokationsrettigheder og regler for nullable decimale grader gælder. Behold indstillingen ved hver cursor; ændringer giver 400 invalid-cursor, så start uden cursor. Hent alle sider; lokationer uden gyldige koordinater kan ikke vises på kortet.

De ekstra tilvalg teltonikaSms, alternativeBankName, mask og timeZone læser lokationens egne eSetting.TeltonikaSMS, eSetting.Bank, eSetting.Access og eSetting.TimeZone fra Log24. De er tekst: valgte, manglende værdier er tomme, fravalgte er null. Telefonpræfikser, maskesyntaks og den gamle tidszoneværdi bevares (fx betyder 100 UTC+1); det er ikke et IANA-tidszone-id eller en beregnet aktuel sommertidsforskydning. Ingen arv fra banken, og masken giver ikke API-rettigheder. Eksempel: GET /api/v1/locations?fields=teltonikaSms,alternativeBankName,mask,timeZone&pageSize=50.

Ekstra faktureringskolonner og sorteringsfelter: vismaCrAcNo, vismaInvoiceVersion, vismaOrdre, vismaPNTurnover, vismaPNSettlement, vismaSettlement, vismaVAT, vismaServiceKey, vismaStart, vismaNote, hiddenNote, vismaGuaranteeMonth, vismaGuaranteeUnder, vismaGuarantee, vismaGuaranteeCustomer, vismaGuaranteeOver, gift, giftBegin, giftEnd, giftSplit, giftPN, soapEnd. Hvert felt læser den tilsvarende eSetting fra lokationens Log24 uden arv fra banken. Værdier er gemt tekst: valgte manglende værdier er tomme, fravalgte er null. Gift og giftBegin/giftEnd sorteres numerisk; de øvrige faktureringsfelter sorteres som gemt tekst, også procenter og gamle datostrings. Gift er gemt i hundrededele; giftBegin/giftEnd er millisekunder siden 2000-01-01 UTC. Portalen formaterer gavebeløbet og disse datoer. Blandede beløb og procenter bevares uden omregning. giftPN læser den gamle lokationsindstilling 1620, som den fælles enum kalder DurationIsETA for enheder; her er det varenummer for opstartsgaven, ikke et ETA-flag. soapEnd læser den gemte datotekst fra lokationens eSetting.SoapEnd (3025), ligesom vismaStart; værdien er ikke MS2000. Den sorteres som gemt tekst og vises uændret; manglende værdier er tomme, og fravalgte værdier er null. Vælg feltet med GET /api/v1/locations?fields=soapEnd&sort=soapEnd; de eksisterende regler for manager, scope, retention og cursor gælder. Implementeret lokalt, ikke udgivet. Eksisterende vismaCustNo og zip giver kundenummer og postnummer.

Tilvalget bankName viser banknavn og bankikon sammen efter lokationen i portalen. API-svarets grundlæggende bankoplysninger er fortsat udfyldt.

Tekstfilteret søger i navne og eksternt id samt alle valgte gemte kolonner før sideinddeling. Fravalgte ekstra felter tæller ikke med, heller ikke når de bruges til sortering. Koordinater og gavebeløb kan også søges som decimaltal med punktum eller komma; gave-/kontaktdatoer kan søges som ISO-dato eller dd.MM.yyyy og dd-MM-yyyy (UTC for kontakttider). Online understøtter online/offline eller 1/0. Eksakt KID og hele aktiveringskoder bevarer eksisterende adgangskrav.

Tilvalgene online og lastContactAt læser kun den konfigurerede tenants Alive-tabel for den præcist tilladte lokations enheder fundet i Log24 inden for RetentionDays. Kun hovedenheder med Alive.UnitId = Alive.MainId tæller med; underenheder udelades. Online er false, hvis en synlig hovedenhed har Offline=1, true kun når et ikke-tomt sæt udelukkende har Offline=0, ellers null. LastContactAt er nyeste gyldige MS2000 større end nul og ikke i fremtiden, som UTC ISO 8601; manglende kontakt er null. Det er seneste kontakt, ikke seneste maskinkørsel. Forældreløse Alive-rækker tæller ikke med. Begge felter er null, og Alive læses ikke, når hverken felter eller sortering er valgt. sort=online sorterer ukendt/offline/online stigende; sort=lastContactAt sorterer kronologisk, ukendt først stigende og sidst faldende. Eksempel: GET /api/v1/locations?fields=online,lastContactAt&sort=lastContactAt&direction=desc&pageSize=50. Eksisterende adgangs- og cursorregler gælder. Portalen viser kontakttider i browserens tidszone.

items indeholder kid (lokation), bankKid, bankName, bankIconKid, name, iconKid, vismaCustNo, bankActivationCode, locationActivationCode, enabled, deleted og deletedAt. Enabled er kun true for gemt 1. Deleted er false ved nul/manglende værdi, true ved positiv MS2000 og null ved ugyldige værdier. DeletedAt er et ISO 8601-tidsstempel i UTC, eller null ved nul/ugyldig/værdi uden for datointervallet. Brug tilgængelige statusmarkeringer for deaktiverede, slettede og ukendt slettestatus. Portalen viser ét statusikon: Enabled=false er altid inaktiv; Enabled=true med Deleted=true er slettet, ellers aktiv når Deleted=false. Sletningstidspunktet kan ses i mouseover-teksten. Listens regler ændrer ikke adgangskontrollen på detaljeendpoints. KID'er er kanoniske; ingen separate numeriske identifikatorer. Manglende navne/kundenumre er tomme strenge; ugyldige ikoner bliver bank_building/house. Sidens hasAllBanksAccess angiver eksplicit adgang til hele tenanten, ikke en liste over individuelle banker. Begge koder kræver denne adgang; bankkoder kræver desuden Bank Create og lokationskoder Location Create. Ellers er koden null. Skjul begge kodekolonner, når flaget er false. Felterne giver aldrig API-rettigheder.

Valgfrit filter: højst 128 tegn, bogstavelig delstreng i bank-/lokationsnavn eller VismaCustNo uden forskel på store/små bogstaver eller accenter. Hele kanoniske, læsbare eller tenant-relative bank-/lokations-KID'er accepteres, fx 166.2000.4 eller 2000.4. Hele aktiveringskoder matches kun, hvis kalderen må se koden. Bankkoder indeholder tenant; lokationskoder bruger sitets tenant. Ingen læsning hos andre tenants.

sort: name (standard), bankName, vismaCustNo, address, zip, longitude, latitude, teltonikaSms, alternativeBankName, mask, timeZone, online, lastContactAt, bankActivationCode eller locationActivationCode. direction=asc (standard) eller desc. Sorteringen gælder alle tilladte søgeresultater, ikke kun den indlæste side. Koordinater og gamle tidszoneværdier sorteres numerisk; manglende/ugyldige tal kommer først stigende og sidst faldende. Postnumre, telefonværdier, masker, navne og eksterne id’er tekstsorteres med utf8mb4_general_ci før SQL LIMIT. Koder sorteres numerisk med FlexActivation og kræver tilsvarende tenant-/Create-adgang, ellers 403 missing-code-access. Kodesortering gennemlæser de tilladte søgeresultater og beholder højst pageSize+1 kandidater; indsnævr filteret ved store resultater for at undgå den eksisterende frist på 12 sekunder. Numeriske bank-/lokations-id’er bryder lighed i samme retning. Sorteringsfeltet læses også, når det ikke er valgt som svarkolonne. Eksempel: GET /api/v1/locations?fields=zip,address&sort=zip&direction=asc&pageSize=50. Sidestørrelse 1–100, standard 50; fortsæt med uændrede parametre og nextCursor indtil null. Cursors udløber efter 15 minutter og bindes til kalder, tenant, adgang, retention og forespørgsel. Start uden cursor ved ændringer. Ingen totaloptælling eller fuld liste i hukommelsen; samtidige omdøbninger kan flytte rækker.

400: invalid-page, invalid-filter, invalid-sort, invalid-cursor (start forfra). 401: log ind igen. 403: missing-banks-tab, missing-bank-read, missing-location-read, missing-resource-access (ret rettigheder). 503 locations-unavailable omfatter tidsgrænsen på 12 sekunder: vis fejl og tillad manuelt nyt forsøg. Svar er no-store. Kun læsning; tilgængelig i udvikling. Genererede klienter og downloadpakker er synkroniseret i version 0.4.2.

Forslag til beboernummer

GetBankUserNumberForNewUser: GET /api/v1/banks/{bankKid}/users/next-number bruger bankens første NumberFormats og gemte NumberFormatUserIndex (standard 1). GetBankNextUserNumber: GET /api/v1/banks/{bankKid}/users/next-number-after?userNumber=1420-01-0004 tager i stedet udgangspunkt i det angivne nummer.

Begge kræver en gyldig manager-bearersession, Users2, User Read, User Create og bankdækkende adgang. Lokationsadgang alene er ikke nok. Tenant bestemmes af sitet, og bankKid skal være bankens kanoniske KID. Kaldet reserverer ikke et nummer og ændrer ikke indekset. FlexOrms konvertering, trinstørrelser og ombrydning ved slutningen af formatet bevares. Op til ni kandidater prøves; numre optaget af aktive beboere springes over. Oprettelsen kontrollerer fortsat entydighed i sin transaktion. Et tomt nummer betyder manglende format eller ingen ledig kandidat inden for søgegrænsen, ikke nødvendigvis at alle numre er optaget.

curl -H "Authorization: Bearer $TOKEN" "$API_BASE/api/v1/banks/$BANK_KID/users/next-number"
curl -G -H "Authorization: Bearer $TOKEN" --data-urlencode "userNumber=1420-01-0004" "$API_BASE/api/v1/banks/$BANK_KID/users/next-number-after"
{"bankKid":"A6Q14o58Cb","number":"1420-01-0005"}

Svaret er et eksempel. Fejl: 400 ugyldig bank eller number-format (nummeret skal følge segmenternes længder og grænser); 401 ugyldig session; 403 utilstrækkelig adgang; 503 utilgængeligt lager eller ugyldige gemte indstillinger, herunder number-format-unavailable. Skeln mellem fejl og et tomt forslag. Svar må ikke caches. Portalen henter først forslaget, når kortet i sidepanelet er synligt. Nye operationer; endnu ikke udgivet.

Opret administrator

Ikke udgivet: CreateManager, POST /api/v1/managers, har ingen request-body. Kræver Managers1, de uafhængige Managers Read- og Create-rettigheder og adgang til hele tenant’en. Write giver ikke Create. Konto, credential-stempel og rettigheder læses på ny før tildeling af ID og igen i skrivetransaktionen, hvor låsene holdes frem til commit. Tenant bestemmes af sitet.

curl -X POST "$API_BASE/api/v1/managers" -H "Authorization: Bearer $TOKEN"
# 201 Created: {"kid":"<kanonisk manager-KID>"}
curl "$API_BASE/api/v1/managers/$MANAGER_KID" -H "Authorization: Bearer $TOKEN"

Location-headeren peger på GetManager. Administratoren er aktiveret og ikke slettet. Der tildeles ingen adgangskode, e-mail, tabs eller scopes, og der sendes ingen invitation. Brug GetManager og de eksisterende operationer til profil, rettigheder, scopes og tabs; redigering har egne adgangskrav, herunder Managers Write.

Oprettelsen finder næste aldrig brugte manager-ID over det højeste ID i historik og aktuel Log7 og gemmer Enabled=1 og Icon=businessperson i samme transaktion. Den nye administrator får businessperson som ikon; eksisterende administratorer beholder deres gemte ikoner. Der skrives ingen Deleted-indstilling; slettede ID’er og efterladte reservationsmarkører genbruges aldrig. En tenant-specifik MySQL-reservationslås tages før ID-opslaget og holdes indtil commit på samme skriveforbindelse. Der ventes højst fem sekunder på låsen inden for operationens frist på 15 sekunder; hvis låsen ikke opnås, returneres 503 uden oprettelse. Der bruges ingen tabellås, strukturændring eller tilfældig markør. Alle oprettere, herunder AMS og FRA, skal bruge samme lås på samme MySQL-skriveserver. Låsen understøtter ikke uafhængige skriveservere; modulo-fordeling efter ServerId med et fælles, fast modulus og en unik restklasse pr. skriveserver er den aftalte fremtidige løsning, hvis behovet opstår, og er endnu ikke implementeret.

401: log ind igen. 403: missing-managers-tab, missing-managers-read, missing-managers-create eller missing-tenant-access; få de nødvendige rettigheder. 409: manager-ids-exhausted (ID-område opbrugt). 503: manager-creation-unavailable. Applikationsfejl bruger ProblemDetails med code; 401 kan være uden body. Oprettelse er ikke idempotent: kontrollér listen efter timeout, afbrydelse eller mistet svar før et manuelt genforsøg. Gentag aldrig automatisk oprettelsen.

Gravatar

Administratorbilleder: GetManagers, GetManager og SetManagerProfileField indeholder gravatarUrl, som kan være null. Gravatar har forrang; det gemte ikon, eller standard-personikonet hvis intet er valgt, bruges som fallback. Når Email er gyldig, returnerer API’et https://www.gravatar.com/avatar/{sha256}?s=96&d=404&r=g ud fra e-mailadressen uden ydre mellemrum og med små bogstaver. Vis billedet med referrerpolicy="no-referrer"; brug iconKid ved 404 eller netværksfejl. Null betyder, at iconKid bruges direkte. Opdatér billedet efter ændring af Email eller Icon. Billedet er ikke identitetsbekræftelse. Browserens direkte opslag deler e-mailhash og klientens IP-adresse med Gravatar. Der kræves ingen Gravatar-konto, API-nøgle, profilopslag eller databaseskrivning. Portalen bruger dette i administratorlisten, detaljeoverskriften og arbejdsområdet. Ikke udgivet; klientpakker opdateres ved beta.

GetManagersWithSameEmail

GetManagersWithSameEmail: GET /api/v1/managers/{managerKid}/same-email returnerer [{"kid":"kanonisk manager-KID","name":"navn"}]. Kræver aktiv session, Managers1, Managers Read og adgang til hele tenant’en som GetManager. Kilden skal være synlig, ellers 404. E-mail sammenlignes præcist uden ydre mellemrum og uden forskel på store/små bogstaver; tomme adresser og kilden udelades. Deaktiverede administratorer medtages; slettede følger kalderens retention. Kandidatindekset kan være 60 sekunder gammelt; match læses igen fra aktuel Log7. Højst 100 kandidater, 20.000 indeksrækker og 12 sekunder; større resultater giver 503 manager-directory-too-large, aldrig en afkortet liste. 400 invalid-manager-kid, 401 kræver login, 403 manglende adgang og 503 managers-unavailable ved lagerfejl. Svar bruger no-store. Eksempel: curl -H "Authorization: Bearer $TOKEN" "$API_BASE/api/v1/managers/$MANAGER_KID/same-email". Portalen henter først listen, når sidepanelet er synligt, og viser kun links ved match. Efter gemt Email eller navigation til en administrator markeres listen til opdatering, som afventer et synligt sidepanel. Ikke udgivet; klienter opdateres ved beta. Hvert match indeholder også iconKid og gravatarUrl (kan være null): vis Gravatar først, derefter ikonet eller standard-personikonet, hvis intet er valgt.

Bankoversigt og valuta i forbrugsikonet

Vis banker inden for administratorens adgang, og vis den hyppigste autoriserede valuta i forbrugsikonet.

GetBanks: GET /api/v1/banks og GetActiveBankCount: GET /api/v1/banks/active-count kræver Banks2 (5), Bank Read og adgang på sitet. Grundfelter: kid, name, iconKid, enabled, deleted, deletedAt. Vælg fields=bankType,bankActivationCode,exportFormat,settlementEmails,settlementDays,bankDays,cluster,hardware; værdierne returneres i hver rækkes fields, og svarets fields-array angiver de valgte nøgler. BankDays (Dage gemt) og SettlementDays (Afregningsdatoer) er separate indstillinger. Bankoplysninger kræver adgang til hele banken eller tenant'en; lokationsadgang giver kun bankidentitet og null i de valgfrie felter. Visning, søgning og sortering af Bankkode kræver desuden adgang til hele tenant'en og Bank Create. Lokationskode er fortsat separat i GetLocations. Svarets canReadBankActivationCode angiver, om kalderen må bruge bankkoder.

filter (0–128 tegn) søger i navn og tilladte indstillinger eller et præcist bank-KID/en bankkode. bankType=Credit|Cash tilføjer et præcist banktypematch til tekstsøgningen, så klienter kan bruge oversatte etiketter uden at miste tekstmatch. sort accepterer name (standard) eller en valgfri feltnøgle; direction asc/desc. Gemte indstillinger tekstsorteres; koder sorteres numerisk med FlexActivation. Kodesortering gennemlæser tilladte match og beholder højst pageSize+1 rækker; indsnævr filteret ved overskridelse af fristen på 12 sekunder. pageSize 1–250, standard 50. Bevar alle parametre med nextCursor (15 minutter, bundet til manager og adgang). Samtidige ændringer kan flytte rækker. Tenantdækkende adgang kan vise alle tilstande; begrænset adgang kræver Enabled=1 og sletning inden for RetentionDays. enabledOnly=true og active-count kræver Enabled=1 og Deleted=0; manglende Deleted betyder nul. Intet kort. De valgfrie felter er ikke udgivet; klienter synkroniseres ved beta.

GetBankAccountIcon: GET /api/v1/banks/{bankKid}/account/icon. Requires Account2 and Bank, Location, Unit and User Read. Counts all Log1 rows across periods in the authorized location scope, independent of amounts and current account filters. Trimmed three-letter codes are uppercased; invalid/blank codes are ignored. Highest row count wins; alphabetical currency breaks ties. Returns currency (null for empty data) and iconKid with the currency in its text. Render iconKid unchanged through GetIconFromSet.

GET /api/v1/banks?pageSize=50&sort=name&direction=asc&enabledOnly=true
Authorization: Bearer YOUR_MANAGER_TOKEN

GET /api/v1/banks/{bankKid}/account/icon
Authorization: Bearer YOUR_MANAGER_TOKEN

Use the existing manager bearer session. All reads are no-store. 400 means invalid input/cursor (restart pagination); 401 requires a new session; 403 requires the appropriate tab/read/scope grants; 503 is temporary storage failure (retry manually). These new endpoints are implemented locally and unreleased.

Beboersøgning — UserFinder1 (77)

GetUsers: GET /api/v1/users viser beboere på tværs af de banker og lokationer, manageren har adgang til. Kræver UserFinder1, User Read og passende ressourceadgang. Tenant kommer fra sitets betroede konfiguration. Synlighed følger Log7, RetentionDays og de eksisterende lokationsregler.

GET /api/v1/users?pageSize=50&sort=identity&direction=asc&enabledOnly=true&filter=Alice
Authorization: Bearer {managerToken}

filter er 0–128 tegn: del af navn/nummer, præcis e-mail/SMS eller fuldt beboer-KID. sort er identity (standard; numerisk bank-/brugerorden), name eller number; navn-/nummersortering gennemlæser hele det tilladte kandidatsæt og kan være langsommere; direction er asc eller desc. enabledOnly=true betyder ikke slettet. items indeholder kid, bankKid, name, number, iconKid og nullable deletedAt. Åbn en beboer via GetBankUsers med bankKid og præcist userKid; dette enkeltopslag accepterer UserFinder1. Banklister, ændringer, aktivering og saldi beholder deres øvrige Users2- og operationskrav.

Følg nextCursor med uændrede indstillinger og pageSize (1–250). Cursoren udløber efter 15 minutter og er bundet til manager, site, adgang og søgning. Ved scanLimitReached kan siden være tom; fortsæt hvis nextCursor findes. Højst 1.000 kandidater pr. kald, intet samlet antal. Samtidige ændringer kan flytte rækker. Undlad at cache resultater eller logge personlige søgetekster. Ved 400 genstartes søgningen, ved 401 fornyes sessionen, 403 er manglende adgang, og ved 503 kan brugeren prøve igen.

Antal administratorer og installatører

GetManagerCount: GET /api/v1/managers/count kræver Managers1 (28), Managers Read og adgang til hele tenant'en. GetInstallerCount: GET /api/v1/installers/count kræver Installers1 (68), Installer Read og adgang til hele tenant'en. Begge kontrollerer den aktive session og returnerer {"count":123,"iconKid":"kanonisk ikon-KID"}. Antallet omfatter alle synlige identiteter uafhængigt af filter og side, også deaktiverede konti og sletninger inden for kalderens RetentionDays. De eksisterende indeks kan være 60 sekunder gamle. Administratorindekset er begrænset til 20.000 identiteter; installatører til deres område på 999. Overskridelse giver 503, aldrig et skøn eller delvist antal. Intet samlet beboerantal udstilles.

curl "$API_BASE/api/v1/managers/count" -H "Authorization: Bearer $TOKEN"
curl "$API_BASE/api/v1/installers/count" -H "Authorization: Bearer $TOKEN"

401: forny sessionen; 403: kontrollér tab, Read og adgang; 503: tillad manuelt genforsøg. No-store. Hent asynkront, når menupunktet bliver synligt. Ikke udgivet; klienter følger ved beta.

Antal services

GetServiceCount: GET /api/v1/services/count returnerer count og iconKid for hele GetServices-kataloget uden filter. Kræver samme aktive session, Services1, Service Read og tenantdækkende adgang. Tæller de samme entydige, konkrete eUserId-definitioner som GetServices uden områdeslutmarkører; der læses ingen serviceindstillinger eller ekstra forretnings-SQL. Den eksisterende sessionskontrol bevares. 401: forny session; 403: kontrollér adgang; 503: prøv igen manuelt. No-store. Ikke udgivet.

curl "$API_BASE/api/v1/services/count" -H "Authorization: Bearer $TOKEN"

Maskiner startet mere end 100 timer før måletidspunktet tælles ikke med; præcis 100 timer medtages.

Kørende maskiner

GetPublicRunningMachines — GET /api/v1/public/statistics/running-machines

Kræver ikke login eller rettigheder til enkelte maskiner; kun tenantens samlede antal er offentligt. Sitet vælger tenant. Tæller forskellige bank/lokation/enhed-identiteter i Log24 ud fra seneste Started og Done (UTC-millisekunder siden 2000-01-01): Started < nu og Done > nu. Manglende, ugyldige eller modstridende værdier tælles ikke. Tidsvinduet er et estimat og kontrollerer ikke online-status. Resultater og fejl caches i 15 sekunder pr. API-instans; forespørg hvert 15. sekund. HTTP 503 med Retry-After: 15 betyder utilgængelig, aldrig nul. Over 100000 tilstandsrækker giver 503. Svaret indeholder tenantKid, count, measuredAtUtc og refreshAfterSeconds (15), uden maskinidentiteter.

curl https://api.team.kombine.technology/api/v1/public/statistics/running-machines

Antal reservationer

GetPublicReservations: GET /api/v1/public/statistics/reservations. Ingen login eller parametre; sitet bestemmer tenant. Returnerer tenantKid, count, measuredAtUtc og refreshAfterSeconds (60).

Tæller daterede reservationer, der ikke er afsluttet, inklusive igangværende. Bruger tenantens aktuelle Log5 med start/bank/lokation/enhed/type som primærnøgle. Her er Text (startminutter) og Amount (varighed i minutter) fortegnede SQL INT NOT NULL-kolonner, som læses som Int32; validering af cifre og tekstlængde for bankens tekstlagrede Log5.Text gælder derfor ikke her. Denne fælles tabel har ingen Period-kolonne: triggeren erstatter kun rækken, når den nye MS2000 er større, også ved aflysning med negativ Amount. Filteret Period=0 hører til bankens Log5-historik, ikke dette snapshot. Amount skal være positiv, og lokal start plus varighed skal være efter nu. Aflyste, afsluttede, AUT/isy-hjælpeposter og ugentlige skabeloner udelades. Lokationens tidszone arves fra banken; manglende indstillinger bruger Europe/Copenhagen. Kun en samlet optælling er offentlig, uden identiteter eller rettigheder.

curl https://api.team.kombine.technology/api/v1/public/statistics/reservations

Opdater hvert minut. HTTP 503 betyder utilgængelig, aldrig nul; prøv igen efter 60 sekunder. Modstridende tidszonerevisioner, ugyldige tidszoner eller over 100000 kandidatrækker giver også 503.

Visningspuljen er et tilfældigt udvalg på højst 1000 maskiner, startet inden for 100 timer og med sluttid inden for de næste 3 timer. Større puljer udvælges i stedet for at blive afvist.

Numeriske navnepladsholdere som [455] oversættes gennem Kombine.Flex.Localization efter Accept-Language, efter læsning af den fælles rå cache.

Kørende maskiner

GetPublicLiveMachines: GET /api/v1/public/statistics/live-machines

Offentlig på alle tenant-sites med data udelukkende fra sitets konfigurerede tenant. Kalderen kan ikke vælge tenant. Returnerer en liste med unitKid, unitName, locationName, startedAtUtc, doneAtUtc og valgfri temperature. Bruger aktuelle Log24-værdier: Started < nu < Done, eSetting.Name og eState.Temperature; ingen beboerdata. Manglende eller modstridende tidsfelter udelades. Hent hvert 60. sekund, vælg forskellige tilfældige maskiner, behold kørende valg og tæl ned lokalt hvert sekund. HTTP 503 betyder utilgængelig; prøv igen efter 60 sekunder. Højst 1000 kørende maskiner og 100000 kilderækker.

GET https://api.team.kombine.technology/api/v1/public/statistics/live-machines

Kun kørende maskiner med et gyldigt sluttidspunkt efter nu og højst 3 timer fremme indgår i puljen; gemte Done-værdier uden for dette interval udelades. Svarskemaet tillader fortsat null for doneAtUtc; klienter må kun vælge poster med et gyldigt sluttidspunkt i dette interval. Temperatur vises kun, når den er kendt. Højst 10000 ekstra metadatarækker læses for de kørende maskiner og deres lokationer.

ErrorUnit

Valgfrit fields=errorUnit returnerer senest gemte eState.ErrorUnit (331) som JSON-tekst i items[].errorUnit. Tom tekst betyder manglende værdi; null betyder ikke valgt. Fejlen er ikke nødvendigvis stadig aktiv. sort=errorUnit sorterer leksikalsk efter gemt tekst før paginering. Eksisterende Units1-, Bank/Location/Unit Read-, scope- og retentionkrav gælder. Bevar parametrene med nextCursor; 400: ugyldige felter/sortering/cursor, 403: manglende rettighed, 503: midlertidigt utilgængelig. Ikke udgivet; klientpakker opdateres ved beta.

GET /api/v1/units?fields=errorUnit&sort=errorUnit&direction=desc&pageSize=50
Authorization: Bearer YOUR_MANAGER_TOKEN

GetCameraNearestLocation — GET /api/v1/camera/nearest-location?latitude=55.7&longitude=12.5&accuracyMeters=5 vælger nærmeste aktiverede, retention-synlige lokation inden for 100 meter. Koordinater er decimalgrader; accuracyMeters er 0–100 meter. Kræver manager bearer-token, Camera1 og Location Read/Write samt tenantens ressourceadgang. Banker og units vælges ikke. Svaret er CameraResourceResponse. 400: ugyldig position; 401/403: afvist; 404: ingen lokation; 409: de to nærmeste kan ikke adskilles inden for dobbelt GPS-usikkerhed (mindst 1 meter); 503: timeout/lagerfejl eller over 100 kandidater. Ved 404/409 åbnes den ønskede lokation manuelt. Normal klargørings-/uploadkontrol gælder stadig. Opslaget gemmer ikke GPS-positionen.

GetRecentCameraPhotoMetadata — GET /api/v1/camera/recent/{id}/metadata læser gemte metadata for en offentlig miniature. Kopiér det uigennemsigtige id fra miniature-URL’en fra GetPublicRecentCameraPhotos og send en manager bearer-token. Camera1, ressourcens Read, scope og retention kontrolleres ved hvert kald; miniature-token giver ikke adgang til private data. JSON svarer til GetCameraCaptureContent med part=metadata. 400: ugyldig/udløbet token (genindlæs listen); 401/403: afvist adgang; 404: manglende metadata; 503: lager utilgængeligt. Svaret må ikke vises offentligt. Portalen viser gemt vejr og orientering ved mouseover; manglende indetemperatur er ukendt, og GPS-bevægelsesretning er ikke kameraretning.

GetCameraResource returnerer også requiresProximityCheck og nullable latitude/longitude i decimalgrader. Lokationer bruger egne koordinater; units arver lokationens koordinater. Banker returnerer false og null-koordinater. Portalen advarer inden foto/video ved en målt afstand over 100 meter eller ukendt afstand (manglende koordinater, afvist GPS, position ældre end 60 sekunder, nøjagtighed dårligere end 100 meter eller fejlet opslag). Brugeren kan annullere eller fortsætte. Genforsøg af upload tager ikke en ny optagelse. Advarslen ændrer ikke API-adgang.

Store JPEG-metadata (rettelse ikke udgivet): originalens bytes bevares uændret. Når rå metadata overskrider kopieringsbudgettet på 256 KiB, eller JSON overstiger 512 KiB, er embeddedMetadata.metadataExtraction original-only: exif er null, og attributes og embeddedBlocks er tomme. embeddedBlockReferences angiver hver JPEG-metadatablok med Kind, Offset og Length; offset er en nulbaseret byteposition i originalfilen og omfatter ikke markør- og længdebytes. De fulde metadata kan hentes fra den adgangskontrollerede original. Mindre JPEG-filer beholder udtrukne metadata og angiver complete. Fotogrænsen på 32 MiB, pixel- og blokgrænser, strukturkontrol og grænser for komprimerede PNG-metadata gælder fortsat.

Mere præcis JPEG-diagnostik (ikke udgivet): photo-pixel-limit, photo-metadata-limit, photo-metadata-block-limit, photo-jpeg-marker, photo-jpeg-truncated-marker, photo-jpeg-missing-frame, photo-jpeg-structure, photo-jpeg-truncated-segment, photo-jpeg-encoding, photo-jpeg-frame og photo-jpeg-missing-end. Koderne skelner mellem ressourcegrænser og ugyldige eller ikke understøttede JPEG-data; HTTP-status og fejlkode er uændret.

Diagnostik ved afvist foto (ikke udgivet): HTTP 422 beholder invalid-camera-media. Det valgfrie ProblemDetails-felt diagnostic angiver den fejlede fase: photo-size, photo-format, photo-jpeg-header, photo-png-header, photo-decode, photo-metadata eller photo-thumbnail. Feltet indeholder ingen billedmetadata eller beskeder fra dekoderen; klienter skal acceptere manglende og ukendte værdier.

Private kameraoptagelser (ikke udgivet)

Camera1 følger den aktuelle managers tenant-, fane- og ressourceadgang. Lister og medier kræver Read; klargøring og upload kræver desuden Write. Tenantlisten viser egne optagelser. resourceKid afgrænser til den konkrete bank, lokation eller enhed og medtager andres autoriserede optagelser. Servicekonti kan ikke bruge kameraet.

GetCameraResource: GET /api/v1/camera/resource?resourceKid=KID giver resourceKid, name og iconKid for den præcise autoriserede bank, lokation eller enhed. Brug opslaget til den lokale kamerafanes overskrift frem for et overordnet overblik eller en liste over underressourcer. Det kræver Camera1, Read for den pågældende ressourcetype, ressourceadgang og synlighed inden for RetentionDays. En lokation kræver ikke Unit Read, og en enhed kræver ikke Location Read. Enheder medtager kun de arvede lokationskoordinater, ikke andre oplysninger om over- eller underressourcer. Brug iconKid uændret i ikon-API. Fejl: 400 ugyldig KID/anden tenant, 401 ugyldig session, 403 afvist adgang, 404 manglende eller uden for retention, 503 utilgængeligt opslag.

GET /api/v1/camera/resource?resourceKid=CANONICAL_RESOURCE_KID HTTP/1.1
Authorization: Bearer YOUR_MANAGER_TOKEN

GetCameraAvailability: GET /api/v1/camera/availability?resourceKid=KID giver hasCaptures, isDegraded og canCapture til lazy navigation. GetCameraCaptures: GET /api/v1/camera?resourceKid=KID&cursor=TOKEN giver items, nextCursor og isDegraded. Udelad resourceKid for egne optagelser. Følg nextCursor med samme ressource, også efter tomme filtrerede sider. Begge regionale indeks flettes uden dubletter. Ved isDegraded kan resultatet være ufuldstændigt; genindlæs fra starten efter genetableret adgang.

Elementer fra GetCameraCaptures indeholder også de nullable felter bankName, locationName og unitName (ikke udgivet). Navnene er de aktuelle visningsnavne, ikke navnene på optagetidspunktet. Hvert navn kræver objektets egen Read-rettighed, ressourcescope og synlighed inden for retention; irrelevante eller ikke-autoriserede navne er null. Enhedsnavne får oversat deres lokaliseringsmarkører. Klienter kan vise de tilgængelige navne i rækkefølgen bank/lokation/enhed og skal fortsat bruge resourceKid til identitet og navigation.

Filimport fra browseren bruger samme prepare/upload-kald og rettigheder. capturedAtUtc accepterer datoer fra 1970-01-01 UTC til fem minutter efter servertiden (endnu ikke udgivet). Hvis optagelsestiden er ukendt, kan klienten bruge filens ændringstid eller uploadtid og angive kilden i sensorData.timestampSource. Knyt ikke enhedens aktuelle sensorer til historiske medier. Portalens filvælger understøtter JPEG/PNG op til 32 MiB og MP4/MOV/WebM op til 128 MiB; serveren validerer originalmediet.

Ved filimport findes målet med GetCameraNearestLocation ud fra indlejret EXIF GPS (JPEG/PNG) eller QuickTime-lokationsdata (MOV/MP4), ikke den uploadende enheds aktuelle position. Portalen bruger et forsigtigt nøjagtighedsvindue på 100 meter, når metadata ikke angiver nøjagtigheden; manglende, ugyldige eller tvetydige geodata stopper importen uden upload. Originalens bytes bevares. Den separate side /camera/upload åbner hverken kamera eller mikrofon.

Når upload åbnes fra en lokation eller unit, bruger portalen den udtrykkeligt valgte, API-godkendte ressource. GPS er her valgfri. En afstand på mere end 100 meter fra lokationens koordinater giver en advarsel med mulighed for at fortsætte; afvisning springer kun den pågældende fil over. Upload fra tenantens kamerafane finder stadig målet ud fra indlejret GPS.

PrepareCameraCapture: POST /api/v1/camera/prepare modtager resourceKid, kind (photo/video/live-photo), originalFileName, originalNameAvailable, capturedAtUtc, mediaLength, mediaSha256 (SHA-256 af originalen) og sensorData (JSON-objekt). Gem svarets uploadToken til genforsøg; den binder ejer, ressource, bytes og metadata inklusive eventuelt vejr i 24 timer. SaveCameraCapture: POST /api/v1/camera modtager multipart-felterne uploadToken og original. Succes betyder, at original, thumbnail og JSON er komplette i én region; den anden kopi fortsætter uafhængigt af navigation.

Efter et usikkert uploadsvar (f.eks. HTTP 504) kan ConfirmCameraCapture kaldes med den oprindelige billet. Kaldet kræver samme godkendte ejer og aktuel Camera1-/ressourceadgang med Read og Write. Kun confirmed: true bekræfter offentliggørelse i det private katalog af netop denne optagelse; false eller 503 beviser ikke en fejl. Behold originalens bytes og billetten til genforsøg; opret ikke automatisk en ny plan. Ugyldig/udløbet billet giver 400, manglende login 401 og tilbagekaldt adgang 403/404. Bekræftelsesoperationen er endnu ikke udgivet; klienter opdateres ved næste beta-release.

Valgfri ?background=true på SaveCameraCapture giver HTTP 202 efter validering og midlertidig lagring af originalens bytes. Det er ikke en gemmebekræftelse. Kald ConfirmCameraCapture med korte forespørgsler indtil confirmed=true. Behandlingen fortsætter uafhængigt af uploadforbindelsen. Den begrænsede behandlingskø er midlertidig: behold originalen og genbrug samme billet efter genstart eller manglende bekræftelse. errorCode camera-processing-failed betyder, at denne instans stoppede behandlingen; genforsøg er tilladt. Uden parameteren bevares det synkrone 200-svar.

Det valgfrie felt processingPercent er et vægtet skøn baseret på afsluttede behandlingstrin, ikke forløbet tid eller forventet resttid. 0–99 er midlertidig fremdrift på den behandlende instans; null betyder ukendt (f.eks. en anden instans eller genstart). Kun confirmed=true giver 100. En procentværdi alene må aldrig fortolkes som bekræftet lagring.

POST /api/v1/camera/confirmation
Authorization: Bearer YOUR_MANAGER_TOKEN
Content-Type: application/json

{"uploadToken":"ORIGINAL_UPLOAD_TOKEN"}
GET /api/v1/camera?resourceKid=CANONICAL_RESOURCE_KID
Authorization: Bearer YOUR_MANAGER_TOKEN

GetCameraCaptureContent: GET /api/v1/camera/{id}/content?part=thumbnail leverer bytes gennem API'et. id kommer fra listen og gælder i 30 minutter. part kan være original, metadata, thumbnail, preview, motion, motion-playback eller playback, når delen findes. Listeelementer indeholder hasPreview, hasMotion og hasPlayback. Video med hasPlayback=true afspilles via part=playback, ellers original. Live Photos bruger original til fotoet og motion-playback til bevægelsen; motion bevarer den originale MOV. Enkeltstående byteintervaller understøttes. Ingen direkte eller signerede Spaces-links udleveres.

JPEG/PNG op til 32 MiB; MP4/WebM/QuickTime MOV med lyd op til 128 MiB. Browseren stopper efter 60 sekunder; API'et accepterer højst 61 sekunder for afslutning af optageren. Kvadratiske thumbnails er 512 pixels; videopreview har tre lydløse frames. Native live-photo består af et JPEG-foto (højst 32 MiB) og det oprindelige QuickTime MOV-klip (højst 32 MiB og 10 sekunder). Klargør med kind=live-photo samt motionLength og motionSha256; upload både original og motion som multipart-filer med samme uploadToken. Begge bestanddeles hash og indhold valideres før publicering. Thumbnail er statisk uden animeret preview. Almindelige QuickTime MOV- og WebM-videoer bevarer de originale bytes og får en særskilt H.264/AAC MP4 til klienter, der ikke kan afspille originalformatet direkte. Live Photo-bevægelse bevarer ligeledes den originale MOV og får en særskilt MP4 uden opdigtet lyd ved lydløse bevægelige fotos. Hele det regionale filsæt inklusive denne afledning gemmes før publicering. Native optagelse kræver fortsat platforms-/enhedstest; integration til Android-producenters Motion Photo-funktion loves ikke. Video kræver ffmpeg/ffprobe på API-værten. Nye optagelsers filnavne bruger en GUID og filtypen uden regionsnavn eller enhedens filnavn (ikke udgivet). Tilhørende filer beholder endelser som .thumbnail.jpg og .json. Originalnavn og oprindelsesregion bevares i JSON-metadata. Eksisterende optagelser beholder deres navne og kan fortsat læses. Behandl returnerede filnavne som uigennemsigtige værdier; filnavnets GUID er ikke optagelsens id. Kopier beholder navnet. Betinget oprettelse beskytter mod overskrivning uden kameradatabase.

400: ugyldigt KID/token/metadata/hash; 401/403: adgang; 404: manglende ressource/del; 413: størrelse; 416: byteinterval; 422: ugyldigt/ikke-understøttet medie; 429: optagebehandling optaget; 503: lager eller nødvendig mediebehandling utilgængelig. Genforsøg bruger samme uploadToken og originalbytes. Manglende sensorer/vejr angives som utilgængelige. Afbrudt kopiering og sjældne konkurrerende oprindelsesmanifest ved samtidige genforsøg kræver senere afstemning. Ingen sletning, galleriimport eller automatisk historisk vejrreparation.

AutoOutOfOrder

Med autoOutOfOrderOnly=true vises enheder med gyldig AutoOutOfOrder-JSON i Log24 og et positivt heltal i Id, uafhængigt af OutOfOrder-indstillingen. Svarfeltet autoOutOfOrder indeholder JSON ved filtrering, ellers null. Følg nextCursor med samme filter for alle fund. Hver side kontrollerer Units1, Bank/Location/Unit Read, scope og RetentionDays. 400: ugyldig eller ændret cursor; 401/403: login/adgang; 503: lager utilgængeligt. Ikke udgivet; klientpakker opdateres ved beta.

GET /api/v1/units?autoOutOfOrderOnly=true&sort=kid&fields=bankName,locationName
Authorization: Bearer YOUR_TOKEN

GetPublicMachineErrors

Kræver ikke login eller bearer-token. Sitet bestemmer tenant. Returnerer items (kid, name, bankName, locationName, errorId som ciffertekst), nextCursor og refreshAfterSeconds=60. Kun positive AutoOutOfOrder-id’er med Log24 MS2000-fejltidspunkt inden for de seneste 10 dage (inklusive grænsen) medtages. Ukendte og fremtidige tidspunkter udelades. Slettede banker/lokationer/enheder og deaktiverede lokationer udelades; deaktiverede enheder medtages. Rå fejl-JSON er ikke offentlig. pageSize=1–100 (standard 50); følg nextCursor uændret med samme pageSize. Portalen henter alle sider automatisk og tilpasser alle ikoner til kortet. Cursor udløber efter 15 minutter og kan ikke bruges på tværs af tenants. 400 ugyldig side/cursor; 503 utilgængelig (Retry-After: 60), aldrig et tomt succesresultat. Ikke udgivet.

GET /api/v1/public/widgets/machine-errors?pageSize=100

Kort-widget med køb

Indlejr det kompakte live-købskort direkte i sin egen Shadow DOM. Ingen iframe, portalskal, login eller bearer-token er nødvendig. API-sitet vælger tenant. Data opdateres hvert 10. sekund; fejl giver 30 sekunders ventetid. Kald destroy(), når widgetten fjernes. Angiv googleMapsBrowserKey fra den indlejrende hjemmesides offentlige browsernøglekonfiguration; brug aldrig en servernøgle til geokodning. Indstillingen har forrang for den nullable googleMapsBrowserKey fra GetPublicWidgetPresentation (kun tilgængelig, hvis API allerede har GoogleMaps:ApiKey konfigureret). API behøver ikke indstillingen, når hjemmesiden angiver sin egen nøgle. Uden en af kilderne afvises ready. Googles HTTP-referrer-begrænsninger skal tillade hjemmesiden; widgetten ændrer dem ikke. Ved Content Security Policy skal Googles Maps CSP-vejledning også følges.

<div id="purchase-map" style="width:100%;height:400px"></div>
<script type="module">
import { mountPurchaseMap } from 'https://YOUR-API/docs/widgets.mjs';
const widget = mountPurchaseMap(document.querySelector('#purchase-map'), {
  apiBase: 'https://YOUR-API', language: 'en-GB',
  googleMapsBrowserKey: 'YOUR_PUBLIC_BROWSER_KEY'
});
await widget.ready;
// Call widget.destroy() before removing the host.
</script>

Efter widget.ready returnerer widget.getView() den aktuelle { startPosition: { latitude, longitude }, zoom }, eller null når kortet er utilgængeligt eller afmonteret. Manuelle kortbevægelser medtages. Ikke udgivet.

Brug startPosition: { latitude: 56.2, longitude: 10.5 } til at vælge kortets startposition. Breddegrad skal være fra -90 til 90 og længdegrad fra -180 til 180. Kombinér med zoom: 6. Med startPosition springes automatisk tilpasning til data over; kortet kan stadig følge nye køb og flyttes manuelt. Udelad parameteren for automatisk tilpasning. Implementeret i kildekoden; ikke udgivet.

Kortlag: showCoins er som standard true; showHeatmap er som standard false. Brug showCoins: false, showHeatmap: true til et kort med kun heatmap. Implementeret i kildekoden; ikke udgivet.

Brug zoom: 7 til et fast zoomniveau efter centrering på data. Værdier fra 0 til 22 understøttes. Udelad zoom for automatisk at vise alle lokationer. Ikke udgivet.

Seneste billeder

Ikke udgivet. GetPublicRecentCameraPhotos: GET /api/v1/public/camera/photos returnerer højst ti billeder, inklusive Live Photo-stillbilleder, nyeste først fra sitets tenant. Intet login eller tenantparameter. Hvert element har thumbnailUrl og workspacePath. Hent thumbnailUrl uændret med GetPublicCameraThumbnail; billetten gælder i ti minutter og giver kun adgang til JPEG-thumbnail. Originaler, ejeroplysninger og sensordata er fortsat private. workspacePath kræver aktiv manager, Camera1, ressourcens Read-rettighed, scope og retention. Overblik viser hvert billede i tre sekunder (komponentparameter DisplaySeconds, 1–60) med 500 ms fade, pause og reduceret bevægelse. Genhent listen hvert minut; gem ikke billetter. Tom liste betyder ingen billeder. 400: ugyldig/udløbet billet eller forkert tenant; 404: thumbnail mangler; 503: lager eller komplet oversigt utilgængelig. Prøv igen efter et minut. Begge regioners indeks skal være tilgængelige; højst 10.000 indeksrækker og 200 kandidatmanifester læses. Videoer er udeladt. Ingen databaseskrivninger eller direkte lagerlinks.

GET /api/v1/public/camera/photos

API-nøgler til administratorer (ikke udgivet)

Åbn Mine indstillinger i portalen, og vælg Generér nøgle. GetMyManagerApiKey — GET /api/v1/session/me/api-key returnerer hasApiKey og revision. GenerateMyManagerApiKey — POST /api/v1/session/me/api-key modtager {"revision":"REVISION_FRA_GET"} og returnerer engangsværdien apiKey samt details med ny status og revision. En aktiv administrator kan kun generere sin egen nøgle og behøver ingen administrationsrettigheder. Kopiér nøglen med det samme; den kan ikke genskabes. Log aldrig svaret.

Både Portal LoginManager og Equipment LoginService accepterer {"kid":"KANONISK_MANAGER_KID","apiKey":"DIN_NØGLE"} på POST /api/v1/session/login. E-mail/password virker fortsat; bland aldrig parrene. Vælg korrekt tenantsite; tokens er adskilt mellem API’er og miljøer. Nøglelogin rydder ikke dubletkonti. Sessionen beholder administratorens Tab/KID/operationsrettigheder og varer tre dage. Den giver ikke serviceadgang til synkronisering. Forny efter aktivitet. Udskiftning af nøglen tilbagekalder nøglesessioner inden for 60 sekunder, også efter fornyelse.

Administrator- og servicenøgler gemmer kun den passwordkompatible hash i eSetting.ApiKeyHash; Password bevares. Services bruger ApiKeyHash, når feltet findes; en tom værdi deaktiverer nøglen. Eksisterende servicenøgler, der kun ligger i Password, kan fortsat læses, indtil de erstattes. Kopiér aldrig administratorpasswords til ApiKeyHash. Håndtér 400 for ugyldigt/blandet input, 401 for ugyldig nøgle eller inaktiv administrator, 409 ved samtidig generering (genindlæs status), og 503 ved utilgængelig lagring eller ukendt resultat. Gentag aldrig generering automatisk.

Brugernavn ved API-login med nøgle

LoginManager · POST /api/v1/session/login — Unreleased. Ved manager-login med API-nøgle sendes brugernavnet fra Mine indstillinger i feltet kid: Kid.ToUserId-formatet tenant.0-userId (fx 166.0-1000002213). Send den oprindelige nøgle i apiKey. Den kodede manager-KID accepteres også. Tenant skal svare til API-sitet; kun administratorer i bank nul accepteres. Ressource-id’er og KID’er i svar er fortsat kodede. Bland ikke kid/apiKey med email/password.

Nærmeste tilgængelige lokationer

GetNearestLocations — GET /api/v1/locations/nearest?latitude=55.6761&longitude=12.5683 returnerer op til ti aktiverede lokationer som et array med {kid,name,bankName,iconKid,distanceMeters}, sorteret efter afstand i fugleflugtslinje i meter. Angiv begge endelige koordinater i decimalgrader (breddegrad −90…90, længdegrad −180…180). Intet søgefilter eller radius anvendes; lokationer uden gyldige koordinater udelades, også koordinatparret 0,0. Kræver aktiv administrator, Locations1, Bank Read, Location Read og matchende KID-adgang; synlighed efter retention bevares. Position giver ingen ekstra rettigheder. Fejl: 400 invalid-position, 401 ugyldig session, 403 missing-locations-access, 503 locations-unavailable. Et tomt array betyder ingen egnede lokationer. Svar bruger no-store. Positionen bruges kun til opslaget; log ikke querystrenge, og gem dem ikke. Portalen henter automatisk browserens position, når kortet vises. Browseren kan bede om positionsadgang; afvisning eller manglende position vises som en lokaliseret besked.

Reservationshistorik

GetLocationReservationHistory — GET /api/v1/locations/{locationKid}/reservation-history Hver post har også kanonisk userKid (null ved ukendt beboer). Vis userKid som reserve, hvis userNumber er tomt; userName forbliver tomt, når navnet mangler.

Hent kun historik for kalenderens synlige datoområde. Kræver Bookings1, Location Read, Unit Read, ressourceadgang og retention-synlighed. Bankens Log5 returnerer revisioner for synlige enheder med userNumber og userName fra bankens Log7 (tomme strenge ved manglende data); User Read kræves. from er en lokal dato fra 2001, days er 1–31. items indeholder unitKid, startMinute (lokale minutter siden 2000-01-01), durationMinutes (positiv absolut varighed), revision (UTC-millisekunder siden 2000-01-01) og cancelled. Tidligere og annullerede revisioner bevares; ugentlige/AUT/isy-poster udelades. Historik angiver ikke aktuel optagethed. 400: ret dato/KID; 401: log ind igen; 403: kontrollér adgang; 404: opdatér lokationen; 503: prøv senere og vis historikken som utilgængelig. Over 5000 poster giver 503, aldrig afkortning. Ikke udgivet.

GET /api/v1/locations/LOCATION_KID/reservation-history?from=2026-10-20&days=7
Authorization: Bearer YOUR_MANAGER_TOKEN

En post, der matcher en aktuel aktiv reservation på MS2000, LocationId, UnitId og UserId i samme betroede tenant og bank, udelades fra historikken.

Enhedens WashDoc-liste og separate optælling accepterer det valgfrie search: et præcist numerisk Doc-ID eller et kanonisk dokument-KID for den valgte enhed. Den inklusive date-grænse gælder stadig. Ugyldige identiteter eller andre enheders dokumenter giver HTTP 400 invalid-document-search. Fjern search for at vise historikken igen. API-svar kan indeholde X-Portal-Operation, det stabile Swagger-operationsnavn til kontekstuel dokumentation, uden forespørgselsværdier.