Changelog

Un registro público de los cambios de la API. Cambios aditivos y compatibles hacia atrás (nuevos campos opcionales); los cambios incompatibles se marcan de forma explícita: las integraciones existentes siguen funcionando sin cambios.

2026-07-28 — Cambio de nombre a ChunkChef

El producto ahora se llama ChunkChef. Tres cambios son visibles en el protocolo; el resto es interno.

  • Ruptura — cabeceras de firma de webhook renombradas. X-Hotdoc-Timestamp y X-Hotdoc-Signature pasan a ser X-Chunkchef-Timestamp y X-Chunkchef-Signature. El esquema de firma no cambia; solo los nombres de las cabeceras. Actualice su verificación.
  • Ruptura — cambió el prefijo de la clave API. Las nuevas claves se emiten como chunkchef_<id>.<secret> en lugar de hotdoc_<id>.<secret>. Las claves existentes dejan de autenticar; genere una nueva en el panel.
  • Ruptura — cambió el discriminador de error tipado. Dentro de Status.details, @type ahora es type.googleapis.com/chunkchef.v1.billing.QuotaExceededDetail. Si compara con esa cadena, actualícela.

Los endpoints, las estructuras de peticiones y respuestas y los dominios hotdoc.io / api.hotdoc.io no cambian.

2026-07-28 — Especificación OpenAPI

  • Publicada la especificación legible por máquina en /openapi.yaml — OpenAPI 3.0.3, generada a partir de las definiciones protobuf del servicio y que cubre las cinco operaciones públicas de Jobs (POST /v1/jobs/upload, POST /v1/jobs, GET /v1/jobs/{id}, GET /v1/jobs/{id}/result, GET /v1/jobs). Pásale el archivo a cualquier generador compatible con OpenAPI para construir un cliente; esta referencia escrita a mano sigue siendo la documentación en prosa.
  • Cambio de comportamiento — el tamaño de página de GET /v1/jobs ahora está acotado. pageSize vale 50 por defecto y se limita a 200. Antes, una llamada que omitía pageSize devolvía todo el historial de trabajos en una sola respuesta. Si dependías de eso, recorre las páginas con pageToken.
  • Cambio de comportamiento — un pageToken que ya no se resuelve devuelve 400. Antes ese token reiniciaba el listado desde la primera página sin ninguna señal, con lo que podías recibir filas que ya habías visto. Interpreta el 400 como «el cursor ya no aplica, vuelve a empezar sin pageToken».
  • Corregido — recorrer GET /v1/jobs por páginas ya no omite ni repite trabajos. El cursor se anclaba en una columna distinta de aquella por la que se ordenaba la lista, de modo que un listado de varias páginas podía perder algunos trabajos y devolver otros dos veces.

2026-07-24 — Confianza por consenso (k-consensus)

  • Nuevo campo consensus (opcional) en POST /v1/jobs — opcional, requiere responseSchema (si no, se rechaza con 400). consensus.mode fija el número de ejecuciones: CONSENSUS_MODE_TWO_RUNS (2, un sondeo de inestabilidad), CONSENSUS_MODE_THREE_RUNS (3, recomendado) o CONSENSUS_MODE_FIVE_RUNS (5, máximo escrutinio). hotdoc ejecuta cada fragmento k veces con su clave y vota campo por campo (por pluralidad) sobre la respuesta.
  • Nuevo campo de resultado llm[].consensusk, runsVotable, minAgreement, incomplete y disagreements[] (variants[] en competencia con el recuento de ejecuciones y si cada una included — ganó la votación). Le permite ver en qué campos el modelo fue estable y en cuáles dudó, al coste de k veces los tokens en su clave. Consulte «Ejecuciones de consenso» en la documentación del Job API.

2026-07-15 — Fusión de fragmentos

  • Nuevo campo merge (opcional) en POST /v1/jobs — fusión opcional en el servidor de las respuestas del modelo por fragmento en un único resultado para todo el documento. merge.scope elige fusión por archivo (MERGE_SCOPE_FILE, por defecto) o por toda la tarea (MERGE_SCOPE_JOB); merge.conflictPolicy decide cómo se resuelven los valores de campo en conflicto (MERGE_CONFLICT_POLICY_FIRST_NON_NULL por defecto, o MERGE_CONFLICT_POLICY_MAJORITY); merge.dedupeBy activa la deduplicación de contenido entre archivos por nombres de campo JSON (solo JSON + responseSchema; vacío conserva el comportamiento seguro por defecto, solo deduplicación técnica).
  • Nuevo campo de resultado merged[] — una entrada por archivo (o una por tarea con scope=job) con el content combinado, schemaValid (cuando se definió responseSchema), conflicts[], los contadores dedupeRemovedTechnical/dedupeRemovedContent, y mergeIncomplete/incompleteChunks cuando un fragmento no pudo fusionarse con seguridad. Las filas llm[] por fragmento existentes no cambian y siguen disponibles. Esto cierra la nota pendiente «el servicio sigue sin fusionar los fragmentos» del 2026-06-24 — la fusión ya está disponible, es opcional y está desactivada por defecto. Consulte «Fusión de resultados fragmentados» en la documentación del Job API.

