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

# List, filter and manage documents

> List your documents with pagination, filters and sort options, and validate, send or delete drafts.

## Overview

The API has three document lists. Each list returns the same paginated response and accepts the same pagination and sort parameters. The filters are different for each list.

Use this guide when you build a document overview, a reconciliation job or a retry procedure.

## The three lists

| List | Endpoint | Direction | States in the list |
| - | - | - | - |
| Drafts | `GET /api/drafts/` | Outbound | `DRAFT`, `TRANSIT` and `FAILED` (default). One state when you set `state`. See [Drafts](#drafts). |
| Outbox | `GET /api/outbox/` | Outbound | `SENT` only |
| Inbox | `GET /api/inbox/` | Inbound | `RECEIVED` only |

<Warning>
  `GET /api/outbox/` does not return drafts, documents in transit or failed documents. An outbound document stays in the drafts list until its state is `SENT`. To see all outbound documents, read the drafts list and the outbox.
</Warning>

| State | Meaning |
| - | - |
| `DRAFT` | The document exists but is not sent. `POST /api/documents/` always creates a document in this state. |
| `TRANSIT` | The send request is accepted and the transmission is in progress. |
| `SENT` | The transmission is complete. |
| `FAILED` | The transmission failed. You can send the document again. |
| `RECEIVED` | The document came in from another Peppol participant. |

You can send a document only when it is in the `DRAFT` or `FAILED` state. For the transitions, the retry behaviour and the webhook events of each state, see [Document lifecycle and delivery tracking](/guides/document-lifecycle).

For the inbox and the procedure to receive documents, see [Receive documents](/guides/receiving-documents).

## Pagination

All three lists use page-based pagination.

<ParamField query="page" type="integer" default="1">
  Page number. The first page is `1`.
</ParamField>

<ParamField query="page_size" type="integer" default="20">
  Number of documents on each page. Minimum `1`, maximum `100`. A value out of this range gives `422 Unprocessable Entity`.
</ParamField>

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET "https://api.e-invoice.be/api/outbox/?page=1&page_size=20" \
       -H "Authorization: Bearer $E_INVOICE_API_KEY"
  ```

  ```javascript Node.js theme={null}
  const response = await fetch(
    'https://api.e-invoice.be/api/outbox/?page=1&page_size=20',
    { headers: { Authorization: `Bearer ${process.env.E_INVOICE_API_KEY}` } }
  );
  const result = await response.json();
  console.log(result.total, result.items.length);
  ```

  ```python Python theme={null}
  import os
  import requests

  response = requests.get(
      "https://api.e-invoice.be/api/outbox/",
      params={"page": 1, "page_size": 20},
      headers={"Authorization": f"Bearer {os.environ['E_INVOICE_API_KEY']}"},
  )
  result = response.json()
  print(result["total"], len(result["items"]))
  ```

  ```php PHP theme={null}
  <?php
  $ch = curl_init('https://api.e-invoice.be/api/outbox/?page=1&page_size=20');
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_HTTPHEADER, [
      'Authorization: Bearer ' . getenv('E_INVOICE_API_KEY'),
  ]);
  $result = json_decode(curl_exec($ch), true);
  curl_close($ch);
  echo $result['total'] . ' ' . count($result['items']);
  ```

  ```csharp C# theme={null}
  using System.Net.Http.Headers;

  var client = new HttpClient();
  client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
      "Bearer", Environment.GetEnvironmentVariable("E_INVOICE_API_KEY"));

  var response = await client.GetAsync(
      "https://api.e-invoice.be/api/outbox/?page=1&page_size=20");
  Console.WriteLine(await response.Content.ReadAsStringAsync());
  ```
</CodeGroup>

The response has the same shape for each list. Each entry in `items` is a full document object (see the [document schema](/api-reference/schemas/document)). The example shows only some of the document fields.

```json theme={null}
{
  "items": [
    {
      "id": "doc-1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d",
      "created_at": "2026-10-01T09:15:42Z",
      "document_type": "INVOICE",
      "state": "SENT",
      "direction": "OUTBOUND",
      "invoice_id": "INV-2026-001",
      "invoice_date": "2026-10-01",
      "due_date": "2026-10-31",
      "vendor_name": "E-INVOICE BV",
      "vendor_tax_id": "BE1018265814",
      "customer_name": "OpenPeppol VZW",
      "customer_tax_id": "BE0848934496",
      "currency": "EUR",
      "invoice_total": "1210.00"
    }
  ],
  "total": 250,
  "page": 1,
  "page_size": 20,
  "pages": 13,
  "has_next_page": true
}
```

| Field | Description |
| - | - |
| `items` | Documents on this page |
| `total` | Number of documents that agree with the filters, on all pages |
| `page` | Page number of this response |
| `page_size` | Page size of this response |
| `pages` | Number of pages |
| `has_next_page` | `true` when `page` is less than `pages` |

### Read all pages

Increase `page` until `has_next_page` is `false`.

<Accordion title="Read all pages (Node.js and Python)">
  <CodeGroup>
    ```javascript Node.js theme={null}
    const headers = { Authorization: `Bearer ${process.env.E_INVOICE_API_KEY}` };
    const documents = [];
    let page = 1;

    while (true) {
      const url = new URL('https://api.e-invoice.be/api/outbox/');
      url.searchParams.set('page', String(page));
      url.searchParams.set('page_size', '100');

      const response = await fetch(url, { headers });
      if (!response.ok) {
        throw new Error(`List request failed: ${response.status}`);
      }

      const result = await response.json();
      documents.push(...result.items);

      if (!result.has_next_page) break;
      page += 1;
    }

    console.log(`${documents.length} documents`);
    ```

    ```python Python theme={null}
    import os
    import requests

    headers = {"Authorization": f"Bearer {os.environ['E_INVOICE_API_KEY']}"}
    documents = []
    page = 1

    while True:
        response = requests.get(
            "https://api.e-invoice.be/api/outbox/",
            params={"page": page, "page_size": 100},
            headers=headers,
        )
        response.raise_for_status()
        result = response.json()
        documents.extend(result["items"])

        if not result["has_next_page"]:
            break
        page += 1

    print(f"{len(documents)} documents")
    ```
  </CodeGroup>
</Accordion>

<Note>
  Pagination uses a page offset, not a cursor. With the default sort (`created_at`, newest first), a document that is added during the loop moves the subsequent documents one position. A document can then be on two pages. Remove duplicates by `id`, or sort with `sort_order=asc` for a stable sequence.
</Note>

## Filters

Filters are combined with a logical AND. Text filters are not case-sensitive and match a part of the value.

### Drafts

`GET /api/drafts/`

| Parameter | Type | Description |
| - | - | - |
| `state` | string | One document state. Use `DRAFT`, `TRANSIT` or `FAILED`. The parameter also accepts `SENT`, but use the outbox for sent documents. Without this parameter, the list contains the states `DRAFT`, `TRANSIT` and `FAILED`. |
| `type` | string | Document type: `INVOICE`, `CREDIT_NOTE`, `DEBIT_NOTE`, `SELFBILLING_INVOICE`, `SELFBILLING_CREDIT_NOTE`. Without this parameter, the list contains all types. |
| `date_from` | date-time | Issue date (`invoice_date`) on or after this date |
| `date_to` | date-time | Issue date (`invoice_date`) on or before this date |
| `search` | string | Text search. See [Search](#search). |

### Outbox

`GET /api/outbox/`

| Parameter | Type | Description |
| - | - | - |
| `type` | string | Document type. Same values as for drafts. |
| `receiver` | string | Matches `customer_name`, `customer_email`, `customer_tax_id`, `customer_company_id` or `customer_id` |
| `date_from` | date-time | Issue date (`invoice_date`) on or after this date |
| `date_to` | date-time | Issue date (`invoice_date`) on or before this date |
| `search` | string | Text search. See [Search](#search). |
| `sender` | string | Deprecated. See [Deprecated items](#deprecated-items). |

The outbox has no `state` parameter. It always returns documents in state `SENT`.

### Inbox

`GET /api/inbox/`

| Parameter | Type | Description |
| - | - | - |
| `type` | string | Document type. Same values as for drafts. |
| `sender` | string | Matches `vendor_name`, `vendor_email`, `vendor_tax_id` or `vendor_company_id` |
| `date_from` | date-time | Issue date (`invoice_date`) on or after this date |
| `date_to` | date-time | Issue date (`invoice_date`) on or before this date |
| `search` | string | Text search. See [Search](#search). |

The inbox has no `state` parameter. It always returns documents in state `RECEIVED`. See [Receive documents](/guides/receiving-documents) for the full procedure.

### Date filters

`date_from` and `date_to` compare with the issue date of the document (`invoice_date`), not with the date of creation or transmission. Both limits are inclusive. The API uses only the date part of the value and ignores the time part.

```bash theme={null}
curl -G "https://api.e-invoice.be/api/outbox/" \
     -H "Authorization: Bearer $E_INVOICE_API_KEY" \
     --data-urlencode "date_from=2026-09-01T00:00:00" \
     --data-urlencode "date_to=2026-09-30T00:00:00" \
     --data-urlencode "type=INVOICE"
