Visena Dokumentasjon
Partner API

Guide

Begreper

Sju begreper forklarer hvert kall du noen gang kommer til å sende. Denne siden sier hva hvert av dem er og hvordan de henger sammen; sidene etterpå bruker dem uten å stoppe for å forklare.

Instance /acme Credential clientId clientSecret Access token token_type: Bearer expires_in POST /acme/auth/api/v1/token grant_type=client_credentials Authorization: Bearer … Scope read · write act-as-user:read act-as-user:write Business API /acme/api/v1/{locale} /person · /company · … id: xK9mQ2 read → GET · write → POST
Instans, legitimasjon, tilgangstoken og scope. Legitimasjonen og alt den kan nå, ligger inne i én instans. Legitimasjonen byttes inn hos token-endepunktet mot et kortlevd tilgangstoken, og det er dette tokenet — aldri legitimasjonen — forretnings-API-et ser, som Authorization: Bearer. Scopene bes det om når tokenet lages, fra settet legitimasjonen har, og de sjekkes deretter på hvert enkelt kall.

Instanser

Hver Visena-kunde kjører i sin egen instans, og instansnavnet er første segment i hver URL du kaller. Base-URL-en får du ved oppstart; eksemplene i denne dokumentasjonen bruker https://api.visena.example og instansnavnet acme.

https://api.visena.example/acme/api/v1/en/person/xK9mQ2
/acme instansnavn /en locale /person ressurs, i entall /xK9mQ2 maskert id

Formen er /{instanceName}/api/v1/{locale}/person: instansnavn, versjon, locale, ressurs. Ressursstier er i entall/person, ikke /persons — og API-et har ikke noe partner/-segment noe sted, på noe plan. Ni ressurser ligger bak dette prefikset: person, company, company-template, project, project-template, activity, document, document/folder og document/changes.

Instansen er isolasjonsgrensen, ikke bare et rutingprefiks. En legitimasjon hører til nøyaktig én instans, og tokenene den produserer, virker bare der: peker du et token mot en annen instans' URL, får du 403 — ikke et delvis svar. Et førstesegment som ikke navngir noen instans i det hele tatt, svarer 404.

Integrerer du mot flere Visena-kunder, holder du derfor én legitimasjon per kunde, lager ett token per kunde og holder dataene deres fra hverandre også på din side. Det finnes ingen spørring på tvers av instanser og ingen kall som lister instansene du har tilgang til — instansnavnet er noe du får oppgitt, ikke noe du oppdager.

Legitimasjon og tokener ligger under samme instansprefiks, men på et annet plan: /{instanceName}/auth/api/v1/… i stedet for /{instanceName}/api/v1/….

Legitimasjon

En legitimasjon er et langlevd par av clientId + clientSecret. Den opprettes inne i kundens instans og autentiseres med en Visena-brukersesjonX-ACCESS-TOKEN-cookien til en innlogget bruker, ikke et bearer-token. Det er med hensikt: en maskinlegitimasjon kan ikke opprette flere legitimasjoner. I de fleste integrasjoner betyr det at kundens Visena-administrator oppretter legitimasjonen og overleverer paret til deg gjennom en sikker kanal.

Paret eies av en virkelig bruker i den instansen, og alt integrasjonen din gjør, tilskrives den eieren i Visenas historikk og revisjonsspor. Dette ene faktumet er grunnen til de praktiske reglene: én legitimasjon per integrasjon, én per miljø, merket etter systemet som holder den. Når noen i kundens organisasjon senere spør hvem som skrev en post, er svaret bare så presist som merkingen din var.

clientSecret vises nøyaktig én gang, i responsen som oppretter den. Visena lagrer bare en enveis-hash og kan aldri vise den igjen; det finnes ingen «vis hemmeligheten» og ingen tilbakestilling, bare opprett og trekk tilbake.

Bare server til server. Legitimasjon og tokener hører hjemme på din egen backend. Legg aldri noen av dem i en mobilapp, en nettleserside eller annen kode du sender ut til sluttbrukere — den som har hemmeligheten, kan opptre som integrasjonen din, med eierens navn på resultatet.

Å opprette, liste, trekke tilbake og rullere en legitimasjon — og å bytte den inn i et token — er Legitimasjon og tokener.

Tilgangstokener

Du sender aldri legitimasjonen til forretnings-API-et. Du bytter den inn — ett standard OAuth 2.0-kall med client_credentials mot /{instanceName}/auth/api/v1/token — i et kortlevd tilgangstoken, og sender det med på hvert kall som Authorization: Bearer ….

