# Archivos

Almacenamiento de archivos por contenido, con carga por partes para archivos grandes.

URL base: `https://data.inovacc.dev`

## 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-<name>` (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 <credential>`; 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: "<sha256>"`.

**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/<id>/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/)

## 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 '{}'
```
