# Documentación para desarrolladores de Inovacc: texto completo Actualizado el 2026-10-10. --- Producto: Chat con IA URL: https://developer.inovacc.dev/es/products/ai.chat/ URL base: https://ai.inovacc.dev Respuestas de chat de los modelos de lenguaje de la plataforma. Endpoints: - POST /v1/chat/completions - GET /v1/models - POST /v1/run Ejemplo: ```sh curl -X POST "https://ai.inovacc.dev/v1/chat/completions" -H "Authorization: Bearer $INOVACC_API_KEY" -H "X-Operation-Id: $(uuidgen)" -H "Content-Type: application/json" -d '{}' ``` ## Descripción general El chat con IA da a tu aplicación respuestas conversacionales de los modelos de lenguaje que Inovacc ejecuta para tu organización. El endpoint, `POST /v1/chat/completions`, usa el formato de red de chat-completions de OpenAI, por lo que una biblioteca cliente de OpenAI existente puede llamarlo cambiando la URL base a `https://ai.inovacc.dev/v1` y agregando dos encabezados. Envías una lista de mensajes y recibes la respuesta del asistente, ya sea como un solo documento JSON o como un flujo de eventos mientras se va escribiendo. El problema que resuelve es el acceso sin infraestructura intermedia: tu organización no tiene cuentas, claves ni contratos con proveedores de modelos. Inovacc decide qué modelo atiende cada una de tus rutas, aplica los límites y presupuestos de tu organización antes de realizar una llamada, y registra el uso de cada llamada para que puedas ver cuánto gasta cada clave y cada ruta. Usa el chat con IA cuando la respuesta sea texto libre: un asistente, un resumen, un borrador, una extracción a JSON, una respuesta que llama a tus herramientas. Otro producto encaja mejor cuando: - necesitas un veredicto estructurado frente a criterios (una puntuación, una elección, un sí o un no, con confianza): usa el [Motor de decisiones](/es/products/decision/); - la respuesta debe salir de tus propios documentos, con citas: consulta una colección en la nube de [Conocimiento y búsqueda vectorial](/es/products/knowledge/); - necesitas vectores en lugar de texto: usa los [Embeddings de IA](/es/products/ai.embeddings/). ## Conceptos **Rutas, no modelos.** Nunca nombras un modelo. A tu organización se le asignan **rutas**: ids opacos como `r00`, configurados por Inovacc solo para tu organización. Una ruta decide qué modelo responde, su techo de salida y los campos que acepta. `GET /v1/models` lista las rutas que tu organización puede usar, cada una con el `endpoint` al que sirve; el `id` es siempre el id de la ruta, nunca el nombre de lo que la respalda. Envía el id como el campo `model`; envía `"auto"`, o deja fuera `model`, y responde la ruta predeterminada de tu organización. Una ruta sirve exactamente un endpoint: una ruta de chat invocada desde `/v1/embeddings` o `/v1/run` se rechaza igual que una ruta que no tienes permitida. **Campos.** Los campos comunes de OpenAI siempre se aceptan: `messages`, `model`, `stream`, `stream_options`, `max_tokens`, `max_completion_tokens`, `temperature`, `top_p`, `stop`, las penalizaciones, `logit_bias`, `logprobs`, `tools`, `tool_choice`, `response_format`, `seed`, `reasoning_effort` y algunos más. Siete campos (`n`, `service_tier`, `store`, `metadata`, `modalities`, `audio`, `web_search_options`) se aceptan solo cuando se han concedido a tu organización. Cualquier otro campo se rechaza por su nombre. Algunas rutas aceptan un conjunto más reducido (`messages`, `model`, `stream`, `stream_options`, los dos límites de salida, `temperature`, `top_p`); en ellas, `tools` y los demás campos se rechazan para esa ruta. **Mensajes e imágenes.** `content` es una cadena, o un arreglo de partes de texto y partes `image_url`. Una imagen es una URL `data:` (PNG, JPEG, WebP o GIF, en base64) o una URL `https://`; una ruta cuyo modelo no puede obtener una URL rechaza la forma `https://`. Una solicitud lleva como máximo 20 imágenes, y cada una cuenta como 1,600 tokens de entrada estimados. **Techo de salida.** `max_tokens` y `max_completion_tokens` solo pueden bajar el techo que fijan la ruta y tu organización; un valor mayor se recorta, nunca se rechaza. Los tokens de razonamiento que gasta un modelo están dentro de `completion_tokens`. **Streaming.** Con `"stream": true` la respuesta es un `text/event-stream` de eventos `chat.completion.chunk` de OpenAI, cada uno una línea `data:`, que termina con `data: [DONE]`. Configura `stream_options.include_usage` para recibir un fragmento final con los conteos de tokens. **Encabezados de uso.** Una respuesta sin streaming lleva `x-route-id`, `x-usage-input-tokens` y `x-usage-output-tokens`, y toda respuesta lleva `x-operation-id`. ## Cómo funciona Toda llamada lleva `Authorization: Bearer ` y un `X-Operation-Id`. Tu organización se obtiene de la clave. El id de operación es tu clave de idempotencia: de 8 a 128 letras, dígitos, `_` o `-`. El servicio verifica, en orden: el id de operación, la clave, si ese id de operación ya se usó, el cuerpo y sus campos, la ruta, los mensajes, el tamaño de entrada estimado (una cuarta parte de los caracteres de todo el cuerpo de la solicitud, más la estimación de imágenes) y luego reserva la llamada contra las cuotas de tu organización. Solo cuando todo eso se cumple se llama a un modelo. El `id` de la respuesta es siempre `chatcmpl-` seguido de tu id de operación, y su `model` es siempre el id de la ruta. Una respuesta cuyo presupuesto de salida completo se fue en razonamiento sigue siendo `200`, con `content` vacío y `finish_reason: "length"`; una respuesta detenida por el filtro de seguridad del modelo es `200` con `finish_reason: "content_filter"`. Una respuesta exitosa sin streaming se almacena durante 24 horas bajo su id de operación. Si envías de nuevo el mismo id, recibes la respuesta almacenada, con `x-idempotent-replay: true`, sin una segunda llamada al modelo, un segundo cobro ni un cambio de cuota. Un error nunca se almacena, así que reintentar una llamada fallida con el mismo id la ejecuta de nuevo. Mientras una llamada sigue en ejecución, una segunda llamada con su id recibe `409 operation_in_progress`. Un stream nunca se almacena. Si el modelo falla a mitad de camino, el stream termina con un evento de error (`upstream_error`) y `data: [DONE]`; un stream que termina sin `[DONE]` se cortó y está incompleto. Lee `delta.content` en cada fragmento, incluidos el primero y el que lleva `finish_reason`. Lo que se mide son tokens: de entrada y de salida, según los reporta el modelo, o la estimación cuando no hay un conteo disponible. `GET /v1/usage/summary` suma el uso de tu organización por clave, ruta, día u hora, y `GET /v1/capabilities` le dice a un agente lo que tu organización puede invocar; ninguno se cobra. ## Primeros pasos Necesitas una clave de API y al menos una ruta de chat habilitada para tu organización. Las claves se crean en la consola de Inovacc; consulta [Autenticación](/es/guides/authentication/). 1. **Lista tus rutas.** Llama a `GET /v1/models`. Cada entrada con `"endpoint": "/v1/chat/completions"` es una ruta con la que puedes chatear; anota su `id`. 2. **Envía un primer mensaje.** Llama a `POST /v1/chat/completions` con ese id como `model`, un mensaje `user` y un `X-Operation-Id` nuevo (consulta [los ejemplos](#example)). El `id` de la respuesta termina con tu id de operación y `x-usage-input-tokens` muestra lo que se contó. 3. **Reinténtalo.** Envía la misma solicitud con el mismo id de operación. La respuesta es idéntica y lleva `x-idempotent-replay: true`: nada se ejecutó dos veces. 4. **Usa streaming.** Agrega `"stream": true` y `"stream_options": {"include_usage": true}` con un id de operación nuevo. Los fragmentos llegan mientras se escribe la respuesta, y el último antes de `[DONE]` lleva `usage`. 5. **Revisa el gasto.** Llama a `GET /v1/usage/summary?group_by=route` para ver las llamadas y los tokens que acabas de generar. ## Casos de uso **Un asistente de soporte.** Tu widget de ayuda transmite la respuesta por streaming para que la persona la vea aparecer de inmediato. Cada turno del usuario es una llamada con un id de operación nuevo; tu servidor conserva la conversación y envía los mensajes recientes cada vez. Si la conexión se cae antes de `[DONE]`, el widget muestra la respuesta como cortada y ofrece preguntar de nuevo. **Extracción estructurada en lote.** Un trabajo nocturno lee documentos entrantes y pide un objeto JSON con `response_format`. Cada documento recibe un id de operación derivado de su propio id, de modo que cuando el trabajo se reinicia tras una caída, los documentos terminados regresan desde las respuestas almacenadas en segundos y no se cobran de nuevo. **Un agente que llama a tus herramientas.** En una ruta que acepta `tools`, el modelo responde con `tool_calls`; tu código ejecuta la herramienta y envía el resultado de vuelta como un mensaje `tool`. Cada paso tiene su propio id de operación, así que un paso reintentado nunca ejecuta una herramienta dos veces del lado del modelo. ## Límites y precios | Límite | Valor | |---|---| | Cuerpo de la solicitud | 1 MiB por defecto; tu organización puede configurarse entre 1 KiB y 20 MiB | | Imágenes por solicitud | 20 | | Costo de entrada estimado de una imagen | 1,600 tokens | | Tokens de entrada por solicitud | el tope de tu organización (`400 input_too_large` por encima de él) | | Tokens de salida por solicitud | el menor entre el techo de la ruta y el de tu organización | | Tiempo hasta la primera respuesta del modelo | 60 segundos | | Respuesta almacenada para una repetición | 24 horas | | Una llamada mantenida como en curso | 5 minutos como máximo | | Solicitudes por minuto y por día; tokens y costo por día y por mes; llamadas concurrentes; presupuestos diarios | según lo configurado para tu organización | **Precios:** a consultar. La unidad de precio son los **tokens**. ## Errores | Estado | Código | Qué significa y qué hacer | |---|---|---| | 400 | `missing_operation_id`, `invalid_operation_id` | Envía `X-Operation-Id` con 8 a 128 letras, dígitos, `_` o `-`. | | 400 | `invalid_body` | El cuerpo no es un objeto JSON, o `user` no es una cadena. Corrige el cuerpo. | | 400 | `invalid_messages` | `messages` está vacío o un mensaje está mal formado. | | 400 | `unsupported_field` | Un campo de nivel superior que este endpoint no acepta; quítalo. | | 400 | `unsupported_field_for_route` | La ruta acepta un conjunto más reducido de campos, o no puede obtener una URL de imagen; quita el campo o envía la imagen como `data:`. | | 400 | `model_not_allowed` | El valor de `model` no es un id de ruta válido. Usa un id de `GET /v1/models`. | | 400 | `input_too_large` | La entrada supera el tope por solicitud de tu organización, o hay más de 20 imágenes. Acórtala. | | 401 | `missing_credentials`, `invalid_credentials` | Envía una clave válida. | | 403 | `key_disabled`, `organization_disabled` | La clave o la organización fue deshabilitada; contacta a tu administrador. | | 403 | `field_not_allowed` | Un campo que no se ha concedido a tu organización. | | 403 | `route_forbidden` | La ruta no es una que tu organización pueda usar en este endpoint. | | 409 | `operation_in_progress` | Ese id de operación sigue en ejecución; espera y reintenta. | | 413 | `request_too_large` | El cuerpo supera tu tope de tamaño. | | 429 | `rate_limited` | Demasiadas solicitudes; espera `Retry-After` segundos. | | 429 | `quota_exceeded`, `concurrency_limited` | Se agotó una asignación de tokens, costo o concurrencia; sin `Retry-After`. | | 429 | `budget_exhausted` | Se gastó un presupuesto diario; se restablece a las 00:00 UTC (`Retry-After`). | | 502 | `upstream_error` | El modelo no respondió de forma utilizable; reintenta con el mismo id de operación. | | 503 | `service_unavailable`, `route_unavailable` | Reintenta más tarde; `route_unavailable` requiere que Inovacc corrija tu ruta. | | 504 | `timeout_error` | El modelo no empezó a responder en 60 segundos; reintenta. | El envoltorio y cada tipo están en [Errores](/es/guides/errors/). ## Buenas prácticas - **Un id de operación por solicitud lógica**, reutilizado solo para reintentar esa misma solicitud. Un id reutilizado devuelve la primera respuesta sin importar lo que diga el cuerpo nuevo. - **Reintenta en `502`, `503`, `504` y en errores de red con el mismo id de operación**, con espera creciente. En `429 rate_limited` y `budget_exhausted`, espera los segundos de `Retry-After`. - **Configura `max_tokens`** con lo que necesitas: reduce la reserva hecha contra tus cuotas y el costo de una respuesta descontrolada. - **Vigila `X-Budget-Warning`**: aparece cuando tu organización supera el umbral de aviso de un presupuesto, antes de que se rechacen las llamadas. - **Usa `stream` para personas, no para trabajos**: una respuesta con streaming no se puede repetir, una sin streaming sí. - **Envía las imágenes como URL `data:`** cuando no sepas si el modelo de la ruta puede obtener una URL, y mantenlas dentro del límite de 20 imágenes. - **Nunca envíes una clave secreta a un navegador**: llama al chat con IA desde tu servidor. ## Relacionados - [Autenticación](/es/guides/authentication/), [Errores](/es/guides/errors/), [Límites](/es/guides/limits/) - [Motor de decisiones](/es/products/decision/): veredictos estructurados con la misma clave - [Conocimiento y búsqueda vectorial](/es/products/knowledge/): respuestas de tus documentos, con citas - [Embeddings de IA](/es/products/ai.embeddings/) --- Producto: Embeddings de IA URL: https://developer.inovacc.dev/es/products/ai.embeddings/ URL base: https://ai.inovacc.dev Convierte texto en vectores para búsqueda y similitud. Endpoints: - POST /v1/embeddings Ejemplo: ```sh curl -X POST "https://ai.inovacc.dev/v1/embeddings" -H "Authorization: Bearer $INOVACC_API_KEY" -H "X-Operation-Id: $(uuidgen)" -H "Content-Type: application/json" -d '{}' ``` ## Descripción general Los embeddings de IA convierten texto en vectores: listas de números cuya distancia entre sí sigue el significado del texto. Dos pasajes sobre lo mismo quedan cerca aunque no compartan palabras. El endpoint, `POST /v1/embeddings`, usa el formato de red de embeddings de OpenAI, por lo que un cliente de OpenAI existente puede llamarlo con la URL base `https://ai.inovacc.dev/v1` y dos encabezados adicionales. El problema que resuelve es la comparación semántica dentro de tus propios sistemas. Con vectores puedes buscar por significado, agrupar elementos similares, encontrar casi duplicados o dirigir un mensaje a la categoría más cercana, con la base de datos o el índice que ya uses. Usa los embeddings de IA cuando **tú** guardas los vectores. Si prefieres que Inovacc los guarde, los busque y devuelva los pasajes coincidentes, usa la base de datos vectorial administrada de [Conocimiento y búsqueda vectorial](/es/products/knowledge/), que genera los embeddings de tus documentos por ti. Cuando necesites texto generado en lugar de vectores, usa el [Chat con IA](/es/products/ai.chat/). ## Conceptos **Rutas.** Igual que con el chat, nunca nombras un modelo. A tu organización se le asignan rutas de embeddings, ids opacos configurados por Inovacc. `GET /v1/models` las lista: una entrada cuyo `endpoint` es `/v1/embeddings` es una ruta de embeddings. Envía su id como `model`, o envía `"auto"` (o ningún `model`) para usar la ruta predeterminada de tu organización. Una ruta sirve un solo endpoint; una ruta de chat invocada aquí se rechaza. **Entrada.** `input` es una cadena no vacía, o un arreglo de cadenas; dentro de un arreglo se acepta una cadena vacía. La respuesta contiene un vector por entrada, en `data[]`, cada uno con el `index` de la entrada a la que pertenece. **Los vectores de una ruta van juntos.** Un vector solo es comparable con vectores producidos por la misma ruta. Guarda el id de la ruta junto a cada vector que conserves, y genera los embeddings de tus documentos y de tus consultas con la misma ruta. **Campos opcionales.** `encoding_format` y `dimensions` se pasan al modelo cuando el modelo de la ruta los admite, y se ignoran en caso contrario. `user` se acepta como cadena y, antes de salir de Inovacc, se reemplaza por un valor derivado de tu organización, de modo que dos organizaciones que envían el mismo valor nunca colisionan. **Uso.** El `usage` de la respuesta contiene `prompt_tokens` y `total_tokens`. Este endpoint no lleva encabezados `x-usage-*`; la ruta que respondió está en `x-route-id`. ## Cómo funciona Toda llamada lleva `Authorization: Bearer ` y un `X-Operation-Id`. Tu organización se obtiene de la clave. El servicio verifica el id de operación y la clave, luego si ese id de operación ya se usó, y después el cuerpo: solo se aceptan `model`, `input`, `encoding_format`, `dimensions` y `user`, y cualquier otro campo se rechaza por su nombre. Resuelve la ruta, verifica que la ruta sirva embeddings, valida `input`, estima el tamaño de la entrada (una cuarta parte de los caracteres de cada cadena de entrada) frente al tope por solicitud de tu organización, y reserva la llamada contra las cuotas de tu organización. Entonces se llama al modelo. La respuesta es `{"object":"list","data":[{"object":"embedding","index":0,"embedding":[...]}],"model":"","usage":{...}}`. `model` es siempre el id de la ruta. No hay streaming. Una respuesta exitosa se almacena durante 24 horas bajo su id de operación: el mismo id enviado de nuevo devuelve los mismos vectores con `x-idempotent-replay: true`, sin una segunda llamada ni cobro. Un error nunca se almacena, así que una llamada fallida puede reintentarse con el mismo id. Lo que se mide son los tokens de entrada. ## Primeros pasos Necesitas una clave de API y una ruta de embeddings habilitada para tu organización ([Autenticación](/es/guides/authentication/)). 1. **Encuentra tu ruta de embeddings.** Llama a `GET /v1/models` y elige una entrada cuyo `endpoint` sea `/v1/embeddings`. 2. **Genera los embeddings de dos pasajes.** Llama a `POST /v1/embeddings` con esa ruta como `model`, un arreglo `input` de dos cadenas y un `X-Operation-Id` nuevo (consulta [los ejemplos](#example)). La respuesta tiene dos entradas en `data`, con `index` 0 y 1, y `model` es el id de tu ruta. 3. **Compáralos.** Calcula la similitud coseno de los dos vectores en tu código. Genera el embedding de un tercer pasaje sobre otro tema y compara de nuevo: su puntuación frente al primero es menor. 4. **Repite.** Envía de nuevo el paso 2 con el mismo id de operación; los vectores son idénticos y la respuesta lleva `x-idempotent-replay: true`. ## Casos de uso **Búsqueda en tu propia base de datos.** Un catálogo de productos guarda un vector por cada descripción de producto en la base de datos que ya usa. La pregunta de un comprador se convierte en embedding con la misma ruta al momento de la consulta, y se muestran los productos más cercanos, incluidos aquellos cuya descripción usa otras palabras. **Detección de casi duplicados.** Un sistema de tickets genera el embedding de cada ticket nuevo y lo compara con los abiertos; una puntuación por encima de un umbral que tú calibras vincula el ticket con el caso existente en lugar de abrir un segundo caso. **Enrutamiento por similitud.** Un conjunto pequeño de mensajes de ejemplo por equipo se convierte en embeddings una sola vez. De cada mensaje entrante se genera el embedding y se envía al equipo cuyos ejemplos estén más cerca, sin ninguna llamada a un modelo por mensaje aparte del embedding. ## Límites y precios | Límite | Valor | |---|---| | Cuerpo de la solicitud | 1 MiB por defecto; tu organización puede configurarse entre 1 KiB y 20 MiB | | Tokens de entrada por solicitud | el tope de tu organización (`400 input_too_large` por encima de él) | | Tiempo hasta la primera respuesta del modelo | 60 segundos | | Respuesta almacenada para una repetición | 24 horas | | Solicitudes, tokens, costo, concurrencia, presupuestos diarios | según lo configurado para tu organización | **Precios:** a consultar. La unidad de precio son los **tokens**. ## Errores | Estado | Código | Qué significa y qué hacer | |---|---|---| | 400 | `missing_operation_id`, `invalid_operation_id` | Envía un `X-Operation-Id` válido. | | 400 | `unsupported_field` | Un campo distinto de `model`, `input`, `encoding_format`, `dimensions`, `user`; quítalo. | | 400 | `invalid_body` | JSON mal formado, `input` con una forma incorrecta, o un `user` que no es cadena. | | 400 | `model_not_allowed` | `model` no es un id de ruta válido. | | 400 | `input_too_large` | La entrada supera tu tope por solicitud; divídela en varias llamadas. | | 401 | `missing_credentials`, `invalid_credentials` | Envía una clave válida. | | 403 | `route_forbidden` | La ruta no es tuya, está deshabilitada o no sirve embeddings. | | 403 | `key_disabled`, `organization_disabled` | Revisa la clave. | | 409 | `operation_in_progress` | Ese id de operación sigue en ejecución; espera y reintenta. | | 413 | `request_too_large` | El cuerpo supera tu tope de tamaño. | | 429 | `rate_limited`, `budget_exhausted` | Espera los segundos de `Retry-After`. | | 429 | `quota_exceeded`, `concurrency_limited` | Se agotó una asignación; sin `Retry-After`. | | 502, 503, 504 | `upstream_error`, `service_unavailable`, `timeout_error` | Reintenta con el mismo id de operación y espera creciente. | Consulta [Errores](/es/guides/errors/) para el envoltorio. ## Buenas prácticas - **Agrupa las entradas en lotes**: envía muchos pasajes en un solo arreglo `input`, dentro de tus topes de tamaño y de tokens, en lugar de una llamada por pasaje. - **Conserva el id de la ruta junto con el vector**, y vuelve a generar todos los embeddings si cambias de ruta: los vectores de dos rutas no son comparables. - **Divide los documentos largos en fragmentos** de unos pocos párrafos antes de generar los embeddings; un solo vector para un documento completo difumina su significado. - **Deriva el id de operación del contenido** (por ejemplo, un hash del lote) para que un trabajo reiniciado repita la respuesta en lugar de volver a pagar. - **Reintenta `502`, `503` y `504` con el mismo id de operación**; espera `Retry-After` en `429 rate_limited`. - **Calibra los umbrales con tus propios datos**: las puntuaciones de similitud son relativas, no probabilidades. ## Relacionados - [Conocimiento y búsqueda vectorial](/es/products/knowledge/): la base de datos vectorial administrada que genera los embeddings y busca por ti - [Chat con IA](/es/products/ai.chat/) - [Autenticación](/es/guides/authentication/), [Errores](/es/guides/errors/), [Límites](/es/guides/limits/) --- Producto: Conocimiento y búsqueda vectorial URL: https://developer.inovacc.dev/es/products/knowledge/ URL base: https://ai.inovacc.dev Ingesta documentos, arma colecciones de conocimiento y consúltalas por significado. Endpoints: - POST /v1/vector/ingest - POST /v1/vector/query - POST /v1/collections - POST /v1/collections/{id}/query - POST /v1/knowledge/jobs Ejemplo: ```sh curl -X POST "https://ai.inovacc.dev/v1/vector/ingest" -H "Authorization: Bearer $INOVACC_API_KEY" -H "X-Operation-Id: $(uuidgen)" -H "Content-Type: application/json" -d '{}' ``` ## Descripción general Conocimiento y búsqueda vectorial mantiene el conocimiento de tu organización donde una aplicación puede hacerle preguntas por significado. Tiene tres partes, todas en `https://ai.inovacc.dev`: - la **Vector Database** (base de datos vectorial): un almacén administrado de pasajes de texto que ingestas y buscas, con filtros, ámbitos y reordenamiento opcional; - las **colecciones de conocimiento**: envía un lote de archivos mixtos (documentos, imágenes, video) y haz que se procesen en paquetes de conocimiento; luego descarga el resultado, consúltalo, chatea con él con citas, o haz que se redacte un informe sobre él; - los **trabajos de conocimiento**: convierten una grabación larga en una base de conocimiento que descargas como un solo archivo comprimido. El problema que resuelve es todo lo que hay entre "tenemos archivos" y "el asistente puede responder a partir de ellos": extraer texto, cortarlo en pasajes, generar embeddings, indexar, mantener el índice consistente y citar la fuente de cada respuesta. Inovacc hace esos pasos y tú llamas a un puñado de endpoints. Úsalo cuando las respuestas deban salir de tu propio material. Si ya ejecutas tu propio índice vectorial, los [Embeddings de IA](/es/products/ai.embeddings/) te dan solo los vectores. Cuando la respuesta es texto libre sin fuentes, usa el [Chat con IA](/es/products/ai.chat/). Inovacc habilita cada parte para tu organización; una parte que no está habilitada responde `403` (`vector_database_disabled`, `collections_disabled` o `knowledge_disabled`). ## Conceptos **Vector Database y su perfil.** Tu organización crea una base de datos con `POST /v1/vector/databases`, eligiendo un **perfil de embeddings** de `GET /v1/vector/profiles`. Un perfil indica la modalidad (`text`), los idiomas (`english` o `multilingual`), las dimensiones y la métrica de distancia; los ids se ven como `text-multilingual-1024`. Eliges un perfil, nunca un modelo. El perfil no puede cambiar: para usar otro, elimina la base de datos, crea una nueva e ingesta de nuevo. **Documentos, ámbitos y modos.** Ingestas pasajes con tu propio `id`, su `text` y `metadata` plana opcional. Un pasaje va a un **ámbito**: `shared` (todos los servicios del espacio de trabajo de tu organización) o `local` (solo la clave de API que lo escribió). Las consultas toman un **modo**: `shared`, `local`, o `hybrid` para buscar en ambos y combinar por puntuación. Se puede filtrar por seis claves de metadatos: `kind`, `source`, `category`, `tag`, `language`, `created_at`. Los fragmentos que comparten un `document_id` forman un documento con una `generation` (tu número de versión), lo que permite eliminar o reemplazar un documento completo de una vez. **Colecciones y su modo.** Una colección se crea en uno de tres modos, fijos durante toda su vida: `download` (procesa archivos, descarga los paquetes y luego no queda nada), `cloud` (Inovacc conserva el contenido procesado y sus vectores para que puedas ejecutar `query`, `chat` y un `analysis`) o `local_vectors` (Inovacc conserva solo vectores, nunca texto: tú haces `embed` de tus propios fragmentos y `query` devuelve ids de fragmentos y puntuaciones). Las colecciones se conservan 30 días por defecto; una organización configurada en retención `zero` las tiene eliminadas una hora después de que empieza la descarga. **Elementos.** Un archivo se une a una colección en la solicitud (hasta 16 MiB), desde una carga completada (video de más de 16 MiB), por URL pública, o por una URL firmada sellada a una clave de un solo uso para que nunca se almacene. Cada elemento pasa por `queued`, `processing`, y luego `done` o `failed`; un elemento fallido no detiene a los demás. **Consistencia eventual y `searchable`.** El índice vectorial aplica las escrituras de forma asíncrona: un vector se puede leer unos segundos, a veces minutos, después de haberse escrito. `GET /v1/vector/databases` y toda respuesta de colección reportan `searchable`; una consulta enviada mientras es `false` puede omitir los pasajes más recientes. El `indexing.complete` de una colección `cloud` es `true` solo cuando cada fragmento está escrito **y** se puede leer. **Trabajos, potencia y presupuesto.** Un trabajo de conocimiento toma una carga completada de una grabación, una transcripción y notas opcionales, un `power` (`light`, `medium` o `max`) y un `max_price_usd` obligatorio. Un trabajo que supera el tope de tu organización se rechaza antes de cualquier trabajo de pago. ## Cómo funciona Toda llamada lleva `Authorization: Bearer `. Tu organización se obtiene de la clave. `X-Operation-Id` es obligatorio en las llamadas `POST` de colecciones, cargas y trabajos y las hace idempotentes: un reintento con el mismo id devuelve la primera respuesta (`X-Idempotent-Replay: true`) y no agrega nada; el mismo id con un archivo distinto es `409 operation_id_reused`. En la Vector Database es opcional, porque la ingesta y la eliminación son idempotentes por construcción: ingestar un `id` existente lo reemplaza. Una **consulta** (`POST /v1/vector/query`) toma `mode`, `query` (de 1 a 8,000 caracteres), `top_k` (de 1 a 50) y un `filter` opcional; devuelve las coincidencias de mejor a peor, cada una con `id`, `score`, `text` y `metadata`. Con `"retrieval": "vector_rerank"` y un `rerank_profile`, una lista de candidatos más amplia es reordenada por una etapa de reordenamiento; el objeto `retrieval` de la respuesta indica si el reordenamiento se aplicó o si se recurrió al orden vectorial. Una **colección** funciona en pasos que puedes observar: créala, agrega elementos (`202`, `queued`), léela hasta que `status` sea `complete`, y luego descarga, consulta o chatea. Leer una colección `cloud` también avanza su indexación, una cantidad acotada por llamada, y cada respuesta lleva el progreso de `indexing`. `chat` responde solo a partir de las fuentes recuperadas, numera cada cita con el elemento, el paquete, el fragmento y el localizador, y dice que no puede responder cuando las fuentes no cubren la pregunta. Un `analysis` se construye a lo largo de varias solicitudes: repite `POST` o `GET` cada pocos segundos hasta que responda `200` con el informe. Un **trabajo** es asíncrono: `POST /v1/knowledge/jobs` responde `202` con `job_id`, `status_url` y `poll_after_seconds` (también como `Retry-After`). Lee el estado cuando se te indique; la plataforma pronostica el tiempo hasta terminar e incluye `eta_seconds` mientras puede. Cuando `state` es `done`, descarga el archivo comprimido; se conserva 24 horas. Lo que se mide: tokens de embeddings y vectores escritos por ingesta e indexación, consultas, reordenamientos como su propia unidad, llamadas de chat en tu ruta de chat, y trabajos por su precio. `POST /v1/knowledge/estimates` calcula el precio de una grabación antes de que la cargues, sin costo. ## Primeros pasos Necesitas una clave de API y la parte que quieras habilitada para tu organización ([Autenticación](/es/guides/authentication/)). 1. **Crea la base de datos.** `GET /v1/vector/profiles`, elige un perfil y luego `POST /v1/vector/databases` con `{"profile":""}`. La respuesta es `201` con la base de datos. 2. **Ingesta pasajes.** `POST /v1/vector/ingest` con `"scope":"shared"` y dos o tres documentos (consulta [los ejemplos](#example)). La respuesta indica cuántos se `ingested` y lista los `skipped` con un motivo. 3. **Espera a `searchable`.** `GET /v1/vector/databases` hasta que `searchable` sea `true`. 4. **Consulta.** `POST /v1/vector/query` con `"mode":"shared"` y una pregunta redactada de forma distinta a tus pasajes. El pasaje más cercano aparece primero, con su `score`. 5. **Prueba una colección.** `POST /v1/collections` con `{"mode":"cloud"}`, agrega un PDF a `/items`, lee la colección hasta que `indexing.complete` sea `true`, y luego `POST /v1/collections/{id}/chat` y revisa las `citations`. ## Casos de uso **Un asistente basado en tu manual.** Tu intranet ingesta cada página de políticas como fragmentos bajo su `document_id`, con metadatos de `category`. El asistente consulta con un filtro de `category` y pasa los mejores pasajes a su propio prompt. Cuando una página cambia, se ingesta de nuevo con una `generation` mayor, y los fragmentos que la nueva versión ya no tiene dejan de aparecer de inmediato. **Preguntas sobre los archivos entregados por un cliente.** Un consultor crea una colección `cloud` por proyecto, agrega los contratos y las hojas de cálculo que envió el cliente, y hace preguntas mediante `chat`. Cada respuesta cita el elemento y la página en que se apoya, de modo que cada afirmación se puede verificar antes de pasar a un informe; `analysis` produce un resumen, entidades y contradicciones entre los archivos. **De grabaciones de reuniones a una base de conocimiento.** Después de cada reunión grabada, la aplicación estima el precio, carga el video en partes de 64 MiB con la propia transcripción de la llamada como referencia, e inicia un trabajo con `max_price_usd`. Cuando el trabajo está `done`, el archivo comprimido (audio, fotogramas clave y la base de conocimiento) se archiva junto con la reunión. ## Límites y precios | Límite | Valor | |---|---| | Cuerpo de solicitud de la Vector Database | 1 MiB | | Documentos por ingesta, ids por eliminación | 100 | | Un documento (`id` + `text` + `metadata`) | 10 KiB | | Texto de consulta; `top_k` | 8,000 caracteres; 50 | | Valores de `$in` en un filtro | 20 | | Elemento en el cuerpo de una solicitud, u obtenido por URL | 16 MiB | | Elementos por colección | 100; 1 GiB de archivos enviados en solicitudes; 20 GiB de video por carga | | `top_k` de consulta de colección; mensajes de chat | 20; de 1 a 20 mensajes de hasta 8,000 caracteres | | Fragmentos por llamada `embed` | 100, cada uno de hasta 8,000 caracteres | | Llamadas al modelo de análisis por informe | 32 | | Tamaño de las partes de una carga | 64 MiB (todas las partes menos la última) | | Carga que no es video (transcripción, notas) | 16 MiB | | Carga de grabación | el tope de tu organización | | Claves de obtención sin usar | 16, cada una válida una vez durante 60 segundos | | Resultado del trabajo conservado | 24 horas | | Retención de colecciones | 30 días por defecto | **Precios:** a consultar. Una grabación procesada por un trabajo se cobra en **minutos de video**, y `POST /v1/knowledge/estimates` devuelve su precio línea por línea antes de que cargues cualquier cosa. El chat sobre una colección se mide como el [Chat con IA](/es/products/ai.chat/) en tu ruta de chat. ## Errores | Estado | Código | Qué significa y qué hacer | |---|---|---| | 400 | `invalid_body`, `unsupported_field` | Un campo o valor que la ruta no acepta; revisa los campos de la ruta. | | 400 | `invalid_profile` | Elige un perfil listado por `GET /v1/vector/profiles`. | | 400 | `confirmation_required` | Eliminar la base de datos requiere `{"confirm":"delete-database-and-all-vectors"}`. | | 400 | `invalid_url`, `signed_url_must_be_sealed` | Solo URL `https` públicas en el puerto 443; una URL firmada debe estar sellada. | | 400 | `fetch_key_invalid`, `invalid_sealed_url` | Solicita una nueva clave de obtención y sella de nuevo. | | 403 | `vector_database_disabled`, `collections_disabled`, `knowledge_disabled` | Esa parte no está habilitada para tu organización. | | 403 | `budget_exceeded` | El `max_price_usd` del trabajo supera el tope de tu organización. | | 404 | `collection_not_found`, `job_not_found`, `upload_not_found`, `analysis_not_found` | Revisa el id; la descarga de un trabajo también es `404` hasta que está `done`. | | 409 | `vector_database_not_created`, `vector_database_exists`, `vector_database_deleting` | Crea primero la base de datos; una por organización; espera a que termine una eliminación. | | 409 | `mode_not_supported` | La operación no existe en el modo de esta colección. | | 409 | `collection_incomplete`, `collection_full` | Espera a que terminen los elementos; la colección está en su límite. | | 409 | `upload_incomplete`, `operation_id_reused`, `operation_in_progress` | Termina la carga; usa un nuevo id de operación para un archivo distinto; espera. | | 410 | `collection_expired`, `result_expired` | La retención terminó; procesa de nuevo. | | 413 | `request_too_large`, `url_too_large` | Usa `POST /v1/uploads` para archivos grandes. | | 415, 422 | `unsupported_media_type`, `input_rejected`, `archive_rejected` | El tipo de archivo no se lee, o el archivo fue rechazado (`reason` indica por qué). | | 429 | `too_many_fetch_keys` y los códigos de cuota | Usa o deja vencer las claves que tienes; consulta [Errores](/es/guides/errors/). | | 502, 504 | `url_fetch_failed`, `url_fetch_timeout`, `upstream_error`, `timeout_error` | Falló la URL o el procesamiento; reintenta. | | 503 | `service_unavailable` | Reintenta más tarde; no se adivinó nada. | ## Buenas prácticas - **Agrupa los fragmentos bajo un `document_id` y aumenta `generation`** cuando cambie la fuente; la eliminación y el reemplazo funcionan entonces por documento. - **Envía `content_sha256` con cada fragmento** y compáralo con `GET /v1/vector/documents` para resincronizar tu copia sin volver a ingestar todo. - **Revisa `searchable` (o `indexing.complete`) antes de confiar en los pasajes más recientes.** - **Mantén la respuesta de un pasaje dentro de sus primeros 2,000 caracteres** cuando uses reordenamiento; el reordenador solo lee esa cantidad. - **Compara `vector_rerank` con `vector` simple en tus propios datos**, sobre todo fuera del inglés, antes de depender de él. - **Estima siempre antes de un trabajo y configura `max_price_usd`**; consulta el estado en `poll_after_seconds`, no más rápido. - **Sella las URL firmadas**; nunca envíes una URL que lleve un inicio de sesión. Carga los bytes en su lugar. - **Revisa las citas**: un modelo puede citar una fuente real para algo que esa fuente no dice. ## Relacionados - [Embeddings de IA](/es/products/ai.embeddings/), [Chat con IA](/es/products/ai.chat/) - [Archivos](/es/products/files/): almacena los archivos fuente en sí - [Autenticación](/es/guides/authentication/), [Errores](/es/guides/errors/), [Límites](/es/guides/limits/) --- Producto: Motor de decisión URL: https://developer.inovacc.dev/es/products/decision/ URL base: https://ai.inovacc.dev Decisiones estructuradas por criterios, en un nivel de velocidad y precisión que tú eliges. Endpoints: - POST /v1/run Ejemplo: ```sh curl -X POST "https://ai.inovacc.dev/v1/run" -H "Authorization: Bearer $INOVACC_API_KEY" -H "X-Operation-Id: $(uuidgen)" -H "Content-Type: application/json" -d '{}' ``` ## Descripción general El motor de decisiones responde preguntas estructuradas sobre un contenido con veredictos estructurados. Envías un **estado** (aquello que se va a juzgar: un texto, un registro, un objeto JSON) y un conjunto de **preguntas**, cada una de un tipo conocido; recibes una respuesta por pregunta, con la etiqueta o puntuación elegida, las probabilidades detrás de ella y una confianza. El endpoint es `POST /v1/run` en `https://ai.inovacc.dev`, invocado en una ruta de decisión de tu organización. El problema que resuelve es convertir un juicio en datos sobre los que un programa pueda actuar. Preguntarle a un modelo de chat "¿esto es bueno?" devuelve prosa que luego tienes que analizar y que no puedes comparar de una llamada a otra. El motor de decisiones devuelve siempre la misma forma, de modo que puedes almacenarla, aplicarle umbrales y auditarla. Úsalo para clasificación, puntuación, triaje y verificaciones frente a criterios: si este ticket necesita a una persona, cuál de cinco categorías encaja, qué tan bien cumple esta respuesta con el estándar. Cuando necesites prosa generada, usa el [Chat con IA](/es/products/ai.chat/); cuando el veredicto deba apoyarse en tus propios documentos, recupera primero los pasajes con [Conocimiento y búsqueda vectorial](/es/products/knowledge/) y colócalos en el estado. ## Conceptos **Rutas de decisión.** Como en toda la IA de Inovacc, llamas a una ruta, nunca a un modelo. `GET /v1/models` lista tus rutas; una entrada cuyo `endpoint` es `/v1/run` es una ruta de ejecución, y las que Inovacc configuró como rutas de decisión responden con la forma que se describe más abajo. **Preguntas y sus tipos.** `questions` es un objeto de 1 a 64 entradas, con claves que son tus propios ids (letras, dígitos, `_`, `.` o `-`, hasta 100 caracteres). Cada pregunta tiene un `type`, normalmente `instructions`, y `criteria` cuya forma queda fijada por el tipo: | Tipo | Pide | `criteria` | |---|---|---| | `noul` | sí o no | un objeto `{"true": "...", "false": "..."}` | | `choice` | una etiqueta entre varias | un objeto `{"label": "description", ...}` | | `score` | un nivel en una escala | un arreglo de niveles, del más bajo al más alto | Las formas son estrictas: una cadena de criterios como `"1-5"` o `"yes|no"` se rechaza. **Respuestas.** Cada respuesta lleva lo que produjo su tipo: el `choice` o el `score` con `probabilities`, una `legend` y una `confidence`. Una respuesta `noul` lleva su probabilidad `p` en lugar de una confianza. **Niveles.** Una ruta de decisión sirve uno de tres niveles: `fast`, `standard` o `precise`. Todos responden con una sola forma, y el `engine.tier` de la respuesta indica cuál respondió. **La confianza no es comparable entre niveles**: cada nivel reporta en su propia escala, así que configura un umbral por nivel. **Escalamiento.** Con `escalate`, las preguntas respondidas por debajo de un umbral de confianza se vuelven a hacer, solas, en el siguiente nivel más fuerte (`fast`, luego `standard`, luego `precise`), y la respuesta más fuerte reemplaza a la más débil. Los valores predeterminados son `fast` 0.40, `standard` 0.80 y `precise` 0.55; puedes pasar `min_confidence` (un número, o uno por nivel) y `max_tier`. La confianza de una respuesta `noul` es la distancia de `p` a un lanzamiento de moneda, `|2p - 1|`. ## Cómo funciona Toda llamada lleva `Authorization: Bearer ` y un `X-Operation-Id`. Tu organización se obtiene de la clave. El cuerpo toma solo `model`, `input` y `escalate`. En una ruta de decisión `input` es `{"state", "questions", "images"?}`: `state` es cualquier valor JSON no nulo, `images` tiene como máximo 16 entradas. Cualquier otro campo dentro de `input` se rechaza con `invalid_decision_input`, y la respuesta nunca lo repite. La respuesta es: `{"model": "", "result": {"answers": {...}, "usage": {"input_tokens", "output_tokens"}}, "state": "Completed", "engine": {"tier", "latency_ms"}}` y sus encabezados llevan `x-route-id`, `x-usage-input-tokens` y `x-usage-output-tokens`. Con escalamiento, cada respuesta también indica qué `tier` la respondió y, cuando fue escalada, `escalated_from` lista los niveles inferiores y la confianza que dio cada uno. Un objeto `escalation` reporta cada intento, cuántas preguntas se hicieron y cuántas volvieron por debajo del umbral, y cuántas respuestas dio cada nivel al final. Cada intento es su propia llamada, reservada contra tus cuotas y medida en su propio nivel. Si un intento falla, el escalamiento se detiene, se conservan las respuestas anteriores, y la solicitud sigue siendo `200`. No hay streaming ni un tiempo límite de 60 segundos en este endpoint. Una respuesta exitosa se almacena durante 24 horas bajo su id de operación: el mismo id devuelve la misma respuesta combinada con `x-idempotent-replay: true` y no llama a nada. Lo que se mide son los tokens, al precio de cada nivel que respondió. ## Primeros pasos Necesitas una clave de API y una ruta de decisión habilitada para tu organización ([Autenticación](/es/guides/authentication/)). 1. **Encuentra tu ruta de decisión** con `GET /v1/models`: una entrada cuyo `endpoint` sea `/v1/run`. 2. **Haz una pregunta `noul`.** `POST /v1/run` con tu ruta como `model`, un texto corto como `state` y una pregunta `{"type":"noul","instructions":"...","criteria":{"true":"...","false":"..."}}` (consulta [los ejemplos](#example)). La respuesta tiene el id de tu pregunta bajo `result.answers`, con su probabilidad `p`. 3. **Agrega una pregunta `score`** con tres niveles a la misma llamada. Ambas respuestas regresan en una sola respuesta; `engine.tier` nombra el nivel. 4. **Escala.** Envía la llamada de nuevo con un nuevo id de operación y `"escalate": true`. La respuesta ahora lleva `tier` por respuesta y un objeto `escalation` que lista los intentos. ## Casos de uso **Triaje de tickets.** Cada ticket de soporte entrante es el `state`; una pregunta `choice` elige el equipo, una pregunta `noul` pregunta si debe responder una persona, una pregunta `score` califica la urgencia. Las respuestas se almacenan con el ticket, y un ticket cuya confianza esté por debajo de tu umbral pasa a una persona. **Verificaciones de calidad sobre texto generado.** Antes de enviar el borrador de un asistente, una llamada de decisión lo puntúa frente a tu estándar ("Por debajo del estándar", "En el estándar", "Por encima del estándar") y verifica algunas reglas `noul`. Con `escalate`, solo los borradores sobre los que el nivel rápido tuvo dudas pagan por el nivel preciso. **Moderación de contenido con imágenes.** El texto de una publicación y hasta 16 imágenes se juzgan frente a las preguntas de tu política; la confianza por pregunta decide entre publicar, rechazar y enviar a revisión. ## Límites y precios | Límite | Valor | |---|---| | Preguntas por llamada | 1 a 64 | | Id de pregunta | 1 a 100 caracteres: letras, dígitos, `_`, `.`, `-` | | Imágenes por llamada | 16 | | Cuerpo de la solicitud | 1 MiB por defecto; tu organización puede configurarse entre 1 KiB y 20 MiB | | Tokens de entrada por solicitud | el tope de tu organización | | Respuesta almacenada para una repetición | 24 horas | | Solicitudes, tokens, costo, concurrencia, presupuestos diarios | según lo configurado para tu organización | **Precios:** a consultar. La unidad de precio son los **tokens**, al precio del nivel que respondió. ## Errores | Estado | Código | Qué significa y qué hacer | |---|---|---| | 400 | `invalid_input` | Falta `input` o no es un objeto JSON. | | 400 | `invalid_decision_input` | `input` no es `{state, questions, images?}` o una pregunta está mal formada; revisa los tipos y los ids. | | 400 | `invalid_escalate` | `escalate` tiene una clave o un valor que no admite, o la ruta no es una ruta de decisión. | | 400 | `unsupported_field` | Un campo de nivel superior distinto de `model`, `input`, `escalate`. | | 400 | `model_not_allowed`, `input_too_large` | Usa un id de ruta válido; acorta el estado. | | 400 | `missing_operation_id`, `invalid_operation_id` | Envía un `X-Operation-Id` válido. | | 401 | `missing_credentials`, `invalid_credentials` | Envía una clave válida. | | 403 | `route_forbidden` | La ruta no es tuya, o no es una ruta de ejecución. | | 409 | `operation_in_progress` | Ese id de operación sigue en ejecución. | | 413 | `request_too_large` | El cuerpo supera tu tope de tamaño. | | 429 | `rate_limited`, `quota_exceeded`, `concurrency_limited`, `budget_exhausted` | Consulta [Errores](/es/guides/errors/); espera `Retry-After` cuando se indique. | | 502 | `upstream_error` | Falló el nivel, o se rechazó la forma de los criterios; revisa los criterios y luego reintenta. | | 503 | `service_unavailable` | No se puede acceder a las decisiones; reintenta más tarde. | ## Buenas prácticas - **Escribe los criterios en las formas estrictas**: un objeto para `noul` y `choice`, un arreglo para `score`. - **Mantén umbrales por nivel**, nunca un solo número para todos los niveles. - **Usa `escalate` con `max_tier`** para limitar el costo: la mayoría de las preguntas se detienen en el nivel más barato. - **Coloca en `state` todo lo que el juicio necesita**, incluidos los pasajes recuperados; el motor no ve nada más. - **Usa ids de pregunta estables**, para que las respuestas puedan compararse entre llamadas y almacenarse por id. - **Deriva el id de operación del elemento juzgado** para que un lote reintentado repita la respuesta en lugar de pagar dos veces. ## Relacionados - [Chat con IA](/es/products/ai.chat/), [Conocimiento y búsqueda vectorial](/es/products/knowledge/) - [Autenticación](/es/guides/authentication/), [Errores](/es/guides/errors/), [Límites](/es/guides/limits/) --- Producto: Datos URL: https://developer.inovacc.dev/es/products/data/ URL base: https://data.inovacc.dev Bases de datos, colecciones y registros con esquemas y reglas, por una sola puerta REST/JSON. Endpoints: - GET /v1/databases - PUT /v1/databases/{database} - PUT /v1/databases/{database}/collections/{collection} - POST /v1/databases/{database}/collections/{collection}/records - POST /v1/databases/{database}/batch Ejemplo: ```sh curl -X GET "https://data.inovacc.dev/v1/databases" -H "Authorization: Bearer $INOVACC_API_KEY" ``` ## Descripción general Datos es la base de datos de tu aplicación, servida por una sola puerta REST/JSON en `https://data.inovacc.dev`. Creas **bases de datos**, les das **colecciones**, y lees y escribes **registros** con simples llamadas HTTP. Una colección es tipada, con campos declarados por un esquema, o libre, y contiene documentos JSON sin esquema. Quién puede hacer qué se decide en cada solicitud, primero por los permisos de la credencial y luego, registro por registro, por las **reglas** que declara la colección. El problema que resuelve es operar datos persistentes sin operar una base de datos: sin servidor, sin pool de conexiones, sin herramienta de migraciones, sin respaldos que programar. Tu aplicación conserva su propio código y almacena aquí sus datos; dónde viven los datos lo decide Inovacc y nunca se muestra. La misma API atiende a tus servidores, a tus páginas web (con una clave de navegador) y a las personas con sesión iniciada en tu app, y un oyente en tiempo real envía los cambios a medida que ocurren. Usa Datos para el estado de la aplicación: notas de usuarios, pedidos, configuraciones, catálogos, cualquier cosa con forma de registros. Almacena contenido binario grande con [Archivos](/es/products/files/), consulta lo que sucedió con el [Registro de actividad](/es/products/activity/), y notifica a otros sistemas con [Eventos](/es/products/events/). ## Conceptos **Bases de datos.** Una base de datos es una clave que elige tu aplicación, como `shop` o `crm/v2` (una `/` se escribe `%2F` en una ruta). Una base de datos pertenece a la aplicación cuya credencial la creó; la base de datos de otra aplicación responde `404`, como si no existiera. Una credencial a nivel de organización ve todas las bases de datos de la organización. **Colecciones: con esquema o libres.** Una colección con **esquema** declara hasta 64 campos, cada uno con un tipo: `text`, `integer`, `number`, `bool`, `datetime`, `json`, `relation` (un id de registro en una colección `target`) o `blob` (el SHA-256 de un archivo en la misma base de datos). Un campo puede ser `required`, `unique`, `indexed` o `fulltext` (en `text`). Una colección **libre** no declara nada: cada registro es un documento JSON, consultado por ruta con puntos (`profile.city`), y se le puede dar un esquema más adelante cuando todos los documentos encajen. Escribir en una colección que no existe crea una libre, cuando tu credencial puede cambiar esquemas. **Registros y versiones.** Un registro es `{id, version, created_at, updated_at, ...fields}`. Su `id` lo genera el servicio (`rec_` más 26 caracteres ordenables) o lo eliges tú. Cada cambio sube `version` en uno; las actualizaciones y eliminaciones deben enviar `If-Match: `, de modo que dos escritores nunca se sobrescriban en silencio. **Reglas.** Una colección declara cinco reglas, `list`, `view`, `create`, `update` y `delete`. Cada una es `null` (nadie), `""` (cualquier credencial de servidor de tu organización), `public` (cualquiera, incluidas las claves de navegador), `users` (las personas con sesión iniciada de la aplicación propietaria, y los servidores), o una expresión como `owner = @auth.id`. Las reglas se aplican a toda credencial, incluida la de un administrador. **Credenciales.** Una **clave secreta** es para servidores. Una **clave publicable** (`apb_...`) es para páginas web: funciona solo desde los orígenes que lista su aplicación y solo donde una regla la admite. El **token de acceso** de una persona con sesión iniciada lleva solo permisos de registros y archivos. ## Cómo funciona Toda llamada lleva `Authorization: Bearer `. No hay encabezado de organización: tu organización, y la aplicación, se obtienen de la credencial, y nada en una ruta, una consulta o un encabezado puede nombrar a otra. Una solicitud sin una credencial válida, o a una ruta que no es una ruta del servicio, recibe un único `401` idéntico. Para cada llamada el servicio primero verifica el permiso de la credencial para la acción (leer, escribir, crear, actualizar, eliminar o cambios de esquema) sobre la base de datos. Cualquier cosa distinta de "permitir" es `403 forbidden` sin detalle. Luego la regla de la colección decide por registro: una lista devuelve solo los registros que admite la regla `list`; leer un registro que la regla `view` oculta es `404`, igual que un registro inexistente; una creación verifica los datos nuevos; una actualización verifica tanto el registro almacenado como los datos nuevos. Las escrituras llevan versión. `POST /records` crea y responde `201` con `ETag: "1"`. `PATCH` cambia los campos nombrados (en una colección libre es un parche de combinación JSON), `PUT` reemplaza el registro, y `PUT` con `If-None-Match: *` lo crea en el id que elijas. Un `If-Match` incorrecto es `409 version_conflict`; uno ausente es `428`. Las listas toman `filter` (`field op value` unidos por `&&` y `||`), `sort`, `limit` (hasta 200) y un `cursor` opaco, y devuelven `next_cursor` hasta la última página; `q` busca en los campos `fulltext`. Un **lote** aplica hasta 100 creaciones, actualizaciones y eliminaciones en una sola transacción: todas tienen éxito, o ninguna. Un **oyente en tiempo real** es un WebSocket en `GET .../collections/{c}/listen`: envía `ready`, luego eventos `added`, `modified` y `removed` para los registros que tu regla te permite listar. Cada llamada se registra en el [Registro de actividad](/es/products/activity/). ## Primeros pasos Necesitas una clave secreta con permiso para crear bases de datos y colecciones ([Autenticación](/es/guides/authentication/)). 1. **Crea una base de datos.** `PUT /v1/databases/notes` sin cuerpo. La respuesta es `201` con `{key, created_at}`; repetirlo responde `200`. 2. **Declara una colección.** `PUT /v1/databases/notes/collections/items` con los campos `owner` y `body` y reglas (consulta [los ejemplos](#example)). La respuesta es la colección con `schema_version` 1. 3. **Escribe un registro.** `POST .../collections/items/records` con los campos. La respuesta es `201` con el registro, su `id` y `version` 1. 4. **Actualízalo.** `PATCH .../records/{id}` con `If-Match: 1`. La respuesta tiene `version` 2; enviar `If-Match: 1` de nuevo es `409 version_conflict`. 5. **Lista.** `GET .../records?filter=owner%20%3D%20%22me%22&limit=10` devuelve tu registro y `next_cursor: null`. ## Casos de uso **Datos por usuario en una aplicación web.** Una aplicación de notas declara una colección cuyas cinco reglas dicen `owner = @auth.id`. La página llama a Datos directamente con el token de la persona con sesión iniciada; cada persona lista y edita solo sus propias notas, y la nota de otra persona es un `404`. Ningún código de servidor lo hace cumplir: lo hacen las reglas. **Un catálogo público.** Una tienda en línea mantiene `products` como una colección libre con `list` y `view` en `public` y las escrituras cerradas. El escaparate la lee desde el navegador con una clave publicable; la administración la escribe desde un servidor con una clave secreta. **Paneles en vivo.** Una pantalla de operaciones abre un oyente sobre `orders` filtrado por los pedidos abiertos de hoy. Ejecuta la consulta después de `ready`, y luego aplica los eventos `added`, `modified` y `removed` a medida que llegan, sin sondeo. ## Límites y precios | Límite | Valor | |---|---| | Cuerpo de solicitud JSON | 1 MiB | | Un registro | 900 KiB | | Un campo `json` | 64 KiB | | Campos por colección; colecciones por base de datos | 64; 64 | | Registros por página | 200 (por defecto 50) | | Operaciones por lote | 100, en una sola transacción | | Filtro | 2,048 caracteres, 40 valores, profundidad de anidamiento 8 | | Cadena de consulta | 16 KiB | | Búsqueda de texto completo `q` | 16 términos, 256 caracteres | | Oyentes por colección; un evento de oyente | 1,000; 512 KiB | | Tasa | 600 solicitudes por minuto por titular de credencial, por ubicación (`429 rate_limited`) | **Precios:** a consultar. ## Errores | Estado | Código | Qué significa y qué hacer | |---|---|---| | 400 | `invalid_body`, `invalid_name`, `unknown_field`, `missing_field`, `invalid_value` | El cuerpo o un nombre incumple las reglas; corrígelo. | | 400 | `record_too_large`, `field_too_large`, `reserved_field` | Reduce el registro; no envíes `id`, `version`, `created_at`, `updated_at` en un cuerpo. | | 400 | `invalid_schema`, `invalid_rule`, `schema_change_unsupported` | La definición de la colección no es válida, o se cambió un campo en el mismo lugar. | | 400 | `invalid_filter`, `invalid_sort`, `invalid_cursor`, `invalid_limit`, `invalid_search` | Corrige la consulta de la lista; empieza de nuevo sin el cursor. | | 400 | `invalid_if_match`, `batch_too_large`, `cross_shard_batch` | Revisa el encabezado o divide el lote. | | 401 | `invalid_credentials` | Envía una credencial válida. | | 403 | `forbidden` | Los permisos de la credencial o la regla de la colección lo rechazan. | | 403 | `origin_not_allowed`, `secret_key_in_browser` | Una clave de navegador desde un origen no listado, o una clave secreta en un navegador. | | 404 | `database_not_found`, `collection_not_found`, `record_not_found` | No existe, o no tienes permiso para verlo. | | 409 | `version_conflict` | Alguien cambió el registro; léelo de nuevo y reintenta. | | 409 | `already_exists`, `unique_violation`, `not_empty`, `schema_violation` | El id o el valor ya está en uso; vacíala antes de eliminar; los documentos no encajan en el esquema. | | 412, 428 | `precondition_failed`, `precondition_required` | El registro ya existe; envía `If-Match`. | | 413 | `payload_too_large` | El cuerpo supera 1 MiB. | | 429 | `rate_limited`, `too_many_listeners` | Reduce el ritmo; cierra los oyentes que no necesites. | | 503 | `service_unavailable` | Reintenta más tarde; no se escribió nada. | ## Buenas prácticas - **Envía siempre `If-Match`** con la versión que leíste, y ante `409 version_conflict` vuelve a leer antes de reintentar. - **Cierra toda colección por defecto** y ábrela regla por regla; una colección nacida de una escritura admite cualquier credencial de servidor hasta que la restrinjas. - **Nunca pongas una clave secreta en una página web**: usa una clave publicable y reglas `public` o `@auth`. - **Elige tus propios ids de registro** para las importaciones, de modo que una importación reintentada no cree nada dos veces (`409 already_exists`). - **Usa un lote** cuando varias escrituras deban tener éxito juntas. - **Indexa los campos por los que filtras y ordenas**, y sigue `next_cursor` en lugar de usar páginas grandes. - **Con un oyente, espera a `ready` antes de consultar**, y luego descarta los eventos que no sean más recientes que lo que devolvió la consulta. ## Relacionados - [Archivos](/es/products/files/): contenido binario junto a tus registros - [Registro de actividad](/es/products/activity/): qué se escribió, y quién lo hizo - [Eventos](/es/products/events/): avisa a otros sistemas de un cambio - [Autenticación](/es/guides/authentication/), [Errores](/es/guides/errors/), [Límites](/es/guides/limits/) --- Producto: Archivos URL: https://developer.inovacc.dev/es/products/files/ URL base: https://data.inovacc.dev Almacenamiento de archivos por contenido, con carga por partes para archivos grandes. Endpoints: - PUT /v1/databases/{database}/blobs/{sha256} - GET /v1/databases/{database}/blobs/{sha256} - DELETE /v1/databases/{database}/blobs/{sha256} - POST /v1/databases/{database}/blobs/{sha256}/uploads Ejemplo: ```sh curl -X PUT "https://data.inovacc.dev/v1/databases/{database}/blobs/{sha256}" -H "Authorization: Bearer $INOVACC_API_KEY" -H "Content-Type: application/json" -d '{}' ``` ## Descripción general Archivos almacena el contenido binario de tu aplicación (imágenes, documentos, exportaciones, grabaciones) en `https://data.inovacc.dev`, con cargas por partes para todo lo que sea grande. Ofrece dos maneras de direccionar un archivo: - **por ruta, en la carpeta de tu aplicación**: `PUT /v1/files/users/123/avatar.png`, como un bucket de almacenamiento en la nube nombra los archivos, con listados por carpeta, metadatos de usuario y carpetas compartidas con otras aplicaciones de tu espacio de trabajo; - **por contenido, dentro de una base de datos**: `PUT /v1/databases/{db}/blobs/{sha256}`, donde el propio SHA-256 del archivo es su nombre, de modo que un registro de [Datos](/es/products/data/) puede apuntar a él con un campo `blob`. El problema que resuelve es mantener los archivos junto a tus datos sin operar almacenamiento: Inovacc elige dónde viven los bytes, los verifica contra su hash, elimina duplicados dentro de tu carpeta y los devuelve con encabezados que impiden que un archivo descargado se ejecute en un navegador. Usa Archivos para todo lo que sean bytes en lugar de campos. Conserva la descripción estructurada de un archivo (propietario, título, estado) como un registro en [Datos](/es/products/data/). Para hacer que un archivo se pueda buscar por significado, agrégalo a una colección de [Conocimiento y búsqueda vectorial](/es/products/knowledge/). ## Conceptos **La carpeta de tu aplicación.** Cada aplicación tiene su propia carpeta. La carpeta se elige a partir de la credencial, nunca de la solicitud: una credencial vinculada a una aplicación usa la carpeta de esa aplicación, y una credencial no vinculada a ninguna aplicación recibe `403 application_required` en toda ruta de rutas de archivo. Las rutas son UTF-8, de 1 a 1,024 bytes, separadas por `/`, sensibles a mayúsculas y minúsculas, sin ningún segmento vacío, `.` ni `..`; una ruta que no esté ya en esa forma se rechaza, nunca se reescribe en silencio. **Metadatos.** Una escritura puede llevar un `Content-Type` y tus propios encabezados `X-File-Meta-` (2 KiB en total). Ambos regresan en cada lectura. Una escritura reemplaza por completo el contenido, el tipo y los metadatos del archivo. **Hashes.** Cada archivo se identifica por su SHA-256. Envía `X-File-Sha256` con una escritura y los bytes se verifican contra él (`400 hash_mismatch`, no se almacena nada). Dentro de la carpeta de una aplicación, dos rutas con los mismos bytes comparten una sola copia almacenada; la copia se conserva hasta que se va la última ruta. La deduplicación nunca cruza aplicaciones ni organizaciones. **Blobs de bases de datos.** Un blob se direcciona por el SHA-256 en su ruta dentro de una base de datos. Cargarlo de nuevo responde `200` en lugar de `201`. Cualquiera con permiso de lectura sobre la base de datos que conozca el hash puede leerlo: las reglas de registros no se aplican a los blobs. **Carpetas compartidas.** Una aplicación puede crear una carpeta `team` que aparece para toda aplicación con acceso como `shared/team/...`. El creador es su propietario y concede a otras aplicaciones del mismo espacio de trabajo `read` o `read_write`. Una aplicación sin acceso no puede saber que la carpeta existe: toda operación responde `404`. **Archivos grandes.** Hasta 100 MiB van en una sola solicitud. Por encima de eso, hasta 5 GiB, cargas por partes y luego vinculas el resultado a una ruta (o se convierte en el blob). ## Cómo funciona Toda llamada lleva `Authorization: Bearer `; no hay encabezado de organización, porque tu organización y tu aplicación se obtienen de la credencial. El permiso que se verifica es lectura, escritura o eliminación de blobs. Una página web puede usar estas rutas con una clave publicable desde un origen que su aplicación liste. **Escribir por ruta.** `PUT /v1/files/{path}` con los bytes y un `Content-Length` (obligatorio). La respuesta es `201` para una ruta nueva o `200` para un reemplazo: `{"path","size","sha256","content_type","updated_at","metadata"}` y `ETag: ""`. **Leer.** `GET /v1/files/{path}` devuelve el archivo completo (no se admiten rangos) con `Content-Type`, `ETag`, `X-File-Sha256`, `X-File-Updated-At` y tus encabezados `X-File-Meta-*`. Cada descarga también lleva `X-Content-Type-Options: nosniff`, una `Content-Security-Policy` que la aísla y `Cache-Control: no-store`, de modo que una página HTML cargada nunca pueda ejecutarse como tu sitio. `HEAD` devuelve solo los encabezados. **Listar.** `GET /v1/files?prefix=photos/&delimiter=/` lista los archivos directamente bajo una carpeta como `items` y cada subcarpeta una vez en `prefixes`. Sigue `next_cursor` hasta que sea `null`: una página puede ser más corta que `limit` y aun así tener una siguiente. **Por partes.** `POST /v1/files-uploads/{sha256}` inicia una carga (o responde `200` con `exists: true` cuando tu carpeta ya contiene esos bytes). Envía las partes de la 1 a la 10,000 con `PUT .../{upload_id}/parts/{n}`, cada una de entre 5 MiB y 100 MiB excepto la última, y luego `POST .../complete` con la lista de partes y sus `etag`. Inovacc vuelve a leer el objeto completo y verifica su SHA-256 y su tamaño. Por último, `PUT /v1/files/{path}` con `X-File-Sha256` y un cuerpo vacío le da una ruta. Los blobs de bases de datos usan los mismos cuatro pasos bajo `/v1/databases/{db}/blobs/{sha256}/uploads`. Las cargas, descargas y eliminaciones se registran en el [Registro de actividad](/es/products/activity/) con el hash y el tamaño del archivo. ## Primeros pasos Necesitas una clave secreta vinculada a una aplicación, con permisos de blobs ([Autenticación](/es/guides/authentication/)). 1. **Carga un archivo.** `PUT /v1/files/reports/2026-10.csv` con el archivo como cuerpo y `Content-Type: text/csv` (consulta [los ejemplos](#example)). La respuesta es `201` con su `sha256`. 2. **Léelo de vuelta.** `GET /v1/files/reports/2026-10.csv`. Los bytes llegan con `ETag` igual al hash del paso 1. 3. **Lista la carpeta.** `GET /v1/files?prefix=reports/&delimiter=/`. Tu archivo está en `items`. 4. **Reemplázalo.** Haz `PUT` en la misma ruta con bytes nuevos. La respuesta es `200`, con un nuevo `sha256`. 5. **Elimínalo.** `DELETE /v1/files/reports/2026-10.csv` responde `204`; un segundo `GET` es `404 file_not_found`. ## Casos de uso **Fotos de perfil desde el navegador.** Una aplicación web carga cada avatar en `users//avatar.png` con una clave publicable y guarda el `sha256` devuelto en el registro del usuario, para que una página pueda saber cuándo cambió la foto. **Exportaciones compartidas con una aplicación asociada.** Una aplicación de reportes crea la carpeta compartida `exports`, concede `read` a la aplicación de facturación, y escribe archivos mensuales bajo `shared/exports/`. La aplicación de facturación los lista y los descarga; revocar la concesión surte efecto en su siguiente solicitud. **Medios grandes con integridad.** Una herramienta de video calcula el hash de una grabación de 2 GiB en el cliente, la carga en partes de 100 MiB y la completa. Inovacc la almacena solo si el SHA-256 coincide, de modo que una transferencia corrupta nunca se convierte en un archivo. ## Límites y precios | Límite | Valor | |---|---| | Archivo o blob en una sola solicitud | 100 MiB, `Content-Length` obligatorio | | Archivo o blob cargado por partes | 5 GiB; partes de 5 MiB a 100 MiB (excepto la última), numeradas de 1 a 10,000 | | Ruta | 1,024 bytes | | Prefijo de listado | 256 bytes | | Metadatos por archivo | 2 KiB | | Página de listado | 1 a 200 (por defecto 50) | | Nombre de carpeta compartida | 1 a 64 letras, dígitos, `_` o `-` | | Tasa | 600 solicitudes por minuto por titular de credencial, por ubicación | **Precios:** a consultar. ## Errores | Estado | Código | Qué significa y qué hacer | |---|---|---| | 400 | `invalid_path`, `invalid_prefix` | La ruta o el prefijo no está en forma canónica; corrígelo de tu lado. | | 400 | `invalid_metadata` | `Content-Type` o `X-File-Meta-*` incumple las reglas o supera 2 KiB. | | 400 | `hash_mismatch` | Los bytes no coinciden con el SHA-256 que enviaste; no se almacenó nada. | | 400 | `invalid_part`, `invalid_limit`, `invalid_cursor`, `invalid_grantee` | Corrige las partes de la carga, el listado o la concesión. | | 401 | `invalid_credentials` | Envía una credencial válida. | | 403 | `application_required` | Las rutas de archivo necesitan una credencial vinculada a una aplicación. | | 403 | `forbidden` | Falta un permiso, o un beneficiario de `read` intentó escribir. | | 404 | `file_not_found`, `blob_not_found`, `share_not_found`, `upload_not_found` | No existe, o no tienes acceso a él. | | 409 | `already_exists`, `not_empty` | El nombre de la carpeta compartida ya está en uso; vacía la carpeta antes de eliminarla. | | 411 | `length_required` | Envía `Content-Length`. | | 413 | `payload_too_large` | Más de 100 MiB en una solicitud (usa partes) o más de 5 GiB. | | 429 | `rate_limited` | Reduce el ritmo. | | 503 | `service_unavailable` | Reintenta más tarde. | ## Buenas prácticas - **Envía `X-File-Sha256` en cada escritura** para que una carga dañada se rechace en lugar de almacenarse. - **Usa partes por encima de 100 MiB**, y comienza con `POST .../uploads`: responde `exists: true` cuando los bytes ya están ahí. - **Guarda el `sha256`, no solo la ruta**, en tus registros; te dice exactamente a qué contenido se refiere un registro. - **Sigue `next_cursor` hasta el final** al listar; las páginas cortas son normales. - **Concede `read` a menos que un asociado deba escribir**, y revoca las concesiones que ya no necesites. - **No dependas de que los blobs sean privados por regla**: cualquiera que pueda leer la base de datos y conozca el hash puede leer un blob. ## Relacionados - [Datos](/es/products/data/): registros que apuntan a archivos con campos `blob` - [Registro de actividad](/es/products/activity/): cargas y descargas con sus hashes - [Conocimiento y búsqueda vectorial](/es/products/knowledge/): haz que los documentos se puedan buscar - [Autenticación](/es/guides/authentication/), [Errores](/es/guides/errors/), [Límites](/es/guides/limits/) --- Producto: Registro de actividad URL: https://developer.inovacc.dev/es/products/activity/ URL base: https://data.inovacc.dev Lee el registro de tu organización con las llamadas a la API y los eventos de archivos y registros. Endpoints: - GET /v1/activity Ejemplo: ```sh curl -X GET "https://data.inovacc.dev/v1/activity" -H "Authorization: Bearer $INOVACC_API_KEY" ``` ## Descripción general El Registro de actividad es el registro propio de tu organización de lo que ocurrió en Inovacc: cada llamada a la API, y cada evento de archivo y de registro que esas llamadas causaron. Lo lees con un solo endpoint, `GET /v1/activity` en `https://data.inovacc.dev`, filtrado por tiempo, tipo de evento o hash de archivo. El problema que resuelve es responder "quién hizo qué, y cuándo" sin construir tu propia pista de auditoría. Inovacc mantiene el registro de toda llamada a las API de Datos, de IA y de Eventos, sin importar si tu aplicación se acuerda de escribir algo. El registro es un hecho del servicio, no una opción, y una organización solo ve sus propios registros. Úsalo para auditorías, para investigar una integración fallida ("¿qué llamadas se rechazaron esta mañana, y con qué código?"), para demostrar que un archivo fue entregado (el evento de descarga lleva el SHA-256 del archivo), y para una vista del tráfico por clave. Para ver cuánto **costó** tu uso de IA, usa el resumen de uso del [Chat con IA](/es/products/ai.chat/) (`GET /v1/usage/summary`): el Registro de actividad registra eventos, nunca precios. ## Conceptos **Eventos y tipos.** Cada registro es un evento con un `kind`: | Tipo | Se escribe cuando | |---|---| | `api.call` | cualquier llamada autenticada, incluidas las rechazadas (`403`, `429`) | | `record.write`, `record.delete` | se crea, cambia o elimina un registro (uno por operación de un lote) | | `blob.write`, `blob.read`, `blob.delete` | se carga, descarga o elimina un blob de una base de datos | | `file.upload.completed`, `file.download`, `file.delete` | se escribe, descarga o elimina un archivo | | `file.upload.started`, `file.processed` | los escriben otros servicios de Inovacc, como las cargas y conversiones de IA | **Qué contiene un registro.** La hora (`at_ms`, milisegundos Unix), la organización, quién actuó (`actor_kind`: `user`, `key` o `service`, y `actor_id`), el servicio de origen `source`, el `method`, la `route` (una plantilla como `/v1/databases/{database}/collections/{collection}/records/{record_id}`, nunca los ids en sí), el `status`, el `outcome`, el `error_code` de un fallo, la latencia, los tamaños de entrada y salida, y para un archivo su tamaño, tipo y SHA-256. `related` apunta a la base de datos, la colección y el registro, o a la carga o el trabajo, de que se trate. Todas las claves están siempre presentes, con `null` cuando no aplican. **Lo que nunca contiene.** Un prompt, el cuerpo de una respuesta ni el contenido de un archivo. Las llamadas de IA nunca registran el nombre de un archivo. **País.** Un registro `api.call` lleva `country`: el país de quien llama tal como lo ve el borde de red de Inovacc (dos letras, `XX` cuando se desconoce). Quien llama no puede establecerlo, y el registro no contiene ciudad, dirección IP ni coordenadas. **Retención.** Los registros se conservan un año. Los últimos 30 días responden de inmediato; los registros más antiguos vienen del archivo y tardan más. ## Cómo funciona Llama a `GET /v1/activity` con `Authorization: Bearer `. La credencial necesita el permiso de lectura de actividad a nivel de organización; una credencial limitada a registros no lo tiene. La organización es siempre la de la credencial: no hay ningún parámetro que nombre otra. Todos los parámetros son opcionales: | Parámetro | Significado | |---|---| | `from`, `to` | milisegundos Unix, UTC; `from` inclusivo, `to` exclusivo | | `kind` | uno o más tipos, separados por comas | | `file_sha256` | 64 caracteres hexadecimales en minúscula: todos los eventos de ese archivo | | `limit` | de 1 a 500, por defecto 100 | | `cursor` | el `next_cursor` de la página anterior, sin cambios | Cualquier otro parámetro, uno repetido o vacío, o un valor no válido es `400 invalid_query`, y no se lee nada. La respuesta viene con lo más reciente primero: `{"items":[...],"next_cursor":,"archive_searched":}`. Sigue `next_cursor` hasta que sea `null`. `archive_searched` te indica que la lectura llegó más allá de los últimos 30 días. Una lectura puede retroceder como máximo 366 días por llamada. Los registros aparecen unos segundos después de la llamada, no al instante. El registro ocurre después de la respuesta y nunca la modifica: si no se puede escribir en el registro, la llamada se responde como de costumbre. Una descarga desde Datos o Archivos se registra cuando comienza; una descarga desde la API de IA se registra cuando la transferencia se completa. La API de IA también ofrece `GET /v1/activity` en `https://ai.inovacc.dev` para sus propias llamadas, con `from` y `to` como instantes RFC 3339; leerlo allí no se cobra. ## Primeros pasos Necesitas una credencial con el permiso de lectura de actividad ([Autenticación](/es/guides/authentication/)). 1. **Haz una llamada para registrar.** Escribe un registro en [Datos](/es/products/data/) o carga un archivo en [Archivos](/es/products/files/). 2. **Lee los eventos más recientes.** `GET /v1/activity?limit=10` (consulta [los ejemplos](#example)). Tu llamada está ahí como `api.call`, con su plantilla de ruta y su estado, seguida de su `record.write` o `file.upload.completed`. 3. **Filtra por tipo.** `GET /v1/activity?kind=api.call&limit=50` muestra solo llamadas; mira `status` y `error_code` para encontrar rechazos. 4. **Sigue un archivo.** Toma el `file_sha256` de tu carga y llama a `GET /v1/activity?file_sha256=`: se listan todas las cargas, descargas y eliminaciones de ese contenido. 5. **Pagina.** Pasa el `next_cursor` de vuelta como `cursor` hasta que sea `null`. ## Casos de uso **Una respuesta de auditoría.** Un cliente pregunta quién eliminó un registro el martes pasado. Filtra `kind=record.delete` con `from` y `to` alrededor de ese día; el evento nombra al actor y `related` nombra la base de datos, la colección y el registro. **Prueba de entrega.** Un equipo de cumplimiento debe demostrar que un informe llegó a un socio. El evento `file.download` lleva el SHA-256 y el tamaño de los bytes enviados, y la hora, que se pueden cotejar con el archivo original. **Salud de una integración.** Una integración empieza a fallar por la noche. Filtrar `kind=api.call` para esa ventana muestra el `status` y el `error_code` de cada llamada hecha con la clave de la integración, por ejemplo una racha de `429 rate_limited` que señala la falta de una espera creciente. ## Límites y precios | Límite | Valor | |---|---| | Retención | un año; los últimos 30 días responden de inmediato | | Rango de una lectura | 366 días | | Tamaño de página | 1 a 500 (por defecto 100) | | Demora antes de que un evento sea legible | unos segundos | | Tasa | 600 solicitudes por minuto por titular de credencial, por ubicación | **Precios:** a consultar. ## Errores | Estado | Código | Qué significa y qué hacer | |---|---|---| | 400 | `invalid_query` | Un parámetro es desconocido, está repetido, vacío o no es válido; corrige la consulta. | | 401 | `invalid_credentials` | Envía una credencial válida. | | 403 | `forbidden` | La credencial no tiene el permiso de lectura de actividad. | | 429 | `rate_limited` | Reduce el ritmo. | | 503 | `service_unavailable` | No se puede leer el registro ahora; reintenta más tarde. | ## Buenas prácticas - **Filtra en el servidor**: pasa `kind`, `from` y `to` en lugar de leer todo y filtrar en tu código. - **Conserva el `at_ms` más reciente que procesaste** y lee desde ahí en la siguiente ejecución, siguiendo los cursores hasta el final. - **Usa `file_sha256`** para rastrear un archivo a través de cargas, descargas y procesamiento de IA. - **Espera unos segundos de demora**; no trates como un fallo la ausencia de un evento recién generado. - **Ignora las claves que no conozcas**: los registros pueden ganar campos. - **Da a cada integración su propia clave**, para que `actor_id` te diga cuál actuó. ## Relacionados - [Datos](/es/products/data/), [Archivos](/es/products/files/), [Eventos](/es/products/events/) - [Chat con IA](/es/products/ai.chat/): uso y costo por clave y ruta - [Autenticación](/es/guides/authentication/), [Errores](/es/guides/errors/), [Límites](/es/guides/limits/) --- Producto: Eventos URL: https://developer.inovacc.dev/es/products/events/ URL base: https://events.inovacc.dev Publica eventos y entrégalos a los suscriptores por webhook, consulta o transmisión en vivo, con mensajes muertos y reenvío. Endpoints: - POST /v1/workspaces/{workspace_id}/events - GET /v1/workspaces/{workspace_id}/subscriptions - POST /v1/workspaces/{workspace_id}/subscriptions - DELETE /v1/workspaces/{workspace_id}/subscriptions/{subscription_id} - POST /v1/workspaces/{workspace_id}/subscriptions/{subscription_id}/rotate-secret - POST /v1/workspaces/{workspace_id}/subscriptions/{subscription_id}/pull - POST /v1/workspaces/{workspace_id}/subscriptions/{subscription_id}/ack - GET /v1/workspaces/{workspace_id}/subscriptions/{subscription_id}/stream - GET /v1/workspaces/{workspace_id}/dead - POST /v1/workspaces/{workspace_id}/dead/{event_id}/replay - GET /v1/workspaces/{workspace_id}/stats Ejemplo: ```sh curl -X POST "https://events.inovacc.dev/v1/workspaces/{workspace_id}/events" -H "Authorization: Bearer $INOVACC_API_KEY" -H "Content-Type: application/json" -d '{}' ``` ## Descripción general Eventos permite que una parte de tu sistema anuncie que algo ocurrió, y que cada parte interesada se entere, sin que las dos se conozcan. Un publicador envía un evento como `order.paid` a `https://events.inovacc.dev`; cada **suscripción** cuyo patrón coincida con el tipo del evento lo recibe, por **webhook** a tu endpoint HTTPS, por **pull** desde tu worker, o por **transmisión en vivo** mediante un WebSocket. Los eventos que no se pueden entregar se conservan como **mensajes muertos**, que puedes inspeccionar y reproducir. El problema que resuelve es la difusión confiable. Llamar directamente a cada servicio interesado los acopla, pierde mensajes cuando uno está caído y reintenta mal. Eventos acepta la publicación una vez, la entrega al menos una vez a cada suscripción, reintenta las entregas fallidas con espera creciente, deduplica las publicaciones repetidas y conserva lo que no pudo entregar. Usa Eventos para reaccionar a los cambios: enviar un correo cuando se paga un pedido, refrescar una caché cuando cambia un registro, enviar una notificación a una página. Mantén el estado en sí en [Datos](/es/products/data/) y los bytes en [Archivos](/es/products/files/): un evento debe decir *qué ocurrió*, no cargar el objeto. Para la historia completa del receptor de webhooks, consulta la [guía de webhooks](/es/guides/webhooks/). ## Conceptos **Espacio de trabajo.** Todas las rutas están bajo `/v1/workspaces/{workspace}`. El espacio de trabajo en la ruta es una afirmación que se verifica contra tu credencial: una clave de aplicación puede nombrar solo su propio espacio de trabajo, y cualquier otro responde `403 forbidden`, igual que uno que no existe. **Eventos y el envoltorio.** Publicas `type` (palabras en minúscula separadas por puntos, como `invoice.created`, hasta 128 bytes), `data` (cualquier valor JSON) y un `idempotency_key` opcional. Los suscriptores reciben un envoltorio con `event_id` (`evt_` y 26 caracteres, asignado por Inovacc), `type`, `source` (tu aplicación, establecida a partir de la credencial), `time`, `tenant`, `idempotency_key` y `data`. **Niveles.** Una publicación puede nombrar un `tier`: `basic` (el predeterminado), `reliable` o `advanced`. El nivel fija el mayor `data` que puede llevar un evento: 16 KiB para `basic` y `reliable`, 32 KiB para `advanced`. **Suscripciones y patrones.** Una suscripción lista de 1 a 20 patrones en `types`, cada uno `*` (todo), un tipo exacto, o un prefijo que termina en `.*` (`order.*` coincide con `order.paid` y `order.item.added`), y un modo de entrega. **Modos de entrega.** `webhook` envía con POST cada evento a tu URL HTTPS, firmado con el secreto de la suscripción. `pull` mantiene un buzón que tu código lee y confirma. `websocket` mantiene el mismo buzón y además lo transmite en vivo; pull sigue funcionando junto a la transmisión. **Al menos una vez.** Cada evento llega una vez a cada suscripción que coincide en operación normal, pero un reintento o una confirmación perdida puede entregarlo de nuevo. Los receptores deduplican por `event_id`. **Mensajes muertos.** Una entrega de webhook que sigue fallando se mueve a los mensajes muertos de tu espacio de trabajo, con el último error y el número de intentos, y se conserva 30 días. ## Cómo funciona Toda llamada lleva `Authorization: Bearer `. La credencial es el tenant: la organización, la cuenta y la aplicación se obtienen de ella, y nada en un cuerpo puede nombrar a otra. Una **clave secreta** es para servidores y puede usar todas las rutas. Una **clave publicable** (`apb_...`) solo puede abrir una transmisión, desde un origen que su aplicación liste. El token de un usuario final no puede usar nada. Cada ruta necesita su propio permiso, como `events:event.publish`, `events:subscription.write` o `events:dead.replay`. **Publicar.** `POST /events` con `{"event": {...}}` o `{"events": [...]}` (de 1 a 100). La respuesta es `202` con `accepted` (cada uno con su `event_id` y `duplicate`) y `rejected` (cada uno con un índice y un código); un evento incorrecto nunca bloquea a los demás. Enviar un `idempotency_key` ya usado en el espacio de trabajo en las últimas 168 horas devuelve el `event_id` original con `duplicate: true` y no entrega nada nuevo. **Entrega por webhook.** Inovacc envía con POST el envoltorio a tu URL con `x-event-id`, `x-event-type`, `x-event-timestamp` y `x-event-signature: v1=`, un HMAC-SHA256 de la marca de tiempo y del cuerpo sin procesar. Cualquier `2xx` dentro de 10 segundos es una entrega; cualquier otra cosa se reintenta después de 10 segundos, y la espera se duplica hasta 10 minutos, hasta que se alcanza el tope de 5 reintentos y el evento se convierte en un mensaje muerto. Las redirecciones nunca se siguen, y tu URL debe ser HTTPS pública en el puerto 443. **Pull.** `POST /subscriptions/{id}/pull` con `max` (de 1 a 100, por defecto 10) y `lease_seconds` (de 5 a 300, por defecto 30) devuelve los mensajes más antiguos, cada uno con su `delivery_count`. Un mensaje obtenido queda oculto durante su arrendamiento; confírmalo con `POST .../ack` y los ids, o regresa cuando termina el arrendamiento. **Transmisión.** `GET /subscriptions/{id}/stream` se actualiza a un WebSocket con el subprotocolo `inovacc.v1`; un navegador pasa su clave como un segundo subprotocolo, `bearer.`. Cada trama es un envoltorio; responde `{"ack": [...]}` para confirmar. Una trama sin confirmar se vuelve a enviar después de su arrendamiento. **Monitoreo.** `GET /stats` devuelve `published`, `completed`, `depth`, `oldest_pending_age_seconds`, `retries`, `retry_rate` y `dlq_inflow` para el espacio de trabajo. Cada llamada se registra en el [Registro de actividad](/es/products/activity/). Lo que se mide es el evento entregado. ## Primeros pasos Necesitas una clave secreta vinculada a tu espacio de trabajo con los permisos de eventos ([Autenticación](/es/guides/authentication/)). 1. **Crea una suscripción pull.** `POST /v1/workspaces/{workspace}/subscriptions` con `{"types":["demo.*"],"delivery":{"mode":"pull"}}` (consulta [los ejemplos](#example)). La respuesta es `201` con un `subscription_id`. 2. **Publica.** `POST .../events` con `{"event":{"type":"demo.hello","data":{"n":1}}}`. La respuesta es `202` con una entrada en `accepted` y su `event_id`. 3. **Obtén con pull.** `POST .../subscriptions/{id}/pull` con `{}`. Tu evento está en `messages`, con `delivery_count` 1. 4. **Confirma.** `POST .../subscriptions/{id}/ack` con `{"ids":[""]}` responde `{"acked":1}`; un segundo pull viene vacío. 5. **Agrega un webhook.** Crea una segunda suscripción con `{"mode":"webhook","url":"https://"}`, conserva el `signing_secret` que la respuesta muestra una sola vez, y sigue la [guía de webhooks](/es/guides/webhooks/) para verificar las entregas. ## Casos de uso **Cumplimiento de pedidos.** El pago publica `order.paid` con el id del pedido en `data` y el id del pago como `idempotency_key`. Una suscripción por webhook para `order.*` llega al sistema del almacén; una suscripción pull alimenta el trabajo de facturación. Un pago reintentado por el cliente se publica una sola vez. **Actualizaciones en vivo en una página.** Un panel abre una transmisión en una suscripción `websocket` para `ticket.*` con una clave publicable desde su propio origen, muestra cada ticket nuevo a medida que llega la trama, y la confirma. **Recuperación tras una caída.** El endpoint de un socio estuvo caído una hora. Sus entregas terminaron en mensajes muertos con `last_error` `webhook_timeout`. Cuando vuelve, tu equipo lista `GET /dead` y reproduce cada evento; va solo a las suscripciones que nunca lo recibieron. ## Límites y precios | Límite | Valor | |---|---| | Eventos por publicación | 100 | | Cuerpo de la solicitud | 5 MiB | | `data` por evento | 16 KiB (`basic`, `reliable`), 32 KiB (`advanced`) | | Ventana de deduplicación de `idempotency_key` | 168 horas | | Suscripciones por espacio de trabajo; patrones por suscripción | 50; 20 | | Mensajes por pull; arrendamiento | 100; de 5 a 300 segundos | | Tiempo de respuesta del webhook | 10 segundos | | Reintentos | desde 10 segundos, duplicándose hasta 10 minutos; tope de 5, luego mensaje muerto | | Mensajes muertos conservados | 30 días | | Trama de transmisión desde el cliente | 49,152 bytes | | Tasa | 600 llamadas por minuto por credencial, por ubicación | **Precios:** a consultar. La unidad de precio es el **evento entregado**. ## Errores | Estado | Código | Qué significa y qué hacer | |---|---|---| | 400 | `invalid_body` | El cuerpo está mal formado, o nombra `source` (proviene de tu credencial). | | 400 | `invalid_query` | Solo `GET /dead` acepta una consulta (`limit`). | | 400 | `invalid_subscription`, `invalid_delivery`, `invalid_limit`, `invalid_id` | Corrige la suscripción, la entrega o el parámetro. | | 400 | `invalid_url`, `url_not_https`, `url_has_credentials`, `url_port_not_allowed`, `url_destination_blocked` | La URL del webhook debe ser HTTPS pública en el puerto 443, sin credenciales. | | 401 | `invalid_credentials` | Envía una credencial válida. | | 403 | `forbidden`, `account_required` | Falta un permiso o el espacio de trabajo es incorrecto; usa una clave de aplicación. | | 403 | `publishable_key_not_allowed`, `origin_not_allowed`, `secret_key_in_browser` | Una clave publicable solo puede transmitir, desde un origen listado; mantén las claves secretas en servidores. | | 404 | `subscription_not_found`, `dead_event_not_found` | No existe en tu espacio de trabajo. | | 409 | `wrong_delivery_mode`, `too_many_subscriptions`, `secret_not_managed` | La operación no encaja con el modo de entrega, o estás en el tope de 50 suscripciones. | | 413 | `payload_too_large` | El cuerpo supera 5 MiB. | | 426 | `upgrade_required` | La ruta de transmisión necesita una actualización a WebSocket. | | 429 | `rate_limited`, `too_many_streams` | Reduce el ritmo; cierra los sockets que no necesites. | | 503 | `service_unavailable`, `events_disabled`, `signing_unavailable` | Reintenta más tarde; una publicación reintentada con las mismas claves reutiliza sus ids. | Un evento rechazado dentro de un `202` lleva su propio código: `invalid_event`, `unknown_field`, `invalid_type`, `invalid_idempotency_key`, `payload_too_large` o `data_required`. ## Buenas prácticas - **Envía un `idempotency_key`** derivado de lo que ocurrió (el id del pago, la versión del registro), para que una publicación reintentada no sea un segundo evento. - **Deduplica por `event_id`** en cada receptor: la entrega es al menos una vez. - **Pon ids en `data`, no objetos**: obtén el estado actual desde [Datos](/es/products/data/) cuando manejes el evento. - **Responde los webhooks rápido** con un `2xx` y haz el trabajo después; 10 segundos es el límite. - **Verifica la firma de cada webhook** antes de analizar el cuerpo ([guía de webhooks](/es/guides/webhooks/)). - **Confirma los mensajes obtenidos con pull** después de procesarlos, y elige un arrendamiento más largo que tu tiempo de procesamiento. - **Vigila `depth` y `dlq_inflow`** en `/stats`, y reproduce los mensajes muertos una vez corregida la causa. ## Relacionados - [Guía de webhooks](/es/guides/webhooks/): recibe, verifica y reproduce entregas - [Datos](/es/products/data/), [Registro de actividad](/es/products/activity/) - [Autenticación](/es/guides/authentication/), [Errores](/es/guides/errors/), [Límites](/es/guides/limits/) --- Guía: Autenticación URL: https://developer.inovacc.dev/es/guides/authentication/ # Autenticación Toda llamada a una API de Inovacc se autentica con un solo encabezado, `Authorization: Bearer `. La credencial identifica a tu organización y, en el caso de la credencial de una aplicación, también a la aplicación, por lo que nunca las nombras en la solicitud. Las llamadas que cambian algo en la API de IA también llevan un `X-Operation-Id`, que las hace seguras de reintentar. Esta guía explica las credenciales, los encabezados que lee cada API, cómo funciona la idempotencia y cómo mantener las claves seguras durante toda su vida. ## Los encabezados | Encabezado | Dónde | Valor | |---|---|---| | `Authorization` | toda llamada, toda API | `Bearer ` | | `X-Operation-Id` | API de IA (`ai.inovacc.dev`): todo `POST`, `PUT` y `DELETE`; opcional en `GET` | un id único que eliges: de 8 a 128 letras, dígitos, `_` o `-` | Datos (`data.inovacc.dev`) y Eventos (`events.inovacc.dev`) no leen ningún encabezado de organización ni de operación: el tenant proviene solo de la credencial, y cualquier encabezado que intente nombrar a otro se ignora. Datos usa versiones HTTP (`If-Match`, `If-None-Match`) y Eventos usa un `idempotency_key` en el cuerpo para reintentos seguros; consulta sus páginas de producto. Una solicitud sin credencial, con una mal formada o con una que no se reconoce se rechaza antes de que se ejecute nada: `401` con `missing_credentials` o `invalid_credentials`. En todas las API esta respuesta es la misma exista o no la ruta que llamaste, de modo que no se puede aprender nada de la API sin una credencial. ## Obtén una clave Las **claves secretas** las crea en la consola de Inovacc una persona de tu organización y se muestran **una sola vez**: Inovacc almacena solo un hash, por lo que una clave perdida no se puede mostrar de nuevo, solo reemplazar. Hoy el acceso es por invitación: pide a tu contacto de Inovacc que te invite, y luego crea una clave bajo tu organización. ## Credenciales | Credencial | Aspecto | Para | Aceptada por | |---|---|---|---| | Clave de API secreta | `aik_` seguido de 64 caracteres hexadecimales | tus servidores | IA, Datos, Eventos | | Token de acceso de cliente OAuth | un token firmado emitido a un cliente de una aplicación | tus servidores | IA, Datos, Eventos | | Clave publicable | `apb_...` | páginas web, desde los orígenes que su aplicación lista | Datos (registros y archivos, donde las reglas lo permitan), Eventos (solo transmisiones) | | Token de acceso de usuario final | emitido cuando una persona inicia sesión en tu aplicación | el navegador o la app de esa persona | Datos (registros y archivos, según las reglas de la colección) | Una clave puede estar **vinculada a una aplicación**. Una clave así alcanza solo lo que esa aplicación ha habilitado: en la API de IA cada ruta necesita una capacidad (`ai.chat`, `ai.embeddings`, `knowledge`, `decision`), y de lo contrario se rechaza con `403 capability_not_enabled`; en Datos ve solo las bases de datos que creó su aplicación y la carpeta de archivos de su aplicación; en Eventos puede nombrar solo su propio espacio de trabajo. **Las claves publicables no son secretos.** Están pensadas para incluirse dentro de una página web. Lo que protege tus datos es la lista de orígenes permitidos y, sobre todo, las reglas de cada colección: una clave publicable no puede hacer nada que una regla no admita. ## Idempotencia En la API de IA, `X-Operation-Id` es una clave de idempotencia. La primera respuesta exitosa a un id de operación se almacena durante 24 horas. Envía el mismo id de nuevo y recibes esa misma respuesta, marcada con `x-idempotent-replay: true`, sin que el trabajo se ejecute dos veces y sin un segundo cobro. Eso es lo que hace seguro un reintento tras un tiempo de espera agotado o una conexión interrumpida. - Un **error nunca se almacena**: reintentar una llamada fallida con el mismo id la ejecuta de nuevo. - Mientras la primera llamada **sigue en ejecución**, una segunda llamada con el mismo id es `409 operation_in_progress`; espera y reintenta. - En chat, embeddings y run, un id reutilizado **devuelve la respuesta almacenada sin importar lo que diga el cuerpo nuevo**. En los elementos de colección, el mismo id con un archivo distinto es `409 operation_id_reused`. - Genera un id nuevo para cada operación nueva, y reutiliza un id solo para reintentar la llamada para la que se creó. Cada respuesta de IA repite el id con el que se ejecutó en `x-operation-id`; en un `GET` sin uno, el servicio inventa uno. Una respuesta de chat con streaming nunca se almacena, por lo que no se puede repetir. ## Ejemplo ```sh export INOVACC_API_KEY="" ``` ```sample GET https://ai.inovacc.dev/v1/models ``` La respuesta lista las rutas configuradas para tu organización. Las rutas se nombran por organización, así que la lista que ves es la tuya. Una llamada que cambia algo agrega su id de operación: ```sh curl -X POST "https://ai.inovacc.dev/v1/chat/completions" \ -H "Authorization: Bearer $INOVACC_API_KEY" \ -H "Content-Type: application/json" \ -H "X-Operation-Id: $(uuidgen)" \ -d '{"model":"auto","messages":[{"role":"user","content":"Hello"}]}' ``` ## Ciclo de vida de las claves Una clave pasa por cuatro etapas, y cada una tiene sus propios cuidados. 1. **Creación.** Crea una clave por aplicación y por entorno (producción, staging, la laptop de un desarrollador), nunca una clave compartida por todo. Copia la clave directamente de la consola a tu almacén de secretos. 2. **Uso.** Carga la clave desde el entorno o un administrador de secretos al arrancar. Envíala solo en el encabezado `Authorization`, solo por HTTPS, y solo desde un servidor: una clave secreta que llega a la API de Datos o de Eventos desde un navegador (con un encabezado `Origin`) se rechaza con `403 secret_key_in_browser`. 3. **Rotación.** Crea una segunda clave, despliégala en todos los lugares donde se usa la primera, confirma en el [Registro de actividad](/es/products/activity/) que el id de la clave anterior ya no aparece como `actor_id`, y luego haz que se deshabilite la clave anterior. Una clave deshabilitada responde `403 key_disabled`; revocar la credencial de una aplicación surte efecto en menos de un minuto. 4. **Retiro.** Quita la clave de todo almacén de secretos y pipeline cuando una aplicación o un entorno desaparezca. Un comportamiento que conviene prever: en la Vector Database, el ámbito `local` pertenece a la clave que lo escribió. Una clave nueva ve un ámbito `local` nuevo y vacío; usa el ámbito `shared` para el conocimiento que debe sobrevivir a una rotación. Los tokens de acceso OAuth caducan. Cuando una llamada responde `401 invalid_credentials` con un token que antes funcionaba, obtén un token nuevo y reintenta una vez; no reintentes un `401` en un bucle. ## Higiene de las claves - **Nunca subas una clave al repositorio**, ni siquiera a un repositorio privado o a un archivo de pruebas. Mantén los archivos `.env` fuera del control de versiones. - **Nunca registres el encabezado `Authorization`**, y ocúltalo en los reportes de errores y en las trazas. - **Da a cada integración su propia clave**, para que su tráfico sea visible en el Registro de actividad y pueda rotarse por separado. - **Prefiere una clave vinculada a una aplicación** con solo las capacidades que necesita en lugar de una clave para toda la organización. - **Rota según un calendario**, y de inmediato cuando se va alguien que tenía acceso o una clave pudo haberse filtrado. - **En un navegador, usa una clave publicable o el token de una persona con sesión iniciada**, y cierra toda colección con reglas. ## Ver también - [Errores](/es/guides/errors/): el envoltorio de errores y los códigos que puedes manejar. - [Límites](/es/guides/limits/): tamaños de solicitud, tiempos de espera y tasas. - [Webhooks](/es/guides/webhooks/): verificar entregas con un secreto de firma. - [Referencia de la API](/es/reference/): cada producto y sus endpoints. --- Guía: Errores URL: https://developer.inovacc.dev/es/guides/errors/ # Errores Cuando una API de Inovacc rechaza o no puede completar una llamada, responde con un estado HTTP y un envoltorio de error JSON. Esta guía describe el envoltorio, los tipos de error, los códigos que comparten todas las API, y qué hacer para cada estado: corregir la solicitud, esperar o reintentar. ## El envoltorio ```json { "error": { "message": "Human-readable explanation", "type": "validation_error", "code": "invalid_body" } } ``` - **`code`** es sobre lo que debe bifurcar tu programa. Los códigos son parte del contrato de cada API: dentro de una ruta de versión como `/v1`, se pueden agregar códigos nuevos, y un código existente conserva su significado. - **`type`** agrupa los códigos en familias, útiles para el registro y el manejo general. - **`message`** es una oración fija en inglés para las personas. Nunca repite un valor de tu solicitud (como máximo el nombre de un campo rechazado) y nunca describe qué falló dentro de Inovacc. No la analices. Algunas respuestas agregan un campo al envoltorio: un archivo rechazado lleva `reason` (por ejemplo `executable` o `password_protected`), y una conversión de esquema que no encaja lleva `failing_documents`, un conteo. Ignora los campos que no conozcas. Dos respuestas no usan el envoltorio. En la API de IA, una ruta que no existe responde `404 {"error":"not_found"}`. Un WebSocket (una transmisión de Eventos o un oyente de Datos) rechazado antes de la actualización responde un error HTTP ordinario; una vez abierto, termina con un código de cierre. ## Tipos | `type` | Significa | Estado típico | |---|---|---| | `validation_error` | La solicitud está mal formada: un encabezado, un campo o un valor incumple las reglas. | 400 | | `invalid_request_error` | La solicitud está bien formada pero no se puede atender tal como se envió: demasiado grande, un tipo de archivo no admitido, una URL que no se puede obtener. | 400, 413, 415, 422 | | `authentication_error` | No hay credencial, o no es válida. | 401 | | `authorization_error`, `permission_error`, `forbidden` | La credencial es válida pero no puede hacer esto. | 403 | | `not_found_error`, `not_found` | La cosa no existe, o no se te permite saber que existe. | 404, 410 | | `conflict_error`, `conflict` | La solicitud entra en conflicto con el estado actual: una versión, un modo, un límite de una colección. | 409 | | `idempotency_error` | Un id de operación está en uso o se usó para otra cosa. | 409 | | `rate_limit_error`, `rate_limited` | Demasiadas llamadas, o se agotó una asignación o un presupuesto. | 429 | | `api_error`, `service_error`, `service_unavailable`, `internal_error` | Algo del lado de Inovacc, o una fuente a la que llamó por ti, falló. | 500, 502, 503, 504 | Las API de Datos, de IA y de Eventos usan las familias anteriores; la cadena exacta de `type` puede diferir entre API para la misma familia, así que maneja el `code`, y recurre al estado HTTP como respaldo. ## Códigos que comparten todas las API | Estado | Código | Significado | Qué hacer | |---|---|---|---| | 401 | `missing_credentials`, `invalid_credentials` | No hay clave, o una clave que no es válida, venció o fue revocada. | Corrige la credencial; no reintentes sin cambios. | | 403 | `forbidden`, `route_forbidden`, `key_disabled`, `capability_not_enabled` | La credencial no puede hacer esto. | Solicita el permiso, la ruta o la capacidad. | | 403 | `origin_not_allowed`, `secret_key_in_browser` | Una llamada desde un navegador con un origen no listado, o una clave secreta en un navegador. | Lista el origen; usa una clave publicable. | | 400 | `missing_operation_id`, `invalid_operation_id` | IA: un `POST`, `PUT` o `DELETE` sin un `X-Operation-Id` válido. | Envía de 8 a 128 letras, dígitos, `_` o `-`. | | 400 | `invalid_body`, `unsupported_field`, `invalid_field` | El cuerpo o un parámetro no es válido para este endpoint. | Corrige la solicitud. | | 404 | `*_not_found` | No existe, o no es tuyo. | Revisa el id. | | 409 | `operation_in_progress` | El mismo id de operación sigue en ejecución. | Espera y luego reintenta con el mismo id. | | 409 | `operation_id_reused` | IA: un id de operación ya usado para otro archivo en una colección. | Usa un nuevo id de operación para una nueva operación. | | 409 | `version_conflict` | Datos: el registro cambió desde que lo leíste. | Vuelve a leerlo y luego reintenta. | | 413 | `request_too_large`, `payload_too_large` | El cuerpo supera el límite de tamaño. | Hazlo más pequeño, o carga por partes. | | 429 | `rate_limited` | Demasiadas solicitudes en la ventana. | Espera `Retry-After` segundos, cuando se indique. | | 429 | `quota_exceeded`, `concurrency_limited` | IA: se agotó una asignación de tokens, costo o concurrencia. | Reduce el ritmo; pide a Inovacc que aumente la asignación. | | 429 | `budget_exhausted` | IA: se gastó un presupuesto diario. | Espera hasta las 00:00 UTC (`Retry-After`). | | 502 | `upstream_error` | Falló una fuente a la que se llamó por ti. | Reintenta con espera creciente. | | 503 | `service_unavailable` | No se pudo acceder a una dependencia; no se hizo ni se adivinó nada. | Reintenta con espera creciente. | | 504 | `timeout_error` | IA: el modelo no empezó a responder a tiempo. | Reintenta con espera creciente. | Cada página de producto lista todos los códigos que puede devolver, con lo que hay que hacer. ## Política de reintentos | Estado | ¿Reintentar? | Cómo | |---|---|---| | 400, 401, 403, 404, 410, 413, 415, 422 | No | La misma solicitud fallará de la misma manera. Corrígela primero. | | 409 `operation_in_progress` | Sí | Tras una breve espera, con el **mismo** id de operación. | | 409 `version_conflict` | Sí, tras volver a leer | Lee el registro, aplica tu cambio a la nueva versión, envía su `If-Match`. | | 409, otros códigos | No | El estado debe cambiar primero (crea la base de datos, espera los elementos, vacía la carpeta). | | 429 con `Retry-After` | Sí | No antes del número de segundos que indica. | | 429 sin `Retry-After` | Más tarde | Se agotó una asignación: espera minutos, no segundos, o auméntala. | | 500, 502, 503, 504 | Sí | Con espera exponencial y variación aleatoria (por ejemplo 1, 2, 4, 8 segundos), unas pocas veces como máximo. | | Error de red, sin respuesta | Sí | Igual que para 503. | **Reintenta con seguridad.** En la API de IA, reintenta una llamada que modifica con el **mismo** `X-Operation-Id`: si el primer intento sí tuvo éxito, recibes su respuesta almacenada en lugar de una segunda ejecución. En Datos, reintenta una creación con tu propio id de registro o `If-None-Match: *`, y una actualización con el mismo `If-Match`. En Eventos, reintenta una publicación con el mismo `idempotency_key`: regresa el `event_id` original con `duplicate: true`. ## Ver también - [Autenticación](/es/guides/authentication/): credenciales e idempotencia. - [Límites](/es/guides/limits/): los tamaños y tasas detrás de `413` y `429`. - [Webhooks](/es/guides/webhooks/): cómo se reintentan las entregas fallidas. --- Guía: Límites URL: https://developer.inovacc.dev/es/guides/limits/ # Límites Toda API de Inovacc acota el tamaño de lo que envías, cuánto puede tardar una llamada y con qué frecuencia puedes llamar. Esta guía lista esos límites por API y explica qué ocurre cuando alcanzas cada uno, para que tu código pueda mantenerse por debajo de ellos o manejar el rechazo. Los valores marcados "por organización" los configura Inovacc para tu organización; los demás son fijos. ## Cómo responden los límites | Alcanzas | Respuesta | Qué hacer | |---|---|---| | Un límite de tamaño (cuerpo, archivo, registro, elemento) | `413` (`request_too_large`, `payload_too_large`, `url_too_large`), o `400` para un campo o documento demasiado grande | Envía menos, o usa la carga por partes | | Un límite de cantidad (documentos por llamada, operaciones por lote, elementos por colección) | `400 invalid_body`, `400 batch_too_large` o `409 collection_full` | Divide la llamada; inicia una colección nueva | | Un límite de tasa | `429 rate_limited`, con `Retry-After` en la API de IA | Espera y luego reintenta | | Una asignación de tokens, costo o concurrencia (IA) | `429 quota_exceeded` o `429 concurrency_limited`, sin `Retry-After` | Reduce el ritmo durante minutos, o aumenta la asignación | | Un presupuesto diario (IA) | `429 budget_exhausted`, `Retry-After` hasta las 00:00 UTC | Espera al siguiente día UTC | | Un tope de entrada por solicitud (IA) | `400 input_too_large` | Acorta la entrada | Una llamada rechazada en un límite se rechaza antes de que empiece su trabajo: no se llama a nada en tu nombre y no se escribe nada. ## IA | Límite | Valor | |---|---| | Cuerpo de solicitud (chat, embeddings, run) | 1 MiB por defecto; por organización, de 1 KiB a 20 MiB | | Tokens de entrada por solicitud | por organización (`400 input_too_large`) | | Tokens de salida por solicitud | el menor entre el techo de la ruta y el de tu organización; un `max_tokens` mayor se reduce, no se rechaza | | Tiempo hasta que el modelo empieza a responder | 60 segundos (`504 timeout_error`); no en `/v1/run` | | Imágenes en una solicitud de chat | 20 | | Costo de entrada estimado de una imagen | 1,600 tokens | | Respuesta almacenada para una repetición idempotente | 24 horas | | Una llamada mantenida como en curso | 5 minutos | | Vector Database: documentos por ingesta, ids por eliminación | 100 | | Vector Database: un documento (`id` + `text` + `metadata`) | 10 KiB | | Vector Database: cuerpo de la solicitud | 1 MiB | | Consulta vectorial: texto; `top_k` | 8,000 caracteres; 50 | | Elemento de colección en el cuerpo de una solicitud, o por URL | 16 MiB | | Colección | 100 elementos; 1 GiB de archivos enviados en solicitudes; 20 GiB de video por carga | | Parte de una carga | 64 MiB, todas las partes menos la última | | Carga que no es video (transcripción, notas) | 16 MiB | | Carga de grabación | por organización | | Resultado de un trabajo de conocimiento | se conserva 24 horas | | Lecturas de actividad y de uso | hasta 366 días y 92 días por llamada | **Cuotas por organización.** Cada una de estas se configura para tu organización, y es ilimitada cuando no se configura: | Asignación | Ventana | Cuando se alcanza | |---|---|---| | Solicitudes | por minuto de reloj UTC, por día UTC | `429 rate_limited`, `Retry-After` hasta el siguiente minuto o día | | Tokens | por día UTC, por mes UTC | `429 quota_exceeded` | | Costo | por día UTC, por mes UTC | `429 quota_exceeded` | | Llamadas concurrentes | en cualquier momento | `429 concurrency_limited` | | Presupuesto diario, por proveedor y opcionalmente por aplicación | por día UTC | `429 budget_exhausted`, `Retry-After` hasta las 00:00 UTC | Una solicitud cuenta contra los límites de solicitudes por minuto y por día en cuanto se admite, aunque falle después. Los límites de tokens y costo permiten una llamada que alcanza exactamente el límite. Desde el nivel de aviso de un presupuesto (80% por defecto), las respuestas llevan `X-Budget-Warning` antes de que se empiecen a rechazar las llamadas. ## Datos y Archivos | Límite | Valor | |---|---| | Cuerpo de solicitud JSON | 1 MiB (`413 payload_too_large`) | | Un registro | 900 KiB (`400 record_too_large`) | | Un campo `json` | 64 KiB | | Campos por colección; colecciones por base de datos | 64; 64 | | Registros por página | 200 (por defecto 50) | | Operaciones por lote | 100, una transacción | | Filtro | 2,048 caracteres, 40 valores, profundidad de anidamiento 8 | | Cadena de consulta | 16 KiB (`400 invalid_query`) | | Archivo o blob en una solicitud | 100 MiB, con `Content-Length` (`411` sin él) | | Archivo o blob por partes | 5 GiB; partes de 5 MiB a 100 MiB, numeradas de 1 a 10,000 | | Ruta de archivo; prefijo de listado; metadatos de archivo | 1,024 bytes; 256 bytes; 2 KiB | | Oyentes en tiempo real por colección; un evento | 1,000; 512 KiB | | Página del Registro de actividad | 1 a 500 (por defecto 100) | | Tasa | 600 solicitudes por minuto por titular de credencial, por ubicación (`429 rate_limited`) | El límite de tasa se cuenta por ubicación y es un freno, no un conteo exacto. ## Eventos | Límite | Valor | |---|---| | Cuerpo de la solicitud | 5 MiB (`413 payload_too_large`) | | Eventos por publicación | 100 | | `data` por evento | 16 KiB (`basic`, `reliable`), 32 KiB (`advanced`) | | Ventana de deduplicación de un `idempotency_key` | 168 horas | | Suscripciones por espacio de trabajo; patrones por suscripción | 50; 20 | | Mensajes por pull; arrendamiento del pull | 100; de 5 a 300 segundos | | Tiempo de respuesta del webhook | 10 segundos | | Reintentos del webhook | desde 10 segundos, duplicándose hasta 10 minutos; tope de 5 | | Mensajes muertos conservados | 30 días | | Tasa | 600 llamadas por minuto por credencial, por ubicación (`429 rate_limited`) | ## Cómo mantenerte por debajo de los límites - **Agrupa en lotes**: envía hasta 100 documentos, operaciones o eventos por llamada en lugar de uno cada vez. - **Carga los archivos grandes por partes** en lugar de aumentar los tamaños de cuerpo. - **Espera ante `429`**: respeta `Retry-After`, y agrega variación aleatoria para que muchos clientes no reintenten en el mismo instante. - **Configura `max_tokens`** en las llamadas de chat: reduce lo que cada llamada reserva contra tus cuotas. - **Vigila `X-Budget-Warning`** y el resumen de uso de IA para ver venir una asignación antes de que se agote. ## Ver también - [Errores](/es/guides/errors/): cada código y la política de reintentos. - [Autenticación](/es/guides/authentication/): credenciales e idempotencia. - Cada página de producto lista sus propios límites completos. --- Guía: Webhooks URL: https://developer.inovacc.dev/es/guides/webhooks/ # Webhooks Un webhook es la forma en que [Eventos](/es/products/events/) entrega a un servidor que tú operas: cada evento que coincide con una suscripción llega a tu endpoint HTTPS como un `POST` firmado. Esta guía cubre cómo crear una suscripción de webhook, la solicitud que recibe tu endpoint, la verificación de su firma, cómo funcionan los reintentos y los mensajes muertos, y cómo rotar el secreto de firma sin perder una entrega. ## Qué es una suscripción de webhook Una suscripción indica qué eventos quiere un receptor y cómo le llegan. Una **suscripción de webhook** tiene una lista de patrones de tipo y un bloque de entrega con `"mode": "webhook"` y tu `url`. A partir de ahí, cada evento publicado en el espacio de trabajo cuyo `type` coincida con uno de los patrones se envía con POST a esa URL, una vez por suscripción. La entrega es **al menos una vez**. En operación normal cada evento llega una vez; un reintento, una respuesta lenta o una conexión perdida pueden hacer que llegue de nuevo. Por lo tanto, tu receptor debe construirse para aceptar un duplicado sin consecuencias, usando como clave el id del evento. La URL debe ser `https`, en el puerto 443, sin nombre de usuario ni contraseña, sin fragmento, y con un host público: los nombres privados, de loopback, de enlace local y de uso local se rechazan cuando se crea la suscripción y se verifican de nuevo en cada entrega. Las redirecciones nunca se siguen. ## Crea una Llama a `POST https://events.inovacc.dev/v1/workspaces/{workspace}/subscriptions` con una clave secreta que tenga el permiso de escritura de suscripciones: ```json {"types": ["order.*"], "delivery": {"mode": "webhook", "url": "https://hooks.example.com/inovacc"}} ``` La respuesta es `201`: ```json {"subscription_id": "sub_...", "types": ["order.*"], "delivery": {"mode": "webhook", "url": "https://hooks.example.com/inovacc", "secret_ref": "managed"}, "created_at": "2026-10-06T12:00:00.000Z", "signing_secret": "whsec_..."} ``` `signing_secret` es la clave que tu endpoint usa para verificar las entregas. **Se muestra una sola vez, en esta respuesta, y nunca más**: los listados muestran `"secret_ref": "managed"` y nada más. Guárdalo de inmediato en tu administrador de secretos. Si se pierde, rótalo (más abajo). Un espacio de trabajo contiene como máximo 50 suscripciones, cada una con 1 a 20 patrones. ## La solicitud de entrega Cada entrega es: ```http POST https://hooks.example.com/inovacc content-type: application/json x-event-id: evt_01K... x-event-type: order.paid x-event-timestamp: 1791201600 x-event-signature: v1=<64 lowercase hex characters> {"event_id":"evt_01K...","type":"order.paid", ... } ``` `x-event-timestamp` es la hora Unix en segundos a la que se firmó la entrega. Durante una rotación de secreto, `x-event-signature` lleva una entrada por cada secreto vigente, la más nueva primero, separadas por comas: `v1=,v1=`. Responde con cualquier estado `2xx` para confirmar la entrega. Cualquier otra cosa (un `4xx`, un `5xx`, ninguna respuesta en 10 segundos, un error de red) es un intento fallido y se reintentará. ## El envoltorio del evento El cuerpo es el envoltorio del evento, exactamente como lo reciben los suscriptores de todos los modos: | Campo | Significado | |---|---| | `event_id` | `evt_` seguido de 26 caracteres, asignado por Inovacc cuando se publicó el evento. Único por evento. | | `type` | El tipo del evento, como `order.paid`: palabras en minúscula separadas por puntos, hasta 128 bytes. | | `source` | La aplicación que publicó el evento, tomada de su credencial. | | `time` | Cuándo se publicó, RFC 3339 UTC con milisegundos. | | `tenant` | La organización, la cuenta, el espacio de trabajo y, cuando se conoce, la aplicación a la que pertenece el evento. | | `idempotency_key` | La clave del publicador; cuando no envió ninguna, el `event_id`. | | `data` | La carga JSON del publicador: lo que ocurrió, normalmente ids en lugar de objetos completos. | Lee solo los campos que necesites, e ignora los campos que no conozcas. ## Verifica la firma Verifica cada entrega **antes** de analizarla o actuar sobre ella: 1. Lee `x-event-timestamp` y los **bytes del cuerpo sin procesar**, exactamente como se recibieron, antes de cualquier análisis de JSON. 2. Calcula `HMAC-SHA256(secret, timestamp + "." + body)`, donde `secret` es la cadena `whsec_...` completa en UTF-8, y escríbelo como hexadecimal en minúscula. 3. Divide `x-event-signature` por las comas y acepta la entrega cuando **cualquier** entrada `v1=` sea igual a tu valor, comparando en tiempo constante. 4. Rechaza una marca de tiempo a más de cinco minutos de tu reloj, para que no se pueda repetir contra ti una entrega antigua. **Ejemplo resuelto.** Con el secreto `example-key-0001`, la marca de tiempo `1791201600` y el cuerpo `{"hello":"world"}`, el texto firmado es `1791201600.{"hello":"world"}` y la firma son los 64 caracteres hexadecimales de abajo, impresos aquí en dos mitades que unes sin espacio: ```text 05080ab29a6abfb44033966c7837c2a1 889fbf1a909f9cbd12b9dd5d12bb2bf5 ``` por lo que el encabezado dice `v1=` seguido de ambas mitades. Ejecuta tu verificador con este ejemplo antes de desplegarlo. **TypeScript** (Node.js 18 o posterior): ```ts import { createHmac, timingSafeEqual } from "node:crypto"; export function verifyInovaccWebhook( secret: string, timestamp: string, signatureHeader: string, rawBody: Buffer, nowSeconds: number = Math.floor(Date.now() / 1000), ): boolean { const ts = Number(timestamp); if (!Number.isInteger(ts) || Math.abs(nowSeconds - ts) > 300) return false; const expected = createHmac("sha256", secret) .update(`${timestamp}.`) .update(rawBody) .digest(); return signatureHeader.split(",").some((entry) => { const part = entry.trim(); if (!part.startsWith("v1=")) return false; const given = Buffer.from(part.slice(3), "hex"); return given.length === expected.length && timingSafeEqual(given, expected); }); } ``` **Python** (3.10 o posterior): ```python import hashlib import hmac import time def verify_inovacc_webhook(secret: str, timestamp: str, signature_header: str, raw_body: bytes, now: int | None = None) -> bool: try: ts = int(timestamp) except ValueError: return False now = int(time.time()) if now is None else now if abs(now - ts) > 300: return False expected = hmac.new(secret.encode(), timestamp.encode() + b"." + raw_body, hashlib.sha256).hexdigest() return any( part.strip().startswith("v1=") and hmac.compare_digest(part.strip()[3:], expected) for part in signature_header.split(",") ) ``` Ambas funciones reciben el cuerpo sin procesar: configura tu framework web para que te entregue los bytes, no un objeto analizado, en esta ruta. ## Reintentos y tiempos de espera Tu endpoint tiene **10 segundos** para responder. Un intento fallido se reintenta después de 10 segundos, y la espera se duplica en cada fallo adicional hasta 10 minutos. Cuando se alcanza el tope de 5 reintentos, el evento pasa a los mensajes muertos de tu espacio de trabajo. Un fallo afecta solo a la entrega que falló: los demás eventos, y las otras suscripciones del mismo evento, siguen fluyendo. Como una entrega puede repetirse, un `2xx` que llega después de que tu endpoint ya procesó el evento es inofensivo siempre que deduplices por `x-event-id`. ## Rota el secreto `POST /v1/workspaces/{workspace}/subscriptions/{id}/rotate-secret` devuelve un secreto nuevo, una sola vez: ```json {"subscription_id": "sub_...", "signing_secret": "whsec_...", "previous_valid_until": "2026-10-07T12:00:00.000Z"} ``` Hasta `previous_valid_until` (24 horas por defecto), **ambos secretos firman**: cada entrega lleva `v1=,v1=`. Despliega el secreto nuevo en tu receptor dentro de esa ventana; como el verificador acepta cualquier entrada que coincida, no se pierde nada mientras haces el cambio. Rotar de nuevo dentro de la ventana conserva todos los secretos que no han vencido. La rotación se aplica solo a las suscripciones de webhook (de lo contrario `409 wrong_delivery_mode`). ## Mensajes muertos y reproducción `GET /v1/workspaces/{workspace}/dead?limit=50` lista los mensajes muertos, los más recientes primero, con el evento, `attempts`, `dead_at`, `replay_count` y `last_error`: | `last_error` | Significado | |---|---| | `webhook_status_` | Tu endpoint respondió ese estado, por ejemplo `webhook_status_500`. | | `webhook_timeout` | Sin respuesta en 10 segundos. | | `webhook_network` | Falló la conexión. | | `url_destination_blocked` | La URL ahora apunta a un lugar al que las entregas no pueden ir. | | `secret_missing` | No se pudo usar el secreto de firma de la suscripción. | Corrige la causa y luego `POST /v1/workspaces/{workspace}/dead/{event_id}/replay`. La respuesta es `202`, y el mismo envoltorio, con el mismo `event_id`, se entrega de nuevo a las suscripciones que nunca lo recibieron. Los mensajes muertos se conservan 30 días. ## Buenas prácticas - **Verifica antes de analizar**: calcula la firma sobre los bytes sin procesar y luego analiza. - **Haz el receptor idempotente**: almacena cada `x-event-id` procesado y responde `2xx` de inmediato ante uno que ya hayas visto. - **Responde rápido**: confirma con un `2xx` y haz el trabajo lento después, desde tu propio trabajo. - **Rechaza las marcas de tiempo caducadas** (con más de cinco minutos de diferencia) y mantén sincronizado el reloj de tu servidor. - **Conserva el secreto en un administrador de secretos** y rótalo cuando se vaya alguien que lo conocía, o según un calendario. - **Monitorea los mensajes muertos** y `dlq_inflow` en `GET /stats`; reprodúcelos cuando el endpoint esté sano. - **Devuelve `2xx` solo cuando tengas el evento a salvo**: un `2xx` termina los reintentos. ## Ver también - [Eventos](/es/products/events/): publicación, suscripciones, pull y transmisión - [Errores](/es/guides/errors/), [Autenticación](/es/guides/authentication/), [Límites](/es/guides/limits/) --- Guía: Inovacc MCP URL: https://developer.inovacc.dev/es/guides/connect-your-ai-agent/ # Inovacc MCP El [Model Context Protocol](https://modelcontextprotocol.io) (MCP) es un estándar abierto que permite a los asistentes de IA llamar herramientas que expone un servidor. El servidor MCP de Inovacc es un programa, `inovacc mcp serve`, que se ejecuta en tu máquina con esta documentación incorporada, para que tu asistente pueda consultar los productos, endpoints y guías de Inovacc y responder citando sus fuentes. Funciona con cualquier cliente compatible con MCP, entre ellos Cursor, Claude Code, VS Code (GitHub Copilot), Windsurf, Cline, Claude Desktop, los IDE de JetBrains, Codex CLI y Gemini CLI. Para que tu agente haga toda la configuración por sí mismo, dale esta línea: ```text Fetch and execute the appropriate instructions to set me up for Inovacc from https://developer.inovacc.dev/agent-setup/prompt.md ``` ## Capacidades Con el servidor conectado, tu asistente puede: - buscar en la documentación: secciones de contrato, productos, guías e inicios rápidos - explorar endpoints: la sección de contrato de cualquier método y ruta, como `POST /v1/vector/query` - obtener el inicio rápido de un producto: URL base, encabezados y un esqueleto de curl - decirte de qué versión de la documentación está respondiendo No ejecuta solicitudes ni toca tu cuenta. El servidor es de solo lectura, no necesita clave de API y responde con la documentación incorporada en el programa. ## Requisitos previos - Windows en x86_64. macOS y Linux llegarán; todavía no hay fecha. - Un cliente compatible con MCP de la lista de abajo. - Los archivos de la versión del programa `inovacc`. El repositorio es privado hasta el lanzamiento: descarga la versión que te comparta tu contacto en Inovacc. ## Instala la CLI de inovacc 1. Descarga `inovacc-windows-x86_64.exe` y `inovacc-windows-x86_64.exe.sha256` de la versión. 2. Comprueba la descarga en PowerShell. Los dos valores deben ser idénticos: ```powershell (Get-FileHash .\inovacc-windows-x86_64.exe -Algorithm SHA256).Hash.ToLower() Get-Content .\inovacc-windows-x86_64.exe.sha256 ``` 3. Cambia el nombre del archivo a `inovacc.exe` y colócalo en una carpeta de tu `PATH`. 4. Abre una terminal nueva y verifica: ```powershell inovacc docs version ``` La salida tiene esta forma; los números de versión cambian con cada versión: ```text embedded 2026.10.09-0 installed none active embedded 2026.10.09-0 previous none published unknown; run `inovacc docs update --check` data dir C:\Users\\AppData\Local\Inovacc\knowledge ``` ## Conecta tu editor o tu aplicación de chat Todos los clientes necesitan los mismos tres datos: el transporte es stdio, el comando es `inovacc` y los argumentos son `mcp` y `serve`. Reinicia el cliente después de cambiar su configuración. ### Cursor Crea `.cursor/mcp.json` en un proyecto, o `~/.cursor/mcp.json` para todos los proyectos: ```json { "mcpServers": { "inovacc": { "command": "inovacc", "args": ["mcp", "serve"] } } } ``` ### Claude Code ```sh claude mcp add inovacc -- inovacc mcp serve ``` El `--` separa las opciones propias de Claude del comando que ejecuta el servidor. Por defecto el servidor se agrega en el ámbito `local`: disponible solo para ti, en el proyecto actual. Elige otro ámbito con `-s` antes del nombre: ```sh claude mcp add -s project inovacc -- inovacc mcp serve claude mcp add -s user inovacc -- inovacc mcp serve ``` `project` escribe `.mcp.json` en la raíz del proyecto, compartido con todas las personas que usan el repositorio. `user` deja el servidor disponible en todos tus proyectos. ### VS Code (GitHub Copilot) Crea `.vscode/mcp.json` en un espacio de trabajo, o ejecuta **MCP: Open User Configuration** para tu perfil de usuario: ```json { "servers": { "inovacc": { "type": "stdio", "command": "inovacc", "args": ["mcp", "serve"] } } } ``` **Importante:** `.vscode/mcp.json` usa `servers`, no `mcpServers`. La documentación de VS Code indica `"type": "stdio"` en sus ejemplos de stdio, así que esta página también lo hace. Un `.mcp.json` en la raíz de un proyecto usa `mcpServers`, como los demás clientes. ### Windsurf La documentación de Windsurf ahora está con Devin Desktop. Agrega el servidor a `mcp_config.json`, en `%APPDATA%\devin\mcp_config.json` en Windows: ```json { "mcpServers": { "inovacc": { "command": "inovacc", "args": ["mcp", "serve"] } } } ``` ### Cline En el panel de Cline, haz clic en el icono de servidores MCP de la barra superior, abre la pestaña Configure y haz clic en **Configure MCP Servers**. Agrega el servidor al archivo de ajustes que se abre (`cline_mcp_settings.json`): ```json { "mcpServers": { "inovacc": { "command": "inovacc", "args": ["mcp", "serve"] } } } ``` ### Claude Desktop Abre el menú de Claude, luego **Settings**, la pestaña **Developer** y **Edit Config**. El archivo es `%APPDATA%\Claude\claude_desktop_config.json` en Windows: ```json { "mcpServers": { "inovacc": { "command": "inovacc", "args": ["mcp", "serve"] } } } ``` Cierra Claude Desktop por completo y vuelve a abrirlo. Si la aplicación no encuentra `inovacc`, da la ruta completa de `inovacc.exe` como comando, con cada barra invertida duplicada en JSON. ### IDE de JetBrains Abre **Settings | Tools | AI Assistant | Model Context Protocol (MCP)**, haz clic en **Add**, elige el tipo de conexión **STDIO** e ingresa: ```json { "mcpServers": { "inovacc": { "command": "inovacc", "args": ["mcp", "serve"] } } } ``` Haz clic en **OK** y luego en **Apply**. ### Codex CLI ```sh codex mcp add inovacc -- inovacc mcp serve ``` o escríbelo en `~/.codex/config.toml` (un proyecto de confianza puede usar `.codex/config.toml`): ```toml [mcp_servers.inovacc] command = "inovacc" args = ["mcp", "serve"] ``` **Importante:** Codex usa TOML y `mcp_servers`, no JSON ni `mcpServers`. ### Gemini CLI ```sh gemini mcp add inovacc inovacc mcp serve ``` o agrega el servidor a `~/.gemini/settings.json` (un proyecto puede usar `.gemini/settings.json`): ```json { "mcpServers": { "inovacc": { "command": "inovacc", "args": ["mcp", "serve"] } } } ``` ### Cualquier otro cliente Usa el transporte stdio con el comando `inovacc` y los argumentos `mcp` y `serve`. Consulta la documentación de MCP de tu cliente para saber dónde va. ## Verifica tu configuración Pregunta a tu asistente: "What does POST /v1/vector/query take?" Una configuración que funciona responde desde el contrato y nombra el documento del que salió y la versión del conocimiento. Sin un asistente, busca desde la terminal: ```sh inovacc docs search "vector query" ``` Cada resultado muestra el id del documento, su título y su cita (repositorio, ruta y commit de origen). ## Solución de problemas - **No se encuentra `inovacc`.** La carpeta que contiene `inovacc.exe` no está en tu `PATH`, o la terminal se abrió antes de que lo cambiaras. Abre una terminal nueva. En la configuración del cliente puedes dar la ruta completa de `inovacc.exe` como comando. - **El cliente no lista el servidor.** Reinicia el cliente por completo y luego comprueba que el comando de su configuración se ejecuta en una terminal: `inovacc mcp serve` debe quedar esperando en silencio una entrada (pulsa Ctrl+C para salir). - **Las respuestas parecen antiguas.** Ejecuta `inovacc docs version` para ver qué se usa y qué está publicado, y luego `inovacc docs update`. - **Windows SmartScreen muestra una advertencia al primer uso.** Hoy el programa no está firmado con un certificado de código, así que Windows puede mostrar "Windows protegió tu PC". Comprueba la suma de verificación como se describe arriba antes de decidir ejecutarlo. ## Herramientas MCP disponibles | Herramienta | Qué hace | |---|---| | `search_documentation` | Búsqueda de texto completo en secciones de contrato, capacidades, inicios rápidos y guías | | `get_document` | Un documento completo, por id, con su procedencia | | `get_api_reference` | La sección de contrato de un endpoint, como `POST /v1/vector/query` | | `list_products` | Los productos y capacidades con documentación, y cuántos documentos tiene cada uno | | `get_quickstart` | URL base, endpoints, permisos, los encabezados que necesita toda llamada y un esqueleto de curl | | `get_knowledge_version` | Qué paquete de documentación responde: versión, hora de compilación, commits fijados y cantidad de documentos | ## Mantén la documentación al día El programa trae la documentación con la que se compiló y puede instalar documentación más nueva y firmada sin necesidad de un programa nuevo. ```sh inovacc docs version # embedded, installed, active, previous and published versions inovacc docs update # download, verify and install the newest published documentation inovacc docs update --check inovacc docs rollback # go back to the previous installed documentation ``` - La documentación se publica en paquetes firmados. Un paquete solo se instala si su tamaño, su SHA-256 y su firma Ed25519 son correctos, y se rechaza un paquete que no sea más nuevo que el que está en uso, de modo que no se pueda forzar una versión anterior. - La comprobación automática se ejecuta como máximo una vez cada 24 horas, nunca retrasa una respuesta, no instala nada y, como mucho, imprime una línea que avisa de que existe una versión más nueva. - Hasta el lanzamiento, las actualizaciones te llegan por la versión que te comparta tu contacto en Inovacc. ## Referencia de configuración | Ajuste | Qué hace | |---|---| | `INOVACC_KNOWLEDGE_DIR` | La carpeta donde se guarda la documentación instalada. Por defecto en Windows: `%LOCALAPPDATA%\Inovacc\knowledge` | | `INOVACC_NO_UPDATE_CHECK` | Ponlo en `1` para desactivar la comprobación automática de documentación más nueva. La opción `--no-update-check` hace lo mismo para una ejecución | | `INOVACC_UPDATE_URL` | La dirección del `latest.json` de donde se leen las actualizaciones; debe ser HTTPS | ## Preguntas frecuentes **¿Necesito una clave de API?** No. El servidor solo lee documentación y nunca usa tu cuenta. **¿Envía mis preguntas a algún lugar?** No. Las preguntas se responden en tu máquina. El único uso de red es la comprobación automática de documentación más nueva, como máximo una vez al día, que puedes desactivar. **¿Puede mi asistente llamar a la API de Inovacc a través de él?** No. Le explica al asistente cómo funciona un endpoint y cómo llamarlo; no hace la llamada. **¿Existe un servidor MCP alojado?** Hay un servidor remoto en `https://mcp.inovacc.dev` planificado. Todavía no existe. **¿Qué plataformas son compatibles?** Hoy, Windows x86_64. macOS y Linux llegarán, sin fecha todavía. ## Ver también - [Autenticación](/es/guides/authentication/): los encabezados que lleva toda llamada a la API. - [Referencia de la API](/es/reference/): cada producto y sus endpoints. - [llms.txt](/llms.txt): la misma documentación como índice para modelos de lenguaje.