Tre egenskaper avgjør hvordan du skriver klienten din:

  • Tokenet er opakt. Det er en uleselig streng, ikke en JWT. Ikke dekod det, og ikke let inni det etter utløpstid, scopes eller bruker — token-responsen oppgir alt det ved siden av tokenet.
  • expires_in kommer fra responsen, hver gang. Det er antall sekunder til tokenet utløper. Les det og forhold deg til det; hardkod aldri en levetid, for tallet er plattformens å endre.
  • Det finnes ikke noe refresh-token — med hensikt. Når et token utløper, gjentar du nøyaktig samme kall. Lag ett, mellomlagre det, gjenbruk det til kort før utløp, og lag så et nytt; ikke lag ett per API-kall.

Et token slutter å virke av fire grunner, og de er verdt å skille: det utløp (401), legitimasjonen bak det ble trukket tilbake (401 — tokener som alt er laget, slutter å virke i løpet av omtrent ett minutt), det ble pekt mot en annen instans (403), eller det ble bedt om noe utenfor scopene sine (403). Feil og feilsøking har hele tabellen.

Scopes

En legitimasjon har et sett av scopes. Et token bærer de scopene du ba om da du laget det — legitimasjonens fulle sett, eller en delmengde av det — og hvert kall sjekkes mot tokenets sett.

ScopeTillaterFamilie
readAll lesing — GET-kall, lister, nedlastinger, endringsstrømmer.Vanlig
writeAll endring — POST, PUT, PATCH, DELETE.Vanlig
act-as-user:readDen samme lesingen, fra en legitimasjon på vegne av en bruker.På vegne av
act-as-user:writeDen samme endringen, fra en legitimasjon på vegne av en bruker.På vegne av

Scope er påkrevd og eksplisitt. Et token-kall uten scope-parameter avvises med 400 invalid_scope; det samme gjelder et kall som ber om et scope denne legitimasjonen ikke har. Plattformen gjetter aldri hva du mente å be om, og det finnes ingen standardverdi.

Håndhevingen skjer deretter per kall: en GET krever lese-scopet, og hver POST, PUT, PATCH og DELETE krever skrive-scopet. Et token laget med bare read svarer 403 på hver endring — som er nøyaktig det lesing virker, skriving feiler betyr når du ser det.

Be om det jobben trenger, ikke om alt legitimasjonen kunne gitt. En nattlig eksportjobb som bare har read, kan ikke skade kundens data selv om den blir kompromittert, og den innsnevringen er verdt å ha skriftlig: se Skrivebeskyttet tilgang.

Vanlig legitimasjon eller på vegne av en bruker

Legitimasjon finnes i to familier, og en legitimasjon hører til nøyaktig én av dem hele sin levetid. Forskjellen er hvilken bruker plattformen registrerer som den som gjorde arbeidet.

En vanlig legitimasjon opptrer som sin eier — brukeren den ble opprettet under. Alt den skriver, bærer den brukeren som ansvarlig, og scopefamilien dens er read og write. En legitimasjon på vegne av en bruker opptrer for én bestemt navngitt bruker i instansen. Den utstedes av en kundeadministrator gjennom Visenas administrasjonsverktøy og ikke gjennom selvbetjenings-API-et for legitimasjon, den er bundet til den ene brukeren, og scopefamilien dens er act-as-user:read og act-as-user:write.

Familien sier hvem tokenet handler som. Hvor bredt innenfor familien legitimasjonen rekker, er et eget valg, tatt da den ble opprettet: les og skriv registrerer begge scopene i familien, bare lesing bare lese-scopet. En legitimasjon opprettet som bare lesing kan aldri utstede et skrivetoken — se To tak, ikke ett.

EgenskapVanligPå vegne av
Scope-parread · writeact-as-user:read · act-as-user:write
Utstedes gjennomLegitimasjons-API-et, inne i instansen, av en innlogget bruker.Visenas administrasjonsverktøy, av en kundeadministrator.
Opptrer somBrukeren som eier legitimasjonen.Den ene navngitte brukeren den ble utstedt for.
Registreres ved skrivingEieren.Brukeren den opptrådte for og aktøren bak.
Respons på endringHar med actedBy, den maskerte id-en til den handlende brukeren.

Konsekvensen handler om identitet, ikke om hva som er mulig: bortsett fra scope-paret og den registrerte identiteten oppfører de to familiene seg likt — samme URL-er, samme forespørselskropper, samme statuskoder. Det som endrer seg, er navnet en saksbehandler ser på posten etterpå, og hva et revisjonsspor svarer når noen spør hvem som gjorde dette. Velg på vegne av når arbeidet skal framstå som en bestemt persons — en saksbehandler som arkiverer et dokument, en rådgiver som registrerer en aktivitet. Velg vanlig når integrasjonen er forfatter i eget navn: en nattlig import, en eksport, et dashbord.

Du kan ikke blande familiene. Hver legitimasjon har nøyaktig ett par, så å be en legitimasjon på vegne av en bruker om vanlig read write — eller en vanlig legitimasjon om act-as-user:* — feiler med 400 invalid_scope hos token-endepunktet, før noen data er berørt.

