API og autentisering
API-et ligger på https://api.utlegg.app. Den interaktive referansen finner du på api.utlegg.app/api-docs (Swagger UI), og OpenAPI-spesifikasjonen som JSON på api.utlegg.app/api-docs-json.
Kontrakt (Prefer: contract=next)
Bruk headeren Prefer: contract=next i alle API-kall, da dokumentasjonen forutsetter dette. Uten denne svarer API-et i et eldre format som ikke stemmer med eksemplene.
Header-format
Alle autentiserte kall sender token i Authorization-headeren:
Authorization: Bearer <token>
Prefer: contract=next
Tokens har prefiks som forteller hva de er:
| Prefiks | Type | Brukes for |
|---|---|---|
ua_ | Personal Access Token (PAT) | Personlige integrasjoner og utviklerbruk |
uas_ | Service Access Token (SAT) | Maskin-til-maskin (organisasjon eller globalt) |
| (JWT) | Bruker-session | Web/mobil etter ordinær innlogging |
API-et identifiserer token-type ut fra prefikset; du trenger ikke spesifisere dette eksplisitt.
Personal Access Tokens (PAT)
En PAT er knyttet til din egen brukerkonto og har samme rettigheter som deg selv i de organisasjonene du tilhører. Bruk den til scripts, ad hoc-integrasjoner og utviklertesting.
Lag en PAT i webdashbordet
- Logg inn på dashbordet.
- Gå til Innstillinger → Utvikler → Personal Access Tokens.
- Klikk Lag ny, gi den et navn og velg varighet.
- Kopier tokenen umiddelbart – den vises kun én gang.
Lag en PAT via API-et
curl -X POST https://api.utlegg.app/users/pat \
-H "Authorization: Bearer <din-eksisterende-token>" \
-H "Prefer: contract=next" \
-H "Content-Type: application/json" \
-d '{"name": "min-integrasjon", "expiresAt": "2026-12-31T00:00:00Z"}'
Endepunktet gir en 201-respons med et token-felt. Tokenen utløper som standard etter 30 dager hvis du ikke sender expiresAt.
Andre PAT-endepunkter:
GET /users/pat– list dine egne PATsDELETE /users/pat/:id– tilbakekall en PAT
Listen inkluderer lastUsedAt – tidspunktet for siste vellykkede autentisering med tokenen, oppdatert med inntil 15 minutters forsinkelse. Verdien er null for tokens som ikke er brukt siden feltet ble innført.
Sikkerhetsregler
- Lagre PAT som en hemmelighet (env var, secret store) – aldri i kildekode eller logger.
- Sett alltid
expiresAtder det er praktisk. - Tilbakekall ubrukte tokens – bruk
lastUsedAtfor å finne dem. - En PAT mister tilgang umiddelbart hvis brukeren deaktiveres eller mister sin organisasjonsrolle.
Service Access Tokens (SAT)
En SAT er en maskin-til-maskin-token knyttet til en service account i en organisasjon (eller globalt for partnere). Den brukes når en integrasjon skal kjøre uten en fysisk innlogget bruker. SAT-er har prefiks uas_.
Opprett og administrer en tjenestekonto i webdashbordet
Organisasjons-administratorer oppretter og administrerer org-scopede tjenestekontoer selvbetjent:
- Logg inn på dashbordet.
- Gå til Innstillinger → Utvikler → Tjenestekontoer.
- Klikk Ny tjenestekonto, gi den et navn og velg tilgangsnivå (se under).
- Åpne tjenestekontoen og lag en tilgangstoken under Tilgangstokens (Nytt token).
- Kopier tokenen umiddelbart – den vises kun én gang.
Tjenestekontoen kan omdøpes eller slettes, og enkelttokens kan tilbakekalles uten å slette kontoen.
Selvbetjeningen dekker org-scopede tjenestekontoer. En globalt-scopet SAT (for partnere/systemintegratorer med cross-tenant behov) opprettes ikke selvbetjent – ta kontakt på chat på utleggsappen.no og oppgi organisasjon, bruksområde, ønsket tilgang og en sikker leveringskanal for tokenen.
SAT-administrasjonsendepunktene under /organizations/:organizationId/service-accounts finnes, men er i dag interne og ikke med i den offentlige API-referansen – bruk dashbordet til å administrere org-scopede tjenestekontoer. Samme lastUsedAt-felt som for PAT-er finnes også på SAT-er, og kan brukes til å vurdere om en tjenestekonto fortsatt er i bruk.
Tilgangsnivå for org-scopede tjenestekontoer
Hver tjenestekonto har ett av to grunnleggende tilgangsnivåer, valgt ved opprettelse og synlig i dashbordet:
| Nivå | Tilgang |
|---|---|
| Admin | Les rapporter; les og oppdater fakturaer; full administrasjon av avdelinger, kategorier og prosjekter; les og administrer kontoer/organisasjon (inkl. provisjonering) |
| Lesetilgang | Kun lesetilgang til rapporter, fakturaer, kategorier, avdelinger og prosjekter – ingen skrivetilgang, og ingen tilgang til kontoer/organisasjon |
Utover grunnivået kan en organisasjons-admin tildele tjenestekontoen eksisterende egendefinerte roller fra tilgangspanelet i dashbordet, på samme måte som for brukere – nyttig for å gi presis tilgang uten å gå til Admin-nivået. Panelet viser også kontoens effektive tilgang (roller og tillatelsesnøkler).
SAT-er kan ikke opprette, endre, godkjenne eller slette rapporter, uansett tilgangsnivå eller tildelte roller. En SAT opptrer aldri som rapportinnleverer.
En globalt-scopet SAT kan lese brukere, kontoer og organisasjoner på tvers av tenants – brukes av partnere og systemintegratorer med cross-tenant behov.
Organisasjonskontekst
De fleste ressurser er organisasjonsavgrenset, og endepunkter har formen:
/organizations/{organizationId}/...
For å finne dine organisasjons-IDer:
curl https://api.utlegg.app/users/me \
-H "Authorization: Bearer ua_..." \
-H "Prefer: contract=next"
Responsen inkluderer brukerens kontoer (accounts). Hver konto inkluderer organizationId og organisasjonen kontoen tilhører.
For SCIM brukes organisasjonens handle i stedet for ID:
/organizations/{organization_handle}/scim/Users
Du finner handle i URL-en på dashbordet (https://utlegg.app/{organization_handle}). SCIM-endepunktene autentiseres med en PAT fra en admin-bruker eller en SAT for en tjenestekonto med Admin-nivå – se SCIM-referansen.
Sjekke effektiv tilgang
For å lese hvilke roller og tillatelsesnøkler du har:
GET /users/me/permissions
Legg til ?organizationId=<id eller handle> for å se din egen tilgang i en enkelt organisasjon. Responsen inneholder roles (liste med rolleobjekter, inkl. scope, scopeId og tier, systemrollen tilgangen bygger på) og permissions (liste med tillatelsesnøkler):
{
"roles": [
{
"id": "r1",
"name": "Approver",
"scope": "ORGANIZATION",
"scopeId": "org-abc",
"type": "CUSTOM",
"tier": null
}
],
"permissions": ["report.approve.manager-org", "report.read.manager-org"]
}
Bruk dette endepunktet til å implementere klient-side tilgangskontroll (can()-sjekker) i integrasjoner uten å hardkode rollenavn.
Endepunktet viser ikke tilgangen til en tjenestekonto, så kall med en SAT gir to tomme lister. Tjenestekontoens tilgang ser du i tilgangspanelet i dashbordet.
Standard responsformater
Paginering
Lister returneres som:
{
"items": [ ... ],
"hasMore": true,
"nextCursor": "eyJ2Ijo...",
"total": 137
}
items er sidens rader. hasMore er true om det finnes flere rader. Da er nextCursor med og kan sendes som cursor for å hente neste side. total er det totale antallet rader som matcher filteret og sendes bare når du ber om det med count=true. limit=0&count=true gir bare antallet.
Lister og filtrering
Lister (GET /users, /expenses osv.) tar flate, typede query-parametere. Hvilke parametere en liste har, står på endepunktet i API-referansen; de er avledet av feltene i listeradene.
| Parameter | Beskrivelse |
|---|---|
felt=verdi | Lik verdi. For id- og enum-felt kan du gjenta parameteren for «en av» (status=PENDING&status=APPROVED) |
feltContains=tekst | Tekstfeltet inneholder teksten (skiller ikke mellom store og små bokstaver) |
feltFrom, feltTo | Intervall for datoer, tidspunkt og beløp. From er inklusiv; To er inklusiv for beløp og eksklusiv for tidspunkt (beskrivelsen på parameteren sier hva som gjelder) |
feltIsNull | true eller false: feltet er eller er ikke tomt |
sort | En av listens navngitte sorteringer |
limit | Antall rader per side |
cursor | nextCursor fra forrige side |
count | true tar med total i svaret |
filter | Et filteruttrykk (en delmengde av Google AIP-160) over listens felter, i tillegg til parameterne over |
offset finnes ikke – bla med cursor.
Validering
Parameterne valideres strengt. En ukjent parameter, en verdi som ikke kan leses, en gjentatt parameter som bare tar én verdi, eller et motstridende par gir 400 med errorCode query_parameter_malformed, og errors[] lister hver feil.
Sortering
sort kan settes til en av sorteringene endepunktet støtter. Disse finner du på endepunktet i API-referansen. Rader med lik verdi sorteres sekundært på id, så rekkefølgen er deterministisk.
Feil
Responsformat
Alle feil følger RFC 9457 og har Content-Type: application/problem+json:
{
"type": "https://api.utlegg.app/problems/report_not_pending",
"status": 422,
"instance": "/reports/123/approve",
"errorCode": "report_not_pending",
"traceId": "4bf92f3577b34da6a3ce929d0e0e4736"
}
| Felt | Når det er med |
|---|---|
type | Alltid. En fast URI per feilkode. |
status | Alltid. Speiler HTTP-status. |
errorCode | Alltid. Stabil nøkkel – bruk denne til å forgrene logikk i klienten. Generiske feil har egne koder, for eksempel unauthorized, not_found, request_validation_failed og internal_error. |
errors | Når feilen gjelder bestemte felt eller parametere: én oppføring per feil, med code og pointer (JSON Pointer inn i body), parameter (query-parameter) eller params. |
instance | Stien i forespørselen. |
traceId | Forespørselens trace-ID. Oppgi denne til support. |
Feilsvarene har ingen menneskelesbar tekst – klienten eier tekstene og velger dem ut fra errorCode.
errorCode for forretningsfeil
Forretningsfeil gir en stabil errorCode-streng i responsen. Eksempler:
errorCode | status | Når |
|---|---|---|
report_not_pending | 422 | Forsøk på å godkjenne/avvise rapport som ikke er PENDING |
report_no_expenses | 400 | Innsendt rapport mangler utlegg |
subscription_feature_not_available | 402 | Forsøk på å bruke en feature som ikke er i abonnementet |
account_blocked | 403 | Brukeren er blokkert i organisasjonen |
account_deleted | 403 | Kontoen er slettet |
flow_validation_failed | 422 | Flyt-konfigurasjon er ugyldig |
invitation_creator_no_longer_authorized | 422 | Den som opprettet invitasjonen har ikke lenger nødvendig tilgang til å tildele rollen |
role_not_found | 404 | Rollen finnes ikke |
system_role_immutable | 403 | Forsøk på å endre eller slette en systemrolle via rolle-API-et |
role_cross_tenant | 403 | Rollen tilhører en annen organisasjon enn den i forespørselen |
report_deleted | 422 | Forsøk på å endre en slettet rapport |
En full oversikt over alle feilkoder med beskrivelse finner du på GET /error-codes.
Anbefalt klientmønster:
if (!res.ok) {
const body = await res.json();
switch (body.errorCode) {
case 'report_not_pending':
// vis bruker-vennlig melding, ikke retry
break;
case 'subscription_feature_limit_reached':
// tilby oppgradering
break;
default:
// generisk fallback, logg traceId
}
}
Hvordan rapportere en feil
Når du melder inn en feil til support, ta med:
traceIdfra responsbody, eller verdien itraceresponse-headeren – den peker rett på forespørselen og er det mest presise vi kan slå opp på.errorCode,statusoginstancefra responsbody – eller hele body-en.- Tidspunkt (UTC, gjerne med sekundoppløsning) og organisasjons-ID/handle.
- Token-type (PAT, SAT, eller bruker-session) – ikke selve tokenet.
- Minimal repro: cURL-kall eller payload som utløser feilen.
Eksempel på et godt feilkall i logg på din side:
2026-05-06T10:14:22Z org=abc traceId=4bf92f3577b34da6a3ce929d0e0e4736 errorCode=flow_validation_failed status=422 path=/flows/123/publish
Med traceId finner vi forespørselen direkte; med tidspunkt + organisasjon + path finner vi den manuelt.
Rate limits
De fleste endepunktene har ingen egen grense, men noen offentlige eller dyre operasjoner har det:
- Kvitteringsskanning (
/capture/attachments/*) – 5 kall/min per IP-adresse uten innlogging, 20 kall/min per bruker eller token med innlogging. - Revisjonsloggen – 120 kall/min per bruker eller token for
GET /organizations/{organizationId}/audit-trail, og 10 kall/min for eksport medGET /organizations/{organizationId}/audit-trail/export.
API-et kan i tillegg begrense antall kall per organisasjon per minutt. Da har svarene headerne RateLimit-Policy og RateLimit, som viser grensen og hvor mye som er igjen. Går du over grensen, får du en 429-respons med errorCode tenant_rate_limit_exceeded.
Alle responser som sendes ved rate-limiting eller overbelastning har headeren Retry-After. Vent minst så mange sekunder som den oppgir før du sender et nytt API-kall.
Sporing
Alle svar har headeren traceresponse. Feilsvar har også trace-ID-en i feltet traceId. Send trace-ID-en til support når du rapporterer en feil.
Sender du headeren traceparent (W3C Trace Context), bruker API-et trace-ID-en din. Da finner du kallet i dine egne logger.
Brukerinnlogging (web og mobil)
Web og mobil bruker ordinær auth-flyt med refresh-tokens. Støttede OAuth-leverandører inkluderer Apple, Google, Microsoft Entra ID og Vipps. Du trenger normalt ikke å implementere denne flyten selv – bruk PAT eller SAT for egne integrasjoner.
AI-assistenter (MCP)
En bruker kan også koble en AI-assistent som Claude til API-et via OAuth og MCP (Model Context Protocol)
på https://api.utlegg.app/mcp. Tilgangen er skrivebeskyttet og begrenset til brukerens egne roller. Se
AI-assistenter og MCP for hvordan tilkobling, samtykke og
tilbakekalling fungerer.
Ekstern tilgang for regnskapsfører eller rådgiver
En organisasjons-Admin kan gi en ekstern praktiker (typisk en regnskapsfører eller controller som allerede har en Utleggsappen-brukerkonto fra en annen organisasjon) midlertidig tilgang til egen organisasjon – uten å opprette et vanlig medlemskap eller sende invitasjon. Dette gjøres i dag kun via API-et; det finnes ikke et eget punkt for dette i dashbordet.
curl -X POST https://api.utlegg.app/organizations/{organizationId}/access-grants \
-H "Authorization: Bearer <admin-PAT>" \
-H "Prefer: contract=next" \
-H "Content-Type: application/json" \
-d '{"userId": "<bruker-uuid>", "roleId": "<rolle-id>", "expiresInDays": 90}'
- Bruker: mottakeren må ha en eksisterende Utleggsappen-brukerkonto – det sendes ingen invitasjon på e-post.
- Rolle: du velger selv hvilken organisasjonsrolle (system- eller egendefinert) praktikeren skal ha – fra lesetilgang til full
Admin. Å giACCOUNT_OWNER-rollen krever at du selv har den rollen i organisasjonen. - Varighet: standard 90 dager, kan settes opp til maks 365 dager (
expiresInDays). Tilgangen utløper automatisk – det finnes ikke en_EXPIRED-hendelse, kun en stille filtrering ved lesing. - Forny: kall samme endepunkt på nytt med samme bruker, organisasjon og rolle – dette forlenger utløpsdatoen fremfor å opprette en ny tilgang.
- Tilbakekall:
DELETE /organizations/{organizationId}/access-grants/:id. - Oversikt:
GET /organizations/{organizationId}/access-grantsviser organisasjonens egne tildelinger; en praktiker kan selv seGET /users/me/access-grantsfor tilganger på tvers av organisasjoner.
Alt knyttet til denne tilgangstypen lagres i revisjonsloggen (tildelt, fornyet, tilbakekalt) – se Sporbarhet, sikkerhet og kontroll.
Reagere på hendelser
Bruk SSE for å reagere på hendelser. Åpne én strøm med GET /sse/events. Strømmen sender alle hendelser du har tilgang til. Les mer i Sanntid, dypelenker og notifikasjoner.
Generelle webhook-abonnement utenom flyt er på roadmap – kontakt support hvis du har et konkret behov.