2026-07-14 — Salida estructurada (JSON Schema)

  • Nuevo campo responseSchema (opcional) en POST /v1/jobs — un JSON Schema autocontenido (cadena en bruto; solo #/$defs internos, ≤128 KiB, profundidad ≤64, ≤10000 nodos) contra el que hotdoc valida la respuesta JSON del modelo. Para OpenAI/Grok debe ser compatible con el subconjunto estricto (cada propiedad en required, opcionalidad vía type: [..., "null"], additionalProperties: false); un esquema escrito en ese subconjunto funciona sin cambios en todos los proveedores. Un esquema inválido se rechaza con 400 al crear la tarea.
  • Nuevos campos de resultado llm[].schemaValid / llm[].schemaErrors — se informan solo para archivos de un único fragmento. Una respuesta no conforme igual se devuelve en content, pero la tarea termina como JOB_STATUS_PARTIAL en lugar de JOB_STATUS_COMPLETE. Los proveedores para los que hotdoc emula salidas estructuradas (DeepSeek, Xiaomi) reciben un reintento de reparación automático antes de evaluarse.

2026-07-09 — Modo de extracción

  • Nuevo campo extractionMode (opcional) en POST /v1/jobsEXTRACTION_MODE_HYBRID (Extracción híbrida optimizada, el valor predeterminado) deja que el convertidor decida por archivo entre extraer texto directamente u OCR; EXTRACTION_MODE_OCR_ALWAYS fuerza el OCR neuronal en todos los archivos. Además, un nuevo valor predeterminado a nivel de cuenta, configurable en el panel bajo Configuración, que se usa cuando una tarea omite extractionMode.

2026-07-08 — Playground público

  • /playground en el sitio — pruebe hotdoc sin registrarse: suba un archivo, ejecute un prompt predefinido y vea el resultado directamente en el navegador.
  • Nuevos endpoints de demo anónimos, /v1/demo/*: subir un archivo, crear una tarea de demo, consultar su resultado y reclamarla en una cuenta real tras registrarse. Las sesiones se rastrean con una cookie (sin inicio de sesión) y están limitadas a 3 ejecuciones por sesión más un presupuesto diario compartido entre todos los visitantes anónimos; los resultados se truncan. Esto es aditivo — la API autenticada /v1/jobs no cambia.

2026-06-30 — OCR multiproveedor (BYOK)

Cambio incompatible. La etapa de OCR ahora toma ocr.{provider, model, providerKey} (antes ocr.mistralApiKey). Elija cualquier proveedor compatible — Mistral es el predeterminado (mistral-ocr-latest). model es obligatorio. Los nuevos marcadores de error de OCR (provider_auth_failed, provider_key_required, rate_limited, ocr_timeout, ocr_backend_*, too_many_pages) reemplazan a mistral_key_required.

2026-06-26 — Claves de idempotencia

POST /v1/jobs acepta idempotencyKey. Reintentar con la misma clave y parámetros idénticos devuelve la tarea original; la misma clave con parámetros diferentes devuelve 400.

2026-06-24 — webhooks de finalización de tareas

  • Nuevos campos webhookUrl y webhookSecret (ambos opcionales) al crear una tarea. webhookUrl es un endpoint absoluto http/https; el servicio le envía un POST una vez con un cuerpo JSON (job_id, account_id, status, finished_at) cuando la tarea alcanza un estado terminal. webhookSecret es un secreto HMAC: cuando está definido, cada solicitud incluye las cabeceras X-Hotdoc-Timestamp y X-Hotdoc-Signature (sha256=hex(hmac_sha256(secret, "<ts>.<body>"))). Ambos campos se aceptan solo como entrada y nunca aparecen en las respuestas. Política de reintentos: hasta 6 intentos con retrasos de 1 m / 5 m / 15 m / 30 m / 30 m. Consulte «Webhooks» para más detalles.

2026-06-24 — división consciente de la estructura y tamaño de fragmento configurable

  • División de texto consciente de la estructura. El texto reconocido largo ahora se corta por los límites de la estructura de su formato (Markdown / HTML / XML / plain): las tablas no se rompen a mitad de fila (el encabezado de una tabla se repite en cada fragmento) y se preserva el contexto de los encabezados de sección. El comportamiento anterior (fragmentos como filas llm[] independientes identificadas por chunkIndex / chunkTotal) no cambia: el servicio sigue sin fusionar los fragmentos.
  • Nuevo campo neural.chunkBudgetTokens (opcional) al crear una tarea: presupuesto de tokens por fragmento para la ventana de contexto de su modelo; rango 160002000000, por defecto el valor conservador del servicio. La respuesta devuelve el presupuesto efectivo real.
  • Nuevo campo ocr[].outputFormat en el resultado de la tarea: el formato del texto reconocido (markdown / html / xml / plain).