```

The response is the paginated shape from [Pagination](#pagination), with only the invoices that have an issue date in September 2026.

### Search

`search` is available on all three lists. It matches a part of one of these fields:

* `invoice_id` (the invoice number)
* `customer_name`
* `vendor_name`
* `note`

```bash theme={null}
curl -G "https://api.e-invoice.be/api/drafts/" \
     -H "Authorization: Bearer $E_INVOICE_API_KEY" \
     --data-urlencode "search=INV-2026-001"
```

<Note>
  `search` is not an exact match. `search=INV-2026-001` also returns `INV-2026-0010`. Compare `invoice_id` in your code when you must have an exact match.
</Note>

## Sort

All three lists accept the same sort parameters.

<ParamField query="sort_by" type="string" default="created_at">
  Field to sort by. One of `created_at`, `invoice_date`, `due_date`, `invoice_total`, `customer_name`, `vendor_name`, `invoice_id`.
</ParamField>

<ParamField query="sort_order" type="string" default="desc">
  Sort direction: `asc` or `desc`.
</ParamField>

The default is `created_at` in descending order: the newest document is first. A value that is not in the list gives `422 Unprocessable Entity`.

```bash theme={null}
curl -G "https://api.e-invoice.be/api/outbox/" \
     -H "Authorization: Bearer $E_INVOICE_API_KEY" \
     --data-urlencode "sort_by=invoice_total" \
     --data-urlencode "sort_order=desc"
