Skip to content

What Altinn authentication level does my call need, and why do I get 403 at level 2?

By , founder of Apier

Every Altinn session and token carries an authentication level that reflects how the caller authenticated, and a service's policy can declare a minimum it will accept. A refusal at level 2 means exactly that: the credential you presented sits below the minimum the policy demands, so the request is refused regardless of how valid your rights and delegations are. Machine callers meet the same wall in a sharper form, because a raw Maskinporten token carries no authentication level claim at all; exchanging it for an Altinn token adds the claim, with 3 as the documented value for an organisation. Level 4 surfaces have no documented machine path, so those steps belong to a human.

Four steps in a row: the credential, which carries a level or none; the service policy, which declares the minimum; the decision, where the level is checked; and the access to the app or API. Dashed callouts below mark where each failure surfaces: a raw Maskinporten token with no level claim at the credential step, a refusal below the minimum at the policy step, and no machine path for level 4 at the decision step.Credentialcarries a level, or noneService policydeclares the minimumDecisionlevel checked hereAccessapp or APIraw Maskinporten token: no level claimbelow the minimum: refusedlevel 4: no machine path
The level gate sits beside the rights gate, not behind it. A caller can hold a perfect delegation and still be refused here, which is why level problems masquerade as permission bugs.

What are Altinn authentication levels?

A measure of how strongly the caller proved who it is, attached to the session or token at authentication time. The weakest methods sit at the bottom: a username-and-password login gives access to services requiring level 1. Stronger electronic IDs authenticate at higher levels, and an ID-porten token records the result in its acr claim, with Level3 as the documented example. The level is not a permission: it says nothing about what the caller may do, only how firmly Altinn knows who is calling.

Machines have levels too, and this is where the model bites integrators. Enterprise certificate authentication provides security level 3, and the documentation is explicit that it provides only that. The Altinn token minted by the exchange endpoint makes the value visible: the documented organisation token carries an AuthenticationLevel claim of 3. So a machine caller's ceiling is level 3, and no scope, delegation or certificate strength raises it.

Level 4 exists above that, reached with the strongest electronic IDs. For the documented message-service case, reading a level 4 service is only possible through ordinary ID-porten login, and machine integration is not possible there. That single fact decides an architecture question: any workflow that touches a level 4 surface needs a human step, and pretending otherwise produces a refusal no code change can fix.

How does a service declare its minimum, and what happens below it?

In Altinn 3 the requirement lives in the service's authorisation policy. The decision returned for a request can carry an obligation whose attribute category is urn:altinn:minimum-authenticationlevel, with an integer value; the documented example sets it to 2. The shape matters more than the number: the minimum is a property of the resource's policy, chosen by the service owner, so two services behind the same host can demand different levels and answer the same credential differently.

When the credential sits below the declared minimum, the request is refused even though the rights check would have passed. That is the answer to the question in this page's title: a 403 at level 2 is the policy demanding more certainty about who is calling than your credential provides. The fix is never in the request payload and always on the credential side, which is what separates this failure from every other 403 in the troubleshooting silo.

How the refusal surfaces is less tidy than the model. Integrators observe that a level shortfall does not always arrive as a labelled denial: it can surface as a generic error page, or as a server error that says nothing about authentication levels. Nothing in the official pages promises those shapes, so treat them as observations rather than contract, and adopt the debugging rule they imply: on a level-gated surface, an unexplained rejection is a level problem until the credential's claims prove otherwise.

The level failure cluster as symptom, likely cause and fix. Items marked observed describe integration experience rather than documented behaviour.
SymptomLikely causeFix
403 at level 2 on a portal-backed flowThe login method sits below the minimum the service's policy declares.Re-authenticate with a stronger electronic ID before retrying the service.
Valid delegation, machine call refusedA raw Maskinporten token was presented; it carries no AuthenticationLevel claim.Exchange the token first and present the Altinn token on the call.
Same call passes as a person, fails as a machineThe service requires level 4, and machine paths reach only level 3.Route that step to a human with a level 4 electronic ID; no code change fixes it.
Generic error page instead of a denial (observed)A level shortfall surfacing without a labelled refusal.Check the credential's level claims before debugging anything else.
500 where a refusal makes sense (observed)A refusal-shaped condition surfacing with a server-error status.Exhaust the level and authority hypotheses before treating it as an outage.

