Skip to content
Apier

How do I set up an Altinn system user from scratch, from registration to first token?

Four steps, in a fixed order. First, register your system in the Altinn System Register, paired with the Maskinporten client it will authenticate as and naming the access packages it needs. Second, create a system user request for each customer; the customer opens the approval link and approves it in Altinn, which delegates the packages to the new system user. Third, request a token from Maskinporten with an authorization_detailsentry naming the customer's organisation number; the issued token carries the system user identity a plain token does not. Fourth, call the target API, exchanging the token first where the surface requires it. Only step two repeats per customer.

Four steps in a row: register the system, paired with your client id; the customer approves, which delegates the packages; the Maskinporten token, which names the customer organisation; and the API call, exchanged when the surface requires it. Dashed callouts below mark the cadence of the first three steps: once per system by you, once per customer in Altinn, and every few minutes by your client.Register the systemwith your client_idCustomer approvesdelegates the packagesMaskinporten tokennames the customer orgCall the APIexchange when requiredonce per system, by youonce per customer, in Altinnevery few minutes, by your client
The row reads left to right exactly once per customer. Registration happens once per system, approval once per customer, and only the token step repeats continuously, which is why it is the one to automate first.

Step 1: what do I register before any customer can approve?

The prerequisite is a Maskinporten client of your own, because the system you are about to register is defined partly by the client it authenticates as. With that in hand, you create an entry in the Altinn System Register. The entry carries your vendor organisation number in ISO 6523 form, a name and description in bokmål, English and nynorsk, the rights and access packages your system requests from customers, the Maskinporten client id array, and optionally redirect urls and a visibility flag that decides whether customers can find the system by name.

The create call is itself a machine-to-machine call: it is guarded by the system register write scope, so your Maskinporten client needs that scope granted before the registration can happen. This is the step where the official documentation is at its most scattered, because the client lives in Digdir's world and the register lives in Altinn's, and each side documents its own half.

The choice that matters most here is the package list. The setup guidance is explicit that the vendor is responsible for registering the correct access packages, and everything downstream inherits the decision: the customer approves exactly what the register entry requests, and an over-broad list makes approvals hesitate while an under-scoped one fails later calls. Which credential construct plays which part in this chain is owned by the credential decision guide.

Step 2: what does the customer see, and who approves it?

For each customer you create a system user request naming the packages your registered system asks for. The response carries an approval link; persist it when you first receive it, because a repeated create for the same request has been observed to answer without it. Your customer opens that link, signs in to Altinn, and sees your system by its registered name together with the packages it requests, described in business terms rather than as raw scopes.

Approval is itself a delegation, and that decides who can do it. The check is an all-or-nothing one: the approver must hold the authority to delegate every package in the request, so one sensitive package can sink an approval a managing director could otherwise give. When an approval fails with a 403, or a 500 that behaves like one, the causes and fixes are owned symptom by symptom by the system user troubleshooting guide, which this walkthrough deliberately does not restate.

One property of the result is worth designing for on day one. The setup guidance states plainly that Altinn does not know who the individual user behind the system user is: the grant names your system, not your employees. Whatever per-user control your product needs on top of the delegated authority is yours to build, and pretending the platform provides it is a compliance gap waiting to surface.

Steps 3 and 4: how do I get the token and make the first call?

The token request goes to Maskinporten, the same signed-JWT grant your client already uses, with one addition: an authorization_details entry of type urn:altinn:systemuser whose systemuser_orgnames the customer's organisation number in ISO 6523 form, the org number behind a 0192: prefix. In the test environment the request goes to the token endpoint on test.maskinporten.no; an optional externalRef pins the request to one specific system user when a customer has granted several.

What comes back is what distinguishes the system user token from a plain Maskinporten token. The scopes are the same, but the issued token carries an authorization_detailsclaim with the system user id and your system id, which is how the receiving API resolves that your client is acting under the customer's delegated authority rather than its own. Nothing about the claim widens what you may do; it changes whose authority the call carries.

