When you have a backlog of technical content to add to Mindloom — scraped articles, exported notes, or a curated reading list — the batch upload endpoint lets you ingest them all in a single HTTP request. You provide a CSV file; the API parses each row, runs the full ingestion pipeline (inference, INSERT, embedding UPDATE, related lookup) for every valid entry, and returns a structured summary showing which rows succeeded, which failed, and how the successful items were distributed across categories. Errors on individual rows never abort the whole batch.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/No-Country-simulation/G9-LATAM-Team-58/llms.txt
Use this file to discover all available pages before exploring further.
File format
The endpoint expects amultipart/form-data request with a single field named file:
title and body.
Format rules
Header row
The first line is always discarded, regardless of its content. If your CSV has no header row, the first data row is silently lost.
Two columns only
Only
title and body can be provided. category is predicted by the inference service; source is fixed to "user" and language to "es".Double-quote escaping
Fields containing commas must be wrapped in double quotes. Multi-line fields are not supported — a newline inside a quoted field splits the row and produces invalid entries.
Encoding
UTF-8 is expected. Non-UTF-8 files may parse incorrectly without an explicit error.
Pre-processing validation
The controller performs these checks before calling any service:| Condition | HTTP Status | Error code |
|---|---|---|
| File field is empty | 400 | VALIDATION_ERROR — “El archivo no puede estar vacío” |
Filename does not end in .csv | 400 | VALIDATION_ERROR — “El archivo debe ser un CSV” |
| File exceeds 5 MB | 400 | VALIDATION_ERROR — “El archivo excede el tamaño máximo permitido” |
| Database not configured | 503 | INTERNAL_ERROR |
The
.csv check is extension-only, not content-based. A file named data.csv that actually contains Excel XML will pass validation and then produce row-level parse errors inside the batch.Per-row processing
Each valid row callsIContentIngestionService.ingest() — the exact same method used by POST /content. For every row this means:
POST /predictto the inference service (body only)INSERT INTO contents(JPA, without embedding)UPDATE contents SET embedding = TO_VECTOR(?, 384, FLOAT32)(JDBC, embedding string viaVectorUtils.toVectorString())SELECT … ORDER BY VECTOR_DISTANCE(COSINE) FETCH FIRST 5 ROWS ONLY(neighbors — computed and discarded; not included in the batch response)
errors if it has fewer than two columns or contains blank values for title or body. All other rows continue processing regardless.
Response format
The endpoint always returns HTTP 200. The response body contains the full per-batch accounting:Response fields
| Field | Type | Description |
|---|---|---|
processed | integer | Number of rows successfully ingested (ids.size()). |
failed | integer | Number of rows that produced an error (errors.size()). |
ids | string[] | Assigned IDs for every successfully ingested row, in order. |
errors | object[] | Per-row error details. Each entry has row (1-based, after the header) and reason (in Spanish). |
byCategory | object | Count of successfully ingested items per predicted category. |
processed + failed equals the total number of non-header rows in the file. Row numbers in errors are 1-based starting after the header — the second line of the file is row: 1.
Critical warnings
Performance considerations
- Sizing your batches
- File size vs. row count
- Retrying failures
A 500-row CSV triggers approximately 500 sequential calls to the inference service, plus 1 500 database operations (INSERT + UPDATE + SELECT per row). With realistic inference latency, this can take several minutes. The HTTP connection stays open for the entire duration — there is no streaming or progress callback.Recommendation: keep batches to roughly 50 rows. This keeps response times under a few seconds and makes partial failures easier to diagnose and retry.
Worked example
Given this four-row CSV (one header + three data rows):"Cuerpo sin título") failed because title was blank. Rows 1 and 2 were committed successfully and their IDs are in the ids array.
For the full endpoint reference including multipart field names and error envelope shapes, see the POST /contents/batch API reference.