På norsk →

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

How a software system submits tax cards, the VAT return, the a-melding (payroll report), the tax return and the annual accounts automatically on behalf of its customers — explained step by step. Every section opens with a short summary in plain language; the details (scopes, tokens, API calls) follow for anyone building the same thing. The pitfalls at the end are ones we walked into ourselves, so you don't have to.

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) — the monthly payroll-and-tax report; delivered as JSON directly to Skatteetaten's reporting API (the old Altinn 2 channel closed on 15 June 2026).
  • Shareholder register statement — the record of who owns the shares. The RF-1086 is built in Skatteetaten's official form format and submitted to the new shareholder register API in a three-step flow (main form → one sub-form per shareholder → confirm).

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 Central Coordinating Register for Legal Entities at the Brønnøysund registers is 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.

Macctthe software system
MaskinportenID card for the machine
Altinn Authenticationtoken exchange
System useryour authorisation
Altinn app / Skatteetaten APIsubmission
Receiptback to Macct
Simplified flow: Macct proves who the machine is (Maskinporten), ties that to your authorisation (system user), submits via Altinn / Skatteetaten and gets a receipt back.

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.

A small glossary

The rest of this page uses these words over and over. Get them down, and everything else reads without stopping.

TermMeaning
TokenA time-limited digital access pass. The machine presents it with every API call instead of a username and password.
ScopeWhat a token is allowed to do — e.g. "fetch tax cards" or "write to Altinn instances". A token only carries the scopes the client has been granted in advance.
System userThe authorisation a company gives a software system in Altinn, approved with BankID. It says who the system may act for.
Access packageA role-based "bundle" of permissions in Altinn (e.g. "VAT") that some apps require in addition to specific rights.
InstanceOne concrete submission in an Altinn 3 app — a "folder" that receives uploaded documents and is driven through process steps to a receipt.
EnvelopeThe XML wrapper around the tax return itself at submission time: who it concerns, which year, and the reference to Skatteetaten's own draft.
Party numberSkatteetaten's internal number for a taxable party. It looks like an organisation number but is not one — it is fetched from their API.

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.

Three things to know about Maskinporten:

  • One client per environment. Test and production are entirely separate worlds, each with its own client, its own key and its own scope grants.
  • Authentication with a signed key. The client proves who it is with a key registered in Digdir's collaboration portal (Samarbeidsportalen) — no passwords anywhere.
  • A token per need. The client signs a short JWT ("here I am, and I want these scopes") and exchanges it for an access token that lasts a few minutes.
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)
skatteetaten:mvameldinginnsendingSubmission of the VAT return (Skatteetaten's scope requirement, in addition to the Altinn app)
skatteetaten:innrapporteringameldingSubmit the a-melding and fetch the feedback (Skatteetaten's reporting API)
skatteetaten:innrapporteringaksjonaerregisteroppgaveSubmit the shareholder register statement (RF-1086, three-step flow)
skatteetaten:formueinntekt/skattemeldingFetch the current tax return/draft — required for the envelope reference when submitting the tax return (see the per-service section)
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 (the production client has, for example, the VAT and a-melding submission scopes that the test client has not yet been granted).
Not every scope is self-service. Several of the Skatteetaten scopes — including skatteetaten:formueinntekt/skattemelding and altinn:instances.read in production — cannot be added yourself in Samarbeidsportalen; they must be granted by the scope owner (Skatteetaten/Digdir) on request. Until the grant is in place, Maskinporten answers invalid_scope to the token request. Plan for this early: the grant itself is quick, but it happens per environment — and it is easy to believe "both are set" when only one environment actually got it.

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

Which token for which API?

The most common practical error is using the wrong type of token against the wrong API. The rule is simple once you see it:

APICorrect token
Altinn 3 app instances (VAT return, tax return, annual accounts)Altinn token — the Maskinporten token exchanged at Altinn (the call above)
Skatteetaten's direct APIs (tax card, a-melding, shareholder register, fetch tax return)Raw Maskinporten token, system-user-bound — without the Altinn exchange
The symptom when they are swapped: an Altinn-exchanged token against one of Skatteetaten's direct APIs gives 401 with "None of the configured JWK keys match the received JWT token" — Skatteetaten validates against Maskinporten's keys and does not recognise Altinn's signature. And vice versa: a raw Maskinporten token does not reach Altinn's app APIs. A token is never "almost right" — it is the right issuer for the right recipient.
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.

A-melding (payroll report)

Delivered as JSON directly to Skatteetaten's reporting API — the old Altinn 2 channel (A02) closed on 15 June 2026. Macct builds the official Leveranse object from the payroll run: income per recipient, withholding tax and attachment-of-earnings deductions (negative per recipient, positive totals in betalingsinformasjon) and employer's national insurance contributions per zone — with the occupational pension premium as its own basis line (tilskuddOgPremieTilPensjon), as the code lists require. The token is system-user-bound with the scope skatteetaten:innrapporteringamelding.

POST /v1/innsending/{periode}/{opplysningspliktig}   (Leveranse JSON + idempotencyKey header)
GET  /v1/forsendelser/{forsendelseId}                (feedback once it is ready)

The feedback is used for reconciliation: Skatteetaten's calculated withholding tax and employer contributions, the KID/account number and due date, plus any discrepancies with severity. As with everything else, the a-melding is never sent automatically — it is validated first, and a human presses "send".

Shareholder register statement (RF-1086)

Delivered to the Tax Administration's shareholder register API in a three-step flow, in the official form format (main form 890/RF-1086 + one sub-form 923/RF-1086-U per shareholder, validated against the Tax Administration's XSDs). The token is system-user-bound with the scope skatteetaten:innrapporteringaksjonaerregisteroppgave; every call carries a mandatory idempotencyKey.

1) POST /{year}/1086H                          → main form id
2) POST /{year}/{mainformid}/1086U             (one call per shareholder)
3) POST /{year}/{mainformid}/bekreft?antall_underskjema=N → forsendelseId

