> This documentation belongs to Invofox, an AI-powered document processing
> platform. The API extracts structured data (fields) from unstructured
> documents (invoices, contracts, forms, bank statements, and more).
> Key concepts: Account, Environment, Document Type, Field, File, Import,
> and Document. When answering, prefer the exact terminology defined in the
> Glossary and cite the relevant API Reference endpoint when applicable.

# Archivos ZIP

> Sube un archivo ZIP que contiene varios documentos en una sola petición — cada archivo de dentro se extrae y se procesa como un documento independiente.

Sube un archivo ZIP que contiene varios documentos en una sola llamada. Cada archivo dentro del ZIP se extrae y se procesa como un documento independiente.

<Info>
Cada archivo dentro del ZIP se convierte en un documento separado — pueden ser de tipos distintos. Agrupa un lote de un sistema externo en un ZIP y envía una sola petición. También puedes combinar una subida ZIP con el [Classifier](/documentation/classifier) para detectar cada tipo automáticamente.
</Info>

## Paso 1 — Sube el ZIP

Sube el ZIP exactamente como cualquier otro archivo — mismo endpoint, mismo campo `info`. Invofox detecta el tipo MIME automáticamente.

**`cURL`**

```bash title="cURL"
curl -X POST https://api.invofox.com/v1/ingest/uploads \
  -H "x-api-key: $INVOFOX_API_KEY" \
  -F "files=@batch.zip" \
  -F 'info={"type":"<TYPE_ID>"}'
```

La respuesta de la subida **no** incluye `documentId`s — Invofox no sabe cuántos archivos contiene el ZIP hasta extraerlo. Guarda el `importId` en su lugar:

**`Respuesta de la subida`**

```json title="Respuesta de la subida"
{
  "accountId": "000000000000000000000006",
  "environmentId": "00000000000000000000001c",
  "importId": "000000000000000000000028",   // ← guarda esto
  "files": [
    { "id": "000000000000000000000029", "filename": "batch.zip" }
  ]
  // sin documentId — es lo esperado en una subida ZIP
}
```

Cualquier `clientData` que incluyas en el campo `info` se propaga a todos los documentos extraídos del ZIP — todos comparten exactamente el mismo `clientData`.

## Paso 2 — Obtén los resultados

Recupera los `documentId`s una vez terminado el procesamiento. **Los webhooks son la opción recomendada**; también puedes hacer polling al import.

<Cards>
  <Card title="Opción A · Webhooks (Recomendado)" icon="bolt">
    Escucha `import.processed` para saber cuándo está listo todo el ZIP, luego obtén cada documento. Sin polling.
  </Card>
  <Card title="Opción B · Polling al import" icon="arrows-rotate">
    Comprueba el estado del import hasta que esté procesado, luego recopila los `documentId`s de los archivos extraídos y obtén cada uno.
  </Card>
</Cards>

### Opción A · Webhooks (recomendado para producción)

Se dispara un evento `document.processed` por cada archivo conforme termina, llevando el documento completo extraído en su campo `data` — el mismo formato de payload que el Paso 3 de [Integrar Invofox](/documentation/integrating-invofox).

Como no sabes de antemano cuántos archivos contiene un ZIP, no puedes fiarte de contar los eventos `document.processed` para saber cuándo está completamente listo. En su lugar, escucha `import.processed` — este evento se dispara una vez, tras procesarse todos los archivos del ZIP. Itera `data.files[]` para los `documentId`s (la entrada del propio ZIP tiene un `documentIds` vacío — sáltala).

**`evento import.processed`**

```json title="evento import.processed"
{
  "type": "import.processed",
  "id": "000000000000000000000028",
  "timestamp": "2026-06-19T03:25:00.500Z",
  "data": {
    "id": "000000000000000000000028",
    "status": "processed",
    "files": [
      { "id": "000000000000000000000029", "filename": "batch.zip",      "status": "processed", "documentIds": [],                            "clientData": {} },
      { "id": "00000000000000000000002a", "filename": "invoice-001.pdf", "status": "processed", "documentIds": ["00000000000000000000002c"], "clientData": {} },
      { "id": "00000000000000000000002b", "filename": "invoice-002.pdf", "status": "processed", "documentIds": ["00000000000000000000002d"], "clientData": {} }
    ]
  }
}
```

### Opción B · Polling al import

Haz polling a `GET /v1/ingest/imports/{importId}` hasta que `status` sea `"processed"`. Mientras procesa, `status` es `"processing"` y el contador `documents` aumenta conforme se extrae cada archivo — no te fíes de él hasta que `status` sea `"processed"`. La respuesta contiene una entrada para el propio ZIP (`compressed: true`, `documentIds: []`) más una entrada por archivo extraído. Los `documentId` reales están en esas entradas hijas — itera sobre todos los `files[]` y salta los que tengan `compressed: true`.

<div className="ivf-endpoint"><span className="ivf-method ivf-method-get">GET</span><span>https://api.invofox.com/v1/ingest/imports/{importId}</span></div>

**`Respuesta del import (processed)`**

```json title="Respuesta del import (processed)"
{
  "id": "000000000000000000000028",
  "status": "processed",                 // ← espera a esto
  "documents": 2,                        // ← total de documentos extraídos (fiable una vez processed)
  "files": [
    {
      "id": "000000000000000000000029",
      "filename": "batch.zip",
      "status": "processed",
      "compressed": true,                // ← el propio ZIP
      "documentIds": []                  // ← salta esta entrada
    },
    {
      "id": "00000000000000000000002a",
      "filename": "invoice-001.pdf",     // ← conserva su nombre original dentro del ZIP
      "status": "processed",
      "compressed": false,
      "parentFileId": "000000000000000000000029",   // ← apunta a la entrada del ZIP
      "documentIds": ["00000000000000000000002c"]   // ← obtén esto
    },
    {
      "id": "00000000000000000000002b",
      "filename": "invoice-002.pdf",
      "status": "processed",
      "compressed": false,
      "parentFileId": "000000000000000000000029",
      "documentIds": ["00000000000000000000002d"]
    }
  ]
}
```

Cada archivo extraído **conserva su nombre original dentro del archivo comprimido** (`files[].filename`) — a diferencia del Splitter, que renombra sus salidas a `split_n.pdf`. Recopila todos los `documentId` de las entradas hijas.

#### Obtén cada documento

Para cada `documentId`, llama a `GET /documents/{documentId}` — mismo formato de respuesta que el flujo estándar. Los documentos extraídos de un ZIP llevan `import.fromZip: true` e `import.filename` con el nombre del archivo dentro del comprimido:

**`Documento de un ZIP`**

```json title="Documento de un ZIP"
{
  "result": {
    "_id": "00000000000000000000002c",
    "type": "<id o alias del document type>",
    "name": "invoice-001.pdf",            // ← conserva su nombre dentro del ZIP
    "publicState": "approved",
    "confidence": "high",
    "data": { "...": "campos extraídos" },
    "import": {
      "ref":  "000000000000000000000028", // ← importId
      "file": "00000000000000000000002a", // ← fileId del archivo extraído
      "fromZip": true,                     // ← marca un documento que vino de un ZIP
      "filename": "invoice-001.pdf"
    }
  }
}
```

<Warning>
**Un archivo dentro del ZIP puede fallar por su cuenta** (formato no soportado, corrupto o protegido con contraseña) mientras el import en conjunto igualmente alcanza `status: "processed"` — así que comprueba siempre el `status`/`error` de cada hijo. Consulta [Archivos rechazados](/documentation/rejected-files) para saber cómo gestionar el webhook `file.rejected`.
</Warning>