TypeScript SDK for the put.io API
Domain-first, schema-validated at the boundary, and designed for all runtimes.
Install with npm:
npm install @putdotio/sdkuserAccessToken in these examples is an already-issued user token from your app or auth flow.
The SDK itself can be constructed without a token.
import { createPutioSdkPromiseClient } from "@putdotio/sdk";
const sdk = createPutioSdkPromiseClient({
accessToken: userAccessToken,
});
const account = await sdk.account.getInfo({
download_token: 1,
});The SDK can also be created without a default token:
const sdk = createPutioSdkPromiseClient();
const validation = await sdk.auth.validateToken(tokenToCheck);
const login = await sdk.auth.login({
clientId,
clientSecret,
password,
username,
});Shared formatting, URL, and error-localization helpers are available from the utilities subpath:
import {
FileURLProvider,
secondsToReadableDuration,
toHumanFileSize,
} from "@putdotio/sdk/utilities";const size = toHumanFileSize(1_572_864);
const duration = secondsToReadableDuration(444);import { Effect } from "effect";
import {
PutioSdk,
createPutioSdkEffectClient,
makePutioSdkLiveClientLayer,
makePutioSdkLiveLayer,
} from "@putdotio/sdk";
const sdk = createPutioSdkEffectClient();
const program = sdk.files
.list(0, {
per_page: 20,
total: 1,
})
.pipe(Effect.provide(makePutioSdkLiveLayer({ accessToken: userAccessToken })));
const result = await Effect.runPromise(program);Effect workflows can also depend on the SDK as a service:
const serviceProgram = Effect.gen(function* () {
const sdk = yield* PutioSdk;
return yield* sdk.files.list(0, { per_page: 20 });
}).pipe(Effect.provide(makePutioSdkLiveClientLayer({ accessToken: userAccessToken })));makePutioSdkLiveClientLayer(...) provides the SDK service, SDK config, and default fetch-backed transport.
makePutioSdkLiveLayer(...) provides both the SDK config and the default fetch-backed transport.
Use makePutioSdkLayer(...) with makePutioFetchLayer(...) or your own PutioHttpClient service when you want to supply custom transport.
Both client styles expose the same domain surface. The Promise client also exposes
dispose() for runtime teardown and files.createUploadFormData(...) for pure
FormData construction.
promiseClient.files.list(0, { per_page: 20 });
effectClient.files.list(0, { per_page: 20 });Choose the Promise client when you want standard async functions. Choose the Effect client when you want the canonical typed error channel and Effect-native composition.
| Client | Use it for |
|---|---|
createPutioSdkPromiseClient(config) |
React apps, scripts, server handlers, React Query |
createPutioSdkEffectClient() |
Effect-native workflows and service composition |
Effect is the canonical typed surface. The Promise client is an adapter for environments that want standard async functions.
- SDK creation does not require an access token
- Authenticated endpoints need a token through client config or the Effect layer config
- Effect client: keeps errors in the Effect error channel with operation-specific typing
- Promise client: throws tagged SDK error objects such as
PutioOperationError,PutioApiError, andPutioRateLimitError - Promise client: owns a managed Effect runtime and exposes
dispose()for explicit teardown
If you create a long-lived Promise client in a script, test harness, or server integration, call await sdk.dispose() during teardown.
| Namespace | Purpose |
|---|---|
account |
account info, settings, confirmations, destructive account actions |
auth |
token validation, login flows, device/OOB helpers, two-factor flows |
config |
app-owned JSON config storage |
downloadLinks |
download-link bundles |
events |
history events and event torrent payloads |
family |
family members and invites |
files |
file listing, search, move/delete/extract, MP4, direct access URLs, upload |
friendInvites |
friend invitation management |
friends |
friend graph and requests |
ifttt |
IFTTT integration endpoints |
oauth |
OAuth app management |
payment |
plans, vouchers, payment flows, payment history |
rss |
RSS feed management |
sharing |
friend shares, public shares, clone flows |
transfers |
transfer list, add/retry/cancel/clean flows |
trash |
trash listing, restore, delete, empty |
tunnel |
route listing |
utilities |
file URLs, localized errors, and shared formatting helpers |
zips |
zip creation and lookup |
- schema-first contracts at every external boundary
- typed errors are first-class
- parameter-conditioned responses are modeled explicitly
- no compatibility namespace shims in the public API
- fetch-native core with runtime-portable Web APIs
The package is designed around standard Web APIs. Host runtimes should provide:
fetchRequest,Response, andHeadersURLandURLSearchParamsAbortControllerFormDatabtoafor username/password auth flows such asauth.login(...)
For upload flows, the host should also provide file-compatible inputs such as File or Blob.
If a target runtime is missing these APIs, provide them with host-level polyfills or adapters instead of patching the SDK surface.
The package compatibility gate installs the packed tarball into external consumers and runs strict Node type/runtime checks, bundled browser checks in Chromium, Firefox, and WebKit, and a Bun runtime import check.
Promise consumers receive tagged SDK error objects:
import {
createPutioSdkPromiseClient,
isPutioOperationError,
isPutioRateLimitError,
} from "@putdotio/sdk";
const sdk = createPutioSdkPromiseClient({
accessToken: userAccessToken,
});
try {
await sdk.files.createFolder({
name: "",
parent_id: 0,
});
} catch (error) {
if (isPutioOperationError(error)) {
if (error.operation === "createFolder") {
console.log(error.body.error_type);
}
}
if (isPutioRateLimitError(error)) {
console.log(error.retryAfter);
}
}Effect consumers keep errors in the typed error channel instead of throwing:
import { Effect } from "effect";
import { PutioSdk, makePutioSdkLiveClientLayer } from "@putdotio/sdk";
const handled = Effect.gen(function* () {
const sdk = yield* PutioSdk;
return yield* sdk.files.createFolder({
name: "",
parent_id: 0,
});
}).pipe(
Effect.catchTag("PutioOperationError", (error) => {
if (error.operation === "createFolder") {
return Effect.succeed(error.body.error_type);
}
return Effect.fail(error);
}),
Effect.provide(makePutioSdkLiveClientLayer({ accessToken: userAccessToken })),
);files exposes both JSON contracts and direct route helpers:
const playlistUrl = await sdk.files.getHlsStreamUrl(fileId, {
maxSubtitleCount: 1,
});
const upload = await sdk.files.upload({
file: new File(["hello"], "hello.txt"),
parentId: 0,
});Upload targets upload.put.io internally because api.put.io/v2/files/upload is only a redirect shim.
The Promise client plugs into TanStack Query directly:
import { useQuery } from "@tanstack/react-query";
import { createPutioSdkPromiseClient } from "@putdotio/sdk";
const sdk = createPutioSdkPromiseClient({
accessToken: token,
});
export const useAccountInfo = () =>
useQuery({
queryKey: ["account", "info"],
queryFn: () => sdk.account.getInfo({ download_token: 1 }),
});The Effect client also works well when you want the canonical typed API:
import { useQuery } from "@tanstack/react-query";
import { Effect } from "effect";
import { PutioSdk, makePutioSdkLiveClientLayer } from "@putdotio/sdk";
const sdkLayer = makePutioSdkLiveClientLayer({
accessToken: token,
});
export const useFiles = (parentId: number) =>
useQuery({
queryKey: ["files", parentId],
queryFn: () =>
Effect.runPromise(
Effect.gen(function* () {
const sdk = yield* PutioSdk;
return yield* sdk.files.list(parentId, { per_page: 50 });
}).pipe(Effect.provide(sdkLayer)),
),
});- Architecture for package shape and boundaries
- Testing for local and live verification
- Readiness for domain readiness
- Distribution for release automation
- Security for private-first vulnerability disclosure
Contributor setup, validation, and live-test workflow live in Contributing.
This project is available under the MIT License.