The statement follows the same approval flow as the tax return (draft → review → approve → submit, with SHA-256 integrity checks) and is never sent automatically. VPS-registered companies are blocked (VPS reports on their behalf); companies with multiple share classes or events such as mergers/demergers/reverse splits are referred to skatteetaten.no for now.

Annual accounts (RR0002)

Delivered to the Register of Company Accounts via the Altinn app app_brg_aarsregnskap-vanlig-202406:

  • Two documents are uploaded: Hovedskjema (RR0002H_M — metadata) and Underskjema (RR0002U_M — profit/loss and balance sheet with detail lines).
  • The signing requires a person. The system drives the process up to the signing step, but the signing itself cannot be performed by a system user — for legal reasons the Register of Company Accounts requires a person to sign with ID-porten in Altinn (system-user signing is rejected with a 403). The system prepares and locks everything, the person signs, and the system detects the completed process by polling the instance afterwards.
  • About the app name: the -202406 suffix is a version date Brønnøysund can roll for new yearly versions — the name is therefore configurable on our side, not hardcoded.

Business specification and tax return (limited company)

The tax return for a limited company is the most involved flow. It has three parts:

  • Build and approve. The business specification (the RF-1167 basis) is built and approved first; then the tax return itself is generated (v2 format, packaged in skattemeldingOgNaeringsspesifikasjonRequest with base64-encoded sub-documents). Submission is never automatic — the status flow is DRAFT → READY_FOR_REVIEW → APPROVED → SUBMITTED, with SHA-256 integrity checks between the steps (a Macct control, not a public requirement) and a mandatory preflight check before anything is sent.
  • Deliver via the Altinn app. The documents are uploaded to an instance in app_skd_formueinntekt-skattemelding-v2 and the process is driven up to the confirmation step.
  • The person confirms. The confirmation task (Task_2) must be performed by a person with ID-porten — see the person requirement below. SUBMITTED is only set once Skatteetaten's feedback actually says validertOK, never merely because the process step was passed.

The envelope must reference Skatteetaten's own draft

This one is easy to miss, and the error only shows up after the person has confirmed: Skatteetaten already holds a draft tax return for the company, and the submission envelope must contain a reference to exactly that document (dokumentreferanseTilGjeldendeDokument) plus Skatteetaten's own party number for the company. Both are fetched from the fetch-tax-return API right before submission:

GET https://api.skatteetaten.no/api/skattemelding/v2/{income year}/{orgnr}
Authorization: Bearer <system-user-bound Maskinporten token,
                       scope skatteetaten:formueinntekt/skattemelding>
Accept: application/xml

