Over the API
Send a document, collect what arrives, with HTTP calls.
Your key goes in X-API-Key on every request. Keys start with pap_.
curl -sS -H "X-API-Key: $KEY" https://api.businessgateway.se/v1/accountThat call is worth making first: it tells you the account and company your key belongs to, which is a quick way to confirm the key works before you build anything on it.
Keep the key on a server. It is not safe in a browser, a mobile app or anything else a customer of yours can read out.
Sending a document
curl -sS -X POST https://api.businessgateway.se/v1/messages \
-H "X-API-Key: $KEY" -H 'Content-Type: application/json' \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"sender": "0007:5561234567",
"receiver": "0007:5569876543",
"type": "invoice",
"payload": "<?xml version=\"1.0\"?>…"
}'sender is your Peppol address, receiver is your trading partner's, payload is the document XML as a string, and type says what kind of document it is — invoice, credit-note, order. The full list is in Document types.
Documents larger than about 1 MB are uploaded separately: POST /v1/messages/uploads gives you a URL to PUT the bytes at, then you submit with payloadObjectKey instead of payload. The ceiling is about 40 MB.
Always send an Idempotency-Key
Idempotency-Key is a value you make up — a UUID is fine — that makes it safe to retry. If the same key arrives again with the same document, you get the original back instead of a second copy. If your network drops the response and you retry without one, your customer receives the invoice twice.
What happens after you submit
You get 202 back with a document id, and a status of QUEUED_VALIDATION. That is not "valid". It means we accepted the document and will now check it against the Peppol rules.
The check takes a moment, which is why it does not happen inside your request. The verdict arrives on a webhook, or you read it later:
curl -sS -H "X-API-Key: $KEY" \
"https://api.businessgateway.se/v1/messages/$ID"| Status | What it means for you |
|---|---|
QUEUED_VALIDATION | Accepted, being checked |
VALIDATION_FAILED | Rejected. validationErrors lists what failed — fix the XML and send again |
QUEUED | Passed, waiting to go out |
SENDING / SEND_RETRY | On its way |
AS4_ACCEPTED | Delivered to the recipient's provider. This is success |
SEND_FAILED | Terminal. lastErrorMessage says why |
DISCOVERY_FAILED | The recipient's address could not be found on the network |
AS4_ACCEPTED is as far as Peppol's guarantee goes: the recipient's access point has the document. What their finance system does with it afterwards is theirs, and may come back as a Message Level Status.
Collecting documents
Ask what is waiting:
curl -sS -H "X-API-Key: $KEY" \
"https://api.businessgateway.se/v1/messages?direction=INBOUND&status=CUSTOMER_DELIVERY_QUEUED"Fetch the XML:
curl -sS -H "X-API-Key: $KEY" \
"https://api.businessgateway.se/v1/messages/$ID/payload"Then acknowledge it, once you have stored it somewhere you trust:
curl -sS -X POST -H "X-API-Key: $KEY" \
"https://api.businessgateway.se/v1/messages/$ID/ack"Acknowledge after your own system has the document, not before. Until you do, the document stays in the waiting list — so if your import crashes halfway, it is still there on the next run. Acknowledging twice is harmless.
Listing and searching
GET /v1/messages takes direction (INBOUND or OUTBOUND), status, from and to as dates, and q as a search string. Results are paginated: read nextCursor from the response and pass it back as cursor until it stops appearing.
format=csv returns the same filter as a download, which is often the fastest way to answer a question from your finance team.
Errors
Failures come back in one shape:
{ "error": { "code": "VALIDATION_FAILED", "message": "…" } }The code is stable and safe to branch on; message is for people and may change. The full list is in Error codes.