```

## Manage a draft

A document that you create with `POST /api/documents/` starts in state `DRAFT`. These are the operations that the API permits on a draft.

| Operation | Endpoint | Permitted states | Response in other states |
| - | - | - | - |
| Read | `GET /api/documents/{document_id}` | All | - |
| Validate | `POST /api/documents/{document_id}/validate` | All | - |
| Send | `POST /api/documents/{document_id}/send` | `DRAFT`, `FAILED` | `405 Method Not Allowed` |
| Delete | `DELETE /api/documents/{document_id}` | `DRAFT`, `FAILED` | `400 Bad Request` |
| Update | Not available | - | - |

<Steps>
  <Step title="Create the draft">
    Create the document with `POST /api/documents/`. See [Create e-invoices](/guides/creating-invoices). Keep the `id` from the response.
  </Step>

  <Step title="Validate the stored draft">
    This step is optional. `POST /api/documents/{document_id}/validate` validates the stored document against Peppol BIS Billing 3.0 and returns the UBL XML.
  </Step>

  <Step title="Send the draft">
    Send the document with `POST /api/documents/{document_id}/send`. The document stays in the drafts list until its state is `SENT`. Then it is in the outbox.
  </Step>

  <Step title="Delete a draft that you do not send">
    Delete the document with `DELETE /api/documents/{document_id}`.
  </Step>
</Steps>

### Validate a stored draft

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.e-invoice.be/api/documents/doc-1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d/validate" \
       -H "Authorization: Bearer $E_INVOICE_API_KEY"
  ```

  ```javascript Node.js theme={null}
  const documentId = 'doc-1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d';

  const response = await fetch(
    `https://api.e-invoice.be/api/documents/${documentId}/validate`,
    {
      method: 'POST',
      headers: { Authorization: `Bearer ${process.env.E_INVOICE_API_KEY}` },
    }
  );
  const result = await response.json();
  console.log(result.is_valid, result.issues);
  ```

  ```python Python theme={null}
  import os
  import requests

  document_id = "doc-1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d"

  response = requests.post(
      f"https://api.e-invoice.be/api/documents/{document_id}/validate",
      headers={"Authorization": f"Bearer {os.environ['E_INVOICE_API_KEY']}"},
  )
  result = response.json()
  print(result["is_valid"], result["issues"])
  ```

  ```php PHP theme={null}
  <?php
  $documentId = 'doc-1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d';

  $ch = curl_init("https://api.e-invoice.be/api/documents/$documentId/validate");
  curl_setopt($ch, CURLOPT_POST, true);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_HTTPHEADER, [
      'Authorization: Bearer ' . getenv('E_INVOICE_API_KEY'),
  ]);
  $result = json_decode(curl_exec($ch), true);
  curl_close($ch);
  var_dump($result['is_valid']);
  ```

  ```csharp C# theme={null}
  using System.Net.Http.Headers;

  var documentId = "doc-1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d";

  var client = new HttpClient();
  client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
      "Bearer", Environment.GetEnvironmentVariable("E_INVOICE_API_KEY"));

  var response = await client.PostAsync(
      $"https://api.e-invoice.be/api/documents/{documentId}/validate", null);
  Console.WriteLine(await response.Content.ReadAsStringAsync());
  ```
</CodeGroup>

```json theme={null}
{
  "id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
  "file_name": null,
  "is_valid": true,
  "issues": [],
  "ubl_document": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Invoice xmlns=\"urn:oasis:names:specification:ubl:schema:xsd:Invoice-2\">...</Invoice>"
}
```

When the document has rule failures, `is_valid` is `false` and `issues` contains one entry for each failure. The `id` in this response identifies the validation result, not the document. See [Validation during development](/guides/validation) for the issue fields.

### Delete a draft

<CodeGroup>
  ```bash cURL theme={null}
  curl -X DELETE "https://api.e-invoice.be/api/documents/doc-1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d" \
       -H "Authorization: Bearer $E_INVOICE_API_KEY"
  ```

  ```javascript Node.js theme={null}
  const documentId = 'doc-1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d';

  const response = await fetch(
    `https://api.e-invoice.be/api/documents/${documentId}`,
    {
      method: 'DELETE',
      headers: { Authorization: `Bearer ${process.env.E_INVOICE_API_KEY}` },
    }
  );
  console.log(response.status, await response.json());
  ```

  ```python Python theme={null}
  import os
  import requests

  document_id = "doc-1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d"

  response = requests.delete(
      f"https://api.e-invoice.be/api/documents/{document_id}",
      headers={"Authorization": f"Bearer {os.environ['E_INVOICE_API_KEY']}"},
  )
  print(response.status_code, response.json())
  ```

  ```php PHP theme={null}
  <?php
  $documentId = 'doc-1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d';

  $ch = curl_init("https://api.e-invoice.be/api/documents/$documentId");
  curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_HTTPHEADER, [
      'Authorization: Bearer ' . getenv('E_INVOICE_API_KEY'),
  ]);
  $body = curl_exec($ch);
  echo curl_getinfo($ch, CURLINFO_HTTP_CODE) . ' ' . $body;
  curl_close($ch);
  ```

  ```csharp C# theme={null}
  using System.Net.Http.Headers;

  var documentId = "doc-1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d";

  var client = new HttpClient();
  client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
      "Bearer", Environment.GetEnvironmentVariable("E_INVOICE_API_KEY"));

  var response = await client.DeleteAsync(
      $"https://api.e-invoice.be/api/documents/{documentId}");
  Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");
  ```
</CodeGroup>

```json theme={null}
{
  "is_deleted": true
}
```

You can delete a document only in state `DRAFT` or `FAILED`. For a document in state `TRANSIT`, `SENT` or `RECEIVED`, the API returns `400 Bad Request`:

```json theme={null}
{
  "detail": "Document is not in draft state"
}
```

A document ID that does not exist in your company gives `404 Not Found`. A deleted document is not in the lists and you cannot get it back through the API.

### Change a draft

The API has no endpoint to update a document. To change a draft, delete it and create it again with the corrected data. The new document has a new `id`.

<Note>
  Validation is not a separate mandatory call. `POST /api/documents/` rejects a payload that does not pass the same rules. Use `POST /api/validate/json` while you develop, because it returns all rule failures and the generated UBL.
</Note>

A payload that you validate before you create the document does not make a draft that you must delete. See [Validation during development](/guides/validation).

### Send a failed document again

You can send a document from state `DRAFT` or `FAILED` only. To try a failed document again, call `POST /api/documents/{document_id}/send` again with the same document ID. A new document is not necessary.

For a document in a different state, the API returns `405 Method Not Allowed`:

```json theme={null}
{
  "detail": "Document is not in a valid state to send (DRAFT, FAILED)"
}
```

Find the failed documents with the `state` filter:

```bash theme={null}
curl -G "https://api.e-invoice.be/api/drafts/" \
     -H "Authorization: Bearer $E_INVOICE_API_KEY" \
     --data-urlencode "state=FAILED"
