Guide
Getting started
Three moves take you from nothing to a real record in your own terminal: get a credential, exchange it for an access token, call the API. This page ends with you having made that call.
Everything is plain HTTPS and JSON over standard OAuth 2.0, so any language with an HTTP client works — and the examples below need nothing but curl and a shell. Read this page once end to end and you will have the shape of every request you ever send to the Partner API; the guides after it fill in the details.
The whole flow in three moves
There is no handshake to negotiate, no SDK to install and no session to keep alive. A credential identifies your integration, a token proves it for a while, and the token authorises each call. The three moves differ mainly in how long what they give you lasts, which is what decides where you keep it.
- Get a credential. A
clientIdand secret pair, created inside the customer's Visena instance by an administrator there. It identifies your integration and decides which user its calls act as — see Credentials and tokens. - Exchange it for an access token. One standard OAuth 2.0
client_credentialsrequest against/{instanceName}/auth/api/v1/tokenreturns a short-lived bearer token with exactly the scopes you asked for. There is no refresh token: when it expires, ask again. - Call the API. Send the token as
Authorization: Beareron every request. Create, read, update, delete, list and sync — the conventions are the same for all nine resources, and Making requests covers them.
Prefer screenshots? Credentials and tokens walks the same loop through the Visena interface, screen by screen, and Read-only access shows what a read-scoped token cannot do — with the 403 to prove it.
What you need before you start
Four values, and you have them all before you write a line of code. Every example on this page uses the placeholders in the right-hand column, so you can substitute yours once and copy the rest verbatim.
| Value | What it is | The examples use |
|---|---|---|
| Base URL | The host you were given during onboarding. Everything else hangs off it. | https://api.visena.example |
| Instance name | The short name of the customer's Visena instance, and the first segment of every URL you call. | acme |
| Client id | Identifies the API credential. Labelled Klient-ID (client id) in the Visena interface. | acme-partner-3f9a |
| Client secret | The credential's password — a vco_… value, shown once and stored only as a one-way hash. Labelled Klienthemmelighet (client secret). |
$CLIENT_SECRET |
The credential itself is created by an administrator in the customer's own instance, under Admin → Tilgangskontroll (access control) → Administrer API-tilganger (manage API credentials). If someone has already handed you a client id and a secret, you have everything this page needs; if not, Credentials and tokens is the page to send them.
Was your credential created «på vegne av en annen»? An on-behalf credential (created for a named user rather than for the administrator themselves) holds the act-as-user:read and act-as-user:write scopes instead of plain read and write. Ask for those below, or the token request answers 400 invalid_scope. And if the credential was also created as read only, act-as-user:read alone is what it can mint. Concepts explains the difference between the two kinds.
Server-to-server only. Credentials and tokens belong on your backend. Never embed them in a mobile app, a browser page or any code you ship to end users — anyone holding the secret can act as your integration, with the permissions of the user it authenticates as.
A 60-second taste
Four commands in a terminal: set your values, exchange the credential for a token, then read the first page of people in the instance. Nothing is created or changed, and the token asks for read only, so this is safe to run against a live instance.
Set your values
Substitute the three values from the table above. The secret is read from a prompt rather than typed into the command, so it never lands in your shell history.
# The values from the table above — substitute your own.
export VISENA_BASE_URL="https://api.visena.example"
export VISENA_INSTANCE="acme"
export CLIENT_ID="acme-partner-3f9a"
# Prompt for the secret, so it stays out of your shell history.
read -rs -p "Client secret: " CLIENT_SECRET && echo
export CLIENT_SECRET
Get an access token
The token endpoint takes the credential over HTTP Basic — the form that belongs in production — and the standard client_credentials grant. scope is mandatory and has no default: state exactly what the token may do.
curl -sS -X POST "$VISENA_BASE_URL/$VISENA_INSTANCE/auth/api/v1/token" \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-d "grant_type=client_credentials" \
-d "scope=read"
{
"access_token": "dcrEUVpMEpYqbdsDKawBb1yTqIAELMgqIO4KrN…",
"scope": "read",
"token_type": "Bearer",
"expires_in": 86399
}
The token is opaque — do not parse it — and lives for expires_in seconds. Note that the response echoes the scope it actually granted; that is the value worth asserting in a test. A production integration mints one token, caches it and re-mints shortly before expiry, which Credentials and tokens shows with a cache you can copy.
Make the call
Paste the access_token from the response and send it as a bearer token. The path carries the instance name, the API version and a locale, then the resource — person, singular, like every resource in the API.
# Paste the access_token from the response above.
export ACCESS_TOKEN="dcrEUVpMEpYqbdsDKawBb1yTqIAELMgqIO4KrN…"
# The first page of people in this instance.
curl -sS "$VISENA_BASE_URL/$VISENA_INSTANCE/api/v1/en/person?offset=0&limit=20" \
-H "Authorization: Bearer $ACCESS_TOKEN"
{
"totalItems": 428,
"totalPages": 22,
"page": 0,
"size": 20,
"items": [
{ "id": "xK9mQ2",
"firstName": "Kari",
"lastName": "Nordmann",
"primaryEmail": "kari.nordmann@example.com",
"isActive": true },
…19 more…
],
"links": { "self": "…", "next": "…/person?offset=20&limit=20" }
}
That is the entire model: credential → token → call. The ids are opaque strings like xK9mQ2 — store them exactly as received and echo them back verbatim rather than parsing or constructing them.
If the first call does not work
Failures are machine-readable, and the five below cover almost every first attempt. The token endpoint answers with the standard OAuth 2.0 error body; the API itself answers with an RFC 9457 problem document.
| What you see | Usual cause |
|---|---|
401 invalid_client from the token endpoint |
The client id and secret do not match, or the credential has been revoked. Check the pair; if it was rotated, deploy the new one. |
400 invalid_scope |
No scope parameter, or a scope this credential does not hold — including asking an on-behalf credential for plain read. |
| 404 from the token endpoint | The instance name in the first path segment is unknown. Check that segment before anything else. |
| 401 on the API call | The token is missing, expired or revoked. Verify the Authorization: Bearer header, then mint a fresh token. |
| 403 on the API call | The token lacks the scope the method needs, or it was minted for a different instance than the one in the URL. |
Every other status, the problem-document fields to log, and a retry policy that will not make things worse are in Errors and troubleshooting.
With that call answered, the rest is detail. Three pages, in the order most teams want them:
Concepts
Instances, credentials, tokens and scopes, plain against on-behalf, masked ids, and what the locale in the URL decides.
CredentialsCredentials and tokens
Create, list, revoke and rotate a credential, the full scope table, and a token cache worth copying.
RequestsMaking requests
URL anatomy, the headers every request carries, create, read, update, delete, and the response conventions.