Sending Invoices via Peppol with VBA (Excel/Access) or Visual Basic 6 (VB6)

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.
  • (Optional) VB6 HTTP Libraries – Use WinHTTP or MSXML2.ServerXMLHTTP if not using VBA-Web.

How to Send an Invoice via Peppol in VBA or VB6

  1. Select a Peppol Access Point (e.g., Scrada) and create a test account.
  2. Get your credentials – Company ID, API key, and password.
  3. Install VBA-Web and import the modules (IWebAuthenticator, WebClient, WebRequest, WebResponse, WebHelpers).
  4. Build your e-invoice – either using your AP’s object model or by generating a UBL 2.1 / Peppol BIS XML file.
  5. Send the document with an HTTP POST request to the API endpoint.
  6. 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.

Try the demo

Give us a call or fill in the form below and we'll contact you. We endeavor to answer all inquiries within 24 hours on business days.