→ response: document id (e.g. SKI:755:14847), document type
  (skattemeldingUpersonligUtkast) and party number (10 digits — NOT the orgnr)
→ both go into the envelope before upload

Three details that must be right at the same time:

  • Without the reference the submission is rejected with konvoluttIkkeRiktigFormatert in the feedback. The requirement is in Skatteetaten's API documentation but is not enforced by the XSDs — the XML can validate green locally and still be rejected. The same happens if the organisation number is used where the party number should be.
  • The system-user binding is the representation itself. A token with the right scope but without a system user in authorization_details is rejected with "the logged-in user does not have the necessary rights in Altinn to represent the taxpayer".
  • Fetch the reference fresh per submission. The draft at Skatteetaten can be replaced; an old document id then points at the wrong version.

The person requirement in the final step

As with the annual accounts, Skatteetaten requires a trace to a person behind the submission: if the confirmation step is completed by machine with a system user, the process appears to deliver — but the feedback comes back validertMedFeil / innkommendeForespoerselManglerSporTilUtfoerende. The right design is therefore that the system prepares and locks the submission and stops at the confirmation, and the person completes it with ID-porten — in the Altinn portal or directly inside the accounting system (the end-user flow below).

Personal confirmation from your own system (ID-porten end-user flow)

The person does not have to be sent to the Altinn portal for the confirmation step: an end-user system can let the person log in with ID-porten inside the system's own flow and perform the confirmation via the API — the same pattern Altinn documents for end-user systems, and how the major vendors deliver. Register an "API client" in Digdir's Samarbeidsportal (grant authorization_code + PKCE S256; client authentication client_secret_post or private_key_jwt), request the scope openid altinn:instances.write, and exchange the person's access token for a personal Altinn token:

GET {altinn}/authentication/api/v1/exchange/id-porten             (prod)
GET {altinn}/authentication/api/v1/exchange/id-porten?test=true   (TT02)
Authorization: Bearer <idporten-access-token>

→ PUT {app}/instances/{instance}/process/next    body: {"action":"confirm"}
  Authorization: Bearer <personal-altinn-token>

Details worth knowing up front:

  • The consent page: ID-porten shows an "Accept / Reject" page for the data-sharing scopes. If the user picks "Reject", the token is issued with only openid/profile — and the exchange responds 403 with no body, because it requires at least one altinn: scope in the token. The consent is remembered after the first "Accept".
  • The read scope is not self-service in production: altinn:instances.read cannot be added on your own in Samarbeidsportalen's production environment. Request only altinn:instances.write — the confirmation is a write operation, and the exchange only needs one altinn: scope.
  • ?test=true: the TT02 exchange requires the parameter for tokens from test.idporten.no (403 without it); in production it must be omitted.
  • private_key_jwt: if you choose a key instead of a client secret, aud in the client_assertion must be the ID-porten issuer (not the token endpoint), and the kid from Samarbeidsportalen is required in the JWT header.
  • Quick sanity test: a token call with a dummy code separates client configuration from the rest of the flow — invalid_client means the client authentication is wrong, invalid_grant means the client is set up correctly (the code was intentionally invalid).

The confirmation then carries the trace to the person (the requirement above), the user never touches Skatteetaten's viewer client (see SMEVB-005 among the pitfalls), and the status rule is unchanged: "submitted" is only set on validertOK in the feedback.

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. Macct is built to handle these situations automatically where possible, so in practice they are rarely something you have to think about.

The right host for M2M. The tax card, VAT and fetch-tax-return 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.
The fetch-tax-return API is fussy about details. Three things that each give an unhelpful error on their own, and that all have to be right at the same time:
  • No trailing slash in the URL…/v2/2025/<orgnr>/ gives a 404 with no explanation; …/v2/2025/<orgnr> hits.
  • Accept must be exactly application/xml — the API only serves application/xml;charset=UTF-8 and replies 406 to everything else (including text/xml). In particular, check that your HTTP client does not have an interceptor overriding the Accept header globally — a JSON default somewhere else entirely in the code base can be the whole bug.
  • The right scope, system-user-bound — without the scope it already stops at Maskinporten (invalid_scope); with the scope but without a system user, the API replies "does not have the necessary rights … to represent the taxpayer".