i

Familien er en designbeslutning, tatt før du skriver en linje kode. Den er fast hele legitimasjonens levetid: det finnes ingen bryter per kall, og en vanlig legitimasjon kan ikke oppgraderes. Og siden en legitimasjon på vegne av en bruker er bundet til én bruker, betyr det å opptre for flere brukere én legitimasjon per bruker — hver utstedt av kundens administrator, hver holdt for seg hos deg. En integrasjon som en dag skal føre arbeid i navngitte brukeres navn, er langt billigere å bygge slik fra starten enn å bygge om senere.

Maskerte id-er

Hver entitets-id som krysser API-et, er en opak maskert id — en streng som xK9mQ2, aldri et tall. Den er ikke postens databasenøkkel: plattformens interne nøkler blir værende inne i plattformen, og API-et deler ut en maskert form i stedet. Derfor kan du ikke gjette en id, ikke telle dem, og ikke slutte fra én av dem hvor mange poster en kunde har.

På din side gjør det den maskerte id-en til en opak fremmednøkkel, og lagringsreglene følger av ordet opak:

  • Lagre den som tekst, nøyaktig som mottatt. Full lengde, uendret. Den er ikke numerisk, så en heltallskolonne kan ikke holde den; id-ene i denne dokumentasjonen har både store og små bokstaver, så lagre og sammenlign dem med forskjell på store og små bokstaver, og hindre at et ORM eller en databasekollasjon visker bort forskjellen.
  • Ikke gå ut fra en lengde eller et alfabet. Eksemplene her er seks tegn, og ingenting lover at de blir seks. Gi kolonnen rom i stedet for å binde den til hvordan dagens id-er ser ut.
  • Send den tilbake ordrett. Den er det eneste håndtaket API-et godtar for den posten, og den er koblingsnøkkelen mellom dine rader og Visenas.
  • Utled ingenting fra den. To id-er kan ikke sammenlignes på rekkefølge, så en id er verken sorteringsnøkkel eller vannmerke for en synkronisering — bruk de tidsvinduede listene i Lister, paginering og synk til det.
  • Konstruer aldri en. En id du ikke har mottatt, peker ingen steder: som referanse i en skrivekropp feiler den med 422 og ingenting skrives; som stisegment gir den 404.

Maskerte id-er dukker opp noen steder en fersk integrator ikke venter dem. Den id-en du får tilbake når du oppretter en legitimasjon, er en maskert id, og tilbaketrekking bruker den id-en og ikke clientId. Location-headeren på en vellykket opprettelse bærer den nye postens sti, som ender med den maskerte id-en, og den samme id-en ligger i responskroppen. actedBy i en endringsrespons er også en maskert id, og det samme er hvert referansefelt som peker på en annen post.

i

En id er bare meningsfull inne i sin instans. En id fra én kundes instans peker ingen steder i en annen, og den samme fysiske personen hos to Visena-kunder er to urelaterte poster med to urelaterte id-er. Strekker systemet ditt seg over flere kunder, nøkler du din side på instansnavnet pluss id-en, ikke på id-en alene.

Locale

{locale}-segmentet ligger mellom versjonen og ressursen. De dokumenterte verdiene er en og no — merk at API-ets norsk er no, ikke den nb-en dette dokumentasjonsnettstedet bruker i sine egne URL-er.

https://api.visena.example/acme/auth/api/v1/en/credentials
https://api.visena.example/acme/auth/api/v1/token
/en legitimasjons-endepunktene har en locale /token token-endepunktet har det ikke

Det segmentet avgjør, er språket i den menneskelesbare teksten i responsene. Klarest ser du det ved en feil: detail-medlemmet i et RFC 9457-problemdokument er strengen du ville satt foran et menneske, og den lokaliseres av dette segmentet. Det segmentet ikke avgjør, er formen på nyttelasten — feltnavn, enum-verdier og id-er er API-kontrakten og leses likt i hver locale, som er grunnen til at endepunktsreferansen på dette nettstedet står på engelsk i begge språktrær.

i

Locale styrer hva API-et returnerer, ikke produktet. origos eget grensesnitt er norsk uansett: å sette en i URL-ene dine oversetter ikke en eneste skjerm, knapp eller rapport for kundens ansatte. Viser integrasjonen din Visena-data ved siden av en norsk skjerm, er no det som holder strengene dine i samsvar med ordene rett ved.

Velg én locale og bruk den konsekvent i hele integrasjonen, også på legitimasjons-endepunktene, som har segmentet de også. Konsekvens er like mye et praktisk som et estetisk spørsmål: sier halvparten av kallene dine en og halvparten no, er ikke feilteksten som ligger i loggen din, den feilteksten du kan søke etter i koden din.