Skip to main content

Norway 🇳🇴

EHF (Elektronisk Handelsformat) and e-invoicing in Norway.

Overview​

AspectDetails
B2G MandateSuppliers to central government since April 2019 (state bodies accept EHF since 2012)
B2B MandateIssuing from January 1, 2027 (bill before the Storting); receiving from January 1, 2030
Digital BookkeepingJanuary 1, 2030 (full digital accounting systems)
CIUSEHF (Elektronisk Handelsformat, UBL-based) / Peppol BIS 3.0
Primary Scheme0192 (Organisasjonsnummer)
Peppol AuthorityDFØ (Direktoratet for forvaltning og økonomistyring)
Central SMPELMA (Digdir) — mandatory for public-sector bodies
PASRApproved 2025-09-17
Norway - Peppol Pioneer

Norway is one of the most mature Peppol markets: about half of the 140 million invoices exchanged each year already travel as EHF. In March 2026, the government accelerated the B2B e-invoicing mandate from 2028 to January 1, 2027.

B2B E-Invoicing Mandate (2027)​

In March 2026, the Norwegian government announced mandatory B2B e-invoicing for all entities subject to bookkeeping obligations, accelerating the timeline from the originally proposed 2028 deadline.

Timeline​

DateMilestone
2012Central government bodies start accepting EHF
Apr 2019Suppliers to central government must send EHF
Jan 2025Ministry of Finance tasked Tax Directorate with drafting proposal
Jun 2025Tax Directorate published consultation document (originally proposed 2028)
Sep 2025Norway PASR approved by OpenPeppol (PA: DFØ)
Mar 2026Government accelerated deadline to Jan 1, 2027
Jan 1, 2027Mandatory B2B e-invoice issuing for all entities with bookkeeping obligations (as proposed; bill before the Storting)
Jan 1, 2030Mandatory fully digital bookkeeping systems (auto-receive/process e-invoices)

Key Details​

  • Format: EHF (Elektronisk Handelsformat, UBL-based) / Peppol BIS Billing 3.0
  • Network: Peppol eDelivery
  • Exemption: Businesses under NOK 50,000/year turnover (if not required to maintain accounting/VAT records)
  • Estimated benefit: about NOK 10 billion over 20 years (Vista Analyse for the Tax Directorate, 2025)
  • Also exploring: B2C invoicing, e-receipts, regulation of accounting software providers

Identifier Schemes​

Organisasjonsnummer (0192) — the only identifier​

Norway's Peppol Authority requires Norwegian participants to be identified by their 9-digit organisation number from Brønnøysundregistrene, scheme 0192. GoRoute refuses a Norwegian participant registered under any other scheme (including the legacy 9908 and 9909, neither of which is in Peppol's address-scheme list any more) or with the VAT form (NO123456789MVA) as its identifier. The refusal is a 400 with error_code PARTICIPANT_IDENTITY_INVALID — fix the identifier you have rather than retrying with a different one. This governs participants GoRoute registers for you; receiving from, or sending to, a counterparty whose own provider identifies them differently is unaffected.

participant = {
"scheme": "0192",
"identifier": "123456789",
"name": "Eksempel AS",
"country": "NO"
}

The last digit is a mod-11 check digit; a number that fails it is refused the same way (400, PARTICIPANT_IDENTITY_INVALID), as Peppol's PEPPOL-COMMON-R041 rule would reject every document carrying it:

def validate_norwegian_org(org_nr: str) -> bool:
if len(org_nr) != 9 or not org_nr.isdigit():
return False
weights = [3, 2, 7, 6, 5, 4, 3, 2]
remainder = 11 - (sum(int(d) * w for d, w in zip(org_nr[:8], weights)) % 11)
if remainder == 10:
return False
return int(org_nr[8]) == (0 if remainder == 11 else remainder)

Before a Norwegian participant is published​

On production, GoRoute looks the number up in Enhetsregisteret before publishing it on the SMP, as the Peppol Authority requires. It refuses, with a 422 and error_code NO_REGISTER_REFUSED:

  • a number that is not a legal entity (a sub-unit, underenhet), naming the legal entity to use;
  • a number Enhetsregisteret does not know;
  • a deleted entity;
  • a public-sector body (institutional sector 6100 or 6500) — those must be registered in ELMA, Norway's central SMP run by Digdir.

