Almacenamiento de archivos
Descargar tipos de TypeScriptSDK
import { FSSClient } from '@ircg/fss'
const apiKey = process.env.IRCG_FSS_API_KEY
if (!apiKey) throw new Error('IRCG_FSS_API_KEY is required')
const fss = new FSSClient({ apiKey })
const file = new File(['invoice'], 'invoice.pdf', { type: 'application/pdf' })
const uploaded = await fss.uploadUnsafe({ file, fields: ['fileId'] })
console.log(uploaded.file.fileId)Los métodos normales devuelven { error } en vez de lanzar una excepción. Usa una
variante Unsafe disponible, como uploadUnsafe, si prefieres controlar
las excepciones directamente.
Dry run
Configura dryRun: true para simular localmente todas las operaciones remotas sin
realizar solicitudes HTTP ni consumir créditos. Las listas regresan vacías; el cliente conserva los metadatos
multipart en memoria, pero no el contenido. Las descargas aceptan rangos simples y de sufijo, y reportan estado 416 para rangos inválidos, insatisfacibles o múltiples.
const fss = new FSSClient({ apiKey: 'unused', dryRun: true })
const file = new File(['invoice'], 'invoice.pdf', { type: 'application/pdf' })
const result = await fss.upload({ file, fields: ['fileId', 'sizeBytes'] })
if (result.error) throw new Error(result.error.message)
console.log(result.file.fileId) // "dry-run-file"API REST
- Todas las rutas usan
Authorization: Bearercon una API key del servicio. - Los cuerpos JSON requieren
Content-Type: application/json; la carga simple usamultipart/form-data. - La API no admite llamadas directas desde el navegador mediante CORS. Úsala desde tu servidor o a través de un endpoint propio.
| Método | Ruta | Descripción |
|---|---|---|
| POST | /api/v1/files | Cargar un archivo. |
| GET | /api/v1/files | Listar archivos activos. |
| GET | /api/v1/files/:fileId | Consultar atributos de un archivo. |
| POST | /api/v1/files/:fileId/sign | Crear una URL de entrega por una hora. |
| PATCH | /api/v1/files/:fileId | Cambiar visibilidad. |
| POST | /api/v1/files/multipart | Iniciar carga multipart. |
| PUT | /api/v1/files/multipart/:uploadId/parts/:partNumber | Subir una parte. |
| POST | /api/v1/files/multipart/:uploadId/complete | Completar la carga. |
| DELETE | /api/v1/files/multipart/:uploadId | Cancelar la carga. |
| DELETE | /api/v1/files/:fileId | Eliminar un archivo. |
Cargar un archivo POST
POST /api/v1/files recibe multipart/form-data.
import { readFile } from 'node:fs/promises'
const file = new File([await readFile('invoice.pdf')], 'invoice.pdf', { type: 'application/pdf' })
// SDK
const result = await fss.upload({
file,
fields: ['fileId'],
metadata: { documentType: 'invoice' },
visibility: 'private'
})
if (result.error) throw new Error(result.error.message)
console.log(result.file.fileId)
// REST
const formData = new FormData()
formData.append('file', file)
formData.append('metadata', JSON.stringify({ documentType: 'invoice' }))
formData.append('visibility', 'private')
const response = await fetch('https://ircg.dev/api/v1/files?fields=["fileId"]', {
method: 'POST',
headers: { Authorization: `Bearer ${apiKey}` },
body: formData
})
if (!response.ok) throw new Error(`Upload failed: ${response.status}`)
const { file: uploadedFile } = await response.json()
console.log(uploadedFile.fileId)Cuerpo
| Campo | Tipo | Requerido | Por defecto | Descripción |
|---|---|---|---|---|
| file | File | Sí | — | Archivo que se cargará. |
| fileName | string | No | Nombre de la parte file | Nombre que guarda el SDK; en REST es el nombre de archivo de la parte multipart file. |
| metadata | JSON object | No | {} | Metadatos de aplicación de texto. |
| visibility | private | public | No | private | Visibilidad inicial. |
Encabezados
| Nombre | Tipo | Requerido | Descripción |
|---|---|---|---|
| Idempotency-Key | string | No | Clave ASCII visible de 8 a 128 caracteres para reintentar la misma carga sin crear un duplicado. |
Parámetros de consulta
| Parámetro | Tipo | Valores permitidos | Por defecto | Descripción |
|---|---|---|---|---|
| fields | JSON array | fileId | originalName | contentType | sizeBytes | etag | createdAt | visibility | metadata | publicUrl | Todos | Campos de la respuesta. |
Los campos disponibles son fileId, originalName, contentType, sizeBytes, etag, createdAt, visibility, metadata y publicUrl. publicUrl solo
se devuelve para archivos públicos.
Respuestas
Listar archivos GET
GET /api/v1/files
// SDK
const page = await fss.getAll({ amount: 50, fields: ['fileId'] })
if (page.error) throw new Error(page.error.message)
console.table(page.files)
// REST
const response = await fetch('https://ircg.dev/api/v1/files?amount=50&fields=["fileId"]', {
headers: { Authorization: `Bearer ${apiKey}` }
})
if (!response.ok) throw new Error(`List failed: ${response.status}`)
const { files } = await response.json()
console.table(files)Parámetros de consulta
| Parámetro | Tipo | Valores permitidos | Por defecto | Descripción |
|---|---|---|---|---|
| amount | number | 1–100 | 50 | Máximo de resultados. |
| ascending | boolean | true | false | false | Orden por creación. |
| cursor | string | — | — | Cursor de la página anterior. |
| fields | JSON array | fileId | originalName | contentType | sizeBytes | etag | createdAt | visibility | metadata | publicUrl | Todos | Campos por archivo. |
El cursor es opaco: envíalo sin modificar. La respuesta no incluye un total exacto. metadata puede ocupar hasta 8 KiB por archivo; omítelo de fields si no lo necesitas para reducir el tamaño de cada página.
Respuestas
Consultar atributos del archivo GET
GET /api/v1/files/:fileId devuelve los atributos del archivo.
const fileId = 'd290f1ee-6c54-4b01-90e6-d701748f0851'
// SDK
const result = await fss.getById({ fileId, fields: ['fileId', 'originalName'] })
if (result.error) throw new Error(result.error.message)
console.log(result.file.originalName)
// REST
const response = await fetch(`https://ircg.dev/api/v1/files/${fileId}?fields=["fileId","originalName"]`, {
headers: { Authorization: `Bearer ${apiKey}` }
})
if (!response.ok) throw new Error(`Lookup failed: ${response.status}`)
const { file } = await response.json()
console.log(file.originalName)| Segmento | Tipo | Descripción |
|---|---|---|
| fileId | UUID | Identificador del archivo. |
Parámetros de consulta
| Parámetro | Tipo | Valores permitidos | Por defecto | Descripción |
|---|---|---|---|---|
| fields | JSON array | fileId | originalName | contentType | sizeBytes | etag | createdAt | visibility | metadata | publicUrl | Todos | Atributos que se devolverán. |
metadata contiene los metadatos de aplicación asociados al archivo; publicUrl solo se devuelve para archivos públicos.
Respuestas
Firmar y descargar contenido POST
POST /api/v1/files/:fileId/sign devuelve una URL de media reutilizable durante una hora. La descarga posterior no lleva la API key.
import { writeFile } from 'node:fs/promises'
const fileId = 'd290f1ee-6c54-4b01-90e6-d701748f0851'
// SDK: firma y descarga sin almacenar el cuerpo en memoria en el cliente
const download = await fss.download({ fileId })
if (download.error) throw new Error(download.error.message)
await writeFile('invoice.pdf', Buffer.from(await download.response.arrayBuffer()))
// REST: primera petición autenticada
const signed = await fetch(`https://ircg.dev/api/v1/files/${fileId}/sign`, {
method: 'POST',
headers: { Authorization: `Bearer ${apiKey}` }
})
if (!signed.ok) throw new Error(`Sign failed: ${signed.status}`)
const { signedUrl } = await signed.json()
// Segunda petición al dominio de media, sin Authorization
const response = await fetch(signedUrl)
if (!response.ok) throw new Error(`Download failed: ${response.status}`)
await writeFile('invoice.pdf', Buffer.from(await response.arrayBuffer()))| Segmento | Tipo | Descripción |
|---|---|---|
| fileId | UUID | Identificador del archivo. |
Usa la signedUrl devuelta con GET o HEAD. Ambos aceptan un solo Range y responden 206 cuando aplica; un rango inválido responde 416. Cada GET consume 1 crédito sin importar el tamaño; HEAD no consume créditos. Los PDF y las imágenes raster compatibles se entregan en línea;
HTML, SVG y los demás tipos se entregan como adjuntos.
Respuestas
Cambiar visibilidad PATCH
PATCH /api/v1/files/:fileId recibe JSON y devuelve publicUrl al publicar.
const fileId = 'd290f1ee-6c54-4b01-90e6-d701748f0851'
// SDK
const result = await fss.updateVisibility({ fileId, visibility: 'public' })
if (result.error) throw new Error(result.error.message)
console.log(result.file.publicUrl)
// REST
const response = await fetch(`https://ircg.dev/api/v1/files/${fileId}`, {
method: 'PATCH',
headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ visibility: 'public' })
})
if (!response.ok) throw new Error(`Update failed: ${response.status}`)
const { file } = await response.json()
console.log(file.publicUrl)| Campo | Tipo | Requerido | Por defecto | Descripción |
|---|---|---|---|---|
| visibility | private | public | Sí | — | Nueva visibilidad. |
Los archivos son privados por defecto. Los públicos se entregan como adjuntos para que HTML, SVG u otro contenido activo no se ejecute.
Respuestas
Cargas multipart POST
Para archivos mayores a 95 MB, inicia la carga, sube partes consecutivas, conserva sus ETags y complétala.
const request = {
contentType: 'application/zip',
fileName: 'archive.zip',
metadata: { source: 'backup' },
sizeBytes: 100000000,
visibility: 'private' as const
}
// SDK
const started = await fss.startMultipartUpload(request)
if (started.error) throw new Error(started.error.message)
console.log(started.upload.uploadId)
// REST
const response = await fetch('https://ircg.dev/api/v1/files/multipart', {
method: 'POST',
headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
body: JSON.stringify(request)
})
if (!response.ok) throw new Error(`Multipart start failed: ${response.status}`)
const upload = await response.json()
console.log(upload.uploadId)| Campo | Tipo | Requerido | Por defecto | Descripción |
|---|---|---|---|---|
| fileName | string | Sí | — | Nombre del archivo. |
| sizeBytes | number | Sí | — | Tamaño total en bytes. |
| contentType | string | No | application/octet-stream | Tipo MIME. |
| visibility | private | public | No | private | Visibilidad final. |
| metadata | JSON object | No | {} | Metadatos de texto. |
Cada parte mide 95 MB salvo la final. Se permiten hasta 10,000 partes y cuatro simultáneas. Conserva uploadId y los ETag para reanudar; la autorización expira a
las 24 horas. Si cancelas, usa abortMultipartUpload.
Respuestas
Subir una parte multipart PUT
PUT /api/v1/files/multipart/:uploadId/parts/:partNumber
// SDK
const uploadedPart = await fss.uploadMultipartPart({ body: archive.slice(0, 95000000), partNumber: 1, uploadId })
if (uploadedPart.error) throw new Error(uploadedPart.error.message)
console.log(uploadedPart.part.etag)
// REST
const body = archive.slice(0, 95000000)
const response = await fetch("https://ircg.dev/api/v1/files/multipart/2~Y1RjM2M4ZTYtMjRjOS00Y2JjLTgxNDgtYzRjZjA2YjY0YzQw/parts/1", { method: "PUT", headers: { Authorization: "Bearer " + apiKey, "Content-Length": String(body.size) }, body })
if (!response.ok) throw new Error("Part upload failed: " + response.status)
console.log((await response.json()).etag)| Segmento | Tipo | Descripción |
|---|---|---|
| uploadId | string | ID recibido al iniciar la carga. |
| partNumber | integer | Número consecutivo de 1 a 10,000. |
Encabezados
| Nombre | Tipo | Requerido | Descripción |
|---|---|---|---|
| Content-Length | integer | Sí | Debe coincidir con el tamaño exacto de la parte. |
Cuerpo
| Campo | Tipo | Requerido | Por defecto | Descripción |
|---|---|---|---|---|
| body | binary | Sí | — | Bytes de la parte; 95 MB salvo la última. |
Respuestas
Completar una carga multipart POST
POST /api/v1/files/multipart/:uploadId/complete
// SDK
const completed = await fss.completeMultipartUpload({ fields: ["fileId"], parts, uploadId })
if (completed.error) throw new Error(completed.error.message)
console.log(completed.file.fileId)
// REST
const response = await fetch("https://ircg.dev/api/v1/files/multipart/2~Y1RjM2M4ZTYtMjRjOS00Y2JjLTgxNDgtYzRjZjA2YjY0YzQw/complete?fields=[%22fileId%22]", { method: "POST", headers: { Authorization: "Bearer " + apiKey, "Content-Type": "application/json" }, body: JSON.stringify({ parts }) })
if (!response.ok) throw new Error("Multipart completion failed: " + response.status)
console.log((await response.json()).file.fileId)| Segmento | Tipo | Descripción |
|---|---|---|
| uploadId | string | ID de la carga iniciada. |
Cuerpo
| Campo | Tipo | Requerido | Por defecto | Descripción |
|---|---|---|---|---|
| parts | array | Sí | — | ETag y partNumber de todas las partes, en secuencia. |
Parámetros de consulta
| Parámetro | Tipo | Valores permitidos | Por defecto | Descripción |
|---|---|---|---|---|
| fields | JSON array | fileId | originalName | contentType | sizeBytes | etag | createdAt | visibility | metadata | publicUrl | Todos | Campos de la respuesta. |
Respuestas
Cancelar una carga multipart DELETE
DELETE /api/v1/files/multipart/:uploadId
// SDK
const aborted = await fss.abortMultipartUpload({ uploadId })
if (aborted.error) throw new Error(aborted.error.message)
console.log(aborted.success)
// REST
const response = await fetch("https://ircg.dev/api/v1/files/multipart/2~Y1RjM2M4ZTYtMjRjOS00Y2JjLTgxNDgtYzRjZjA2YjY0YzQw", { method: "DELETE", headers: { Authorization: "Bearer " + apiKey } })
if (!response.ok) throw new Error("Abort failed: " + response.status)
console.log((await response.json()).success)| Segmento | Tipo | Descripción |
|---|---|---|
| uploadId | string | ID de la carga que se cancelará. |
Respuestas
Eliminar un archivo DELETE
DELETE /api/v1/files/:fileId oculta el archivo de inmediato y responde { success: true }.
const fileId = 'd290f1ee-6c54-4b01-90e6-d701748f0851'
// SDK
const deleted = await fss.delete({ fileId })
if (deleted.error) throw new Error(deleted.error.message)
console.log(deleted.success)
// REST
const response = await fetch(`https://ircg.dev/api/v1/files/${fileId}`, {
method: 'DELETE',
headers: { Authorization: `Bearer ${apiKey}` }
})
if (!response.ok) throw new Error(`Delete failed: ${response.status}`)
const { success } = await response.json()
console.log(success)| Segmento | Tipo | Descripción |
|---|---|---|
| fileId | UUID | Identificador del archivo que se eliminará. |
El objeto se conserva 30 días para auditoría y no consume almacenamiento durante esa retención.
Respuestas
Límites y costos
- El máximo predeterminado por archivo es 50 GB; puede aprobarse hasta 950 GB con multipart.
metadataadmite 50 pares de texto, claves de hasta 128 caracteres, valores de hasta 2,048 y 8 KiB en total. - El almacenamiento cuesta 1 crédito por MB completo en cada ciclo. Cada descarga de contenido cuesta 1
crédito sin importar el tamaño. Las cargas y
HEADno consumen créditos de solicitud. - La API autenticada tiene un límite predeterminado de 120 solicitudes por 60 segundos y 3,000 por 3,600 segundos por API key; ambas ventanas se aplican de forma independiente. Puedes solicitar cambios a esos límites desde el panel.
- La entrega de archivos tiene límites independientes y no consume el cupo de la API key: 300 solicitudes
por 60 segundos por organización y dirección cliente, 600 por archivo y 1,500 por organización. Son aproximados
por ubicación de entrega; al alcanzarlos,
GETyHEADresponden429conRetry-After: 60.