Biblioteca multimedia
La biblioteca multimedia contiene archivos que un espacio de trabajo sube una vez y usa en cualquier clip: imágenes, video MP4, sonido y fuentes. El contenido hace referencia a un archivo como media://{id}, nunca por URL, y Reelwire entrega al renderizador una descarga privada de él para cada trabajo. Un archivo de la biblioteca no tiene ninguna dirección pública duradera.
Nada se redimensiona, recodifica ni reduce a miniatura al entrar. Un archivo se guarda byte a byte y se cuenta con el tamaño con el que llegó.
En el panel es la pantalla Medios, con tres tarjetas: Medios (la biblioteca), Clips renderizados y Reparto del almacenamiento.
| Método | Ruta | Permiso |
|---|---|---|
GET | /v1/media | media:read |
POST | /v1/media | media:write |
POST | /v1/media/import | media:write |
POST | /v1/media/upload-link | media:write |
POST | /v1/media/{id}/rename | media:write |
POST | /v1/media/{id}/delete | media:write |
GET | /v1/media/{id}/uses | media:read |
GET | /v1/media/renders | media:read |
GET | /v1/media/renders/{id}/reuse | media:read |
POST | /v1/media/renders/{id}/delete | media:write |
POST | /v1/media/renders/{id}/copy | media:write |
GET | /v1/assets/{id} | media:read |
GET | /v1/assets/{id}/download | ninguna: la firma en la URL |
POST | /v1/storage/split | propietario con sesión iniciada |
El asistente de IA crea imágenes, videos, música y fuentes, y guardar uno de ellos lo incorpora a esta biblioteca con las mismas reglas que una subida.
La subida es multipart, y solo multipart
POST /v1/media acepta exactamente un archivo como multipart/form-data. No base64 en un cuerpo JSON, no una URL que obtener. El nombre del campo no importa: la ruta toma la primera parte de archivo que encuentra, así que envíe solo una.
Con curl, eso es -F y un @path. Indique el tipo explícitamente, porque curl lo deduce de la extensión y se equivoca con las fuentes:
curl http://localhost:4000/v1/media \
-H "Authorization: Bearer rw_your_key_here" \
-F "file=@docs-example-logo.png;type=image/png"
No establezca además Content-Type: multipart/form-data a mano. El boundary es parte de ese encabezado, y uno escrito a mano sin él hace que el cuerpo no se pueda interpretar.
{
"file": {
"id": "01M2SQSRPWVG87Y0TD6C77XE61",
"name": "docs-example-logo.png",
"mimeType": "image/png",
"kind": "Image",
"byteSize": 74,
"included": false,
"createdAt": "2026-09-18T07:48:21.347Z",
"uploadedBy": "apikey:01M2SQKKZ3ZPXK8RB7BK5QJ3GH",
"ref": "media://01M2SQSRPWVG87Y0TD6C77XE61",
"url": "http://localhost:4000/v1/assets/01M2SQSRPWVG87Y0TD6C77XE61/download?exp=1789721301&sig=8eeefc2bed6398b1bfcadc69286fce5436c6a28f4c29f5321091351e160c527f"
},
"storage": {
"maxMb": 500,
"maxBytes": 524288000,
"usedBytes": 28182659,
"usedBy": { "media": 10821961, "renders": 17360698 },
"remainingBytes": 496105341,
"includedMb": 500,
"mediaPercent": 30,
"media": { "maxBytes": 157286400, "usedBytes": 10821961, "maxMb": 150, "usedMb": 10.3, "remainingBytes": 146464439 },
"clips": { "maxBytes": 367001600, "usedBytes": 17360698, "maxMb": 350, "usedMb": 16.6, "remainingBytes": 349640902 },
"split": { "minPercent": 10, "maxPercent": 90 }
}
}
201. ref es lo que pone en el contenido. url es una vista previa firmada, válida durante una hora, para mostrar el archivo a una persona. Guarde el id, nunca la URL. El contenido también puede nombrar un archivo por su nombre, media://docs-example-logo.png, que es lo que hacen los ejemplos propios de las plantillas.
uploadedBy registra qué credencial lo hizo, apikey:{keyId} o user:{email}, y es null para los archivos iniciales con los que llegó un espacio de trabajo. included es true exactamente para esos archivos mientras sigan como llegaron: no cuentan contra ningún almacenamiento (consulte Con qué empieza un espacio de trabajo nuevo), y byteSize sigue indicando su tamaño.
Campos de formulario adicionales
Dos partes de texto opcionales cambian el significado de una subida:
| Campo | Qué hace |
|---|---|
replace | "true" permite que la subida asuma un nombre que ya está en la biblioteca. |
replaces | Un id de archivo multimedia. Sustituye ese archivo, conserva su nombre con la extensión del archivo nuevo y apunta al nuevo cada marca en la que /uses (abajo) encuentra el archivo antiguo, y cada publicación de una lista de publicaciones que lo nombraba. |
Una subida con replaces debe ser del mismo tipo de archivo: un logo no se convierte en un video. El archivo nuevo recibe un id nuevo en lugar de asumir el del antiguo, de modo que nada que haya guardado en caché los bytes antiguos por id pueda renderizar la imagen antigua con el nombre nuevo.
Sin ninguno de los dos, un nombre que ya está en la biblioteca es un 409:
{
"error": {
"type": "https://reelwire.io/errors/conflict",
"code": "conflict",
"message": "docs-example-logo.png is already in the media library. Upload it again and choose to replace it.",
"requestId": "01M2SREXKAJRENJMBM4S1CXZ74",
"details": []
}
}
Lo que admite la biblioteca
| Tipo | Formatos | Límite |
|---|---|---|
| Imagen | image/png, image/jpeg, image/webp, image/gif, image/svg+xml | 2048 por 2048 píxeles |
| Video | video/mp4 | 50 MB |
| Audio | audio/mpeg, audio/wav | 20 MB |
| Fuente | font/woff2, font/woff, font/ttf, font/otf | 10 MB |
50 MB es el límite que ninguna subida puede superar, sea del tipo que sea, y la cuota de almacenamiento del espacio de trabajo sigue limitando el total.
El límite de píxeles se lee del propio encabezado del archivo y no de lo que declara el cliente, y una imagen cuyo tamaño no se puede leer se rechaza, porque no se puede demostrar que esté dentro del límite. El SVG está exento: es un dibujo, que se renderiza nítido al tamaño que necesite un clip.
Directamente desde un teléfono
Lo que produce un teléfono no cabe en ninguno de estos límites, así que la biblioteca lo adapta al llegar, por cualquier vía de entrada (subida, enlace de subida, importación):
- Un PNG, JPEG o WebP de más de 2048 píxeles por lado se reduce para que quepa, en su propio formato, girado como indicó su cámara, y se eliminan sus datos EXIF (incluido el lugar donde se tomó). Un GIF no, y tiene que caber.
- Una foto HEIC o HEIF de un iPhone se convierte en un JPEG, girado como lo sostenía su cámara, reducido donde supere los 2048 píxeles por lado, con los metadatos eliminados y un nombre que termina en
.jpg. Envíela comoimage/heicoimage/heif, o con un nombre.heico.heif. - Un video que no es un MP4 H.264 que la biblioteca pueda conservar: un MOV, HEVC, de más de 1920 píxeles por lado o de más de 50 MB. Se convierte en un MP4 H.264 de como máximo 1920 píxeles por lado, de menos de 45 MB, con los metadatos eliminados y un nombre que termina en
.mp4. Una subida puede tener hasta 300 MB al entrar (una importación, 50 MB), y un video largo tarda uno o dos minutos en convertirse.
Un archivo que ya cabe se conserva byte a byte, como siempre.
Las fuentes y los archivos de sonido se reconocen por su extensión de archivo y no por su tipo declarado. Los navegadores no coinciden en cómo llamar a un .otf o a un .wav, así que decide la terminación.
Un archivo que no cabe en lo que queda de la parte de medios del almacenamiento se rechaza con un 429 que indica cuánto espacio hay. La biblioteca nunca elimina nada para hacer sitio: eso le corresponde a usted, o dar a los medios una parte mayor (abajo).
Otras dos formas de entrar
Desde una dirección pública. POST /v1/media/import obtiene un archivo de la web y lo incorpora a la biblioteca, que es como un asistente trae una imagen a la que se le indicó. Acepta url y, opcionalmente, name y replace: true, y responde exactamente como una subida. La dirección tiene que ser HTTPS público sin nombre de usuario ni contraseña; se comprueba cada dirección a la que se resuelve o redirige, con un máximo de tres redirecciones, y se rechaza una interna.
curl http://localhost:4000/v1/media/import \
-H "Authorization: Bearer rw_your_key_here" \
-H "Content-Type: application/json" \
-d '{"url":"https://www.example.com/press/logo.png","name":"press-logo.png"}'
A través de un enlace de subida de un solo uso. POST /v1/media/upload-link entrega un enlace que admite archivos en esta biblioteca sin credencial, para algo que tiene un archivo pero ninguna clave, como el entorno aislado de código de un asistente. Envíe { "replace": true } si los archivos pueden asumir nombres que ya están en la biblioteca.
{
"url": "http://localhost:4000/v1/upload-files?token=...",
"expiresAt": "2026-09-18T08:03:21.000Z",
"maxFiles": 10,
"method": "POST",
"field": "file",
"example": "curl -F \"file=@picture.png\" \"http://localhost:4000/v1/upload-files?token=...\""
}
El enlace dura quince minutos y admite hasta diez archivos, uno por solicitud y como máximo 30 solicitudes por minuto, cada uno sujeto a las reglas anteriores. Un campo de formulario name junto al archivo lo renombra al entrar. Deja de funcionar de inmediato si se revoca la clave que lo pidió.
Con qué empieza un espacio de trabajo nuevo
Cada espacio de trabajo nuevo encuentra estos archivos en su biblioteca, de modo que el primer clip que renderiza muestra lo que pueden hacer una plantilla y una marca en lugar de cuadros grises:
| Archivo | Para qué sirve |
|---|---|
example-image.jpg, example-image.png, example-image.svg | Las imágenes que piden por nombre los ejemplos propios de las plantillas de referencia. |
example-image-with-animation.svg | El mismo paisaje con el clima cambiando, un SVG animado. Los espacios de trabajo creados antes de que se añadiera también lo recibieron. |
example-logo.png, example-badge.png | El logo y el rótulo de la Default brand. |
example-background-image-universal.jpg | El fondo de la Default brand: una imagen, un cuadrado de 1440 hecho para recortarse desde el centro, que llena cada formato. |
example-stinger-video-universal.mp4, example-intro-video-universal.mp4, example-outro-video-universal.mp4 | La cortinilla, el intro y el outro de la Default brand: un clip cada uno, un cuadrado de 1440 hecho para recortarse desde el centro, que se reproduce en cada formato. Están desactivados hasta que la marca los activa. |
Una versión anterior de este conjunto daba además un fondo, una cortinilla, un intro y un outro para cada uno de los cinco formatos, veinte archivos más. Ya no están: se retiraron de toda biblioteca que aún los tenía tal como se entregaron, y de toda marca que los usaba, que reproduce en su lugar el archivo universal. | example-font.ttf | Bebas Neue, la tipografía de titulares de la Default brand. | | example-sound.mp3 | Treinta segundos de música, la banda sonora de la Default brand. |
Los espacios de trabajo creados antes de que se añadieran el fondo cuadrado y los clips de cortinilla, intro y outro también los recibieron. Son copias propias del espacio de trabajo, que puede renombrar, sustituir o eliminar libremente, y están incluidos: no cuentan contra ningún almacenamiento, y la biblioteca dice Incluido bajo Tamaño. La marca solo se mantiene mientras un archivo sea exactamente como llegó. Un archivo que sustituye a uno cuenta por completo, y también cualquier subida, incluso de los mismos bytes, de modo que el conjunto gratuito no puede crecer ni cambiarse por algo mayor. El ejemplo de una plantilla encuentra sus imágenes por nombre, así que solo las muestra mientras la biblioteca siga teniendo un archivo con ese nombre. Consulte la Default brand.
El resto de la biblioteca
GET /v1/media lista cada archivo, el más reciente primero, con el almacenamiento que ocupa. No está paginado: toda la biblioteca vuelve en una sola respuesta, porque una biblioteca son cientos de archivos y las pantallas la buscan y la ordenan por sí mismas.
{
"files": [
{
"id": "01M2S2CPZVEWKJ6TW9EATDYMJB",
"name": "test-font-roboto-mono.ttf",
"mimeType": "font/ttf",
"kind": "Font",
"byteSize": 183700,
"included": false,
"createdAt": "2026-09-18T01:34:13.501Z",
"uploadedBy": "user:enterprise2-1@example.com",
"ref": "media://01M2S2CPZVEWKJ6TW9EATDYMJB",
"url": "http://localhost:4000/v1/assets/01M2S2CPZVEWKJ6TW9EATDYMJB/download?exp=1789721209&sig=a7f7bb9d4bc230b28b59b634056a10071941c066b25f7ed16ebbefd0762594d2"
}
],
"storage": { "maxMb": 500, "usedBytes": 28182585 }
}
GET /v1/media/{id}/uses indica qué se rompería si eliminara un archivo: las marcas que lo usan, esté donde esté el archivo en la marca (su logo, rótulo, un fondo o un clip para un formato como intro-9x16, una fuente o la banda sonora, incluidos los archivos propios de un idioma), y las publicaciones de listas de publicaciones, que aún han de salir, cuyo contenido lo nombra. La Default brand toma su tipografía y su banda sonora de la biblioteca exactamente de esa manera.
curl http://localhost:4000/v1/media/01M2SQSRPWVG87Y0TD6C77XE61/uses \
-H "Authorization: Bearer rw_your_key_here"
{
"uses": [
{ "kind": "brand", "id": "01M1VFRWE7VHP3H54ESRJQ0SAJ", "label": "Brand Brand 1: Logo" }
]
}
Pregunte antes de eliminar. Un uses vacío es la única respuesta segura.
POST /v1/media/{id}/rename admite { "name": "..." }, de 1 a 200 caracteres, y conserva la terminación del archivo escriba lo que escriba: un .png sigue siendo un .png. Las referencias son por id, así que nada que use el archivo lo nota. POST /v1/media/{id}/delete elimina el archivo y sus bytes y responde con el almacenamiento tal como queda. Todos esos espacios de marca que lo contenían se vacían en el mismo paso; una publicación que sigue esperando conserva su contenido y se renderiza sin la imagen.
Clips renderizados
Los clips que produjo un render se conservan aparte de las subidas, en la parte del almacenamiento destinada a clips, y son la tarjeta Clips renderizados de la pantalla Medios. Las exportaciones directas conservadas, los videos creados con Crear clip en el Estudio de clips o a través de la API, también aparecen aquí, con source: "export" donde el clip de una publicación dice source: "post". Cuando la parte de clips está llena, se elimina el clip más antiguo de cualquiera de los dos tipos para hacer sitio a uno nuevo, con el mismo efecto sobre sus publicaciones que eliminarlo (abajo); un clip que se está entregando en ese momento se salta.
El clip de una publicación tiene al lado una pequeña imagen de vista previa. La imagen no aparece en la lista, ocupa su espacio en la misma parte y desaparece cuando desaparece su clip.
curl http://localhost:4000/v1/media/renders \
-H "Authorization: Bearer rw_your_key_here"
{
"clips": [
{
"id": "01M2SQPAPH4YF66ZKK5Y5XA9Y7",
"kind": "video",
"mimeType": "video/mp4",
"byteSize": 740641,
"ratio": "9x16",
"locale": "en",
"width": 1080,
"height": 1920,
"durationMs": 10000,
"createdAt": "2026-09-18T07:46:28.697Z",
"templateId": "tpl_dd7e6b264fb8",
"templateName": "Big number",
"channelName": "Channel 10 en",
"url": "http://localhost:4000/v1/assets/01M2SQPAPH4YF66ZKK5Y5XA9Y7/download?exp=1789721392&sig=ae02f120693a5043646e04d5bd65a899deea94add53450aea4e9e510053bf1fb",
"needed": false,
"neededBecause": "Awaiting approval",
"source": "post"
}
]
}
GET /v1/media/renders/{id}/reuse responde, bajo reuse, con aquello de lo que se hizo un clip: su plantilla, variant, version, brandId, ratio, locale y data, el contenido en la forma propia de la plantilla con locales para otros idiomas. Es la misma respuesta que la reutilización de una publicación, para el clip de una publicación o una exportación directa conservada, lista para enviarse a POST /v1/direct-export/video tras un cambio. No lee ni cambia nada más.
POST /v1/media/renders/{id}/copy, Copiar a la biblioteca en la tarjeta Clips renderizados, pone una copia de un clip, de una publicación o de una exportación directa conservada, en la biblioteca multimedia como archivo propio, para usarlo de nuevo como una subida: como intro u outro de una marca, o como video en el contenido de una plantilla. El clip permanece donde está y rota como antes; la copia permanece hasta que se elimine. Se guarda según las reglas propias de la biblioteca, de modo que se aplican el tamaño de un archivo y la cuota de almacenamiento, y un rechazo es el que recibiría una subida del mismo archivo. El cuerpo puede nombrar el archivo, { "name": "Summer teaser" }, cuya terminación es siempre la del clip, y un nombre propio que la biblioteca ya contiene es un 409. Si se omite, el nombre es la plantilla, el formato, el idioma y el día en que se renderizó, dejado libre con -2, -3 donde está ocupado, así que copiar dos veces crea dos archivos. Un id de clip que ya no existe, eliminado para hacer sitio o borrado, es un 404. La respuesta es 201 con el archivo de la biblioteca, como la de una subida:
{
"file": {
"id": "01M2SR3V8N0Q4K7XG5D2W9PJ6T",
"name": "Big number 9x16 en 2026-10-03.mp4",
"mimeType": "video/mp4",
"kind": "Video",
"byteSize": 740641,
"createdAt": "2026-10-03T09:12:44.120Z",
"uploadedBy": "user:enterprise2-1@example.com",
"ref": "media://01M2SR3V8N0Q4K7XG5D2W9PJ6T",
"url": "http://localhost:4000/v1/assets/01M2SR3V8N0Q4K7XG5D2W9PJ6T/download?exp=..."
},
"storage": { "maxMb": 500, "usedBytes": 28182659 }
}
neededBecause indica qué espera una publicación que aún usa el clip. POST /v1/media/renders/{id}/delete elimina el clip de todos modos, y cada publicación de su trabajo que aún lo espera se cancela con él, que es lo que cuenta cancelled en la respuesta:
{ "deleted": true, "cancelled": 1, "storage": { "maxMb": 500, "usedBytes": 27442018 } }
El único rechazo es needed: true: una publicación se está entregando con este clip en este momento (estado PUBLISHING), y eliminarlo es un 409 hasta que termine. Un clip cuyas publicaciones se han entregado, o han terminado de cualquier otra forma, solo se conserva, y eliminarlo libera almacenamiento; un enlace a él que se entregó entonces responde 404.
POST /v1/storage/split mueve el límite entre ambos, con { "mediaSharePercent": 30 }, que es la tarjeta Reparto del almacenamiento de la pantalla Medios. Cambia en qué se gasta el almacenamiento de pago del espacio de trabajo, así que es del propietario, con la sesión iniciada: una clave es rechazada tenga lo que tenga. Debe ser un entero, y el bloque split de cualquier respuesta de almacenamiento indica el rango que puede tomar; ninguna parte puede bajar de una décima ni de lo que ya contiene, y un valor fuera del rango es un 403.
Descargas firmadas
GET /v1/assets/{id}/download sirve los bytes de cualquier recurso: un clip renderizado, un archivo de marca, un archivo de la biblioteca multimedia, una exportación directa conservada o algo que creó el asistente de IA. Se autoriza con exp y sig en la URL y no con un token bearer, porque estas URL se entregan a un navegador y se pegan en un <video src>, y un elemento de video no puede adjuntar un encabezado Authorization. La firma nombra exactamente un recurso y caduca, que es lo que lo hace seguro. Una firma incorrecta o caducada es un 403: pida una dirección nueva. Admite Range, para que un navegador pueda saltar. Añada &save=1, y &name= para el nombre de archivo, para que se guarde en lugar de abrirse. Una fuente se sirve a cualquier origen (Access-Control-Allow-Origin: *), de modo que una página web o una aplicación de correo en otro sitio pueda cargarla por su dirección firmada; nada más.
GET /v1/assets/{id} devuelve metadatos sin los bytes y admite un token bearer. Responde solo para los clips renderizados de publicaciones: aquí un id de la biblioteca multimedia es un 404, porque los metadatos de un archivo de la biblioteca están en /v1/media, y también los de una exportación directa conservada, que lista /v1/media/renders.
curl http://localhost:4000/v1/assets/01M2SQPAPH4YF66ZKK5Y5XA9Y7 \
-H "Authorization: Bearer rw_your_key_here"
{
"assetId": "01M2SQPAPH4YF66ZKK5Y5XA9Y7",
"kind": "video",
"ratio": "9x16",
"locale": "en",
"mimeType": "video/mp4",
"width": 1080,
"height": 1920,
"durationMs": 10000,
"byteSize": 740641,
"sha256": "19c629bfd5a1ec22a1fb9baed2c043ce23d523d01c5886ffd0d4278be444e5c8",
"url": "http://localhost:4000/v1/assets/01M2SQPAPH4YF66ZKK5Y5XA9Y7/download?exp=1789722005&sig=48542502fdc9cc4b9d0368cefd53b04b7f71f696a3926e0f7e4aadfb9ee5fa23",
"expiresAt": "2026-09-18T09:00:05.000Z"
}
Verifique sha256 después de una descarga. Una URL firmada no es una credencial que guardar: conserve el id y pida una URL nueva.
Errores frecuentes
Enviar JSON. La subida es multipart. Un cuerpo JSON es un 415 que lo dice. Envíe el archivo como parte de formulario, o use /v1/media/import cuando lo que tiene es una dirección.
Establecer usted mismo Content-Type: multipart/form-data. Tiene que llevar el boundary. Deje que lo escriba su cliente HTTP.
Enviar más de un archivo. Uno por solicitud. El límite se aplica, no es una recomendación.
Referirse a un archivo por su URL firmada. El contenido admite media://{id}, y una dirección web en un campo de imagen se rechaza. Una URL firmada sería una URL que caduca, y cualquier otra dirección es una que el renderizador no obtiene.
Eliminar un archivo sin comprobar /uses. Una marca pierde su logo, y el siguiente clip se renderiza sin uno.
Fiarse de la suposición de curl sobre el tipo de una fuente. Indique ;type=font/otf explícitamente, o confíe en la extensión, que es lo que hace el servidor.