Introdução
De uma conta vazia até o seu primeiro arquivo enviado, em cinco etapas.
npm install @uploadcenter/sdk-jsimport { 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 os métodos são gerados a partir do esquema OpenAPI do UploadCenter; portanto, o preenchimento automático abrange toda a superfície da API — visualizar o pacote JS no npm ou o pacote Python no PyPI.
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"
}'curl -X PUT "$UPLOAD_URL" \
-H "Content-Type: image/jpeg" \
--data-binary @photo.jpgcurl -X POST https://api.uploadscenter.com/v1/uploads/complete \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{"file_id": "file_..."}'Lendo um arquivo novamente — GET /v1/files/{file_id}/url — sempre exige uma chave de API ou sessão válida para arquivos privados. Os arquivos públicos funcionam de maneira diferente — veja abaixo.
GET /v1/files/{file_id}
{
"id": "file_...",
"visibility": "public",
"status": "ready",
"url": "https://cdn.uploadscenter.com/o/…/photo.jpg"
}Arquivos privados (por padrão) não possuem um campo de URL — em vez disso, chame GET /v1/files/{id}/url, o que retorna um novo link assinado, válido por um período limitado.
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. São permitidas até 3 tentativas de entrega (com intervalos de 30 s, 2 min e 10 min) antes que a entrega seja considerada falha.
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"]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.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)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.GET /v1/files/{file_id}
{
"id": "file_...",
"status": "ready",
"moderation_status": "flagged", // null (not evaluated) | "clean" | "flagged" | "error"
"moderation_score": 0.87
}Um arquivo marcado permanece exatamente como foi enviado — seu aplicativo decide o que “marcado” significa para o seu produto. Disponível nos planos pagos; consulte a página de recursos.
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://…" }
]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 uploadO resultado é um arquivo comum — chamado “file_ids” após a geração — com sua própria visibilidade, entrega e ciclo de vida, exatamente como qualquer arquivo que você envie diretamente.
GET /v1/ai/search?project_id={project_id}&q=cat+sitting+on+a+couch&limit=20
# Ranks files by AI caption similarity (files.ai_caption, produced by the
# analyze-image capability) — not full-text search over filenames.
# GET /v1/files/{file_id}/similar finds files with a similar caption.Quer ter uma visão completa?
Confira todos os recursos — controle de versões, domínios personalizados, funções da equipe e muito mais — na página de recursos.