Archivos ZIP

Sube un archivo ZIP con muchos documentos en una sola llamada.

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.

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 para detectar cada tipo automáticamente.

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
$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 documentIds — Invofox no sabe cuántos archivos contiene el ZIP hasta extraerlo. Guarda el importId en su lugar:

Respuesta de la subida
1{
2 "accountId": "666839c78ea472012a9b8875",
3 "environmentId": "6a338e5239207f9311eccd36",
4 "importId": "6a34b66df55a9cdc5e6d1ef0", // ← guarda esto
5 "files": [
6 { "id": "6a34b66df55a9cdc5e6d1ef3", "filename": "batch.zip" }
7 ]
8 // sin documentId — es lo esperado en una subida ZIP
9}

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 documentIds una vez terminado el procesamiento. Los webhooks son la opción recomendada; también puedes hacer polling al import.

Opción A · Webhooks (Recomendado)

Escucha import.processed para saber cuándo está listo todo el ZIP, luego obtén cada documento. Sin polling.

Opción B · Polling al import

Comprueba el estado del import hasta que esté procesado, luego recopila los documentIds de los archivos extraídos y obtén cada uno.

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.

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 documentIds (la entrada del propio ZIP tiene un documentIds vacío — sáltala).

evento import.processed
1{
2 "type": "import.processed",
3 "id": "6a34b66df55a9cdc5e6d1ef0",
4 "timestamp": "2026-06-19T03:25:00.500Z",
5 "data": {
6 "id": "6a34b66df55a9cdc5e6d1ef0",
7 "status": "processed",
8 "files": [
9 { "id": "6a34b66df55a9cdc5e6d1ef3", "filename": "batch.zip", "status": "processed", "documentIds": [], "clientData": {} },
10 { "id": "6a34b66e6e5e80ef22435412", "filename": "invoice-001.pdf", "status": "processed", "documentIds": ["6a34b68afb139487f5bcaf92"], "clientData": {} },
11 { "id": "6a34b66e6e5e80ef2243541d", "filename": "invoice-002.pdf", "status": "processed", "documentIds": ["6a34b68bfb139487f5bcafb7"], "clientData": {} }
12 ]
13 }
14}

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.

Respuesta del import (processed)
1{
2 "id": "6a34b66df55a9cdc5e6d1ef0",
3 "status": "processed", // ← espera a esto
4 "documents": 2, // ← total de documentos extraídos (fiable una vez processed)
5 "files": [
6 {
7 "id": "6a34b66df55a9cdc5e6d1ef3",
8 "filename": "batch.zip",
9 "status": "processed",
10 "compressed": true, // ← el propio ZIP
11 "documentIds": [] // ← salta esta entrada
12 },
13 {
14 "id": "6a34b66e6e5e80ef22435412",
15 "filename": "invoice-001.pdf", // ← conserva su nombre original dentro del ZIP
16 "status": "processed",
17 "compressed": false,
18 "parentFileId": "6a34b66df55a9cdc5e6d1ef3", // ← apunta a la entrada del ZIP
19 "documentIds": ["6a34b68afb139487f5bcaf92"] // ← obtén esto
20 },
21 {
22 "id": "6a34b66e6e5e80ef2243541d",
23 "filename": "invoice-002.pdf",
24 "status": "processed",
25 "compressed": false,
26 "parentFileId": "6a34b66df55a9cdc5e6d1ef3",
27 "documentIds": ["6a34b68bfb139487f5bcafb7"]
28 }
29 ]
30}

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
1{
2 "result": {
3 "_id": "6a34b68afb139487f5bcaf92",
4 "type": "<id o alias del document type>",
5 "name": "invoice-001.pdf", // ← conserva su nombre dentro del ZIP
6 "publicState": "approved",
7 "confidence": "high",
8 "data": { "...": "campos extraídos" },
9 "import": {
10 "ref": "6a34b66df55a9cdc5e6d1ef0", // ← importId
11 "file": "6a34b66e6e5e80ef22435412", // ← fileId del archivo extraído
12 "fromZip": true, // ← marca un documento que vino de un ZIP
13 "filename": "invoice-001.pdf"
14 }
15 }
16}

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 para saber cómo gestionar el webhook file.rejected.