The last step is the call itself, and it has one fork: some Altinn 3 surfaces accept the Maskinporten-issued token directly, while Altinn Apps and the classic platform APIs require it exchanged for an Altinn token first. That decision, and the exchange call, are owned by the token exchange guide. For testing, run the whole chain in the test domain: the test Maskinporten issues the token and TT02, at platform.tt02.altinn.no, evaluates the delegation. The two environments are separate trust domains, so nothing proven in one carries into the other.

The walkthrough as a checklist: each step, where it happens, and the decision or trap it carries. The approval failures themselves are owned by the troubleshooting guide.
StepWhere it happensThe decision or trap
Register the Maskinporten clientDigdir's side: the client your system authenticates as.The client needs the system register write scope before the next step works.
Create the system register entryAltinn System Register, paired with your client id array.The requested package list: narrow enough to approve, wide enough to work.
Create the system user requestYour integration, once per customer.Persist the approval link from the first response; repeats have been observed without it.
Customer approvesIn Altinn, by someone able to delegate every requested package.All or nothing: one package outside the approver's authority sinks the approval.
Request the token, call the APIMaskinporten, then the target API; TT02 hosts for the test chain.The RAR entry names the customer org; exchange the token where the surface requires it.

Make the first call

The sandbox call answers who may act for a company at all, with no key. The TypeScript sample is the brokered version of this whole walkthrough: Apier holds the client and the system registration, the fullmakt request starts the per-customer leg, and the customer's approval in Altinn stays the one human step. The brokered flow runs on the mock Altinn adapter until live credentials land.

# 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
// The brokered walkthrough: one Apier key to apier.no, no government
// credential in your stack. Steps 1 and 3 collapse into this request;
// step 2, the customer's approval, stays a human decision in Altinn.
const res = await fetch("https://www.apier.no/api/v1/fullmakt/request", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.APIER_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({
    agent_principal_id: "22222222-2222-4222-8222-222222222222",
    org_number: "999999999",
    scopes: ["altinn:accessmanagement/authorizedparties.read"],
  }),
});

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 } = await res.json();
// Persist data.delegation_url and put it in front of the customer.
// Act only after GET /api/v1/fullmakt/999999999 answers full.
console.log(data.status, data.delegation_url);

Frequently asked questions

What do I need before I can register a system?
A Maskinporten client of your own, because the system register entry pairs your system with the client id array it will authenticate as, and the create call itself is guarded by the systemregister write scope. The register entry then carries your vendor organisation number, a name and description in three languages, the rights and access packages the system requests, and optionally redirect urls and a visibility flag.
Who at the customer can approve the system user request?
Someone whose own authority covers every package the request names. Approval is itself a delegation, and it is an all-or-nothing check: the approver must be able to delegate all of the requested packages, so one package outside their authority sinks the whole approval. Registry roles such as the managing director cover most packages; the sensitive ones need the business's hovedadministrator.
What makes a system user token different from a plain Maskinporten token?
The authorization_details claim. A plain token asserts your organisation and its scopes. A system user token is requested with an authorization_details entry naming the customer organisation, and the issued token carries the system user id and system id back to you, which is what lets the API resolve whose authority the call acts under. The scopes themselves are not widened by this.
How do I run the whole sequence in a test environment?
Use the test chain end to end: the test Maskinporten issues the token, and TT02, Altinn's test environment at platform.tt02.altinn.no, evaluates the delegation. Test and production are separate trust domains, so nothing carries across: not the system registration, not the customer approval, not the token. A sequence proven in TT02 must be re-established piece by piece in production.
What is Apier's equivalent of this whole sequence?
One brokered flow. Apier holds the Maskinporten client and the system registration, so your application authenticates with one Apier API key. request_fullmakt starts the per-customer leg and returns the approval link, the customer approves in Altinn as before, and check_fullmakt answers whether the delegation is active before you act. The customer approval is the one step no broker can absorb.