One SMP per participant. Most Norwegian companies are already registered in ELMA or with another provider. The Peppol network allows one SMP per participant, so GoRoute refuses to publish a company another SMP already hosts — again a 422 with NO_REGISTER_REFUSED — and the message names ELMA or the other provider. To receive through GoRoute, the company's current provider must remove that registration first. Sending to any Norwegian company needs none of this.

On the test network the register lookup is not enforced, so test numbers from Brønnøysund's test range (910000xxx) can be used.

Who may register a Norwegian company (fullmakt)​

Norway's Peppol Authority (DFØ) requires that whoever registers a Norwegian legal entity has the right to sign for it. Since 24 September 2026 GoRoute establishes that right before it publishes a Norwegian participant, and refuses to publish until it has. This is separate from the register checks above: a number can pass every one of them and still not be published because of who is asking.

There are two routes through the check.

Route 1 — the register already lists you. If the name on the signed-in account matches a person Enhetsregisteret lists in a role that carries authority to sign, the participant is published straight away with no extra step. These are the roles that count:

Register codeRole
DAGLGeneral manager (daglig leder)
LEDEChair of the board
NESTDeputy chair of the board
MEDLBoard member
DTSOPartner with full liability
DTPRPartner with proportional liability
INNHProprietor
KOMPGeneral partner
BESTManaging owner
FFØRBusiness manager
SIGNSignature holder (signatur)
PROKProcuration holder (prokura)

Auditors, accountants, contact persons, observers and deputy board members are not on that list, and neither is anybody the register shows as resigned (fratrådt) or deceased. The names are compared rather than proved: your first and last name must both appear in the name the register holds, so a register entry of "Jan-Arve Dragland Johansen" matches an account named "Jan-Arve Johansen". A single name on its own never matches.

Route 2 — a signed power of attorney (fullmakt). Everybody else takes this route: an employee, an accountant, or a software partner onboarding its own customers. You upload the authority signed by one of the role holders above, GoRoute checks it by hand, and then the participant is published.

An API key always takes the fullmakt route

An API key has no person behind it, so there is no name for GoRoute to match against the register. Every integration that authenticates with an API key alone is refused on the first registration attempt for a Norwegian company and has to supply a fullmakt, even if the person who created the key is the general manager. Plan the fullmakt step into any Norwegian onboarding flow you build; it is not an exception case.

The refusal you actually get​

POST /api/v1/participants/{participant_id}/register answers 409 with error_code NO_AUTHORITY_REQUIRED. Nothing is published, and the participant record stays as it was:

{
"error": "authority_required",
"error_code": "NO_AUTHORITY_REQUIRED",
"message": "Norway's Peppol Authority requires that whoever registers 0192:123456789 has the right to sign for it. The name on this account is not among the people the register lists with that right (Kari Nordmann (general manager (daglig leder)), Ola Nordmann (chair of the board)). Upload a power of attorney (fullmakt) signed by one of them; GoRoute checks it and publishes the participant, usually within one working day.",
"status": "needs_fullmakt",
"role_holders": [
{ "name": "Kari Nordmann", "role": "general manager (daglig leder)" },
{ "name": "Ola Nordmann", "role": "chair of the board" }
],
"next_step": "POST /api/v1/participants/$PARTICIPANT_ID/authority with the signed fullmakt (PDF or image, max 10 MB) and authorised_by = the role holder who signed it.",
"request_id": "b0f4c2a8-5d11-4e77-9a23-7c81de6f4b90"
}

role_holders is the list of people who may sign, taken from the register — show it to whoever is registering, because the fullmakt has to be signed by one of them. Treat the 409 as an instruction, not an error to retry: retrying the same call changes nothing.

next_step is a ready-to-follow instruction rather than a template: where $PARTICIPANT_ID is shown above, the real response carries this participant's own UUID.

