Kombine Flex Portal API · v1 · Integrationsvejledning

Byg din egen portal eller tilslut en AI-agent

API’et giver adgang til de samme oplysninger, som den officielle portal bruger. Du sender almindelige HTTPS-kald og får JSON tilbage. Du behøver hverken databaseadgang, et særligt SDK eller en særlig agentprotokol.

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.1.

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 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

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.1.

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

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.

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. Kun synlige felter bundet til den aktuelle enhed tillades. 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.

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.1.

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. Beskedargumenter og 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. 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.1; 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. UnitType2 har forrang; ellers udledes typen med (værdi >> 1) & 255. 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.1.

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.UsersLast 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–99999 inklusive. Der tilføjes ikke bankfilter. Svaret indeholder også amount = -SUM(Amount)/100 og currency = MAX(Currency). Uden køb returneres 0 og null. MAX(Currency) forudsætter samme valuta blandt købene; der foretages 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. De almindelige CORS-regler gælder for JavaScript fra andre sites.

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

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, 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(). For JavaScript på et andet site skal API’ets Cors:AllowedOrigins indeholde klientens præcise origin, som beskrevet nedenfor.

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.

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.1.

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.1.

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.1.

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. Positive antal vises fuldt ud i en rød kapsel med 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.1.

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.

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.Password, 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.Password, 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.1.

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":"[email protected]","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.

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.1. 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
}

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 kræver fortsat en konfigureret skriveforbindelse med de nødvendige databaserettigheder; 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 eller ugyldige gemte værdier returneres som 0.

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 g. 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":"[email protected]","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 [email protected]; 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.1. 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 [email protected]. 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":"[email protected]","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.1.

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 [email protected], 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.1.

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.10

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, negative eller ugyldige værdier giver 0, som skjuler slettede objekter. 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 portalens skriveforbindelse mangler, afviser rettigheder 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. Der er endnu ingen maskinkonto, API-nøgle, OAuth-delegering eller MCP-server; 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.

Assistenten finder otte godkendte læseoperationer i OpenAPI: SearchBanks, SearchLocations, SearchUsers, GetSearchBank, GetBankLocations, GetLocations, GetLocationUnits og GetTenantStatus. Hvert opslag bruger managerens bearer-session og eksisterende Tab-, KID- og Read-rettigheder. Ingen ekstra adgang eller skrivehandlinger. Spørgsmål, medsendt historik og relevante autoriserede resultater sendes til OpenAI; legitimationsoplysninger sendes aldrig til modellen. Når logafsendelse er aktiveret, logges det aktuelle spørgsmål én gang i Logz.io efter sessionskontrol og før modellen kaldes. Historik og svar indgår ikke i hændelsen. Levering er ikke garanteret; opbevaring følger kontoens indstillinger i Logz.io. 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.1.

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":"[email protected]","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.

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. En tom enhedsuge arver den registrerede controllers plan, mens egne undtagelser stadig gælder. 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.1 til denne betaudgivelse.

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.1 til denne betaudgivelse.

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

Estimated giver 0–99% og forventede resterende sekunder fra positive Started/Done-værdier i MS2000. Complete giver kun 100% i DONE-området, dog ikke forbindelsesmarkøren LinkOnline. En passeret sluttid giver EstimateExpired og beviser ikke, at maskinen er færdig. Nulstillede, ugyldige eller manglende tider, markøren for ukendt sluttid, forskellige forløbs-DocId'er eller en brøkværdi i Connected giver UnknownEndTime under et aktivt forløb. Begge har null i percent og remainingSeconds og kan vises med en skraveret fremdriftslinje.

Disabled, OutOfOrder, AutoOutOfOrder, Repair, Disconnected, Error, Idle og Unknown har ingen procent. Kun Enabled=1 aktiverer en enhed. Connected=0 eller en frakoblet cyklus stopper estimatet; manglende Connected betyder ikke automatisk offline, og der anvendes ingen fælles tærskel for brøkværdier. Tider læses fra state Text; TagId er forløbets ID. Beregn ikke procenter ud fra cyklusnummeret. calculatedAtUtc er beregningstidspunktet og garanterer ikke frisk kontakt til maskinen. Data kan være ti sekunder gamle eller stamme fra en ældre enhedsrapport. Manglende/nye statuskoder vises som ukendte. Ved 401: log ind igen; 403/404: utilgængelig; 503: prøv senere. Vis ikke et gammelt estimat som aktuelt.

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.error 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.error 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.error 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();
  • En eksisterende UnitType2 er den direkte type og har forrang, også hvis værdien er ugyldig. Ellers afkodes UnitType efter FlexOrm-reglen (value >> 1) & 63. Manglende/ugyldig type giver null, aldrig en opdigtet Type000; ukendte tal bevares.
  • Grupperne kommer fra de installerede Kombine.Flex.Units-descriptorpakker: 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.

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. Descriptorens type-, påkrævet-, interval-, mønster- og valgmulighedsregler 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.

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. Værdien bindes til SQL-parametret @limit; cachen er adskilt pr. 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 nu samme legacy-decoder som FlexOrm, herunder program, varighed, sæbe og overførsler. 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; betalings-id'er udleveres ikke.

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 udleverer kun paymentKind og 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.1.

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("[email protected]")}`, { 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.1 til denne betaudgivelse.

Installatører — GetInstallers

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–100, 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 "[email protected]" \
  --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–100, 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.

Send alle syv 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 syv 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}}"

Kaldet erstatter alle syv 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 syv 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 42 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 bevarer tabs. 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":"[email protected]","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.

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.4.1 NuGet.org NuGet.org: produktionsudgaver

Kombine.Flex.Portal.Client giver typede C#-metoder til alle 100 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. Produktionsudgaver publiceres på NuGet.org efter deploy-verifikationen. Hvis denne beta-version endnu ikke findes dér, bruges den direkte pakkedownload og lokal installation nedenfor.

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

Produktionsudgaver publiceres på NuGet.org efter deploy-verifikationen. Hvis denne beta-version endnu ikke findes dér, bruges den direkte pakkedownload og lokal installation nedenfor.

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

Læg den leverede Kombine.Flex.Portal.Client.0.4.1.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.4.1

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 · 100 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.4.1-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 105 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 · 100 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.4.1.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 105 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 100 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 105 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 105 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.

PHP · Portal API

64-bit PHP 8.2+, ext-curl og ext-json. Ingen ekstra PHP-biblioteker. Version 0.4.2 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.4.2

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.

Changelog

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-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.

Banks2 lokationsliste

Antal aktive lokationer

GetActiveLocationCount — GET /api/v1/locations/active-count. Returnerer {"count":123} til Lokationer/Banks2-ikonet. Kræver samme aktive administrator, Banks2, 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 røde 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, Banks2 (5), 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.

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. 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. 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.1.

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.