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-sdk

Create 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. Pass replace: true to overwrite it.
  • Pass idempotencyKey to make the upload safe to retry.
  • Omit contentType and 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.