API de archivos
Descripción general
La API de archivos aloja archivos en qr3 y proporciona una URL estable para ellos. El código QR apunta a esta URL; puedes reemplazar el archivo que hay detrás en cualquier momento sin tener que volver a imprimir el código.
URL base: https://qr3.app/v1/files
Opcionalmente, puedes vincular un archivo a un código mediante code_id. Esta es la base para la página de destino por código, que enumera todos los archivos públicos de un código.
Los tipos admitidos se verifican mediante magic bytes, no por la extensión del archivo: PDF, PNG, JPEG, WebP, MP4, glTF/GLB, JSON. Un archivo .pdf que no sea un PDF real será rechazado.
Roles
| Operación | Requerido | Recibe 403 |
|---|---|---|
Todos los GET | Cualquier rol | — |
POST /upload, PUT /:id | Rol de escritura | viewer |
DELETE /:id | Rol de eliminación | viewer, contributor |
La regla especial es contributor: este rol puede crear y modificar, pero no eliminar nada.
Subir un archivo
POST /v1/files/upload
Espera multipart/form-data.
| Campo | Obligatorio | Descripción |
|---|---|---|
file | Sí | El archivo |
code_id | No | Vincular a un código (debe pertenecer al Workspace) |
visibility | No | private (por defecto) o public |
curl -X POST https://qr3.app/v1/files/upload \ -H "Authorization: Bearer qr3_sk_..." \ -F "code_id=qr_a1b2c3d4" \ -F "visibility=public"const form = new FormData();form.append('file', fileInput.files[0]);form.append('code_id', 'qr_a1b2c3d4');form.append('visibility', 'public');
const res = await fetch('https://qr3.app/v1/files/upload', { method: 'POST', headers: { Authorization: 'Bearer qr3_sk_...' }, body: form, // Content-Type NICHT selbst setzen — der Boundary fehlt sonst});Respuesta (HTTP 201):
{ "data": { "id": "file_a1b2c3d4", "code_id": "qr_a1b2c3d4", "filename": "datenblatt.pdf", "mime_type": "application/pdf", "size_bytes": 284913, "hash_sha256": "9f86d081884c7d65…", "visibility": "public", "status": "active", "created_at": "2026-03-14T12:00:00.000Z", "download_url": "https://qr3.app/v1/files/file_a1b2c3d4/download", "public_url": "https://qr3.app/f/file_a1b2c3d4" }, "meta": { "request_id": "req_abc123" }}public_url se establece solo si visibility: "public" y status: "active". La dirección no requiere autenticación y es directamente adecuada como URL de destino de un código QR.
Listar archivos
GET /v1/files
Consulta: code_id y visibility, ambos opcionales. Devuelve los archivos activos del Workspace, los más recientes primero.
curl "https://qr3.app/v1/files?code_id=qr_a1b2c3d4" \ -H "Authorization: Bearer qr3_sk_..."Respuesta (HTTP 200):
{ "data": [ { "id": "file_a1b2c3d4", "…": "…" } ], "meta": { "request_id": "req_abc123", "file_count": 3, "total_size_bytes": 812004 }}Obtener información de un archivo
GET /v1/files/:id
Devuelve los metadatos, incluyendo download_url (y public_url para archivos públicos). Accesible para todos los roles.
Descargar un archivo
GET /v1/files/:id/download
Transmite el contenido como Content-Disposition: attachment con Cache-Control: private, no-store; por lo tanto, la respuesta siempre refleja el estado actual, a diferencia de la URL pública /f/:id almacenada en caché.
404 ocurre de dos maneras: el archivo no existe en el Workspace (o ha sido eliminado), o existe pero falta el objeto almacenado.
Reemplazar un archivo
PUT /v1/files/:id
Reemplaza el contenido y conserva la id, y por lo tanto, la dirección pública /f/:id y la download_url. Un código QR ya impreso sigue siendo válido.
curl -X PUT https://qr3.app/v1/files/file_a1b2c3d4 \ -H "Authorization: Bearer qr3_sk_..." \visibility, code_id y created_at se conservan; filename, mime_type, size_bytes y hash_sha256 se actualizan. El límite del plan por archivo se aplica igual que al subirlo, y el límite de almacenamiento del Workspace se verifica contra la diferencia. El límite de “archivos por código” no se aplica, ya que no se añade ningún archivo nuevo.
Eliminar un archivo
DELETE /v1/files/:id
Eliminación lógica (soft-delete): el archivo desaparece inmediatamente de la lista, ya no se puede recuperar y, si es público, deja de entregarse en /f/:id.
Respuesta (HTTP 200):
{ "data": { "id": "file_a1b2c3d4", "deleted": true }, "meta": { "request_id": "req_abc123" }}Límites
| Plan | Máx. por archivo |
|---|---|
| Free | 5 MB |
| Pro | 25 MB |
| Business | 100 MB |
| Agency | 100 MB |
| Enterprise | 250 MB |
Además, se aplica un número máximo de archivos por código y un almacenamiento total por Workspace según el plan. El límite de velocidad de subida es de 100 subidas por hora y Workspace.
Errores
| Estado | Cuándo |
|---|---|
| 400 | Firma de archivo desconocida o no coincidente, cuerpo multipart no válido |
| 401 | API Key faltante o no válida |
| 403 | El rol no tiene permisos para escribir o eliminar |
| 404 | code_id o archivo no encontrado en el Workspace; en descargas también: falta el objeto en el almacenamiento |
| 413 | El archivo supera el límite del plan |
| 422 | Falta el archivo, visibility no válida, se alcanzó el límite de “archivos por código”, se superó el límite de almacenamiento del Workspace |
| 429 | Límite de velocidad de subida (100/hora/Workspace) o límite de velocidad general |
Relacionado
- Archivos y fichas técnicas — a través del panel de control
- Página de destino por código