Visena Documentation
Partner API

Guide

Errors and troubleshooting

Every failure is machine-readable. The business API answers RFC 9457 problem documents; the token endpoint answers OAuth 2.0 error JSON. This page is the whole error surface — the body, the responses every endpoint shares, and what to do about each of them.

The problem document

Any non-2xx from the business API carries Content-Type: application/problem+json and an RFC 9457 problem document. The five RFC 9457 members are always there, and the platform adds three of its own:

Problem document
{
  "timestamp": "2026-08-21T11:58:21.123Z",
  "status": 422,
  "type": "urn:visena:validation:illegal-argument",
  "errorType": "ILLEGAL_ARGUMENT",
  "title": "Unprocessable Entity",
  "detail": "companyId 'bQ4wR8' does not resolve to an existing company",
  "instance": "/acme/api/v1/en/person"
}
MemberTypeWhat it is
timestampstring (date-time)When the platform produced the problem. Log it — it is how we find the request on our side.
statusintegerThe HTTP status, repeated inside the body.
typestring (URI)The problem type: a stable URN such as urn:visena:validation:illegal-argument or urn:visena:data:instance-not-found, and about:blank where there is nothing more specific to say than the status. This is the member to branch on.
errorTypestringThe same classification under the platform's own name, ILLEGAL_ARGUMENT for the URN above. Quoting either one identifies the case exactly.
titlestringThe HTTP reason phrase for the status — Unprocessable Entity. A protocol string, not a message for your users.
detailstringThe human-readable explanation. It is localized by the {locale} segment of the URL, and on a validation failure it names the offending field.
instancestringThe path of the request that failed.
additionalDataobjectStructured extras, on the errors that carry them. Absent from the body entirely when there are none, rather than null.

Branch on status and type; show detail to people, and log it, but never parse it. It is prose, and it is translated.

instance is not the Visena instance. In an RFC 9457 document instance means the request that failed, so it holds a path: /acme/api/v1/en/person. The instance name is the first segment of that path — acme.

The token endpoint is different

Minting an access token is an OAuth 2.0 client_credentials exchange, and it fails the OAuth 2.0 way rather than with a problem document: a JSON body with an error code — invalid_client, invalid_grant, invalid_scope — and normally an error_description beside it. Credentials, scopes and the exchange itself are on Credentials and tokens.

Shared errors — every endpoint

The reference pages list only the responses that are specific to an endpoint. On top of those, every endpoint can answer these four:

StatusMeaning
401Missing, expired or revoked token. Mint a fresh token and retry once.
403The token lacks the scope this method requires — the read scope for safe methods, the write scope for mutations — or it was minted for another instance.
404Unknown instance name in the URL.
500Unexpected server error. Retry with exponential backoff, and report the problem body's timestamp if it persists.

The instance name is the first path segment of every business URL — /{instanceName}/api/v1/{locale}/person — so a typo there answers 404 on every endpoint at once, not just the one you happened to call.

i

An endpoint may give the same code a narrower meaning. A 403 can also say that the capability behind that endpoint is not enabled for the instance, and most 404s are about the id in the path rather than the instance name. Read type and detail before you conclude the token is at fault: for the codes an endpoint documents itself, its own response table in the reference is the authority.

Status codes, and what to do about them

StatusMeaningYour move
400Invalid request — malformed JSON, failed validation, a parameter value the endpoint does not accept. Fix the request; detail names the problem. Do not retry it unchanged.
401Missing, expired or revoked token. Mint a fresh token and retry once. Recurring 401s are a credential problem, not a timing problem.
403The token lacks the scope the method needs, was minted for another instance, or the endpoint's capability is not enabled here. Compare the token's scope with the method, check the instance segment, and re-mint with the scopes you actually need.
404Unknown instance name, or an id that does not exist — or is not yours to see. Verify the instance segment, then the id. In a sync, treat a 404 on an id you know as “gone”.
409Conflict — an organisation number already in use, a move that collides with what is already there, or a delete that would cascade further than you asked for. Read detail. Probe for duplicates first, or resend with the acknowledgement the endpoint offers, such as acknowledgeCascade.
415Wrong Content-Type — nearly always a PATCH sent without application/merge-patch+json. Set the content type the endpoint documents.
422A well-formed request pointing at something unresolvable — a referenced id that does not exist. Fix the reference. Nothing was written.
500Unexpected server error. Retry with exponential backoff. If it persists, send us the timestamp and the body.

A sane retry policy

  1. Classify before you retry. Network failures and 5xx are transient. 400, 403, 409, 415 and 422 are not — the same request produces the same answer.
  2. Re-mint once on 401. One fresh token, one retry. A second 401 on a brand-new token is a credential or scope problem, and it needs a person rather than another attempt.
  3. Back off exponentially, with jitter. 1s → 2s → 4s → 8s is a fine ladder, and the jitter is what keeps a fleet of workers from retrying in lockstep.
  4. Log the whole problem document. timestamp, type and detail, with the request's method and path. That is the difference between a support request we can trace in seconds and one that needs a reproduction.
  5. Cap the attempts, then surface the failure. When the ladder runs out, stop and raise it where a person will see it. A job that retries forever looks healthy while it achieves nothing.
!

A retry loop can be worse than the failure it is hiding. Retrying a 4xx unchanged cannot succeed, and answering every 401 with a fresh token mint turns one expired credential into a load problem for every other integration on the instance. Retry only what can succeed, only with backoff, and never mint a token per request.

Quick diagnosis table

SymptomUsual cause
Every request 401The token expired and your cache is not refreshing it, or the credential was revoked in a rotation.
Reads work, writes 403The token was minted with the read scope only.
Everything 403 on one instanceThe token was minted for a different instance than the one in the path.
Every endpoint 404, including ones that worked yesterdayThe instance segment is wrong — a typo, or a name carried over from another environment.
Token request 400 invalid_scopeNo scope parameter, or a scope the credential does not hold. The platform never picks a default for you.
PATCH 415Content-Type is not application/merge-patch+json.
Company create 409A company with that organisation number already exists — probe /company/duplicates and update instead.

When you do need us, send the failing request's method and path, the problem document — timestamp, type, detail — and your clientId. Never the secret. That is everything we need to find the request. Send it to sales@visena.com.