UploadCenter

Primeros pasos

De una cuenta vacía a tu primer archivo subido, en cinco pasos.

Instala el SDK
SDK oficiales y completamente tipados para TypeScript/JavaScript (Node.js y el navegador) y Python. Cada paso que se indica a continuación muestra también la solicitud cURL equivalente, por si prefieres llamar a la API directamente.
bash
npm install @uploadcenter/sdk-js
typescript
import { createClient } from "@uploadcenter/sdk-js";

const client = createClient({
  baseUrl: "https://api.uploadscenter.com",
  token: process.env.UPLOADCENTER_API_KEY, // an API key from your project settings
});

Todos los métodos se generan a partir del esquema OpenAPI de UploadCenter, por lo que la función de autocompletado abarca toda la superficie de la API — Ver el paquete JS en npm o el paquete de Python en PyPI.

1
Crear una cuenta
Al registrarte, se crea automáticamente un espacio de trabajo (organización) para ti.
2
Crea un proyecto y una clave de API
Desde tu panel de control: crea un proyecto y, a continuación, genera una clave API específica para ese proyecto; la clave completa solo se muestra una vez, así que cópiala inmediatamente.
3
Firmar previamente un archivo subido
Solicita a la API una URL prefirmada: los archivos nunca pasan por tu propio servidor.
bash
curl -X POST https://api.uploadscenter.com/v1/uploads/presign \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "project_id": "project_...",
    "filename": "photo.jpg",
    "size_bytes": 128000,
    "mime_type": "image/jpeg",
    "visibility": "private"
  }'
4
Sube el archivo
COLÓCALA directamente en la URL de subida que se te ha facilitado; esto es lo que permite que las subidas sean rápidas, independientemente del tamaño.
bash
curl -X PUT "$UPLOAD_URL" \
  -H "Content-Type: image/jpeg" \
  --data-binary @photo.jpg
5
Confirma la subida
Esto es lo que activa el análisis y el procesamiento del antivirus: el archivo no se marca como listo hasta que supera dicho análisis.
bash
curl -X POST https://api.uploadscenter.com/v1/uploads/complete \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"file_id": "file_..."}'

Volver a leer un archivo — GET /v1/files/{file_id}/url — Siempre se requiere una clave API o una sesión válida para los archivos privados. Los archivos públicos funcionan de forma diferente; consulta más abajo.

URL públicas
Súbelo con visibilidad «pública» y, una vez que el archivo esté listo, su campo de URL será un enlace permanente que no requiere autenticación: se puede incrustar directamente de forma segura, se almacena en caché en el borde de la red y nunca caduca.
json
GET /v1/files/{file_id}

{
  "id": "file_...",
  "visibility": "public",
  "status": "ready",
  "url": "https://cdn.uploadscenter.com/o/…/photo.jpg"
}

Los archivos privados (por defecto) no tienen campo de URL; en su lugar, utiliza la llamada GET /v1/files/{id}/url, que devuelve un enlace nuevo y firmado, válido durante un tiempo limitado.

Reaccionar ante los acontecimientos
Añade un webhook desde la configuración de tu proyecto para recibir notificaciones sobre eventos de carga, procesamiento y cuota. Cada entrega está firmada; compruébala antes de confiar en la carga útil.
http
POST <your webhook URL>
Content-Type: application/json
X-Signature: sha256=<hex-encoded HMAC-SHA256>
X-Timestamp: <unix timestamp, seconds>

{"event":"file.processed","created_at":"2026-01-01T12:00:00Z","data":{"id":"file_...","project_id":"project_...","status":"ready", ...}}

Eventos: file.uploaded, file.processed, file.failed, file.quarantined, file.flagged, storage.limit_reached. Se realizan hasta tres intentos de entrega (con intervalos de 30 segundos, 2 minutos y 10 minutos) antes de que la entrega se marque como fallida.

typescript
import crypto from "node:crypto";

// Sign over "${timestamp}.${rawBody}" — rawBody must be the exact bytes
// received, not a re-serialized JSON.stringify(JSON.parse(rawBody)).
function verifyWebhook(rawBody: string, timestamp: string, signatureHeader: string, secret: string): boolean {
  const expected = crypto.createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex");
  const provided = signatureHeader.replace(/^sha256=/, "");
  return crypto.timingSafeEqual(Buffer.from(expected, "hex"), Buffer.from(provided, "hex"));
}

// signatureHeader = req.headers["x-signature"]
// timestamp       = req.headers["x-timestamp"]
Transforma imágenes sobre la marcha
Cambiar el tamaño, recortar y convertir el formato mediante parámetros de consulta: el resultado se almacena en caché tras la primera solicitud y no se vuelve a calcular en cada visita.
http
GET /v1/files/{file_id}/transform?w=480&h=320&format=auto&fit=cover

