Sending
Submit documents on behalf of the companies under your account.
Sending is one call. You do not name the company: the sender participant determines it, and we refuse a sender that is not under a company your credential reaches.
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\"?>…"
}'Exactly one of payload (the XML as a string) and payloadObjectKey (see below). type is a short alias; pass documentTypeId and processId instead if you need a document type that has no alias.
Both participants matter. The sender must be READY or ACTIVE with a matching SEND capability. The receiver is looked up on the network, and if it is not registered for that document type the send fails — usually the recipient's fault, but it will surface as your customer's problem.
202 does not mean valid
The response is 202 with status QUEUED_VALIDATION for any document that has validation rules. Peppol validation runs after your request returns, not inside it, so a slow validator never becomes your latency.
What you get back is a document id and a promise to tell you the verdict. That verdict arrives as VALID or VALIDATION_FAILED on a webhook or on a later GET /v1/messages/{id}.
| Status | Where it is |
|---|---|
QUEUED_VALIDATION | Accepted, waiting for validation |
VALIDATION_FAILED | Rejected by the Peppol rules. validationErrors says what failed |
QUEUED | Valid, waiting to go to the network |
SENDING / SEND_RETRY | On its way, or being retried |
AS4_ACCEPTED | The recipient's access point took it. This is delivery |
SEND_FAILED | Terminal. lastErrorCode and lastErrorMessage say why |
DISCOVERY_FAILED | The recipient could not be found on the network |
AS4_ACCEPTED means the other access point accepted the document — that is as far as Peppol guarantees. Whether the recipient's finance system then processes it is between them and their provider, and a Message Level Status may follow.
Idempotency
Idempotency-Key is optional and worth setting on every send. Same key with the same body returns the original document and 200 instead of creating a second one; same key with a different body is 409 IDEMPOTENCY_CONFLICT. Keys are unique per account forever, up to 128 characters.
Without the header, a retried request creates a second document — and your customer's trading partner gets the invoice twice.
Large documents
Inline payload is capped at about 1 MB. Above that, upload the bytes separately:
# 1. ask for a URL
curl -sS -X POST https://api.businessgateway.se/v1/messages/uploads \
-H "X-API-Key: $KEY" -H 'Content-Type: application/json' \
-d '{"companyId":"'"$COMPANY_ID"'","bytes":4200000}'
# → {"payloadObjectKey":"…","uploadUrl":"https://…","expiresAt":"…"}
# 2. PUT the bytes straight at uploadUrl
# 3. submit, naming the key instead of the payload
curl -sS -X POST https://api.businessgateway.se/v1/messages \
-H "X-API-Key: $KEY" -H 'Content-Type: application/json' \
-d '{"sender":"…","receiver":"…","type":"invoice","payloadObjectKey":"…"}'The hard ceiling for a document is about 40 MB, inbound and outbound alike.
Checking on a document
curl -sS -H "X-API-Key: $KEY" \
"https://api.businessgateway.se/v1/messages/$ID"Or list them. GET /v1/messages takes direction, status, companyId, from/to, q, and cursor pagination; format=csv gives the same filter as a download, and counts=true adds a breakdown by status. Across your whole account, without companyId, you see every company at once — which is the view worth putting in front of your support team.