Almacenamiento de archivos

Descargar tipos de TypeScript

SDK

typescript
	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.

typescript
	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: Bearer con una API key del servicio.
  • Los cuerpos JSON requieren Content-Type: application/json; la carga simple usa multipart/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étodoRutaDescripción
POST/api/v1/filesCargar un archivo.
GET/api/v1/filesListar archivos activos.
GET/api/v1/files/:fileIdConsultar atributos de un archivo.
POST/api/v1/files/:fileId/signCrear una URL de entrega por una hora.
PATCH/api/v1/files/:fileIdCambiar visibilidad.
POST/api/v1/files/multipartIniciar carga multipart.
PUT/api/v1/files/multipart/:uploadId/parts/:partNumberSubir una parte.
POST/api/v1/files/multipart/:uploadId/completeCompletar la carga.
DELETE/api/v1/files/multipart/:uploadIdCancelar la carga.
DELETE/api/v1/files/:fileIdEliminar un archivo.

Cargar un archivo POST

POST /api/v1/files recibe multipart/form-data.

typescript
	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

CampoTipoRequeridoPor defectoDescripción
fileFileArchivo que se cargará.
fileNamestringNoNombre de la parte fileNombre que guarda el SDK; en REST es el nombre de archivo de la parte multipart file.
metadataJSON objectNo{}Metadatos de aplicación de texto.
visibilityprivate | publicNoprivateVisibilidad inicial.

Encabezados

NombreTipoRequeridoDescripción
Idempotency-KeystringNoClave ASCII visible de 8 a 128 caracteres para reintentar la misma carga sin crear un duplicado.

Parámetros de consulta

ParámetroTipoValores permitidosPor defectoDescripción
fieldsJSON arrayfileId | originalName | contentType | sizeBytes | etag | createdAt | visibility | metadata | publicUrlTodosCampos 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

typescript
	// 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ámetroTipoValores permitidosPor defectoDescripción
amountnumber1–10050Máximo de resultados.
ascendingbooleantrue | falsefalseOrden por creación.
cursorstringCursor de la página anterior.
fieldsJSON arrayfileId | originalName | contentType | sizeBytes | etag | createdAt | visibility | metadata | publicUrlTodosCampos 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.

typescript
	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)
SegmentoTipoDescripción
fileIdUUIDIdentificador del archivo.

Parámetros de consulta

ParámetroTipoValores permitidosPor defectoDescripción
fieldsJSON arrayfileId | originalName | contentType | sizeBytes | etag | createdAt | visibility | metadata | publicUrlTodosAtributos 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.

typescript
	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()))
SegmentoTipoDescripción
fileIdUUIDIdentificador 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.

typescript
	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)
CampoTipoRequeridoPor defectoDescripción
visibilityprivate | publicNueva 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.

typescript
	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)
CampoTipoRequeridoPor defectoDescripción
fileNamestringNombre del archivo.
sizeBytesnumberTamaño total en bytes.
contentTypestringNoapplication/octet-streamTipo MIME.
visibilityprivate | publicNoprivateVisibilidad final.
metadataJSON objectNo{}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

typescript
	// 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)
SegmentoTipoDescripción
uploadIdstringID recibido al iniciar la carga.
partNumberintegerNúmero consecutivo de 1 a 10,000.

Encabezados

NombreTipoRequeridoDescripción
Content-LengthintegerDebe coincidir con el tamaño exacto de la parte.

Cuerpo

CampoTipoRequeridoPor defectoDescripción
bodybinaryBytes de la parte; 95 MB salvo la última.

Respuestas

Completar una carga multipart POST

POST /api/v1/files/multipart/:uploadId/complete

typescript
	// 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)
SegmentoTipoDescripción
uploadIdstringID de la carga iniciada.

Cuerpo

CampoTipoRequeridoPor defectoDescripción
partsarrayETag y partNumber de todas las partes, en secuencia.

Parámetros de consulta

ParámetroTipoValores permitidosPor defectoDescripción
fieldsJSON arrayfileId | originalName | contentType | sizeBytes | etag | createdAt | visibility | metadata | publicUrlTodosCampos de la respuesta.

Respuestas

Cancelar una carga multipart DELETE

DELETE /api/v1/files/multipart/:uploadId

typescript
	// 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)
SegmentoTipoDescripción
uploadIdstringID 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 }.

typescript
	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)
SegmentoTipoDescripción
fileIdUUIDIdentificador 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. metadata admite 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 HEAD no 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, GET y HEAD responden 429 con Retry-After: 60.

Reportar abuso de un archivo público