Skip to main content

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:
  1. Synchronous - Immediate processing with instant results (recommended for most cases)
  2. 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]"
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.

Creating a Document from Extracted UBL

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

File Format

  • 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

  1. Use text-based PDFs: Generated from software rather than scanned images
  2. Clear layout: Structured format with clear sections for vendor, customer, line items
  3. Readable fonts: Standard fonts with good contrast
  4. Complete information: All required invoice fields clearly visible
  5. Single invoice per PDF: Don’t combine multiple invoices in one file
  6. 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

Extraction Inaccuracies

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
}
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);
Review extracted data before sending, especially for critical fields:
const extracted = response.data;

// Check critical fields
if (!extracted.invoice_number || !extracted.total_amount) {
  console.error('Missing critical data');
  return;
}

// Validate tax IDs are in correct format
if (extracted.supplier_tax_id && !extracted.supplier_tax_id.match(/^BE\d{10}$/)) {
  console.warn('Invalid vendor tax ID format');
}

Next Steps