This guide explains:
- What Peppol is and why it matters for e-invoicing.
- How to send invoices via VBA or Visual Basic.
- Which libraries you need (e.g., VBA-Web by Tim Hall).
- How to connect to a Peppol Certified Access Point like Scrada.
- Sample code to help you get started quickly.
Why Use VBA or Visual Basic for Peppol E-Invoicing?
Many existing business solutions are built in VBA (Visual Basic for Applications) or VB6.
- VB6 applications still run on modern Windows, even though the VB6 IDE is no longer supported.
- VBA is built into Microsoft Excel and Access (Windows & macOS) and is still widely used for automation and integration.
Because VBA and VB6 share the same programming foundation, code written for VBA often works in VB6 with minimal changes, and vice versa. This makes it easy to integrate Peppol e-invoicing into your current tools without rewriting everything.
What is Peppol?
Peppol (Pan-European Public Procurement On-Line) is a secure European network for exchanging standardized electronic documents such as UBL e-invoices between businesses (B2B) and public authorities (B2G).
In Belgium, B2B e-invoicing via Peppol will become mandatory from January 1, 2026.
For a detailed explanation, see our article What is Peppol?.
Requirements: Tools & Libraries for VBA / Visual Basic + Peppol
- Peppol Certified Access Point (AP) – e.g., Scrada.
Used to send and receive Peppol documents via API. - VBA-Web by Tim Hall
- Makes HTTP requests, JSON handling, and authentication easy in VBA.
- Works on both Windows and macOS.
- VBA-Dictionary by Tim Hall
- Cross-platform replacement for
Scripting.Dictionary(ActiveX), which isn’t available on Mac.
- Cross-platform replacement for
- (Optional) VB6 HTTP Libraries – Use
WinHTTPorMSXML2.ServerXMLHTTPif not using VBA-Web.
How to Send an Invoice via Peppol in VBA or VB6
- Select a Peppol Access Point (e.g., Scrada) and create a test account.
- Get your credentials – Company ID, API key, and password.
- Install VBA-Web and import the modules (
IWebAuthenticator,WebClient,WebRequest,WebResponse,WebHelpers). - Build your e-invoice – either using your AP’s object model or by generating a UBL 2.1 / Peppol BIS XML file.
- Send the document with an HTTP POST request to the API endpoint.
- Check the API response – track status, handle errors, confirm delivery.
Using Scrada as Your Peppol Certified Access Point
Scrada offers two API options:
- Peppol Only API – For developers who only need to send/receive via Peppol.
- Full API – Adds fallback email, forwarding to accounting software, and more.
Key Benefit:
Scrada can generate a valid Peppol BIS XML from a simple object.
This means you don’t have to manually write every XML tag—Scrada handles it for you.
How to Get Started with Scrada:
- Request a Peppol Only account via info@scrada.be or register for the Full API at my.scrada.be.
- Access the test environment (Peppol sandbox) if needed.
- After registration, find your credentials in:
- Menu → Settings → API Keys (API key & password)
- Menu → Settings → Company (Company ID)
On our postman page https://www.postman.com/scrada/overview/overview, you can find various examples of how to use our API, such as sending via API, receiving via API, downloading sent UBL files, etc.
Example: Sending an invoice via Peppol with VBA (Excel/Access)
Define constants
We define three constants to store our CompanyID, API Key, and password:
Public Const COMPANY_ID_SCRADA = ""
Public Const API_KEY_SCRADA = ""
Public Const PASSWORD_SCRADA = ""
Send invoice via Peppol (Peppol Only)
If we use the Peppol Only version, our code for sending an invoice via Peppol could look like this:
'https://www.scrada.be/api-documentation/#tag/Peppol-outbound/paths/~1v1~1company~1%7BcompanyID%7D~1peppol~1outbound~1salesInvoice/post
Dim wClient As New WebClient
wClient.BaseUrl = "https://apitest.scrada.be/v1/company/" & COMPANY_ID_SCRADA & "/peppol/outbound/salesInvoice"
Dim req As New WebRequest
req.Resource = "" 'Base URL already set
req.Method = HttpPost
req.Format = json
' Add Authorization header
req.AddHeader "X-API-KEY", API_KEY_SCRADA
req.AddHeader "X-PASSWORD", PASSWORD_SCRADA
'Build JSON invoice object
Dim invoiceObject As Dictionary
Set invoiceObject = BuildInvoiceJsonPeppol(False)
' Send request
' This will send the invoice to Scrada API that will put the invoice on Peppol.
' This is done async so we have to call the status of the invoice to know if already sent on Peppol or that there was an issue.
Set req.Body = invoiceObject
Dim res As WebResponse
Set res = wClient.Execute(req)
' Handle response
If Not res Is Nothing Then
If res.StatusCode >= 200 And res.StatusCode < 300 Then
' The content of the response contains the ID of the invoice at Scrada
s_lastSendInvoiceIDPeppolOnly = Replace(res.Content, """, "")
MsgBox "Invoice successfully sent. Received ID of invoice is " & res.Content, vbInformation
Else
MsgBox "Error sending invoice. Status: " & res.StatusCode & vbCrLf & res.Content, vbCritical
End If
Else
MsgBox "Error sending invoice. No message received from Scrada.", vbCritical
End If
Check the status of an invoice
Upon receipt of an invoice, Scrada will not immediately place it on Peppol. That is why you will receive an ID upon delivery of the invoice, which you can use to check the status afterwards:
'https://www.scrada.be/api-documentation/#tag/Peppol-outbound/paths/~1v1~1company~1%7BcompanyID%7D~1peppol~1outbound~1document~1%7BdocumentID%7D~1info/get
Dim wClient As New WebClient
wClient.BaseUrl = "https://apitest.scrada.be/v1/company/" & COMPANY_ID_SCRADA & "/peppol/outbound/document/" & s_lastSendInvoiceIDPeppolOnly & "/info"
Dim req As New WebRequest
req.Resource = "" 'Base URL already set
req.Method = HttpGet
req.Format = json
' Add Authorization header
req.AddHeader "X-API-KEY", API_KEY_SCRADA
req.AddHeader "X-PASSWORD", PASSWORD_SCRADA
' Do API call
Dim res As WebResponse
Set res = wClient.Execute(req)
' Handle response
If Not res Is Nothing Then
If res.StatusCode >= 200 And res.StatusCode < 300 Then
Dim status As String
status = res.Data("status")
If status = "Created" Then
MsgBox "Scrada will pickup the invoice soon to send it over Peppol", vbInformation
ElseIf status = "Processed" Then
MsgBox "The invoice is sent over Peppol to access point with seat ID " & res.Data("peppolC3SeatID") & ". The reception ID of that access point is " & res.Data("peppolC3MessageID"), vbInformation
ElseIf status = "Retry" Then
MsgBox "There was an issue (" & res.Data("errorMessage") & ") when sending invoice over Peppol. Scrada will retry to send it. This is retry " & res.Data("attempt"), vbExclamation
Else
MsgBox "Error when sending invoice over Peppol. Status: " & status & vbCrLf & "Error: " & res.Data("errorMessage"), vbCritical
End If
Else
MsgBox "Error getting status of invoice. Status: " & res.StatusCode & vbCrLf & res.Content, vbCritical
End If
Else
MsgBox "Error getting status of invoice. No feedback received from Scrada.", vbCritical
End If
Exit Sub
Example function: BuildInvoiceJsonPeppol
In the example above, the invoice is constructed by the function BuildInvoiceJsonPeppol. Below you can see what this function might look like.
Private Function BuildInvoiceJsonPeppol(buildForFullVersion As Boolean) As Dictionary
Dim inv As Object: Set inv = New Dictionary
' Invoice header
If buildForFullVersion Then
'The full version need also the property bookYear and journal to identify an invoice unique
inv.Add "bookYear", ""
inv.Add "journal", ""
End If
inv.Add "number", "test-json-invoice-scrada-2"
inv.Add "externalReference", "DB_1|46a5e373-4337-4d68-92be-30ec6d2c7d98"
inv.Add "creditInvoice", False
inv.Add "invoiceDate", "2024-12-20"
inv.Add "invoiceExpiryDate", "2024-12-25"
inv.Add "buyerReference", "0150abc"
'The full version does not need a supplier because for every supplier a company is created in Scrada and that company is used as supplier info
If (Not buildForFullVersion) Then
' Supplier
Dim sup As Object: Set sup = New Dictionary
sup.Add "peppolID", "9915:test-sender"
sup.Add "name", "SupplierTradingName BV"
Dim supAdr As Object: Set supAdr = New Dictionary
supAdr.Add "street", "Main street"
supAdr.Add "streetNumber", "1"
supAdr.Add "streetBox", ""
supAdr.Add "city", "Antwerpen"
supAdr.Add "zipCode", "2000"
supAdr.Add "countrySubentity", ""
supAdr.Add "countryCode", "BE"
sup.Add "address", supAdr
sup.Add "vatStatus", 1
sup.Add "legalPersonRegister", "RPR Antwerpen"
sup.Add "vatNumber", "BE0000000097"
' Supplier extraIdentifiers (array)
Dim extraIds As New Collection
Dim extraId1 As Object: Set extraId1 = New Dictionary
extraId1.Add "identifier", "ITAA nummmer: 155554"
extraIds.Add extraId1
sup.Add "extraIdentifiers", extraIds
inv.Add "supplier", sup
End If
' Customer
Dim cust As Object: Set cust = New Dictionary
cust.Add "name", "Scrada BV"
Dim custAdr As Object: Set custAdr = New Dictionary
custAdr.Add "street", "Unknown street"
custAdr.Add "streetNumber", "32"
custAdr.Add "streetBox", "5"
custAdr.Add "city", "Sint-Lievens-Houtem"
custAdr.Add "zipCode", "9521"
custAdr.Add "countryCode", "BE"
cust.Add "address", custAdr
cust.Add "vatNumber", "BE0793904121" 'The vat number of Scrada is used so that this test invoice will be delivered to Scrada
inv.Add "customer", cust
' Delivery
Dim deliv As Object: Set deliv = New Dictionary
deliv.Add "deliveryDate", "2024-12-23"
Dim delAdr As Object: Set delAdr = New Dictionary
delAdr.Add "street", "Delivery street"
delAdr.Add "streetNumber", "2"
delAdr.Add "city", "Gent"
delAdr.Add "zipCode", "9000"
delAdr.Add "countryCode", "BE"
deliv.Add "address", delAdr
inv.Add "delivery", deliv
' Invoice totals
inv.Add "totalExclVat", 1302.45
inv.Add "totalInclVat", 1575.65
inv.Add "totalVat", 273.2
inv.Add "currency", "EUR"
inv.Add "payableRoundingAmount", 0.05
' Invoice lines (array)
Dim lines As New Collection
Dim ln As Dictionary
' Invoice line 1
Set ln = New Dictionary
ln.Add "lineNumber", "1"
ln.Add "itemName", "item name"
ln.Add "quantity", 7
ln.Add "unitType", 103
ln.Add "itemExclVat", 400.05
ln.Add "vatType", 1
ln.Add "vatPercentage", 21
ln.Add "totalDiscountExclVat", 0
ln.Add "totalExclVat", 2800.35
lines.Add ln
' Invoice line 2
Set ln = New Dictionary
ln.Add "lineNumber", "2"
ln.Add "itemName", "item name 2"
ln.Add "quantity", -3
ln.Add "unitType", 103
ln.Add "itemExclVat", 500
ln.Add "vatType", 1
ln.Add "vatPercentage", 21
ln.Add "totalDiscountExclVat", 0
ln.Add "totalExclVat", -1500
lines.Add ln
' Invoice line 3
Set ln = New Dictionary
ln.Add "lineNumber", "3"
ln.Add "itemName", "item name 3"
ln.Add "quantity", 2.0096
ln.Add "unitType", 202
ln.Add "itemExclVat", 0.5242
ln.Add "vatType", 1
ln.Add "vatPercentage", 6
ln.Add "totalDiscountExclVat", 0
ln.Add "totalExclVat", 1.05
lines.Add ln
' Invoice line 4
Set ln = New Dictionary
ln.Add "lineNumber", "4"
ln.Add "itemName", "item name 4"
ln.Add "quantity", 2.0096
ln.Add "unitType", 202
ln.Add "itemExclVat", 0.5242
ln.Add "vatType", 1
ln.Add "vatPercentage", 6
ln.Add "totalDiscountExclVat", 0
ln.Add "totalExclVat", 1.05
lines.Add ln
inv.Add "lines", lines
' Invoice vatTotals (array)
Dim vatTotals As New Collection
Dim vt As Dictionary
' vat total 21%
Set vt = New Dictionary
vt.Add "vatType", 1
vt.Add "vatPercentage", 21
vt.Add "totalExclVat", 1300.35
vt.Add "totalVat", 273.07
vt.Add "totalInclVat", 1573.42
vatTotals.Add vt
' vat total 6%
Set vt = New Dictionary
vt.Add "vatType", 1
vt.Add "vatPercentage", 6
vt.Add "totalExclVat", 2.1
vt.Add "totalVat", 0.13
vt.Add "totalInclVat", 2.23
vatTotals.Add vt
inv.Add "vatTotals", vatTotals
' paymentTerms
inv.Add "paymentTerms", "Payment within 5 days"
' paymentMethods (array)
Dim pms As New Collection
Dim pm As Dictionary: Set pm = New Dictionary
pm.Add "paymentType", 2
pm.Add "paymentReference", "Snippet1"
pm.Add "name", "AccountName"
pm.Add "iban", "BE39900000010119"
pm.Add "bic", "GNOMBEBB"
pm.Add "totalPaid", 0
pm.Add "totalToPay", 1575.7 ' JSON doesn't require trailing zero; value = 1575.70
pms.Add pm
inv.Add "paymentMethods", pms
' attachments (array)
Dim atts As New Collection
Dim att As Object: Set att = New Dictionary
att.Add "filename", "invoice.pdf"
att.Add "fileType", 1
att.Add "mimeType", "application/pdf"
att.Add "base64Data", "test" 'A correct base 64 must be put here if you want to add a PDF of the invoice as attachment
att.Add "note", "Invoice"
atts.Add att
inv.Add "attachments", atts
' return invoice
Set BuildInvoiceJsonPeppol = inv
End Function
As you can see, it is very easy to add functionality to VB6 (Visual Basic 6) or VBA applications to send invoices or documents via Peppol. If you have any questions about Peppol or how to do this, please feel free to contact us.
Working example
A working example Excel file can be downloaded via the following link.
Common pitfalls & tips
- Format and validation: make sure your UBL/Peppol document validates against the correct BIS profile of your customer/country or use, for example, Scrada object if you do not want to build the XML yourself.
- Recipient identifier: use the correct Peppol identifier (scheme + number, e.g., Belgian VAT number). If you use the Scrada object, Scrada will automatically determine the correct Peppol identifier based on the information you have provided at customer level.
- Test vs production: start in the test environment (test endpoints/keys) and only switch to production once the flow is stable.
- Error handling: log status codes and response body.
- Security: Never hard-code secrets in your code; retrieve them from an encrypted store or environment variables.
FAQ (short answers)
Does this work on Mac?
Yes, VBA-Web and VBA-Dictionary work on macOS. VB6 is Windows-only, but VBA in Excel/Access also runs on Mac.
Do I have to create UBL-XML?
For Peppol, usually yes. Some Access Points also accept JSON and convert it to UBL, but check the documentation of your AP. With Scrada, you can choose whether to create your own UBL or use a Scrada object that is converted to the correct UBL format by Scrada.
Is Peppol mandatory in Belgium?
A B2B obligation has been announced from January 1, 2026.
Can I send on behalf of my customers?
Yes, as a software developer, you can place/retrieve documents on behalf of your customers via a Peppol Only API. You do need permission from your customer to do this.
Which libraries do I need in VBA?
We recommend VBA-Web. Additionally, VBA-Dictionary for a cross-platform Dictionary.