A troubleshooting matrix that tries the combinations systematically (token variant × host) and logs the status per combination is worth its weight in gold here — that is how each of these was isolated.
konvoluttIkkeRiktigFormatert — the requirement is not in the XSD. The submission envelope must reference Skatteetaten's current draft (dokumentreferanseTilGjeldendeDokument) and use their party number — not the organisation number. The XSDs have both fields as optional, so local schema validation stays green regardless; the rejection only arrives in the feedback after the person has confirmed. The requirement exists only in the API documentation's prose. Fetch the reference from the fetch-tax-return API right before every submission (see the per-service section).
VAT validation vs. submission. The pre-validation API ("grensesnittstøtte") appears, as of today, to require 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. Should Skatteetaten change this practice, Macct follows the current arrangement.
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.
Confirmation and signing steps require a person. In app_skd_formueinntekt-skattemelding-v2 the confirmation task (Task_2) cannot be completed by a system user: the process appears to deliver, but the feedback comes back validertMedFeil with reason innkommendeForespoerselManglerSporTilUtfoerende — the Tax Administration requires a trace to a person behind the submission. Likewise, Altinn replies 403 when a system user attempts to sign the annual accounts. Design the flow so the system prepares and stops, and a person confirms/signs with ID-porten — and never set a "submitted" status without reading the feedback document (validertOK).
SMEVB-005 in the tax-return viewer. The Tax Administration's viewing client (skatt.skatteetaten.no/web/skattemelding-visning), which the person is sent to for the final review-and-submit, can fail with "Det skjedde en teknisk feil … SMEVB-005" — observed in both the test environment and production (as of July 2026), and affecting several end-user systems. Systematic testing shows that the client's only data call (GET /api/skattemelding-visning/skattemelding-altinn3) responds HTTP 500 with that code for all valid non-personal end-user-system content — even a minimal envelope — regardless of process step, while empty/corrupt instances yield SMEVB-002. The fault is thus server-side at the Tax Administration and has been reported to them (keep the korrelasjonsid header from the error response for the support case). Meanwhile the instance with the uploaded tax return stays safely locked in Altinn — it is a blocked final click, not lost data. The robust way around it is the ID-porten end-user flow described above: the person then confirms inside the accounting system and never touches the viewer client.

Frequently asked questions (FAQ)

What is a system user?

A system user is an authorisation a company grants to a software system (such as Macct) in Altinn, so the system can report to the public authorities on the company's behalf. It is approved with BankID, limited to specific services, traceable, and can be withdrawn at any time.

What is Maskinporten?

Maskinporten is a state OAuth2 service from Digdir for machine-to-machine communication. It gives a software system a time-limited "ID card" (access token) that proves which enterprise the system is acting as, without a human logging in.

What is the difference between Maskinporten and ID-porten?

ID-porten is used when a person logs in (for example with BankID) to use a public service. Maskinporten is used when a computer / system authenticates with no person present. Some APIs (such as VAT pre-validation) appear to require ID-porten; the submission itself goes via Maskinporten and Altinn.

What is the difference between a system user and Maskinporten?

Maskinporten proves who the machine is. A system user states what the machine is allowed to do, and for whom — the authorisation from a specific company. Both are needed: the Maskinporten token is bound to the system user in order to report on behalf of the company.

How does Altinn 3 work for accounting systems?

Altinn 3 offers "apps" (for example for the VAT return and annual accounts) that a software system creates instances in via API. The system exchanges the Maskinporten token for an Altinn token, creates an instance for the customer's organisation number, uploads the data and takes the process through to a receipt.

How does an accounting system submit a VAT return?

Via the Altinn 3 app for the VAT return: the system builds the XML message, creates an instance at Altinn with the customer's organisation number as owner, uploads the message and a submission wrapper, and takes the process through to a receipt. Submission never happens without the user's approval.

Why is the tax return rejected with "konvoluttIkkeRiktigFormatert"?

The submission envelope is missing the reference to Skatteetaten's current tax return (dokumentreferanseTilGjeldendeDokument), or uses the organisation number where Skatteetaten's party number should be. Both are fetched from the fetch-tax-return API right before submission. The requirement is in Skatteetaten's API documentation but is not enforced by the XSDs — the XML can therefore validate green locally and still be rejected.

How does the tax-card API work?

Macct fetches the electronic tax card directly from Skatteetaten's "Tax card for employers" API with a system-user-bound Maskinporten token. The response (deduction type, table number and deduction rate) is written onto the employee and used in the payroll run.

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