TypeScript SDK
TypeScript SDK
A typed, dependency-free client for the REST API.
Install #
@buildinternet/uploads-sdk wraps the REST API in typed methods. It has no
dependencies and uses only fetch, so it runs in Node 22+, Bun, Deno, Cloudflare Workers and
browsers.
npm install @buildinternet/uploads-sdkCreate a client with a workspace token. A personal token comes from uploads login or
Developers. For CI and bots, use a service token from the workspace
settings.
import { createUploadsClient } from "@buildinternet/uploads-sdk";
const uploads = createUploadsClient({ token: process.env.UPLOADS_TOKEN!, workspace: "acme",});A token can read and write the whole workspace, so keep it on a server. Never ship one to a browser.
Upload a file #
put takes the object key, the bytes, and optional metadata. It returns the public url and an
embedUrl for GitHub markdown.
const file = await uploads.put("screenshots/settings.png", bytes, { contentType: "image/png", metadata: { path: "/settings", state: "after" },});console.log(file.url);- An existing key throws
KEY_EXISTS. Passreplace: trueto overwrite it. - Pass
idempotencyKeyto make the upload safe to retry. - Omit
contentTypeand the API detects the type from the bytes.
To post a workspace’s uploads on a pull request as the uploads.sh comment:
await uploads.upsertGithubComment({ repo: "acme/web", num: 42, kind: "pull" });Methods #
| Area | Methods |
|---|---|
| Files | put, previewPut, head, delete, list, listAll, findFiles, findFilesAll, getMetadata, patchMetadata, listMetadataKeys, listMetadataValues |
| Holds | listHeld, approve |
| Galleries | createGallery, getGallery, listGalleries, deleteGallery, addGalleryItem, plus linking a gallery to a PR or issue |
| Live links | createFeed, getFeed, listFeeds, deleteFeed |
| GitHub | upsertGithubComment |
| Usage | usage, reconcile, purgeExpired, health |
listAll and findFilesAll follow the cursor for you. For a route with no method,
uploads.request(method, path, options) sends an authenticated request and returns the parsed
JSON. Every route is in the endpoint reference.
Errors and retries #
Every method throws UploadsError on a failed request. Branch on code, a stable string, and
read status for the HTTP status.
import { UploadsError } from "@buildinternet/uploads-sdk";
try { await uploads.put(key, bytes);} catch (err) { if (err instanceof UploadsError && err.code === "KEY_EXISTS") { console.log("already uploaded:", err.existingUrl); } else throw err;}Common codes are UNAUTHORIZED, INSUFFICIENT_SCOPE, NOT_FOUND, KEY_EXISTS,
STORAGE_QUOTA, UPLOAD_BUDGET and NETWORK.
Requests time out after 15 seconds, or 60 seconds for an upload. A GET, or an upload with an
idempotencyKey, is retried once after a network error, a 429 or a 503.
Options #
const uploads = createUploadsClient( { token, workspace, apiUrl: "https://api.uploads.sh" }, { fetch, // your own fetch, for tests or tracing userAgent: "my-app/1.0", onRetry: ({ reason, delayMs }) => console.warn(`retrying: ${reason}`), onTokenExpiry: (expiresAt) => console.log(`token expires ${expiresAt}`), },);The client never reads environment variables or config files. Pass everything in.