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.

shell
export KEY=...   # from https://api.wsystem.dev/account/free-key
curl -s https://api.wsystem.dev/einvoice/sample > invoice.json

2. 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.

shell
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.

shell
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.xml

4. 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.

shell
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.

shell
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.

shell
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.

shell
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.