If Brønnøysundregistrene cannot be reached, the call answers 503 with error_code NO_REGISTER_UNAVAILABLE. Nothing is published and no authority is recorded. Retry in a few minutes; this one is worth retrying.

Uploading the fullmakt​

POST /api/v1/participants/{participant_id}/authority — a multipart upload, permission participants:manage. Two fields:

FieldRequirement
authorised_byThe full name of the role holder who signed the document, 3 to 200 characters. It must match a person currently listed in one of the roles above.
fileThe signed fullmakt as application/pdf, image/png or image/jpeg, at least 1 byte and at most 10 MB.
curl -X POST \
https://app.goroute.ai/peppol-api/api/v1/participants/$PARTICIPANT_ID/authority \
-H "X-API-Key: $GOROUTE_API_KEY" \
-F "authorised_by=Kari Nordmann" \
-F "file=@fullmakt-signed.pdf;type=application/pdf"

A successful upload answers 202, and the participant is not yet on the network:

{
"status": "pending_review",
"message": "Received. GoRoute checks the fullmakt against the register and publishes the participant, usually within one working day. You will receive an email when it is live."
}

What the upload refuses:

What is wrongWhat you get back
authorised_by is not a person the register currently lists with signing rights422 with error_code FULLMAKT_SIGNER_NOT_AUTHORISED, and role_holders listing who may sign
The file is not a PDF, PNG or JPEG415 with error_code FULLMAKT_TYPE
The file is empty, or larger than 10 MB413 with error_code FULLMAKT_SIZE
The participant is not Norwegian (any scheme other than 0192)400 with error_code AUTHORITY_NOT_REQUIRED
Brønnøysundregistrene could not be reached to check the signer503 with error_code NO_REGISTER_UNAVAILABLE

Uploading when the authority is already established is not an error: the call returns the status it already has and changes nothing.

Checking where a participant stands​

GET /api/v1/participants/{participant_id}/authority — permission participants:read. It returns the participant ID, its scheme, whether the check applies at all (required), a status, and the full record GoRoute holds. The status is the field to read:

statusWhat it means
not_requiredNot a Norwegian (0192) participant. This check does not apply.
not_checkedNorwegian, and nothing has been established yet. Registering will trigger the check.
role_matchThe register lists the registrant with signing rights. Established — registration proceeds.
needs_fullmaktA signed power of attorney is required and has not been supplied.
pending_reviewA fullmakt has been uploaded and GoRoute is reviewing it. Not on the network yet.
approvedGoRoute accepted the fullmakt. Established — the participant is published.
rejectedGoRoute could not accept the fullmakt. Upload a corrected one; the reason is in the record.

Only role_match and approved count as established. Everything else leaves the participant off the network.

The review is a person at GoRoute, not an automated check, and it is usually done within one working day. The decision — published, or rejected with a reason — is emailed to the signed-in user who uploaded the document. An upload made with an API key alone has no user behind it, so there is no address to write to and no email is sent. Poll GET /api/v1/participants/{participant_id}/authority for the outcome if you upload from an integration.

The sandbox will not show you this refusal

Outside production the gate only applies to organisation numbers the live Norwegian register actually knows. Brønnøysund's synthetic test range (910000xxx) is not in it, so sandbox registrations keep working exactly as before and you will never see the 409 there. A successful sandbox registration is not evidence that the same call will publish on production. Test the fullmakt path by reading this page, not by trying it on test.

A participant that is already published on the network is not re-checked, so re-registering one (to change its document types, for example) does not ask for a fullmakt again.

EHF Format​

EHF Billing is Peppol BIS Billing 3.0​

Norway uses native Peppol BIS Billing 3.0 for invoices and credit notes:

<cbc:CustomizationID>urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0</cbc:CustomizationID>
<cbc:ProfileID>urn:fdc:peppol.eu:2017:poacc:billing:01:1.0</cbc:ProfileID>

The Norwegian rules live inside BIS Billing itself and apply when the seller is Norwegian:

RuleSeverityWhat it requiresGoRoute field
NO-R-001ErrorSeller VAT identifier is NO + 9 digits + MVAseller.vat_number
NO-R-002WarningAn AS/ASA states "Foretaksregisteret"seller.tax_registration_statement

