> ## Documentation Index
> Fetch the complete documentation index at: https://docs.e-invoice.be/llms.txt
> Use this file to discover all available pages before exploring further.

# Creating Documents from PDF

> Learn how to create invoices from PDF files with optional auto-conversion to UBL

## 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

```bash theme={null}
curl -X POST "https://api.e-invoice.be/api/documents/pdf" \
     -H "Authorization: Bearer YOUR_API_KEY" \
     -F "file=@invoice.pdf"
```

### Upload with Tax IDs (Recommended)

Providing tax IDs improves extraction accuracy:

```bash theme={null}
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 "file=@invoice.pdf"
```

### Response

The response contains extracted invoice data and, if successful, a UBL document ready for sending:

```json theme={null}
{
  "success": true,
  "document_type": "INVOICE",
  "state": "DRAFT",
  "direction": "OUTBOUND",
  "invoice_id": "INV-2024-001",
  "invoice_date": "2024-10-24",
  "invoice_total": "1210.00",
  "currency": "EUR",
  "vendor_name": "E-INVOICE BV",
  "vendor_tax_id": "BE1018265814",
  "customer_name": "OpenPeppol VZW",
  "customer_tax_id": "BE0848934496",
  "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>"
}
```

<Note>
  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.
</Note>

### Creating a Document from Extracted UBL

If the response contains a `ubl_document`, create and send it:

```bash theme={null}
# Step 1: Create document from the extracted UBL
# /api/documents/ubl takes multipart/form-data with a "file" field — not a raw XML body
curl -X POST "https://api.e-invoice.be/api/documents/ubl" \
     -H "Authorization: Bearer YOUR_API_KEY" \
     -F "file=@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

```bash theme={null}
curl -X POST "https://api.e-invoice.be/api/conversion/pdf" \
     -H "Authorization: Bearer YOUR_API_KEY" \
     -F "file=@invoice.pdf"
```

Response:

```json theme={null}
{
  "task_id": "task_abc123",
  "status": "pending"
}
```

### Step 2: Check Status

```bash theme={null}
curl -X GET "https://api.e-invoice.be/api/conversion/pdf/task_abc123" \
     -H "Authorization: Bearer YOUR_API_KEY"
```

Response:

```json theme={null}
{
  "task_id": "task_abc123",
  "status": "completed"
}
```

Status values: `pending`, `completed`, `failed`

### Step 3: Retrieve Result

```bash theme={null}
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

```javascript theme={null}
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 ID:', response.data.invoice_id);
    console.log('Invoice total:', response.data.invoice_total);

    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

```python theme={null}
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 ID: {data.get("invoice_id")}')
        print(f'Invoice total: {data.get("invoice_total")}')

        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

```javascript theme={null}
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:

```javascript theme={null}
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_id}`);
    console.log(`  Amount: ${extracted.currency} ${extracted.invoice_total}`);

    // 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
    // /api/documents/ubl takes multipart/form-data with a "file" field
    console.log('Creating document...');
    const ublForm = new FormData();
    ublForm.append('file', fs.createReadStream(ublPath));
    const documentResponse = await api.post(
      '/api/documents/ubl',
      ublForm,
      { headers: ublForm.getHeaders() }
    );

    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

<Note>
  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.
</Note>

### 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

<AccordionGroup>
  <Accordion title="Always Provide Tax IDs">
    Include vendor and customer tax IDs as query parameters for better extraction:

    ```javascript theme={null}
    // ✓ 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);
    ```
  </Accordion>

  <Accordion title="Verify UBL Generation">
    Always check if a UBL document was generated before proceeding:

    ```javascript theme={null}
    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
    }
    ```
  </Accordion>

  <Accordion title="Use Asynchronous Processing for Large Batches">
    For processing multiple PDFs, use the async endpoint to avoid timeouts:

    ```javascript theme={null}
    const tasks = await Promise.all(
      pdfFiles.map(file => initiateConversion(file))
    );

    // Poll for results separately
    const results = await waitForResults(tasks);
    ```
  </Accordion>

  <Accordion title="Keep Original PDFs">
    Store original PDFs for audit and compliance purposes:

    ```javascript theme={null}
    const backup = `./backup/${invoiceNumber}.pdf`;
    fs.copyFileSync(pdfPath, backup);
    ```
  </Accordion>

  <Accordion title="Validate Extracted Data">
    Review extracted data before sending, especially for critical fields:

    ```javascript theme={null}
    const extracted = response.data;

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

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

## Next Steps

<CardGroup cols={2}>
  <Card title="Creating Invoices" icon="file-invoice" href="/guides/creating-invoices">
    Learn standard JSON invoice creation
  </Card>

  <Card title="UBL Documents" icon="file-code" href="/guides/ubl-documents">
    Create from UBL XML files
  </Card>

  <Card title="Validation Guide" icon="check-circle" href="/guides/validation">
    Test invoice data
  </Card>

  <Card title="API Reference" icon="code" href="/api-reference">
    Explore all endpoints
  </Card>
</CardGroup>
