Norway 🇳🇴
EHF (Elektronisk Handelsformat) and e-invoicing in Norway.
Overview​
| Aspect | Details |
|---|---|
| B2G Mandate | Suppliers to central government since April 2019 (state bodies accept EHF since 2012) |
| B2B Mandate | Issuing from January 1, 2027 (bill before the Storting); receiving from January 1, 2030 |
| Digital Bookkeeping | January 1, 2030 (full digital accounting systems) |
| CIUS | EHF (Elektronisk Handelsformat, UBL-based) / Peppol BIS 3.0 |
| Primary Scheme | 0192 (Organisasjonsnummer) |
| Peppol Authority | DFØ (Direktoratet for forvaltning og økonomistyring) |
| Central SMP | ELMA (Digdir) — mandatory for public-sector bodies |
| PASR | Approved 2025-09-17 |
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​
| Date | Milestone |
|---|---|
| 2012 | Central government bodies start accepting EHF |
| Apr 2019 | Suppliers to central government must send EHF |
| Jan 2025 | Ministry of Finance tasked Tax Directorate with drafting proposal |
| Jun 2025 | Tax Directorate published consultation document (originally proposed 2028) |
| Sep 2025 | Norway PASR approved by OpenPeppol (PA: DFØ) |
| Mar 2026 | Government accelerated deadline to Jan 1, 2027 |
| Jan 1, 2027 | Mandatory B2B e-invoice issuing for all entities with bookkeeping obligations (as proposed; bill before the Storting) |
| Jan 1, 2030 | Mandatory 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 code | Role |
|---|---|
DAGL | General manager (daglig leder) |
LEDE | Chair of the board |
NEST | Deputy chair of the board |
MEDL | Board member |
DTSO | Partner with full liability |
DTPR | Partner with proportional liability |
INNH | Proprietor |
KOMP | General partner |
BEST | Managing owner |
FFØR | Business manager |
SIGN | Signature holder (signatur) |
PROK | Procuration 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 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:
| Field | Requirement |
|---|---|
authorised_by | The 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. |
file | The 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 wrong | What you get back |
|---|---|
authorised_by is not a person the register currently lists with signing rights | 422 with error_code FULLMAKT_SIGNER_NOT_AUTHORISED, and role_holders listing who may sign |
| The file is not a PDF, PNG or JPEG | 415 with error_code FULLMAKT_TYPE |
| The file is empty, or larger than 10 MB | 413 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 signer | 503 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:
status | What it means |
|---|---|
not_required | Not a Norwegian (0192) participant. This check does not apply. |
not_checked | Norwegian, and nothing has been established yet. Registering will trigger the check. |
role_match | The register lists the registrant with signing rights. Established — registration proceeds. |
needs_fullmakt | A signed power of attorney is required and has not been supplied. |
pending_review | A fullmakt has been uploaded and GoRoute is reviewing it. Not on the network yet. |
approved | GoRoute accepted the fullmakt. Established — the participant is published. |
rejected | GoRoute 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.
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:
| Rule | Severity | What it requires | GoRoute field |
|---|---|---|---|
| NO-R-001 | Error | Seller VAT identifier is NO + 9 digits + MVA | seller.vat_number |
| NO-R-002 | Warning | An AS/ASA states "Foretaksregisteret" | seller.tax_registration_statement |
The Norwegian usage guide also covers:
- KID —
payment.payment_id:cbc:PaymentIDin the EHF, andram:PaymentReferencein 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).
| Code | Rate | Use |
|---|---|---|
| S | 25% | Standard rate |
| S | 15% | Food and non-alcoholic drink |
| S | 12% | Passenger transport, accommodation, cinema and more |
| S | 11.11% | Raw fish (compensation rate) |
| Z | 0% | Zero-rated supplies (e.g. newspapers) |
| E | 0% | Exempt (with exemption reason) |
| G | 0% | Export outside the EEA (with exemption reason) |
| AE | 0% | 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​
- DFØ - Norwegian Peppol Authority
- DFØ - EHF Post-Award G3 Specifications
- Brønnøysundregistrene
- Norway's Peppol Authority Specific Requirements
- EHF Validator
Next Steps​
Further reading​
- The Peppol network
- 2026 e-invoicing mandate tracker — every country, one page
- How to choose a Peppol Access Point
- One API for multi-country e-invoicing
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.