Skip to main content

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.

The POST /contents/batch endpoint accepts a CSV file and ingests each data row as an independent content item, running the full ingestion pipeline — inference, embedding storage, and related-item search — for every row in sequence. Rows that fail (due to blank fields, malformed CSV, or inference errors) are recorded individually and do not abort the rest of the batch. This makes the endpoint resilient to partial data quality issues, but it also means you must inspect the failed count and errors array in the response to determine whether the upload was fully successful; the HTTP status code alone is not sufficient.

Endpoint

POST /contents/batch
Content-Type: multipart/form-data

Request

file
file
required
A .csv file to upload as a multipart/form-data field named exactly file. The file must not be empty, must have a .csv extension, and must not exceed 5 MB. The maximum total request size is 10 MB.

CSV format

The file must have exactly two columns — title and body — in that order. The first row is always discarded (treated as a header), regardless of its content.
title,body
"First article title","Full article body text here"
"Second article","More content here"
Formatting rules:
  • Header row: The first line is unconditionally discarded. If your file has no header, the first data row is silently lost.
  • Two columns only: title and body. Fields like category, source, and url cannot be set via batch upload. The category is predicted by the model; source is fixed to "user" and language to "es" for every row, matching the behavior of POST /content.
  • Double-quote escaping: Commas within field values are supported if the field is wrapped in double quotes.
  • No multi-line field support: A body value that spans multiple lines within double quotes will be split across rows, producing invalid records.
  • Row numbering: Error row numbers start at 1 after the header. The second line of the file is row: 1, the third line is row: 2, and so on.

Example request

curl -X POST http://localhost:8080/contents/batch \
  -F 'file=@articles.csv'

Pre-processing validation

The following checks run before any row is processed. A failure here returns an error immediately with no rows ingested.
ConditionResponse
file field is empty400 VALIDATION_ERROR"El archivo no puede estar vacío"
Filename does not end in .csv400 VALIDATION_ERROR"El archivo debe ser un CSV"
File exceeds 5 MB (or request exceeds 10 MB)400 VALIDATION_ERROR"El archivo excede el tamaño máximo permitido"
Database not configured503 INTERNAL_ERROR"Base de datos no configurada. Use app.database.enabled=true"
The .csv extension check is based on the filename only, not the file’s actual content. A renamed Excel file will pass this check and then generate per-row errors.

Response — 200 OK

This endpoint always returns HTTP 200, even when processed: 0. Do not rely on the HTTP status code to determine success. Always inspect the failed count and the errors array in the response body.
processed
number
The number of rows successfully ingested. Equals ids.length.
failed
number
The number of rows that could not be ingested. Equals errors.length. A value greater than 0 does not prevent the remaining rows from being processed.
ids
string[]
Array of IDs assigned to the successfully ingested items, in processing order. Each ID has the format usr-<UUID>.
errors
object[]
Array of per-row error objects for rows that failed. Row numbers start at 1 after the header row.
byCategory
object
A map of category name to the count of successfully processed rows assigned to that category (e.g. {"Backend": 3, "Bases de datos": 1}). Only categories that appear in at least one successfully processed row are included; categories with zero rows are omitted entirely.
The byCategory map reflects only successfully ingested rows. Failed rows do not contribute to any category count.

Example response

{
  "processed": 2,
  "failed": 1,
  "ids": ["usr-aaa", "usr-bbb"],
  "errors": [
    { "row": 3, "reason": "Título y cuerpo no pueden estar vacíos" }
  ],
  "byCategory": {
    "Backend": 1,
    "Bases de datos": 1
  }
}

Error responses

Errors that occur during pre-processing validation (before any row is ingested) return a standard ApiError envelope and a non-200 status code. Every error response contains three fields:
FieldTypeDescription
errorstringMachine-readable error code (e.g. VALIDATION_ERROR, INTERNAL_ERROR).
messagestringHuman-readable description of what went wrong, in Spanish.
timestampstringISO 8601 UTC timestamp of when the error was generated (e.g. "2026-07-28T10:32:41.123456Z").
HTTP Statuserror codeWhen it occurs
400 Bad RequestVALIDATION_ERRORFile is empty, filename is not .csv, or the file or request exceeds the size limit.
503 Service UnavailableINTERNAL_ERRORDatabase is not configured. Start the API with app.database.enabled=true.

Example error — 400 Validation Error (empty file)

{
  "error": "VALIDATION_ERROR",
  "message": "El archivo no puede estar vacío",
  "timestamp": "2026-07-28T10:32:41.123456Z"
}

Example error — 400 Validation Error (size exceeded)

{
  "error": "VALIDATION_ERROR",
  "message": "El archivo excede el tamaño máximo permitido",
  "timestamp": "2026-07-28T10:32:41.123456Z"
}

Transaction behavior and partial batches

Transactions are scoped per row, not per batch. Each row calls the same ingest() method used by POST /content, which carries its own @Transactional boundary. If row 300 fails, rows 1–299 are already committed to the database and cannot be automatically rolled back. Use the ids array in the response to identify and manually delete items from a partial or incorrect batch.
Because each row runs the full ingestion pipeline — POST /predict to the inference service, INSERT, UPDATE of the embedding, and a SELECT vector-distance query for related items — large files involve a proportional number of network and database round-trips. The related items calculated during batch processing are computed internally and discarded; they do not appear in the BatchUploadResponse. Recommendations for large uploads:
  • Keep batches to approximately 50 rows to stay well within timeout limits. The hard limit is 5 MB, but processing time is the practical bottleneck.
  • Save the ids array from every successful response. It is the only way to identify and clean up a partial upload, since there is no batch-level rollback.
  • Inform users that the upload may take several minutes for larger files — there is no progress stream; the HTTP response arrives only after all rows have been processed.

Build docs developers (and LLMs) love