```

The response is the paginated shape from [Pagination](#pagination). See [Errors and troubleshooting](/guides/errors) for the causes of a failed send.

## Find duplicates before a retry

The API has no idempotency key. If a create request has a timeout and you send it again, the result can be two drafts with the same `invoice_id`. The API does not reject the second one.

Before you repeat a create request, look for the invoice number in the drafts list and in the outbox:

<Steps>
  <Step title="Search the drafts list">
    Call `GET /api/drafts/?search=INV-2026-001`. This finds documents in state `DRAFT`, `TRANSIT` and `FAILED`.
  </Step>

  <Step title="Search the outbox">
    Call `GET /api/outbox/?search=INV-2026-001`. This finds documents in state `SENT`.
  </Step>

  <Step title="Compare the invoice number">
    `search` matches a part of the value. In the results, keep only the documents whose `invoice_id` is equal to your invoice number.
  </Step>

  <Step title="Continue with the document that exists">
    If you find a document, use its `id` and do not create a new one. If you find more than one draft, delete the extra drafts.
  </Step>
</Steps>

## Deprecated items

| Deprecated | Replacement | Remarks |
| - | - | - |
| `GET /api/outbox/drafts` | `GET /api/drafts/` | The deprecated endpoint has no `date_from` and `date_to` filters. |
| `sender` parameter on `GET /api/outbox/` | `receiver` parameter | The API uses `sender` as `receiver` when `receiver` is not set. |

## Next Steps

<CardGroup cols={2}>
  <Card title="Document lifecycle and delivery tracking" icon="arrows-rotate" href="/guides/document-lifecycle">
    Learn the document states and the transitions between them.
  </Card>

  <Card title="Receive documents" icon="inbox" href="/guides/receiving-documents">
    Read the inbox and process inbound documents.
  </Card>

  <Card title="Errors and troubleshooting" icon="triangle-exclamation" href="/guides/errors">
    Find the cause of an error response or a failed send.
  </Card>
</CardGroup>
