På norsk →

Altinn 3, Maskinporten and system user — how machine submission works

A technical walkthrough of how a software system submits tax cards, the VAT return, the business specification, the tax return and the annual accounts automatically on behalf of customers — via Digdir (Maskinporten), Altinn 3 and data from the Brønnøysund registers. With concrete scopes, access packages, token exchange, API calls and the pitfalls that are worth knowing about before you start.

What the integration does

Not technical? Skip straight to "In plain terms — without the jargon" further down, and get the whole picture in everyday words.

Macct submits to the public authorities on behalf of the customer, without the customer sharing a password or logging in manually. The following happens automatically:

  • Tax card for employers — fetches from Skatteetaten (the Norwegian Tax Administration) how much tax to withhold from employees' pay.
  • VAT return — the value-added-tax statement; delivered via an Altinn 3 app hosted by Skatteetaten.
  • Business specification + tax return (limited company) — the underlying figures (formerly called RF-1167) and the company's tax return itself.
  • Annual accounts — submitted as form RR0002 to the Register of Company Accounts in Brønnøysund via Altinn.
  • A-melding (payroll report) and shareholder register statement — the monthly payroll-and-tax report, and the record of who owns the shares — both to Skatteetaten.

Three building blocks make this possible: Maskinporten (a kind of ID card that lets one computer prove who it is to another — run by the state via Digdir), a system user (the authorisation you give Macct, approved with BankID in Altinn), and Altinn / Skatteetaten (where the submission is actually delivered). The Brønnøysund registers are used to check the organisation number, your role and that the company is in good standing when you register. All three are explained more fully just below.

In plain terms — without the jargon

Most of this page is technical. Here is the same thing in plain English, using a picture you will recognise from everyday life.

