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

# Export Invoice PDFs

> Download a ZIP with the PDF of every invoice issued in a date range, plus a CSV manifest

Returns a ZIP containing one PDF per issued invoice in the requested range
(same rules as [Download Invoice PDF](/api-reference/billing/download-invoice-pdf)
for which PDF each invoice kind gets) plus a CSV manifest listing every invoice
in the range — including the ones a PDF could not be produced for. **Payer
accounts (cuentas a convenio) are included.** Drafts are never included.
Nothing is persisted; the ZIP is built fresh per request.

## Query Parameters

<ParamField query="from" type="string" required>
  Start date, `YYYY-MM-DD`, inclusive. Filters by the invoice's **emission
  date** (`issuedAt`; legacy invoices without it fall back to `issueDate`) as
  a **Colombian calendar day** (`America/Bogota`).
</ParamField>

<ParamField query="to" type="string" required>
  End date, `YYYY-MM-DD`, inclusive. Must be on or after `from`.
</ParamField>

<ParamField query="invoiceType" type="string" default="all">
  One of `all`, `electronic`, `manual`. `electronic` includes both Alegra FEVs
  and external-provider FEVs.
</ParamField>

## Rules

* Includes invoices in status `issued`, `partially_paid`, `paid`, `voided` or
  `filed`. Drafts are excluded.
* **Maximum 100 invoices per request.** A larger range is rejected outright
  (never silently truncated) — see `INVOICE_EXPORT_TOO_LARGE` below. The cap
  and the default range are also available from the
  [form-schema endpoint](/api-reference/billing/export-invoice-pdfs-form-schema).
* **One export at a time per clinic** (the Alegra credential is shared across
  the whole deployment): a second concurrent request gets `429
  INVOICE_EXPORT_IN_PROGRESS`. The endpoint also caps at 10 exports per minute
  per clinic.
* A provider failure is **per-invoice, never fatal to the export**: if Alegra
  can't produce a given invoice's PDF, that row is skipped and recorded in the
  manifest with a reason; the rest of the ZIP still ships. If Alegra is down
  or rejects the credential, the remaining Alegra invoices in that request are
  all skipped as `provider_unavailable` while Nevatal-generated PDFs (manual,
  external) still ship. **A provider failure never produces a `500` or aborts
  the ZIP.**
* ZIP and manifest are ordered by emission date, ascending.

## File names

* The displayed invoice number is the DIAN number when available, otherwise
  Nevatal's internal number; sanitized to `[A-Za-z0-9._-]`.
* PDF: `<number>.pdf`, or `<number>-VOIDED.pdf` for a voided invoice.
* ZIP: `invoices_<from>_<to>.zip`. Manifest: `manifest.csv`.
* A name collision inside the ZIP gets a `-2`, `-3`, ... suffix.

## Response

`200` returns the ZIP as `application/zip`, with `Content-Disposition:
attachment` and `Content-Length`. **A `200` can still contain skipped rows** —
check the manifest's `Reason` column rather than assuming every invoice in
range got a PDF.

Every non-2xx response is the standard JSON error envelope, never binary.

### ZIP contents

```
invoices_2026-08-01_2026-08-31.zip
├── manifest.csv
├── SETP990000123.pdf            ← Alegra official PDF
├── FE-4521.pdf                  ← external FEV (Nevatal internal support copy)
├── INV-2026-0001-00042.pdf      ← manual (Nevatal)
└── INV-2026-0001-00043-VOIDED.pdf
```

### `manifest.csv`

UTF-8 with BOM. One header row plus one row per invoice in range, **including
skipped ones**. Formula-injection guard on cells starting with `=`, `+`, `-`,
`@` (prefixed with `'`).

|  #  | Column            | Value                                                      |
| :-: | ----------------- | ---------------------------------------------------------- |
|  1  | `Number`          | Displayed number (DIAN or internal)                        |
|  2  | `Internal number` | Nevatal's own `invoiceNumber`                              |
|  3  | `Type`            | `Electronic` · `Electronic (external provider)` · `Manual` |
|  4  | `Emission date`   | `YYYY-MM-DD`, Colombian calendar day                       |
|  5  | `Customer`        | Patient or payer name                                      |
|  6  | `Total`           | Amount due                                                 |
|  7  | `Currency`        | e.g. `COP`                                                 |
|  8  | `Status`          | `Issued` · `Partially paid` · `Paid` · `Voided` · `Filed`  |
|  9  | `PDF included`    | `Yes` / `No`                                               |
|  10 | `File`            | PDF file name inside the ZIP; empty if skipped             |
|  11 | `Reason`          | Empty if included; otherwise one of the reasons below      |

### Skip reasons

| Reason                        | Meaning                                                                              |
| ----------------------------- | ------------------------------------------------------------------------------------ |
| `not_accepted`                | DIAN rejected this electronic invoice — no official graphical representation exists. |
| `missing_provider_document`   | This electronic invoice has no document registered with the provider.                |
| `provider_file_not_available` | Alegra doesn't have the PDF of this invoice available.                               |
| `provider_unavailable`        | Alegra could not be reached — download again later.                                  |
| `generation_failed`           | The PDF of this invoice could not be generated.                                      |

## Errors

| Status | Code                         | When                                                                    |
| :----: | ---------------------------- | ----------------------------------------------------------------------- |
|  `400` | `VALIDATION_ERROR`           | `from`/`to` missing or invalid, `to < from`, or invalid `invoiceType`   |
|  `400` | `INVOICE_EXPORT_TOO_LARGE`   | More than `maxInvoices` in range; `details: { count, max }`             |
|  `401` | —                            | Missing or invalid token                                                |
|  `403` | —                            | Caller lacks `BILLING:read`                                             |
|  `404` | `INVOICE_EXPORT_EMPTY`       | No issued invoices of the requested type in that range                  |
|  `429` | `INVOICE_EXPORT_IN_PROGRESS` | The clinic already has an export running, or exceeded 10 exports/minute |

<RequestExample>
  ```bash cURL theme={null}
  curl -X GET "https://api.nevatal.com/api/v1/billing/invoices/pdf-export?from=2026-08-01&to=2026-08-31" \
    -H "Authorization: Bearer nvtl_your_api_key" \
    -o invoices.zip
  ```
</RequestExample>

<ResponseExample>
  ```text 200 theme={null}
  HTTP/1.1 200 OK
  Content-Type: application/zip
  Content-Disposition: attachment; filename="invoices_2026-08-01_2026-08-31.zip"
  Content-Length: 1843220
  ```

  ```json 400 theme={null}
  {
    "success": false,
    "error": "The end date must be on or after the start date.",
    "code": "VALIDATION_ERROR",
    "correlationId": "req-1d2e",
    "details": { "field": "to" }
  }
  ```

  ```json 400 theme={null}
  {
    "success": false,
    "error": "The range has more invoices than can be downloaded together. Narrow the date range.",
    "code": "INVOICE_EXPORT_TOO_LARGE",
    "correlationId": "req-8f4a",
    "details": { "count": 181, "max": 100 }
  }
  ```

  ```json 404 theme={null}
  {
    "success": false,
    "error": "There are no issued invoices in that date range.",
    "code": "INVOICE_EXPORT_EMPTY",
    "correlationId": "req-3b7c"
  }
  ```
</ResponseExample>
