Overview
The e-invoice.be API allows you to create documents directly from PDF files using AI-powered data extraction. This is useful when:
You have existing PDF invoices you want to send via Peppol
Your system generates PDF invoices but not structured data
You want to digitize paper invoices
You’re migrating from traditional PDF invoicing to e-invoicing
How It Works
Upload a PDF invoice and the API automatically extracts invoice data and attempts to generate a valid UBL document. If the ubl_document field is present in the response, sufficient details were extracted to create a Peppol-compliant document ready for sending.
The API provides two processing modes:
Synchronous - Immediate processing with instant results (recommended for most cases)
Asynchronous - Long-running conversions with task polling (for complex PDFs)
Method 1: Synchronous PDF Upload
Upload a PDF file and receive immediate extraction results.
Basic Upload
curl -X POST "https://api.e-invoice.be/api/documents/pdf" \
-H "Authorization: Bearer YOUR_API_KEY" \
-F "[email protected] "
Upload with Tax IDs (Recommended)
Providing tax IDs improves extraction accuracy:
curl -X POST "https://api.e-invoice.be/api/documents/pdf?vendor_tax_id=BE1018265814&customer_tax_id=BE0848934496" \
-H "Authorization: Bearer YOUR_API_KEY" \
-F "[email protected] "
Response
The response contains extracted invoice data and, if successful, a UBL document ready for sending:
{
"success" : true ,
"invoice_number" : "INV-2024-001" ,
"issue_date" : "2024-10-24" ,
"total_amount" : 1210.00 ,
"currency" : "EUR" ,
"supplier_name" : "E-INVOICE BV" ,
"supplier_tax_id" : "BE1018265814" ,
"customer_name" : "OpenPeppol VZW" ,
"customer_tax_id" : "BE0848934496" ,
"line_items" : [
{
"description" : "Professional Services" ,
"quantity" : 10.0 ,
"unit_price" : 100.00 ,
"tax_rate" : 21.00 ,
"amount" : 1000.00
}
],
"ubl_document" : "<?xml version= \" 1.0 \" encoding= \" UTF-8 \" ?><Invoice>...</Invoice>"
}
If the ubl_document field is present in the response, the PDF contained sufficient information to generate a valid UBL document. You can then create a document from this UBL and send it via Peppol.
If the response contains a ubl_document, create and send it:
# Step 1: Create document from the extracted UBL
curl -X POST "https://api.e-invoice.be/api/documents/ubl" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/xml" \
--data-binary '@extracted_invoice.xml'
# Step 2: Send via Peppol
curl -X POST "https://api.e-invoice.be/api/documents/doc_abc123/send" \
-H "Authorization: Bearer YOUR_API_KEY"
Method 2: Asynchronous PDF Upload
For complex PDFs or when immediate processing isn’t required, use the asynchronous endpoint.
Step 1: Initiate Conversion
curl -X POST "https://api.e-invoice.be/api/conversion/pdf" \
-H "Authorization: Bearer YOUR_API_KEY" \
-F "[email protected] "
Response:
{
"task_id" : "task_abc123" ,
"status" : "pending"
}
Step 2: Check Status
curl -X GET "https://api.e-invoice.be/api/conversion/pdf/task_abc123" \
-H "Authorization: Bearer YOUR_API_KEY"
Response:
{
"task_id" : "task_abc123" ,
"status" : "completed"
}
Status values: pending, completed, failed
Step 3: Retrieve Result
curl -X GET "https://api.e-invoice.be/api/conversion/pdf/task_abc123/result" \
-H "Authorization: Bearer YOUR_API_KEY"
Response contains the same structure as the synchronous endpoint.
Code Examples
Node.js - Synchronous Upload
const axios = require ( 'axios' );
const FormData = require ( 'form-data' );
const fs = require ( 'fs' );
async function uploadPDFInvoice ( pdfPath , vendorTaxId , customerTaxId ) {
const formData = new FormData ();
formData . append ( 'file' , fs . createReadStream ( pdfPath ));
// Build URL with optional tax IDs
let url = 'https://api.e-invoice.be/api/documents/pdf' ;
const params = new URLSearchParams ();
if ( vendorTaxId ) params . append ( 'vendor_tax_id' , vendorTaxId );
if ( customerTaxId ) params . append ( 'customer_tax_id' , customerTaxId );
if ( params . toString ()) url += `? ${ params . toString () } ` ;
try {
const response = await axios . post ( url , formData , {
headers: {
'Authorization' : `Bearer ${ process . env . E_INVOICE_API_KEY } ` ,
... formData . getHeaders ()
}
});
console . log ( 'PDF processed successfully' );
console . log ( 'Invoice number:' , response . data . invoice_number );
console . log ( 'Total amount:' , response . data . total_amount );
if ( response . data . ubl_document ) {
console . log ( '✓ UBL document generated - ready to send' );
return response . data ;
} else {
console . log ( '⚠ Could not generate UBL - manual review required' );
console . log ( 'Extracted data:' , response . data );
return null ;
}
} catch ( error ) {
console . error ( 'Error:' , error . response ?. data || error . message );
}
}
// Usage
uploadPDFInvoice ( './invoice.pdf' , 'BE1018265814' , 'BE0848934496' );
Python - Synchronous Upload
import requests
import os
API_KEY = os.environ.get( 'E_INVOICE_API_KEY' )
BASE_URL = 'https://api.e-invoice.be'
def upload_pdf_invoice ( pdf_path , vendor_tax_id = None , customer_tax_id = None ):
headers = {
'Authorization' : f 'Bearer { API_KEY } '
}
files = {
'file' : open (pdf_path, 'rb' )
}
params = {}
if vendor_tax_id:
params[ 'vendor_tax_id' ] = vendor_tax_id
if customer_tax_id:
params[ 'customer_tax_id' ] = customer_tax_id
try :
response = requests.post(
f ' { BASE_URL } /api/documents/pdf' ,
headers = headers,
files = files,
params = params
)
response.raise_for_status()
data = response.json()
print ( 'PDF processed successfully' )
print ( f 'Invoice number: { data.get( "invoice_number" ) } ' )
print ( f 'Total amount: { data.get( "total_amount" ) } ' )
if data.get( 'ubl_document' ):
print ( '✓ UBL document generated - ready to send' )
return data
else :
print ( '⚠ Could not generate UBL - manual review required' )
print ( f 'Extracted data: { data } ' )
return None
except Exception as error:
print ( f 'Error: { error } ' )
# Usage
upload_pdf_invoice( './invoice.pdf' , 'BE1018265814' , 'BE0848934496' )
Node.js - Asynchronous Upload with Polling
const axios = require ( 'axios' );
const FormData = require ( 'form-data' );
const fs = require ( 'fs' );
const api = axios . create ({
baseURL: 'https://api.e-invoice.be' ,
headers: {
'Authorization' : `Bearer ${ process . env . E_INVOICE_API_KEY } `
}
});
async function uploadPDFAsync ( pdfPath ) {
// Step 1: Initiate conversion
const formData = new FormData ();
formData . append ( 'file' , fs . createReadStream ( pdfPath ));
const taskResponse = await api . post ( '/api/conversion/pdf' , formData , {
headers: formData . getHeaders ()
});
const taskId = taskResponse . data . task_id ;
console . log ( 'Conversion started:' , taskId );
// Step 2: Poll for completion
while ( true ) {
await new Promise ( resolve => setTimeout ( resolve , 2000 )); // Wait 2 seconds
const statusResponse = await api . get ( `/api/conversion/pdf/ ${ taskId } ` );
const status = statusResponse . data . status ;
console . log ( 'Status:' , status );
if ( status === 'completed' ) {
// Step 3: Get result
const resultResponse = await api . get ( `/api/conversion/pdf/ ${ taskId } /result` );
console . log ( 'Conversion complete' );
return resultResponse . data ;
} else if ( status === 'failed' ) {
throw new Error ( 'Conversion failed' );
}
}
}
// Usage
uploadPDFAsync ( './invoice.pdf' )
. then ( result => console . log ( 'Result:' , result ))
. catch ( error => console . error ( 'Error:' , error ));
Complete Workflow Example
Here’s a complete example that extracts data from a PDF and sends it via Peppol:
const axios = require ( 'axios' );
const FormData = require ( 'form-data' );
const fs = require ( 'fs' );
const api = axios . create ({
baseURL: 'https://api.e-invoice.be' ,
headers: {
'Authorization' : `Bearer ${ process . env . E_INVOICE_API_KEY } `
}
});
async function processAndSendPDFInvoice ( pdfPath , vendorTaxId , customerTaxId ) {
try {
// Step 1: Upload PDF and extract data
console . log ( 'Processing PDF invoice...' );
const formData = new FormData ();
formData . append ( 'file' , fs . createReadStream ( pdfPath ));
const params = new URLSearchParams ();
if ( vendorTaxId ) params . append ( 'vendor_tax_id' , vendorTaxId );
if ( customerTaxId ) params . append ( 'customer_tax_id' , customerTaxId );
const extractResponse = await api . post (
`/api/documents/pdf? ${ params . toString () } ` ,
formData ,
{ headers: formData . getHeaders () }
);
const extracted = extractResponse . data ;
console . log ( '✓ PDF processed' );
console . log ( ` Invoice: ${ extracted . invoice_number } ` );
console . log ( ` Amount: ${ extracted . currency } ${ extracted . total_amount } ` );
// Step 2: Check if UBL was generated
if ( ! extracted . ubl_document ) {
console . error ( '✗ Could not generate UBL document' );
console . log ( 'Extracted data:' , extracted );
console . log ( 'Please review and create document manually' );
return null ;
}
console . log ( '✓ UBL document generated' );
// Step 3: Save UBL to file
const ublPath = './extracted_invoice.xml' ;
fs . writeFileSync ( ublPath , extracted . ubl_document );
// Step 4: Create document from UBL
console . log ( 'Creating document...' );
const documentResponse = await api . post (
'/api/documents/ubl' ,
extracted . ubl_document ,
{ headers: { 'Content-Type' : 'application/xml' } }
);
const documentId = documentResponse . data . id ;
console . log ( '✓ Document created:' , documentId );
// Step 5: Send via Peppol
console . log ( 'Sending document...' );
const sendResponse = await api . post ( `/api/documents/ ${ documentId } /send` );
console . log ( '✓ Document sent:' , sendResponse . data . state );
return {
extracted ,
documentId ,
state: sendResponse . data . state
};
} catch ( error ) {
console . error ( 'Error:' , error . response ?. data || error . message );
throw error ;
}
}
// Usage
processAndSendPDFInvoice ( './invoice.pdf' , 'BE1018265814' , 'BE0848934496' )
. then ( result => console . log ( 'Complete:' , result ))
. catch ( error => console . error ( 'Failed:' , error ));
PDF Requirements
Format : PDF (Portable Document Format)
Content : Must be valid and non-empty
Text extraction : PDFs with readable text produce better results
Text-based PDFs generated from software (not scanned documents) produce the most accurate extraction results. Providing vendor and customer tax IDs as query parameters significantly improves extraction accuracy.
Best Practices for PDFs
Use text-based PDFs : Generated from software rather than scanned images
Clear layout : Structured format with clear sections for vendor, customer, line items
Readable fonts : Standard fonts with good contrast
Complete information : All required invoice fields clearly visible
Single invoice per PDF : Don’t combine multiple invoices in one file
Provide tax IDs : Include vendor_tax_id and customer_tax_id query parameters
Common Issues
Invalid or Empty PDF
Status Code : 415 Unsupported Media Type
Solution :
Ensure the file is a valid PDF document
Check the file is not corrupted
Verify the file is not empty
No UBL Document Generated
Issue : Response contains extracted data but no ubl_document field
Cause : The PDF didn’t contain sufficient information to generate a complete UBL document
Solution :
Review the extracted data in the response
Provide vendor_tax_id and customer_tax_id query parameters
Use a more detailed PDF with complete invoice information
Manually create the document using the JSON API with the extracted data as a starting point
Issue : Extracted amounts, dates, or tax IDs are incorrect
Solution :
Always provide vendor_tax_id and customer_tax_id as query parameters
Use text-based PDFs rather than scanned images
Ensure the PDF has a clear, structured layout
Review extracted data before creating documents
Authentication Errors
Status Code : 401 Unauthorized
Solution :
Verify your API key is correct
Ensure the Authorization header is properly formatted: Bearer YOUR_API_KEY
Check your API key has not expired
Best Practices
Include vendor and customer tax IDs as query parameters for better extraction: // ✓ Good: Provide tax IDs
const url = `/api/documents/pdf?vendor_tax_id=BE1018265814&customer_tax_id=BE0848934496` ;
await api . post ( url , formData );
// ✗ Less accurate: No tax IDs
await api . post ( '/api/documents/pdf' , formData );
Always check if a UBL document was generated before proceeding: const result = await api . post ( '/api/documents/pdf' , formData );
if ( ! result . data . ubl_document ) {
console . warn ( 'Manual review required - incomplete extraction' );
// Handle manually or notify user
}
Use Asynchronous Processing for Large Batches
For processing multiple PDFs, use the async endpoint to avoid timeouts: const tasks = await Promise . all (
pdfFiles . map ( file => initiateConversion ( file ))
);
// Poll for results separately
const results = await waitForResults ( tasks );
Store original PDFs for audit and compliance purposes: const backup = `./backup/ ${ invoiceNumber } .pdf` ;
fs . copyFileSync ( pdfPath , backup );
Next Steps