Før du begynder
App Console åbnes på app.hours.dk/console.
Du skal kunne modtage login-koder på din arbejdsmail. Til udstedelse af klienthemmeligheder, review og kundeautorisation skal din Hours-session desuden være bekræftet med TOTP. En udvikleridentitet, et udviklerarbejdsrum og en kundeorganisation er tre forskellige ting. Du behøver ikke oprette et regnskabsabonnement for at beskrive en app; du må heller ikke oprette en tom produktionskunde for at få testdata.
Aftal, hvem der ejer integrationen, hvad den skal udføre, hvor den kører, og hvem der modtager dens data. Opret én app pr. integration med selvstændigt formål og ansvar. Brug ikke en fælles app til uvedkommende kundeløsninger, fordi credentials, scopes, reviewhistorik og tilbagekaldelse ellers bliver vanskelige at afgrænse. En ny funktion kan høre til den samme app, men en udvidelse af adgangsbehovet kræver en ny revision.
Den første publicerbare profil er afgrænset til DKK, journalposteringer, produkter, fakturakladder, udvalgte kontoreferencer og egne operationskvitteringer. Den er ikke en generel kopi af Hours’ interne API. Bankdata, betalinger, generel dokumenteksport og vilkårlig databasesøgning findes ikke som scopes.
1. Log ind med en udvikleridentitet
Åbn https://app.hours.dk/console/login. Indtast din arbejdsmail. Markér kun oprettelsesvalget, når der faktisk skal oprettes en ny loginidentitet. Identitetskontrollen bruger Hours’ eksisterende loginløsning; konsollen har ikke et separat passwordregister.
Indtast koden fra mailen, eller følg det modtagne bekræftelseslink. En besked om, at en kode er anmodet, beviser ikke, at en konto findes. Koden bruger ikke Hours-appens kundeonboarding eller en lokal demo-session som adgangskilde.
Ved manglende eller ugyldig session må du ikke forsøge at indsætte et bruger-id i et request. Identiteten udledes på serveren. Et medlem kan ikke gøre sig til intern reviewer ved at ændre en browservariabel, og en login-cookie er ikke et partner-API-token.
2. Opret eller tilslut et arbejdsrum
Vælg Opret arbejdsrum, og angiv et navn, der identificerer udgiveren eller udviklerteamet. Først dette eksplicitte klik opretter arbejdsrummet. Det bliver ikke til en kundeorganisation og får ikke automatisk et abonnement. Opretteren bliver arbejdsrummets ejer.
Er du inviteret, åbner du invitationslinket og accepterer det med den inviterede, bekræftede e-mailadresse. Du kan også indsætte hin_-koden uden først at oprette dit eget arbejdsrum. Invitationen udløber efter 72 timer. Den udstedende person skal stadig være ejer eller administrator, når invitationen accepteres. Et gammelt link fra en fjernet administrator kan derfor ikke genoprette adgangen.
Invitationer leveres som kopierbare links. Konsollen sender ikke automatisk en invitationsmail. Send linket gennem en passende kanal. Undgå at indsætte det i offentlig issuehistorik: koden er et adgangsbevis, selv om den også er bundet til modtagerens e-mail.
3. Opret appens profil
Vælg Opret app. Udfyld appnavn, udgiver, formål, hjemmeside, privatlivspolitik, supportadresse, klienttype og præcise redirectadresser. Logoet er valgfrit. Formålet skal beskrive faktiske handlinger: eksempelvis “opret fakturakladder fra afsluttede ordrelinjer”. “Adgang til økonomi” er ikke tilstrækkeligt til at bedømme nødvendige rettigheder.
Appnavnet må højst være 80 tegn; udgiveren højst 160. Formålet skal være 40–4.000 tegn. Der kan registreres 1–10 forskellige redirectadresser. Logoet skal være PNG eller WebP, højst 64 KiB og 1.024 × 1.024 pixels. SVG og animerede billeder accepteres ikke. Kontrollen er en format- og størrelseskontrol, ikke en påstand om fuld filscanning.
Adresserne skal være præcise, kanoniske HTTPS-URL’er uden wildcard, credentials eller fragment. En rodadresse angives med afsluttende /. HTTP-loopback kan stå i en testregistrering, men Hours godkender ikke en produktionsrevision med HTTP-redirects. Der udføres heller ikke automatisk domæneejerskabsverifikation. Reviewerens dokumenterede vurdering af udgiver og adresser er derfor nødvendig.
4. Vælg klienttype og scopes
En confidential klient kører på en server, hvor klienthemmeligheden kan opbevares uden at blive udleveret til slutbrugere. En public klient kan ikke holde en fælles klienthemmelighed hemmelig. Indlejring i JavaScript, et programbundle eller en mobilapp gør ikke en hemmelighed fortrolig.
Vælg derefter de konkrete scopes, integrationen har brug for. invoices:write opretter en fakturakladde, men giver ikke invoices:read. journal:prepare kontrollerer input, men giver ikke journal:post. configuration:read viser kun kundens udvalgte kontoreferencer; det er ikke fri kontoplan- eller bankadgang.
De valgte scopes er på dette tidspunkt en ansøgning. De er ikke et produktionsmandat. En senere tokenrettighed er begrænset af både appens godkendte revision og kundens konkrete grant. En kundes godkendelse kan ikke udvide appens review, og et appreview kan ikke autorisere en kunde.
5. Opret og åbn sandbox
Vælg appen og fanen Sandbox. Opret testmiljøet eksplicit. Kontrolserveren registrerer appens sandboxgeneration, og en separat serverklient provisionerer syntetiske referencer i det konfigurerede, isolerede projekt. Kan dette projekt ikke bekræftes, stopper handlingen. Der findes ingen fallback til produktion.
Den grønne bjælke viser den valgte app og generation og ligger over siden i dokumentflowet. Den skjuler ikke headeren og ligner ikke en lille statusmærkat inde i et kort. Selve isolationen kommer fra serverkonfiguration, credentials, databaseplan og appgeneration, ikke fra bjælkens farve.
Opret et hsk_-token med så få testscopes som muligt. Værdien vises én gang og kan ikke hentes frem igen fra credentiallisten. Den udløber efter én time, og der kan højst være 10 aktive sandboxtokens. Start med GET configuration, kopiér testkonto-id’erne, og brug dem i et efterfølgende kald. En syntetisk UUID i dokumentationen er ikke en eksisterende konto i din sandbox.
6. Test både succes og afvisning
Send først en balanceret journal til journal/prepare. Prøv derefter bevidst en ubalance, et ukendt konto-id, et produktions-token på sandboxruten, et ikke-tildelt scope og samme idempotensnøgle med ændret input. Negative prøver er en del af integrationen, ikke ekstra pynt efter en grøn happy-path-test.
journal/prepare returnerer altid posted: false. Et faktisk sandboxkald til POST journal bruger Hours’ eksisterende bogføringsfunktion i sandboxdatabasen. Resultatet er en testpostering i den isolerede generation, aldrig en postering hos en produktionskunde.
Nulstilling kræver teksten NULSTIL SANDBOX og den aktuelle generation. Den skaber en ny generation og tilbagekalder tidligere sandboxtokens. Den sletter ikke immutable regnskabsposter ved at deaktivere deres beskyttelse. Et request, der allerede er optaget i den gamle generation, kan afsluttes dér; det bliver ikke flyttet til den nye generation eller til produktion.
7. Send revisionen til review
Åbn Review og indsend den gemte revision. Serveren flytter appen til reviewkøen. Det sender ikke automatisk en integrationsinvitation til kunder og udsteder ikke et produktions-token.
En intern Hours-reviewer kontrollerer formål, udgiver, redirectadresser, webhookdestination, privatlivspolitik, ønskede scopes og testgrundlag. Review kræver AAL2 og må ikke foretages af appens opretter eller et medlem af dens arbejdsrum. Resultatet er godkendelse af udvalgte scopes, anmodning om afklaring eller afvisning med begrundelse. Vurderingen er knyttet til revisionsnummeret.
En rettelse af appprofilen skaber en ny revision og tilbagekalder den gamle adgang. Det gælder også en tilsyneladende lille profilrettelse. Denne bevidst strenge model skal indregnes i en releaseplan; der findes ikke nul-nedetidsudgivelse med parallelle godkendte revisioner.
8. Tilslut først en kunde efter review
Efter appgodkendelse starter integrationen Authorization Code-flowet med S256-PKCE. Kunden logger ind på Hours, vælger en virksomhed, som vedkommende ejer, og tager stilling til scopes, kontoreferencer, udløb og eventuelt særskilt bogføringsmandat. Grantet kræver ejerens tofaktorbekræftelse (aal2), og dets levetid kan vælges fra 1 til 90 dage.
Appens backend bytter engangskoden til et hat_-token, der gælder 15 minutter, og et roterbart hrt_-token, der gælder 30 dage. Koden er gyldig i 60 sekunder og skal bindes til app, redirect, resource og verifier. Udfør tokenudvekslingen på den understøttede klienttype; der findes ingen generel CORS-konfiguration til direkte tokenudveksling fra en vilkårlig webside.
Afviste scopes, udløbet review, tilbagekaldt grant eller forkert organisation skal medføre stop. Integrationens supportflow må ikke foreslå kundens login-token, en intern servicenøgle eller et andet kunde-id som løsning.
9. Drift, rotation og ophør
Brug Credentials til at oprette og tilbagekalde klienthemmeligheder. Højst to aktive hemmeligheder pr. app giver plads til en kontrolleret rotation, og hver hemmelighed gælder 90 dage. Opret den nye værdi, installer den i secret manageren, verificér tokenudveksling, og tilbagekald derefter den gamle. En tabt engangsvisning kræver en ny credential; den gamle værdi kan ikke genskabes fra et hash.
Brug aktivitetsloggen til hændelser og referencesøgning, ikke til opbevaring af fulde payloads. Ved et uklart transportudfald genbruges idempotensnøglen. Ved et kompromitteret grant skal kunden tilbagekalde adgangen; ved et kompromitteret appmiljø skal appen suspenderes og credentials roteres. Allerede udleverede data forsvinder ikke ved tokenrevokering, og allerede påbegyndte netværksleveringer kan ikke trækkes tilbage.
Afslut først produktionsidriftsættelsen, når OAuth-forløbet, miljøisolationen, bogføringen, webhookmodtageren, abuse-beskyttelsen og rollbackproceduren er afprøvet. Læs sandboxguiden, autorisation, review og frigivelseskriterier som en del af dette forløb.