# fit: "cover" (crop to fill) or "inside" (contain)
# format: auto (default) | webp | jpeg | png
# quality: auto (default) | 1-100
#
# format=auto picks WebP or JPEG based on the request's Accept header —
# no more branching client-side for older browsers. quality=auto applies
# a sensible per-format default (80 for WebP, 82 for JPEG). Pass explicit
# values any time you want full control instead.
Añade una marca de agua a tus imágenes
Superpón el logotipo de tu proyecto sobre cualquier transformación: la posición, la opacidad y el tamaño se pueden configurar totalmente, y el resultado se almacena en caché como cualquier otra transformación.
http
PUT /v1/projects/{project_id}/watermark
{"file_id": "file_..."}   # any image already uploaded to the project

GET /v1/files/{file_id}/transform?w=1200&watermark=true&gravity=south_east&opacity=60&overlay_scale=0.2

# gravity: north_west | north_east | south_west | south_east | center
# opacity: 1-100 (default 60)
# overlay_scale: 0.05-1.0 (default 0.2, fraction of the output image's width)
Dominios personalizados
Publica los archivos públicos desde tu propio dominio en lugar de desde la CDN compartida, una vez que se haya verificado la titularidad a través del DNS.
http
POST /v1/domains
{"project_id": "project_...", "hostname": "cdn.yourapp.com"}

# Add the returned TXT record at your DNS provider, then:
POST /v1/domains/{domain_id}/verify

# Once verified, point cdn.yourapp.com at UploadCenter (A/CNAME — shown in
# the dashboard) and a TLS certificate is issued automatically. From then
# on, every public file's url uses your domain instead of the shared CDN.
Moderación de contenidos
Las imágenes subidas se revisan automáticamente para detectar contenido explícito; se trata únicamente de una detección, nunca se bloquea ni se elimina nada en tu nombre.
json
GET /v1/files/{file_id}

{
  "id": "file_...",
  "status": "ready",
  "moderation_status": "flagged",  // null (not evaluated) | "clean" | "flagged" | "error"
  "moderation_score": 0.87
}

Un archivo marcado permanece exactamente tal y como se subió: tu aplicación decide qué significa «marcado» en el contexto de tu producto. Disponible en los planes de pago; consulta la página de características.

Miniaturas de vídeo y archivos derivados
Cada vídeo que se sube recibe automáticamente una miniatura en formato JPEG; además, los vídeos de menor tamaño se transcodifican a MP4 a 720p. La URL de cada variante está lista para usarse directamente, sin necesidad de ningún paso adicional de autenticación.
json
GET /v1/files/{file_id}/variants

[
  { "variant": "thumbnail", "format": "jpg", "width": 640, "height": 360, "url": "https://…" },
  { "variant": "720p", "format": "mp4", "width": 1280, "height": 720, "url": "https://…" }
]
Generación de imágenes y vídeos mediante IA
Genera, edita, elimina fondos, amplía o crea variaciones de una imagen; genera vídeo a partir de texto o de una imagen existente. Todas las llamadas son asíncronas: envía el código 202 con un identificador de generación y, a continuación, comprueba su estado o espera a que se active el webhook «ai.generation.completed» o «ai.generation.failed».
typescript
const generation = await client.ai.generateImageEndpointV1AiImagesGeneratePost({
  project_id: projectId,
  prompt: "a minimalist logo of a fox, flat vector style",
  use_brand_kit: true, // pulls in this org's brand kit colors/fonts
});

// 202 Accepted — poll until it settles (or listen for the webhook below).
const result = await client.ai.getGenerationEndpointV1AiGenerationsGenerationIdGet(
  generation.id,
  projectId,
);
// result.file_ids — the new file(s), same shape as anything you upload

El resultado es un archivo normal —«file_ids» una vez finalizada la generación— con su propia visibilidad, entrega y ciclo de vida, exactamente igual que cualquier archivo que subas directamente.

Fiabilidad, sinceramente
UploadCenter es una plataforma joven y en constante desarrollo: no contamos con un historial de tiempo de actividad de varios años que podamos mostrar, y no vamos a inventarnos una cifra. Lo que tenemos hoy en día: cada archivo se analiza con un antivirus antes de ser servido, las subidas se procesan de forma atómica (nada queda a medias si falla un paso) y la entrega se realiza a través de la red de Cloudflare. Aún no existe un SLA formal; si tu caso de uso requiere garantías contractuales, ponte en contacto con nosotros y hablaremos de lo que sea realista.

¿Quieres tener una visión completa?

Consulta todas las funciones —control de versiones, dominios personalizados, roles de equipo y mucho más— en la página de características.