How do I debug a level failure instead of a permission failure?

Separate the two gates first. A permission failure is about what the caller may do, and its trail runs through delegations, packages and scopes; that cluster is owned by the system user troubleshooting guide and the delegation troubleshooting guide. A level failure is about how the caller authenticated, and its trail is one item long: what level claim does the presented credential actually carry.

For machine callers that one item almost always resolves the case. Decode the token you are actually sending and look for the AuthenticationLevel claim: a raw Maskinporten token will not have one, and the fix is to route it through the exchange first, which the token exchange guide owns end to end. If the claim is present and the service still refuses, you are either below a higher minimum or on a level 4 surface, and the resolution is a stronger credential or a human step rather than another retry.

Apier's part in this discipline is the first read, not the pre-flight. GET /api/v1/company/{org}/authority answers who may act for the company from Brønnøysund before you ask anyone to approve anything; the level requirement itself, and the checks a filing would face, stay with Altinn and your own client. The actions endpoint that used to run those checks as a dry run was parked on 2026-10-21 and answers 410 CAPABILITY_WITHDRAWN.

Make the first call

The sandbox call answers who may act for a company at all, with no key. The TypeScript sample is the production read of the same question, with one Apier key: the registered signatories for a company, so the approval you ask for is one the approver can give.

# Zero-auth sandbox: who may act for a company, from the registry.
curl -s https://www.apier.no/api/v1/sandbox/public/company/999999999/authority
// Before the chain: who may act for the company, from the registry,
// with one Apier key. Nothing is submitted.
// Replace {org} with a real organisasjonsnummer; 999999999 exists only in the sandbox.
const res = await fetch("https://www.apier.no/api/v1/company/{org}/authority", {
  headers: { Authorization: `Bearer ${process.env.APIER_API_KEY}` },
});

if (!res.ok) {
  // Every non-2xx answers the same structured envelope.
  const { error_code, explanation } = await res.json();
  throw new Error(`${error_code}: ${explanation.summary}`);
}

const { data, _meta } = await res.json();
// "sole" | "joint" | "by_role" | "prokura_only" | "no_authority" | "unknown"
console.log(data.classification, data.summary, _meta.data_source);

Frequently asked questions

Where does my authentication level actually come from?
From how the caller authenticated, not from what it is allowed to do. A username-and-password login sits at level 1, and stronger electronic IDs authenticate at higher levels. On the machine side, enterprise certificate authentication provides level 3, and the Altinn token minted by the exchange endpoint carries an AuthenticationLevel claim, with 3 as the documented value for an organisation token. The level travels with the session or token; rights and delegations are checked separately.
My delegation is valid. Why is the call still refused?
Because the level check and the rights check are different gates, and passing one says nothing about the other. The common machine-side shape is a raw Maskinporten token sent to a surface whose policy requires a minimum level: the raw token carries no AuthenticationLevel claim at all, so there is nothing for the policy to accept. Exchanging the token first adds the claim, and the same delegation then passes.
Can a machine reach a service that requires level 4?
Not in the documented message-service case. The documentation states that reading message services at security level 4 is only possible through ordinary ID-porten login, and that machine integration is not possible there, since certificate authentication provides only level 3. When a flow hits such a service, the honest design is a handover: a human authenticates with a level 4 electronic ID and performs that one step.
Why do I sometimes get a generic error page or a 500 instead of a clear denial?
Integrators observe that a level shortfall does not always surface as a labelled refusal: the answer can arrive as a generic error page or a server error with no mention of authentication levels. None of that is documented behaviour to rely on. The practical rule is to treat an unexplained rejection on a level-gated surface as a level problem first, and check what your credential carries before treating it as an outage.
What does Apier answer before I hit the level wall?
The registry half of the chain: who holds signaturrett or prokura for the company, from Brønnøysund, with source and date, so the person you ask to approve a system user or a level 4 step is one whose authority covers it. Apier does not run pre-flight checks for filing actions; that endpoint was parked on 2026-10-21 and answers 410 CAPABILITY_WITHDRAWN, and nothing Apier does raises or bypasses an authentication level.