Quickstart for invoicing, ERP and accounting software
Check every e-invoice before it's sent
Seven calls, from one example invoice to a client company tracked under your key. Steps 1 to 4 run in the sandbox with a free key. Step 5 records a real mandate, so run it when a client company is ready to go live: live Peppol sending is switched on per company after its mandate and our KYC check. Step 7 is for Platform plan keys.
Needs curl and jq. All endpoints: API reference.
1. Get a key and an example invoice
A free key works on every endpoint in the sandbox. No card needed. The example is a German XRechnung invoice in our JSON format; your software maps its own invoice fields to the same JSON.
export KEY=... # from https://api.wsystem.dev/account/free-key
curl -s https://api.wsystem.dev/einvoice/sample > invoice.json2. Check the invoice before it is sent
One call runs every check that a live send runs, without sending anything: buyer VAT in VIES, buyer on Peppol, sanctions screen of the buyer, and EN 16931 + XRechnung validation on the official rules. mode=live makes "buyer not on Peppol" a block.
jq '{invoice: ., mode: "live"}' invoice.json \
| curl -s -X POST https://api.wsystem.dev/einvoice/preflight \
-H "X-API-Key: $KEY" -H 'Content-Type: application/json' -d @-Response (trimmed, from a real run):
{
"verdict": "block",
"safe_to_send": false,
"blocking": ["peppol"],
"checks": {
"vat": {"result": "pass", "message": "Buyer VAT ID DE811193231 is valid in VIES."},
"peppol": {"result": "block", "registered": false,
"recommended_channel": "E-mail or Peppol", "recommended_format": "xrechnung"},
"sanctions": {"result": "pass", "lists_checked": ["EU", "UN", "OFAC", "UK"]},
"invoice": {"result": "pass", "message": "The invoice passes the EN 16931 and XRechnung rules."}
}
}3. Pick the channel from the answer
Most German buyers are not on Peppol yet, and German B2B does not require it: a valid XRechnung or ZUGFeRD sent by e-mail is fine. So: if the buyer is on Peppol, send over Peppol (step 4). If not, create the XRechnung and deliver it through your own e-mail channel. Either way the invoice was validated first, and it counts once as a checked invoice however many calls you made for it.
jq '. + {"format": "xrechnung"}' invoice.json \
| curl -s -X POST https://api.wsystem.dev/einvoice/create \
-H "X-API-Key: $KEY" -H 'Content-Type: application/json' -d @- \
| jq -r .xml > INV-2026-001.xml4. Send it (sandbox first)
The sandbox runs the whole flow for real (checks, create, validate, route) and only simulates the transmission. Each send returns a signed receipt (Ed25519 JWS) to keep as evidence. For live sends add recipient_check=strict: a buyer who isn't on Peppol is then refused with 422 recipient_not_on_peppol before anything is transmitted.
jq '{invoice: ., mode: "sandbox", transport: "mock"}' invoice.json \
| curl -s -X POST https://api.wsystem.dev/einvoice/send \
-H "X-API-Key: $KEY" -H 'Content-Type: application/json' -d @-
# live, once the seller company is live (step 5):
# jq '{invoice: ., mode: "live", recipient_check: "strict"}' invoice.json | curl ...Response (trimmed, from a real run):
{
"status": "simulated",
"receipt_id": "ev_d000083e192a4adf9f1dc8d78b3608f3",
"transmission": {"transport": "mock", "transmitted": false},
"preflight": {"verdict": "warn", "warnings": ["peppol"]},
"evidence": {"jws": "eyJhbGciOiJFZERTQSIs..."}
}5. Switch a client company on for live Peppol
Each client company accepts the Peppol mandate in your app (a real, signed record, not a sandbox call): show it the current terms text, then record the acceptance with your own customer id in client_ref. We review the company (VIES, business register, sanctions) and set up its own Peppol sending account. No setup fee.
curl -s https://api.wsystem.dev/einvoice/mandate-terms -H "X-API-Key: $KEY" > terms.json # show .text to the signer
jq '{"company_name": "Muster GmbH", "company_id": "DE123456789", "company_email": "[email protected]", "company_phone": "+49 30 1234", "signer_name": "Anna Muster", "signer_role": "Managing Director", "accept": true, "authority_confirmed": true, "client_ref": "cust-10442"} + {terms_version: .version, terms_sha256: .sha256}' terms.json \
| curl -s -X POST https://api.wsystem.dev/einvoice/mandates \
-H "X-API-Key: $KEY" -H 'Content-Type: application/json' -d @-6. Track every company by your own customer id
One call lists all companies under your key with their onboarding stage, the next step and live document counts. Stages in order: kyc_pending, not_provisioned, not_registered, live_switch_off, live.
curl -s 'https://api.wsystem.dev/platform/companies?client_ref=cust-10442' -H "X-API-Key: $KEY"Response (trimmed, from a real run):
{
"count": 1,
"companies": [{
"client_ref": "cust-10442",
"company_name": "Muster GmbH",
"onboarding_stage": "kyc_pending",
"live_ready": false,
"next_step": "W System is reviewing the company (VIES, business register, sanctions). Usually within one business day."
}]
}7. Watch the bill (Platform plan)
This month's checked invoices, Peppol documents, active companies and the cost so far. An optional monthly spend cap refuses a call that would go over it (429 spend_cap_reached) and nothing is charged for it. Free keys get a clear 403 platform_plan_required here.
curl -s https://api.wsystem.dev/platform/usage -H "X-API-Key: $KEY"
curl -s -X PUT https://api.wsystem.dev/platform/spend-cap -H "X-API-Key: $KEY" \
-H 'Content-Type: application/json' -d '{"eur_month": 300}'Response (trimmed, from a real run):
{
"plan": "peppol_platform",
"checked_documents": 4,
"peppol_documents_billed": 0,
"active_companies": 1,
"estimate": {"platform_fee": 149.0, "total_eur": 149.0}
}When a check says no
- 422 invoice_invalidRule errors (official rule ID, field, fix hint). Nothing sent.
- 422 buyer_vat_invalidBuyer VAT ID invalid in VIES. Nothing sent.
- 422 buyer_sanctions_matchBuyer name matches a sanctions list (score ≥ 95). Nothing sent; review it.
- 422 recipient_not_on_peppolrecipient_check=strict and the buyer isn't on Peppol. Use your e-mail channel.
- 403 mandate_required / kyc_pending / sending_account_pendingThe seller company isn't live yet; GET /platform/companies shows the next step.
- 409 terms_mismatchThe terms changed; fetch GET /einvoice/mandate-terms again and show the current text.
- 429 spend_cap_reachedYour monthly spend cap would be exceeded. Nothing charged.
Scope
- German B2B e-invoices don't have to go over Peppol; we validate and create the XRechnung or ZUGFeRD file and send it over Peppol when the buyer is reachable there.
- We don't submit to Poland's KSeF and we aren't a French approved platform (PA).
- Prices: Platform plan. Design partners get the first month free.