Think of the public authorities — Skatteetaten, Brønnøysund — as a set of locked offices where you hand in paperwork. Normally you go there yourself, log in with BankID and submit. Macct does that job for you. But for a computer to be let in and submit on your behalf, three things have to be in place:

  • 1. An ID card for machines (Maskinporten). Just as you show your BankID, the Macct machine has to prove who it is. Maskinporten — a state service from Digdir — gives Macct a digital "ID card" that renews itself automatically. No humans log in; it is machine talking to machine.
  • 2. An authorisation from you (the system user). The ID card only says "this is Macct". It does not say that Macct may act for your company specifically. So you sign, once, with BankID an authorisation: "Macct may submit VAT, annual accounts and the like for my company." This authorisation is called a system user. It is limited to exactly what it says, it is traceable, and you can withdraw it at any time.
  • 3. The delivery window itself (Altinn / Skatteetaten's APIs). With the ID card and the authorisation in hand, Macct goes to the right "window" at the authorities, hands in the document and gets a receipt back.

In short: Maskinporten = who the machine is. System user = what it is allowed to do, and for whom. Altinn = where it submits. The rest of this page is the detail behind each of those three.

What do you notice as a customer? Almost nothing. You approve one link with BankID the very first time. After that Macct keeps the books and prepares the submissions, and you press "send" when you are ready. Nothing goes off automatically — neither to Skatteetaten nor to Brønnøysund — without your approval.

Digdir / Maskinporten: client, JWT grant and scopes

In short: Macct asks the state for a temporary access pass by presenting a signed digital "stamp". The pass only covers the tasks Macct has been approved for in advance — called scopes — for example fetching tax cards or submitting VAT.

Maskinporten is Digdir's OAuth2 service for machine-to-machine (M2M). The software system has one client per environment (test and production kept separate), authenticated with an enterprise certificate / a key registered in the collaboration portal (Samarbeidsportalen). Tokens are obtained with grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer: a signed JWT assertion is exchanged for an access token for the scopes the client has been granted.

POST https://maskinporten.no/token
grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&
assertion=<signed JWT: iss=clientId, aud=maskinporten, scope=...>

The scopes we use

ScopeFor what
skatteetaten:skattekorttilarbeidsgiverFetch the electronic tax card
skatteetaten:mvameldingvalideringPre-validation of the VAT return (see pitfall)
altinn:instances.writeCreate/write Altinn 3 app instances (VAT, tax return, annual accounts)
altinn:authentication/systemregister.writeRegister/update the system in the System Register
altinn:authentication/systemuser.request.read / .writeCreate and read system user requests
Worth knowing: the scope names are literal. The correct one is skatteetaten:skattekorttilarbeidsgiver — a small deviation gives invalid_scope and no explanation. Maskinporten also rejects the entire token request if just one scope in the set is not granted, so the test and production clients should each have their own scope set (production has, for example, the VAT scopes that test does not).

System user: System Register, request and BankID approval

In short: This is your actual authorisation, and it is set up in three steps: Macct registers itself as a "system" with Altinn (done only once), then creates a request for your company, and finally you approve it with BankID. Only then can Macct submit on your behalf.

A system user is the customer's authorisation of the software system in Altinn. The flow has three steps:

1. Register the system (once per environment)

The software system is registered in Altinn's System Register with a system ID of the form <vendor-orgnr>_macct, the client ID(s) from Maskinporten, and which rights and access packages the system manages.

POST {altinn}/authentication/api/v1/systemregister/vendor
{ "id": "<orgnr>_macct", "vendor": { "ID": "0192:<orgnr>" },
  "clientId": ["..."], "rights": [...], "accessPackages": [...],
  "allowedRedirectUrls": ["https://macct.no/altinn/callback"] }

2. Create a system user request per customer

POST {altinn}/authentication/api/v1/systemuser/request/vendor
{ "systemId": "<orgnr>_macct",
  "partyOrgNo": "<customer's orgnr>",
  "externalRef": "selskap-123",
  "rights": [ { "resource": [ { "id": "urn:altinn:resource", "value": "app_skd_formueinntekt-skattemelding-v2" } ] }, ... ],
  "accessPackages": [ { "urn": "urn:altinn:accesspackage:merverdiavgift" }, ... ],
  "redirectUrl": "https://macct.no/altinn/callback" }
→ the response contains a confirmUrl (am.ui.altinn.no/.../systemuser/request?id=...)

3. The customer approves with BankID

The customer opens the confirmUrl and approves in Altinn. After approval, the actual system user is looked up (the "bysystem" list), and a systemUserId is stored on the customer. It is not used directly in the token — the authorisation itself is carried by externalRef + the customer's organisation number (below).

Worth knowing: externalRef must be unique per system user. If a customer needs a new system user (for example with extended access) without losing the old one, the new one must be created with a different externalRef — otherwise Altinn replies that a system user already exists for the system ID. A tidy convention (selskap-123, selskap-123-mva) keeps this manageable.

Access packages vs. rights

In short: Altinn requires two kinds of access at the same time — a broad "role bundle" (that Macct may work with accounting and VAT at all) and a specific "key" to each individual service. If either of the two is missing, the submission is rejected. Macct sets up both for you.

Altinn 3 distinguishes between two types of authorisation, and submission often requires both:

  • Rights — point to a specific resource/app, e.g. app_skd_formueinntekt-skattemelding-v2 (tax return), app_brg_aarsregnskap-vanlig-202406 (annual accounts), ske-innrapportering-amelding (a-melding).
  • Access packages — role-based "bundles" that Altinn's policy engine (PDP) requires in order to instantiate certain apps. We use urn:altinn:accesspackage:regnskap-okonomi-rapport and urn:altinn:accesspackage:merverdiavgift.
Worth knowing: an access package must be present on the system in the same environment (test vs. prod) before a system user request can ask for it — otherwise Altinn replies AUTH-00063 "accesspackage is not found in the referenced system". The System Register is environment-split, so packages added in test must also be added in prod. Rights alone (without an access package) tend to give a 403 from the PDP when the app is to be instantiated.

Tokens: Maskinporten token and Altinn token exchange

In short: The access pass from Maskinporten is exchanged for a "building-specific" pass at Altinn, and tied to your authorisation so the submission lands on the right company. Test companies and real companies use separate systems — they must never be mixed, or Altinn won't find the authorisation.

To act on behalf of an enterprise other than the client owner, we bind the token to the system user via authorization_details (the RAR type urn:altinn:systemuser):

authorization_details = [{
  "type": "urn:altinn:systemuser",
  "systemuser_org": { "authority": "iso6523-actorid-upis", "ID": "0192:<customer's orgnr>" },
  "externalRef": "selskap-123"
}]

The Maskinporten token is then exchanged for an Altinn token to reach the Storage/App APIs:

GET {altinn}/authentication/api/v1/exchange/maskinporten
Authorization: Bearer <maskinporten token with altinn:instances.write>
→ Altinn JWT used against app instances
Worth knowing: production and test companies are routed to separate environments — production customers to maskinporten.no + platform.altinn.no, test/demo to test.maskinporten.no + platform.tt02.altinn.no. If the environments are mixed, the system user does not resolve (404 / MP-303). We choose the environment per company, not globally.

Per service: API calls and parameters

Tax card for employers

Directly against Skatteetaten's "Tax card for employers" (v3, advance-tax API) with a system-user-bound Maskinporten token and the scope skatteetaten:skattekorttilarbeidsgiver. Host: api.skatteetaten.no (prod) / api-test.sits.no (test). Parameters: income year, the employer's organisation number and the employee's national identity number. Response: deduction type (table/percentage), table number and deduction rate — which are written onto the employee and used in the payroll run.

VAT return

Delivered via the Altinn 3 app mva-melding-innsending-v1 (instances.write + system user), not the discontinued direct API. Two XML documents are built: the return itself (mvamelding, namespace skattemeldingformerverdiavgift) and a submission wrapper (mvameldinginnsending). Flow:

1) POST .../instances              (create instance, instanceOwner = customer's orgnr)
2) PUT/POST data: mvamelding + wrapper  (replace the auto-created element)
3) PUT .../process/next            (data → confirmation → feedback)
→ receipt: instanceOwnerPartyId/instanceGuid