The Norwegian usage guide also covers:

  • KID — payment.payment_id: cbc:PaymentID in the EHF, and ram:PaymentReference in the CII inside the hybrid PDF.
  • Norwegian bank account (BBAN) — payment.bank_account.iban; no BIC is written with a BBAN.
  • COâ‚‚ data per line — lines[].item_properties.

Other EHF documents​

Reminders, forward billing, payment requests, self-billing, catalogues, orders, order agreements, advanced ordering, despatch advice and Peppol Logistics documents are covered in Orders, Catalogues & More.

VAT Requirements​

Seller VAT identifier: NO + organisation number + MVA (NO-R-001).

CodeRateUse
S25%Standard rate
S15%Food and non-alcoholic drink
S12%Passenger transport, accommodation, cinema and more
S11.11%Raw fish (compensation rate)
Z0%Zero-rated supplies (e.g. newspapers)
E0%Exempt (with exemption reason)
G0%Export outside the EEA (with exemption reason)
AE0%Reverse charge (buyer VAT identifier required)
O—Outside the scope of VAT (no seller VAT identifier)

Every rate is category S with its percentage — EN 16931 has no separate code for Norway's reduced rates (AA is not in its code list and is rejected).

Example Invoice​

<?xml version="1.0" encoding="UTF-8"?>
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">

<cbc:CustomizationID>urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0</cbc:CustomizationID>
<cbc:ProfileID>urn:fdc:peppol.eu:2017:poacc:billing:01:1.0</cbc:ProfileID>

<cbc:ID>NO-FAK-2024-00123</cbc:ID>
<cbc:IssueDate>2024-01-15</cbc:IssueDate>
<cbc:DueDate>2024-02-15</cbc:DueDate>
<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
<cbc:DocumentCurrencyCode>NOK</cbc:DocumentCurrencyCode>

<!-- Order reference often expected -->
<cac:OrderReference>
<cbc:ID>PO-2024-001</cbc:ID>
</cac:OrderReference>

<!-- Norwegian Seller -->
<cac:AccountingSupplierParty>
<cac:Party>
<cbc:EndpointID schemeID="0192">123456789</cbc:EndpointID>
<cac:PartyName>
<cbc:Name>Eksempel AS</cbc:Name>
</cac:PartyName>
<cac:PostalAddress>
<cbc:StreetName>Karl Johans gate 1</cbc:StreetName>
<cbc:CityName>Oslo</cbc:CityName>
<cbc:PostalZone>0154</cbc:PostalZone>
<cac:Country>
<cbc:IdentificationCode>NO</cbc:IdentificationCode>
</cac:Country>
</cac:PostalAddress>
<cac:PartyTaxScheme>
<cbc:CompanyID>NO123456789MVA</cbc:CompanyID>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:PartyTaxScheme>
<cac:PartyTaxScheme>
<cbc:CompanyID>Foretaksregisteret</cbc:CompanyID>
<cac:TaxScheme>
<cbc:ID>TAX</cbc:ID>
</cac:TaxScheme>
</cac:PartyTaxScheme>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Eksempel AS</cbc:RegistrationName>
<cbc:CompanyID schemeID="0192">123456789</cbc:CompanyID>
</cac:PartyLegalEntity>
<cac:Contact>
<cbc:Name>Ola Nordmann</cbc:Name>
<cbc:ElectronicMail>faktura@eksempel.no</cbc:ElectronicMail>
</cac:Contact>
</cac:Party>
</cac:AccountingSupplierParty>

<!-- Norwegian Buyer -->
<cac:AccountingCustomerParty>
<cac:Party>
<cbc:EndpointID schemeID="0192">987654321</cbc:EndpointID>
<cac:PartyName>
<cbc:Name>Kunde AS</cbc:Name>
</cac:PartyName>
<cac:PostalAddress>
<cbc:StreetName>Storgata 10</cbc:StreetName>
<cbc:CityName>Bergen</cbc:CityName>
<cbc:PostalZone>5003</cbc:PostalZone>
<cac:Country>
<cbc:IdentificationCode>NO</cbc:IdentificationCode>
</cac:Country>
</cac:PostalAddress>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Kunde AS</cbc:RegistrationName>
</cac:PartyLegalEntity>
</cac:Party>
</cac:AccountingCustomerParty>

