Formål og præcis afgrænsning
Brug operationen, når appen har et udtrykkeligt mandat til at bogføre en færdig, balanceret postering. Det er ikke en kladde og ikke en forhåndsvisning. Gatewayen kalder Hours' eksisterende bogføringsfunktion i regnskabskernen med serverbestemt organisation og aktør; et succesfuldt kald har derfor en regnskabsmæssig effekt.
Kontrakten åbner ikke en bankkobling, en betalingsudførelse eller et frit valg af intern kilde. Den udleverer kun posteringens identitet, nummer og den tilhørende operationskvittering. Journaltekst og samtlige kundens øvrige poster returneres ikke som en del af skrivesvaret.
Før kaldet
Vælg den eksplicit konfigurerede installation. HOURS_BASE er enten dens /api/partner/sandbox/v1 eller /api/partner/v1. Basisadressen vises i App Console for appen. Et sandboxtoken begynder med hsk_; et produktions-access-token begynder med hat_. De er ikke indbyrdes udskiftelige og er ikke Supabase-login-tokens.
Produktion kræver en godkendt apprevision, en kundegrant og de relevante scopes. Kundegrantet er givet af organisationsejeren med tofaktor (aal2), gælder de valgte konti og varer højst 90 dage. Endelig journalbogføring kræver et særskilt mandat. Sandbox kræver en klargjort appgeneration i et separat projekt. En gyldig tokenform alene er ikke adgang.
Input og grænser
| Felt | Type | Påkrævet | Regler |
|---|---|---|---|
posting_date | string | Ja | date |
transaction_date | nullable | Nej | null eller string; date |
document_date | nullable | Nej | null eller string; date |
description | nullable | Nej | null eller string; minLength=1; maxLength=1000 |
currency | string | Nej | const="DKK"; default="DKK" |
lines | array | Ja | minItems=2; maxItems=500 |
Linjefelter
| Felt | Type | Påkrævet | Regler |
|---|---|---|---|
lines[].account_id | string | Ja | uuid |
lines[].debit_minor | integer | Nej | minimum=0; maximum=99999999999999; default=0 |
lines[].credit_minor | integer | Nej | minimum=0; maximum=99999999999999; default=0 |
lines[].tax_code_id | nullable | Nej | null eller string; uuid |
lines[].dimension_refs | array | Nej | maxItems=20; default=[] |
lines[].line_text | nullable | Nej | null eller string; minLength=1; maxLength=500 |
Hver linje har præcis én af debit_minor og credit_minor større end 0. Ukendte felter afvises. Hele body er højst 256 KiB, datoer skal være gyldige kalenderdatoer, og beløb er heltal i hele øre, højst 99 999 999 999 999 pr. linje. Valgfrie null-værdier og defaults normaliseres af serverens inputmodel. Organisation, aktør, intern kilde og bankkobling bestemmes ikke af payloaden.
Eksempel
Gem følgende syntetiske input som input.json. UUID'er erstattes med referencer fra den konkrete sandbox eller kundeautorisation; de må ikke genbruges mellem miljøer.
{
"posting_date": "2026-09-30",
"currency": "DKK",
"description": "Syntetisk prøve",
"lines": [
{
"account_id": "00000000-0000-4000-8000-000000000001",
"debit_minor": 12500
},
{
"account_id": "00000000-0000-4000-8000-000000000002",
"credit_minor": 12500
}
]
}curl --silent --show-error --fail-with-body \
--request POST "$HOURS_BASE/journal" \
--header "Authorization: Bearer $HOURS_TOKEN" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: $IDEMPOTENCY_KEY" \
--data-binary @input.jsonSucces og output
Forvent HTTP 201 ved succes:
{
"entry_id": "00000000-0000-4000-8000-000000000100",
"entry_no": 1,
"posted": true,
"operation_id": "00000000-0000-4000-8000-000000000900",
"replayed": false,
"environment": "sandbox"
}Et identisk replay giver samme successtatus og sætter replayed til true. Replay-svaret udelader environment, så klienten må ikke kræve feltet på replay. Produktions- og sandboxkontekst skal allerede være kendt fra kaldet, ikke udledes af en eventuelt manglende kvitteringsetiket.
Fejl og genforsøg
Manglende eller forkert miljøtoken afvises. Manglende scope, tilbagekaldt grant eller ændret apprevision stopper kaldet. Reference- og domænefejl skal afklares i stedet for at blive omskrevet til tilfældige gyldige værdier.
Ved timeout med ukendt udfald genbruges samme idempotensnøgle og samme input. En ændret payload under samme nøgle giver konflikt. En ny grant kan også give idempotency_grant_changed. Input, der afvises før regnskabskernen (invalid_input, invalid_amount, journal_unbalanced og reference_not_allowed), gemmer ingen kvittering, så samme nøgle kan genbruges med rettet input. En afvisning fra kernen (domain_rejected, for eksempel et lukket regnskabsår) gemmes og afspilles igen. Klientens forretningslog skal derfor gemme den oprindelige kontekst.
Fra forretningshændelse til postering
Ved en syntetisk omkostning på 125,00 DKK angives beløbet som 12500, ikke 125.00. To linjer kan fordele beløbet som debet på en udgiftskonto og kredit på en gældskonto. Kontiene vælges fra den autoriserede konfiguration; de konkrete UUID'er i eksemplet er ikke rigtige referencer.
Hver linje skal have et positivt beløb i præcis én af debit_minor og credit_minor. Den anden side er nul. Samlet debet skal være lig samlet kredit, og hver linje må højst være 99 999 999 999 999 øre. Beregningen bruger heltal, så en afrundingsfejl ikke skjules ved at runde inde i bogføringsmotoren.
Eksemplet illustrerer inputstrukturen og er ikke en beslutning om korrekt kontering eller moms for en virkelig hændelse. Skattebehandling og kontovalg skal være afklaret af den ansvarlige. En momsreference skaber ikke automatisk manglende momslinjer.
Efter et timeout gemmer klienten sit oprindelige normaliserbare input, miljø, app, grant og idempotensnøgle. Den prøver det samme kald igen. Et nyt tilfældigt idempotens-ID kan skabe endnu en postering og er ikke en løsning på et uklart udfald.
Test din integration
Test korrekt kontekst, manglende scope, forkert miljø og fremmede referencer. Test også to samtidige identiske requests, ændret payload med samme nøgle og tabt svar efter commit. Kontrollér den faktiske effekt i regnskabet: et 201-svar fra en simuleret server er ikke bevis på præcis én bogføring.
Se rettigheder, fejl og idempotens.