Sending and receiving Peppol documents in Norway for the companies you onboard
Getting started with Peppol e-invoicing for Norwegian companies you onboard from your own software platform: register and verify the company, send Peppol invoices and credit notes, pull incoming documents through webhooks, go live. Organisation numbers under scheme 0192, registered in ELMA, the Norwegian receiver register, and checked against Enhetsregisteret.
This guide walks through everything needed to exchange Peppol documents for a company registered in Norway, assuming you are integrating Recommand into your own product and onboarding Norwegian companies as your users, and that the company sends and receives documents over Peppol. Change any of the three answers at the top of the page to get the guide for a different situation.
What you are building
You are onboarding companies that are not your own: one Recommand team, and a company inside it for every customer you put on the network. Your customers never need a Recommand account. They see your interface, and Recommand stays behind your API calls.
That shape has a few consequences worth knowing before you write code:
- One team, many companies. There is no limit on companies per team, and billing is per team with the document volume of all companies pooled together, so the more companies you onboard the lower your price per document. See pricing per team.
- Every company is registered and verified individually. Peppol identifies companies, not platforms. Each company gets its own Peppol address and its own authorisation record.
- Verification is taken care of. Recommand hands you a URL that the company's authorised representative opens to confirm their identity. You present or forward that link; you never need to handle identity documents yourself. If you prefer to handle verification yourself, reach out to us at support@recommand.eu, we have a few other flows we can set up for you.
- You can run the whole flow under your own brand. The API is designed for white-label use, see can I whitelabel Recommand.
The Recommand dashboard shows the same teams, companies and documents your API calls produce, which is the quickest way to see what a customer is looking at while you are debugging.
Only setting up your own company?
If the only company you will register is your own, switch the first answer above to One company for the shorter version of this guide. The endpoints are the same; there is simply less to organise.
Peppol in Norway
Norway is one of the oldest Peppol countries: public sector buyers have required EHF invoices for years, and EHF Billing 3.0 is Peppol BIS Billing 3.0. Documents are exchanged as Peppol BIS 3 UBL over the standard Peppol billing process, through Recommand's own access point.
What is specific to Norway:
- A B2B mandate in two steps. From 1 January 2027, businesses with a bookkeeping obligation must issue e-invoices to other such businesses. Until receiving becomes mandatory as well, on 1 January 2030, the obligation in practice applies when the buyer can receive them, which means being registered in ELMA or on Peppol.
- Receivers are registered in ELMA. ELMA (Elektronisk mottakeradresseregister) is the Norwegian receiver register, run by the Norwegian Digitalisation Agency. Norwegian companies that receive through Recommand are published there rather than on Recommand's own SMP. Senders find them the usual way, through the Peppol network.
- The organisation number is the Peppol address. Norwegian companies are
published under scheme
0192with their 9-digit organisasjonsnummer, so a Norwegian Peppol address looks like0192:123456785. - Organisations are checked against Enhetsregisteret. An organisation number that the Norwegian register of legal entities does not know, or has deleted, is not registered as a receiver, and a registration is removed when the organisation is deleted from the register later.
- Representatives are checked for signing authority. During verification, the representative is held against the people Enhetsregisteret lists as able to sign for the company or holding procuration.
Create your team and API credentials
- Sign up at app.recommand.eu/signup. The team you get is the container for every company you will onboard.
- Create an API key on the API keys page. Note the key, the secret and your team ID.
- Check the credentials with a request that needs no data of its own:
curl -X GET https://app.recommand.eu/api/core/auth/verify \
-u key_xxx:secret_xxx
It answers {"success": true} when the key and secret are accepted, and 401
when they are not, so it tells you about your credentials and nothing else.
All endpoints in this guide live under https://app.recommand.eu/api/v1 and
accept HTTP Basic authentication with the key as username and the secret as
password. If you would rather not store a long-lived secret, JWT API keys and
OAuth2 with a JWT assertion are available too, see the
authentication guide.
Try it safely first
Build the whole flow against a playground team before you touch production. Playgrounds look and behave like production teams, but nothing is delivered over the real Peppol network, there are no SMP registrations, no subscription checks and no billing.
Create one from the team switcher at the top of the dashboard: Add playground, give it a name, and you are switched into it. There is no limit on how many you create.
Everything that follows in this guide is identical there: same endpoints, same validation, same webhooks (triggered by simulated inbound delivery). Register a company in the playground and use it as both sender and recipient to see a document arrive.
Three things worth knowing before you start:
POST /:companyId/generatetakes the same body as the send endpoint minus the email delivery options, which have no meaning when nothing is delivered, and returns the exact XML that sending would produce, fully validated, without transmitting, storing or billing it. Raw XML is not accepted here: there is nothing to generate from a document you already have. It also returns the resolveddocumentType,doctypeIdandprocessId, which is the quickest way to check that your country-specific fields map to the format and process you expect.- Failure addresses. In a playground that is not connected to the Peppol Test
Network, sending to
404:404or0208:1234567894always fails, so you can exercise your error handling. Any other unregistered recipient is skipped without an error. - The Peppol Test Network. For genuine end-to-end tests with real counterparties, tick Use Peppol Test Network when you create the playground. It then uses dedicated test access point and SMP endpoints while staying fully separated from production. The setting cannot be changed after creation, so make a second playground if you want both.
More detail in the getting started guide and how do I use the playground environment.
Register the company
Create one company per customer with the create company endpoint. Registration on the Peppol network happens as part of this call: identifiers and document types are set up for you, based on the company's country.
const auth =
"Basic " + Buffer.from("key_xxx:secret_xxx").toString("base64");
const response = await fetch("https://app.recommand.eu/api/v1/companies", {
method: "POST",
headers: { Authorization: auth, "Content-Type": "application/json" },
body: JSON.stringify(company),
});
const result = await response.json();
if (!result.success) throw new Error(JSON.stringify(result.errors));
const companyId = result.company.id;
const verificationUrl = result.verificationUrl; // hand this to your userThe response carries a verificationUrl straight away. Keep it: the next step is
to put it in front of the company's representative.
Only register companies you are allowed to act for
Register the companies that use your platform, not the companies they invoice. Customers and suppliers manage their own Peppol registration; adding them causes registration conflicts. See managing companies.
The exact identifier fields to send depend on the country, which is what the next
section covers. If you would rather create identifiers and document types
yourself instead of accepting the country defaults, pass
skipDefaultCompanySetup: true and use the company
identifiers and
company document
types endpoints.
Norwegian identifiers and Peppol address
| Field | Norwegian value |
|---|---|
country | "NO" |
enterpriseNumber | The organisasjonsnummer, exactly 9 digits |
enterpriseNumberScheme | Optional: "0192" |
vatNumber | Optional: NO + organisation number + MVA, for companies in the VAT register |
{
"name": "Eksempel AS",
"address": "Storgata 1",
"postalCode": "0155",
"city": "Oslo",
"country": "NO",
"enterpriseNumber": "123456785",
"vatNumber": "NO123456785MVA"
}The organisation number is checked against its format and its check digit (modulo
11), and registered as the company's Peppol identifier: 0192: followed by the
organisation number. The VAT number is written into the documents the company
sends, but it is not registered as a Peppol identifier.
Only 0192 in ELMA
ELMA registers Norwegian organisation numbers and nothing else. A Norwegian
company registered in ELMA therefore carries 0192 identifiers only: adding an
identifier under another scheme, such as a GLN (0088), is refused with an error
that names the scheme. Without an organisation number the company has no Peppol
address, so enterpriseNumber is effectively required in Norway.
The organisation has to exist
Before a 0192 identifier is published as a receiver on the production network,
the organisation number is looked up in Enhetsregisteret:
- a number the register does not know is refused, with a message that says so;
- a number the register has deleted is refused, with the date it was deleted.
When an organisation is deleted from Enhetsregisteret later, its 0192
identifier is removed and the company stops receiving on it. The team members
get an email about it.
On the test network, organisation numbers are not looked up, so you can test with synthetic organisations.
Registering for both directions
Sending needs no registration of its own; receiving does. So register the company as a recipient, which is the default:
{
"isSmpRecipient": true
}What that means:
- The company becomes findable on the Peppol network and can be delivered to through Recommand's access point, while sending its own documents out through the same access point.
- Recipient registration is exclusive. If the company already receives through another Peppol provider, registration fails until it is deregistered there. What that takes depends on the country, which the next section covers.
- The document types the company accepts are registered along with it, based on its country. Which ones those are is covered further down.
- The company is only published once it is verified.
Sending first, receiving later
If the company still receives elsewhere and you do not want to move that yet,
register it with isSmpRecipient: false and start with sending only. Flipping
the field later publishes it as a recipient.
Registration in ELMA
A Norwegian company registered as a recipient is published in ELMA under its organisation number, with the processes its document types belong to, and with its name and contact details (name, email address and phone number) as the participant's contact. When the company's name, email address or phone number changes, the ELMA entry is updated with it.
Senders find the company the usual way, and the verify endpoint finds it shortly after it is registered.
On the test network, Norwegian companies are registered in ELMA's test environment in the same way.
Moving an existing Norwegian registration
Recipient registration is exclusive. How a Norwegian company that already receives elsewhere moves to Recommand depends on where it is registered:
- In ELMA, through another service provider. When the verified company is registered, Recommand asks ELMA to transfer the participant to it. If ELMA does not allow the transfer, the registration fails with a message that names the organisation number, and the current provider has to transfer it to Recommand in ELMA.
- On another SMP. The company has to be deregistered there first. ELMA does not take over participants with SML migration keys, so they do not apply to Norwegian companies.
If you do not know who the current provider is, look the organisation number up
as a recipient (0192:123456785): the verify
endpoint returns the SMP it is published
on.
Verify the company
A company cannot exchange documents on Peppol until an authorised representative
has confirmed their identity. The company object exposes this as isVerified,
and it stays false until the check is done.
For Norwegian companies, the representative's authority is held against Enhetsregisteret:
- Open the verification URL (from the create-company response, the dashboard, or a fresh one from the verify company endpoint).
- Pick your name from the list. The list shows the people Enhetsregisteret names for signature (signatur) or procuration (prokura). Someone who is not on it cannot verify the company and is asked to contact support@recommand.eu. When the register lists nobody, the representative types their name instead, and it is checked the same way.
- Complete the identity check. When the representative may act for the company
alone,
isVerifiedflips totrueand the company is published in ELMA.
Of the people on the list, a representative may act alone when the register says they can sign or hold procuration on their own, or when they hold a role with full rights, such as general manager (daglig leder), chair of the board (styrets leder) or owner of a sole proprietorship (innehaver).
When the register cannot settle it
Some cases need a person to look at them, for example a board member who may only
sign together with others, or a register entry that cannot be interpreted. The
identity check still runs, and the verification is then held for review by our
team: isVerified stays false until it is decided, and the representative is
told so during the verification.
Building verification into your onboarding
With one company you would click through this once. With many, verification is part of the flow you build: every company you register needs its own, and it is the step most likely to leave a customer stuck halfway.
Show the URL immediately. The create-company response already carries
verificationUrl, so no extra call is needed. Put it in front of the user
while they are still in your onboarding.
Ask for a fresh one when the moment has passed. Links get lost, and companies you created earlier never had one shown. The verify company endpoint starts a new verification session:
curl -X POST https://app.recommand.eu/api/v1/companies/{companyId}/verify \
-u key_xxx:secret_xxx
The company.verification webhook fires
when verification reaches a final state: verified, rejected or error. A
company that comes back rejected or error and is not surfaced anywhere sits
silently unusable. See working with webhooks.
Respect isVerified in your own UI. Do not let a user press send for a
company that is not verified yet; this will result in an error. You should inform the user what is missing instead.
Re-verify after identifier changes. Updating a company's vatNumber or
enterpriseNumber resets isVerified to false. Check the field after an
update and present a new verificationUrl if it flipped.
The full mechanics are in the company verification guide.
Pick the document format
Send Peppol BIS 3 UBL, over the standard Peppol billing process. EHF Billing 3.0, the Norwegian invoice format, is Peppol BIS Billing 3.0, so there is no separate Norwegian document type to select:
| What you send | doctypeId |
|---|---|
| Invoice | not needed, defaults to Peppol BIS 3 UBL |
| Credit note | not needed, defaults to Peppol BIS 3 UBL |
Peppol BIS 3 has a rule of its own for Norwegian sellers: a VAT number, when
there is one, has to be written as NO + organisation number + MVA, for example
NO123456785MVA.
Before a first send to a new recipient, check that the recipient exists with the verify endpoint. That lookup also tells you whether a Norwegian buyer can receive e-invoices over Peppol at all.
Send a document
One endpoint sends everything: POST /:companyId/send. The companyId is the
sender, recipient is the Peppol address of the receiver, and document is the
invoice or credit note as JSON. Recommand will validate the document and generate the XML.
Already producing UBL or CII XML?
Raw XML sending is supported as well: set documentType to xml and pass the
document string in document, along with the correct doctypeId. See working
with raw UBL.
const response = await fetch(
`https://app.recommand.eu/api/v1/${companyId}/send`,
{
method: "POST",
headers: { Authorization: auth, "Content-Type": "application/json" },
// The body below, with doctypeId and countrySpecific where the country
// needs them.
body: JSON.stringify(sendRequest),
}
);
const result = await response.json();
if (!result.success) {
// result.errors is keyed by field path, e.g. { "buyer.vatNumber": [...] }
}{
"recipient": "0208:0123456789",
"documentType": "invoice",
"document": {
"invoiceNumber": "INV-2026-001",
"issueDate": "2026-08-17",
"dueDate": "2026-09-16",
"currency": "EUR",
"buyer": {
"name": "Customer Company",
"street": "Customer Street 1",
"city": "Antwerp",
"postalZone": "2000",
"country": "BE",
"vatNumber": "BE0987654321"
},
"paymentMeans": [{ "iban": "BE68539007547034" }],
"lines": [
{
"name": "Consulting Services",
"quantity": "10.00",
"unitCode": "HUR",
"netPriceAmount": "100.00",
"vat": { "category": "S", "percentage": "21.00" }
}
]
}
}
The seller block is filled in from the company when you leave it out, which is usually what you want: it keeps the company's registered identifiers and the document in agreement.
Things worth wiring up while you are here:
- Validation errors. Outgoing documents are always validated. A
success: falseresponse witherrorskeyed by field path is a document your recipient would have rejected, so surface it to the user who typed the data. - Delivery outcomes.
success: trueconfirms the send request succeeded. CheckdeliveryStatusanddeliveries, and subscribe to delivery status webhooks for outcomes reported later. - Email fallback. Pass
email.towithwhen: "on_peppol_failure"to fall back to email on Peppol failure, including one reported after the send returns, or send withrecipient: nullfor email-only delivery. See email delivery and notifications. - PDFs.
pdfGeneration.enabledattaches a generated PDF of the document. You can also attach an existing PDF (or other files) viaattachments; see adding attachments.
For a full field reference, see sending invoices and sending credit notes.
Document types registered for you
When you register a Norwegian company as a recipient, it is published in ELMA for the two document types of EHF Billing 3.0 (Peppol BIS Billing 3.0):
| Document type | Process |
|---|---|
| Invoice (Peppol BIS 3 UBL) | urn:fdc:peppol.eu:2017:poacc:billing:01:1.0 |
| Credit note (Peppol BIS 3 UBL) | urn:fdc:peppol.eu:2017:poacc:billing:01:1.0 |
Anything sent to the company in one of these formats is accepted, validated, stored and handed to you. A sender who tries a document type the company is not published for gets an error before delivery, which is exactly the point of the registration.
You can add document types with the create company document type endpoint, as long as ELMA offers them. A document type that ELMA does not offer is refused with an error that names it.
Get incoming documents into your product
Recommand receives, validates and stores incoming documents for every company in your team. You pick them up in one of two ways.
Webhooks (recommended)
Register an endpoint once with the create webhook
endpoint or through the dashboard and events are pushed to you as
they happen, document.received among them:
await fetch("https://app.recommand.eu/api/v1/webhooks", {
method: "POST",
headers: { Authorization: auth, "Content-Type": "application/json" },
body: JSON.stringify(webhook), // the body below
});{
"url": "https://your-app.example/webhooks/recommand",
"companyId": null,
"secret": "your_webhook_signing_secret"
}
companyId: null covers every company in the team.
Pass a secret and verify the HMAC SHA-256 signature on every delivery before
you trust the payload. Switch on event.eventType, and acknowledge with a 200
before doing the heavy processing. Both are covered in working with
webhooks.
Polling the inbox
If you would rather pull, the inbox endpoint lists unread documents:
const inbox = await fetch("https://app.recommand.eu/api/v1/inbox", {
headers: { Authorization: auth },
}).then((r) => r.json());Mark each document as read with the mark as read endpoint once your system has it, so it drops off the list.
After you have the document
- Fetch details, the original XML or a rendered PDF from the documents endpoints.
- Route documents to the right customer with labels and suppliers, and automate that routing with rules.
- Let Recommand mail incoming documents on to an address of your choosing when that is easier than an API call, see email delivery and notifications.
For the full picture, including retries and idempotency, see receiving documents.
Going live
Before you switch your first real customer over, walk this list:
- A valid subscription in production. Playgrounds skip the subscription check; production does not.
- Verification handled in your UI. Show the
verificationUrlat the right moment, make it forwardable, and handle thecompany.verificationwebhook so a company that comes backrejectedorerrordoes not sit silently unusable. isVerifiedrespected. Do not let a user press send for a company that is not verified yet; explain what is missing instead.- Webhook endpoint hardened. Signature verification, a fast 200, retries and idempotency on your side.
- Errors surfaced, not swallowed. Validation errors name the field that is wrong; put that in front of the person who can fix it.
- One real document, end to end. Send an invoice between two companies you control on production before letting customers in.
Volume and pricing
Documents are counted per team, with the volume of all your companies pooled,
so onboarding more companies lowers your price per document rather than adding
per-company fees. Received documents count towards the quota as well as sent
ones, so budget for both sides of the exchange. Generated XML through
generate is not billed; emails and submitted reports are.
Where to get help
- Troubleshooting. Common rejections, delivery failures and validation errors are collected in the troubleshooting guide.
- Questions. The FAQ covers Peppol, addressing, VAT and billing.
- Email. support@recommand.eu:include the company ID and, for a delivery problem, the document ID.
- Discord. Join the server for release announcements and quick questions.
- Keep track of changes. New endpoints and behaviour are recorded in the changelog.
Other guides for Norway
- Sending Peppol documents in Norway for the companies you onboard
- Receiving Peppol documents in Norway for the companies you onboard
- Sending Peppol documents in Norway for your own company
- Receiving Peppol documents in Norway for your own company
- Sending and receiving Peppol documents in Norway for your own company