<!-- Norwegian bank account -->
<cac:PaymentMeans>
<cbc:PaymentMeansCode>30</cbc:PaymentMeansCode>
<!-- KID number (structured reference) -->
<cbc:PaymentID>1234567890123</cbc:PaymentID>
<cac:PayeeFinancialAccount>
<cbc:ID>NO9386011117947</cbc:ID>
<cbc:Name>Eksempel AS</cbc:Name>
</cac:PayeeFinancialAccount>
</cac:PaymentMeans>

<cac:TaxTotal>
<cbc:TaxAmount currencyID="NOK">250.00</cbc:TaxAmount>
<cac:TaxSubtotal>
<cbc:TaxableAmount currencyID="NOK">1000.00</cbc:TaxableAmount>
<cbc:TaxAmount currencyID="NOK">250.00</cbc:TaxAmount>
<cac:TaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>25</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:TaxCategory>
</cac:TaxSubtotal>
</cac:TaxTotal>

<cac:LegalMonetaryTotal>
<cbc:LineExtensionAmount currencyID="NOK">1000.00</cbc:LineExtensionAmount>
<cbc:TaxExclusiveAmount currencyID="NOK">1000.00</cbc:TaxExclusiveAmount>
<cbc:TaxInclusiveAmount currencyID="NOK">1250.00</cbc:TaxInclusiveAmount>
<cbc:PayableAmount currencyID="NOK">1250.00</cbc:PayableAmount>
</cac:LegalMonetaryTotal>

<cac:InvoiceLine>
<cbc:ID>1</cbc:ID>
<cbc:InvoicedQuantity unitCode="HUR">10</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="NOK">1000.00</cbc:LineExtensionAmount>
<cac:Item>
<cbc:Name>Konsulenttjenester</cbc:Name>
<cac:ClassifiedTaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>25</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:ClassifiedTaxCategory>
</cac:Item>
<cac:Price>
<cbc:PriceAmount currencyID="NOK">100.00</cbc:PriceAmount>
</cac:Price>
</cac:InvoiceLine>
</Invoice>

KID Number (Structured Reference)​

Norwegian payments often use KID (kundeidentifikasjon):

<cac:PaymentMeans>
<cbc:PaymentMeansCode>30</cbc:PaymentMeansCode>
<!-- KID number for bank payment reference -->
<cbc:PaymentID>1234567890123</cbc:PaymentID>
</cac:PaymentMeans>

KID Validation​

def validate_kid(kid: str) -> bool:
"""Validate Norwegian KID using Mod10 or Mod11."""
if not kid.isdigit() or len(kid) < 2 or len(kid) > 25:
return False
# Mod10 (Luhn) or Mod11 validation
return True # Implement based on agreed algorithm

Looking Up Norwegian Companies​

# Is this company on the Peppol network, and with whom?
response = requests.get(
"https://app.goroute.ai/peppol-api/api/v1/participants/lookup",
params={"peppol_id": "0192:123456789"},
headers={"X-API-Key": "your_api_key"}
)

# Returns Peppol registration status in the "found" field

Government E-invoicing​

Public Sector​

All Norwegian public entities require EHF/Peppol invoices:

# Sending to Norwegian government
response = requests.post(
"https://app.goroute.ai/peppol-api/api/v1/documents",
headers={"X-API-Key": "your_api_key"},
json={
"receiver_scheme": "0192",
"receiver_id": "974760673", # Example government entity
# The buyer reference is always required for B2G, and is carried
# in the UBL document as cbc:BuyerReference, not as a request field.
"document": invoice_xml
}
)

Altinn​

Some government interactions go through Altinn, but invoicing uses Peppol.

Resources​

Next Steps​

Further reading​

Ready to build? The free developer sandbox gives you test credentials and a registered participant, provisioned within 24 hours on business days. Recorded ERP integration walkthroughs are on the tutorials page.