The return contains, among other things, skattleggingsperiode (a two-month term + year), fastsattMerverdiavgift, mvaSpesifikasjonslinje[], meldingskategori=alminnelig and skattepliktig>organisasjonsnummer. Optional pre-validation is available at /api/mva/grensesnittstoette/mva-melding/valider.

Annual accounts (RR0002)

To the Register of Company Accounts via the Altinn app app_brg_aarsregnskap-vanlig-202406. The -202406 suffix is a version date (June 2024) that Brønnøysund can roll when they publish a new yearly version of the app — so the name is configurable on our side rather than hardcoded, and it may differ from what is shown here if Brreg has released a newer version. Two dataTypes are uploaded: Hovedskjema (RR0002H_M — metadata) and Underskjema (RR0002U_M — profit/loss and balance sheet with detail lines). The process is taken to PROSESS_FULLFORT, which is the receipt that the submission has been completed.

Business specification and tax return (limited company)

The business specification (the RF-1167 basis) is built and approved first; then the company's tax return is generated (v2 format, skattemeldingForFormuesOgInntektsskatt, packaged in skattemeldingOgNaeringsspesifikasjonRequest with base64-encoded sub-documents) and delivered via app_skd_formueinntekt-skattemelding-v2. Submission is never automatic: the status flow is DRAFT → READY_FOR_REVIEW → APPROVED → SUBMITTED with a SHA-256 integrity check at every step and a mandatory preflight check (requiring, among other things, a completed year-end close and explicit approval) before anything is sent.

Common to all of them: the general ledger is kept in NOK, amounts in BigDecimal with two decimals, and foreign currency is converted at Norges Bank's rate on the transaction date. No submission happens unless it has been triggered and approved — the system is built never to send anything "on its own".
About the app names: like the annual accounts' -202406, several of the names carry a version or date token — -v2 for the tax return, -v1 for the VAT app, v3 for the tax-card API — that Skatteetaten and Brønnøysund can upgrade over time. All of them are configurable in Macct, so the live integration follows the current version even if the names in this text fall behind.

Pitfalls worth avoiding

The points below are the ones that typically cost the most time. All are handled in Macct.

The right host for M2M. The tax card and VAT APIs are reached at api.skatteetaten.no. The host idporten.api.skatteetaten.no validates ID-porten keys and replies 401 to a Maskinporten token — the right host is decisive.
VAT validation vs. submission. The pre-validation API ("grensesnittstøtte") requires an ID-porten token (interactive login) and replies 403 to a machine token. The submission itself runs fully automatically via the Altinn 3 app and is independent of the validation — so validation is an optional pre-check, not an obstacle to delivering.
Verify app name and dataType. The Altinn 3 apps' names and dataTypes must match the applicationmetadata — e.g. the annual accounts' main document is called Hovedskjema (not "Skjema"), and the VAT app is called mva-melding-innsending-v1.
Choice types in XML. Skatteetaten's forms use "choice" elements: norskIdentifikator must wrap an <organisasjonsnummer>, not plain text. Missing mandatory fields (such as skattepliktig>organisasjonsnummer) give internal app errors in the process step rather than a clear validation error.
Access package per environment. See above: packages must be present in the System Register in the same environment as the request (AUTH-00063 otherwise).
Unique externalRef. Reuse gives AUTH-00004; use a distinct reference for new/extended system users.

What a Macct customer has to do

For Macct to be able to submit the VAT return, annual accounts, tax return, etc. on behalf of your company, we need one one-time approval from you:

  1. You get a link to Altinn (am.ui.altinn.no) from Macct.
  2. You log in with BankID and approve the system user request — you must have a role on the company (typically the general manager or chair of the board, or a delegated role) in Brønnøysund/Altinn.
  3. The approval gives Macct a system user with exactly the access packages and rights stated in the request — no more and no less.

You never share passwords, BankID codes or API keys. The authorisation is scoped, traceable, and you can withdraw it at any time in Altinn under Access management → System access. And even with the authorisation in place, Macct never submits anything unless it has been triggered and approved — submission always requires an explicit human "send".

Want to see how it looks in practice?

Macct keeps the books and prepares the submissions — you approve once and press "send" when you are ready.

Try free for 30 days See the Altinn status