SDK Setup
The Oblien SDK provides a typed, ergonomic interface for the full Workspace API. It handles authentication, request formatting, error handling, and streaming for you.
Installation
npm install oblien# or with your preferred package manager
yarn add oblien
pnpm add oblienInitialize the client
import Oblien from 'oblien';
const client = new Oblien({
clientId: process.env.OBLIEN_CLIENT_ID!,
clientSecret: process.env.OBLIEN_CLIENT_SECRET!,
});The client.workspaces namespace gives you access to all workspace operations. You can also use client.workspace(id) to get a scoped handle bound to a single workspace - see Scoped workspace handle below.
SDK structure
// ── Core CRUD ──────────────────────────────────────
client.workspaces.create(params) // Create a workspace
client.workspaces.list(params?) // List workspaces
client.workspaces.get(workspaceId) // Get workspace details
client.workspaces.update(workspaceId, data) // Update workspace
client.workspaces.delete(workspaceId) // Delete workspace
client.workspaces.getDetails(workspaceId) // Aggregated details
client.workspaces.getQuota() // Get resource quota
client.workspaces.getEstimate(params?) // Estimate monthly cost
// ── Power ──────────────────────────────────────────
client.workspaces.start(workspaceId, { force? }) // Start workspace
client.workspaces.stop(workspaceId) // Stop workspace
client.workspaces.restart(workspaceId) // Restart workspace
client.workspaces.pause(workspaceId) // Pause workspace
client.workspaces.resume(workspaceId) // Resume paused workspace
client.workspaces.ping(workspaceId, { ttl_seconds? }) // Keep-alive
// ── Runtime (data plane) ───────────────────────────
const rt = await client.workspaces.runtime(workspaceId)
rt.files // .list(), .read(), .write(), .mkdir(), .stat(), .delete(), .stream()
rt.transfer // .download(), .upload()
rt.exec // .run(), .stream(), .subscribe(), .list(), .get(), .kill(), .input()
rt.terminal // .create(), .list(), .close(), .scrollback()
rt.search // .content(), .files(), .status(), .init()
rt.watcher // .create(), .list(), .get(), .delete()
rt.ws() // WebSocket for terminal I/O + watcher events
// ── Lifecycle ──────────────────────────────────────
client.workspaces.lifecycle // .get(), .makePermanent(), .makeTemporary(), .updateTtl(), .ping(), .destroy()
// ── Network ────────────────────────────────────────
client.workspaces.network // .get(), .update(), .applyOutboundIp()
// ── SSH ────────────────────────────────────────────
client.workspaces.ssh // .status(), .enable(), .disable(), .setPassword(), .setKey()
// ── Public Access ──────────────────────────────────
client.workspaces.publicAccess // .list(), .expose(), .revoke()
// ── Resources ──────────────────────────────────────
client.workspaces.resources // .get(), .update(), .patch()
// ── Snapshots ──────────────────────────────────────
client.workspaces.snapshots // .create(), .restore(), .createArchive(), .listArchives(),
// .getArchive(), .deleteArchive(), .deleteAllArchives()
// ── Workloads ──────────────────────────────────────
client.workspaces.workloads // .create(), .list(), .get(), .update(), .patch(), .delete(),
// .deleteAll(), .start(), .stop(), .status(), .logs(),
// .stats(), .logsStream(), .statsStream(), .allStatsStream()
// ── Monitoring ─────────────────────────────────────
client.workspaces.metrics // .stats(), .statsStream(), .info(), .config()
// ── Usage ──────────────────────────────────────────
client.workspaces.usage // .get(), .totals(), .creditsChart(), .global(), .activity(),
// .wipe(), .startTracking(), .stopTracking()
// ── Metadata ───────────────────────────────────────
client.workspaces.metadata // .get(), .update(), .patch()
// ── API Access ─────────────────────────────────────
client.workspaces.apiAccess // .status(), .enable(), .disable(), .rotateToken(), .getToken(), .rawToken()
// ── Logs ───────────────────────────────────────────
client.workspaces.logs // .get(), .clear(), .listFiles(), .getFile(), .streamBoot(), .streamCmd()
// ── Images ─────────────────────────────────────────
client.workspaces.images // .list()Scoped workspace handle
When working with a single workspace, use client.workspace(id) to get a scoped handle. Every method is pre-bound to that workspace ID - no need to pass it repeatedly:
const ws = client.workspace('ws_abc');
// Core operations - no ID needed
await ws.get();
await ws.start();
await ws.stop();
await ws.delete();
// Sub-resources are also scoped
await ws.ssh.enable();
await ws.network.get();
await ws.snapshots.create();
await ws.workloads.list();
await ws.metrics.stats();
await ws.logs.get();
// Runtime - smart enable + token caching
const rt = await ws.runtime();
await rt.exec.run(['ls', '-la']);
await rt.files.list({ directory: '/app' });Use the collection style (client.workspaces.…) when iterating over many workspaces.
Use the scoped handle (client.workspace(id)) when focused on a single workspace.
Scoped handle reference
const ws = client.workspace('ws_abc');
ws.id // 'ws_abc'
// ── Core ──────────────────────────────────────────
ws.get() // Fetch workspace data
ws.update(params) // Update name/config
ws.delete() // Permanently delete
ws.getDetails() // Aggregated details
// ── Power ─────────────────────────────────────────
ws.start({ force? }) // Start
ws.stop() // Stop
ws.restart() // Restart
ws.pause() // Pause (preserves memory)
ws.resume() // Resume paused
ws.ping({ ttl_seconds? })// Keep-alive
// ── Runtime ───────────────────────────────────────
ws.runtime() // → Runtime (auto-enables + caches token)
ws.invalidateRuntime() // Clear cached runtime token
// ── All sub-resources are available ───────────────
ws.lifecycle // .get(), .makePermanent(), .makeTemporary(), …
ws.network // .get(), .update(), .applyOutboundIp()
ws.ssh // .status(), .enable(), .disable(), …
ws.publicAccess // .list(), .expose(), .revoke()
ws.resources // .get(), .update()
ws.snapshots // .create(), .restore(), .createArchive(), …
ws.workloads // .create(), .list(), .get(), .start(), .stop(), …
ws.metrics // .stats(), .statsStream(), .info(), .config()
ws.usage // .get(), .totals(), .creditsChart(), .wipe()
ws.metadata // .get(), .update(), .patch()
ws.apiAccess // .status(), .enable(), .disable(), .rotateToken(), …
ws.logs // .get(), .clear(), .listFiles(), .streamBoot(), …Runtime - smart enable
Calling .runtime() uses a smart enable-if-needed flow:
- Check cache - if a valid runtime token exists (within TTL), return it instantly
- Check status - lightweight
GETto see if the Runtime API is already enabled - Get token - if enabled, fetch the gateway JWT (read-only)
- Enable - if not enabled, enable it first (only writes when necessary)
- Cache - store the token with a 55-minute TTL (tokens expire at 60 min)
This means repeated .runtime() calls in a hot loop are essentially free - no redundant network requests.
// Token is cached - second call is instant
const rt1 = await ws.runtime();
const rt2 = await ws.runtime(); // ← cache hit, no API call
// Force a fresh token when needed
const rt3 = await ws.runtime({ force: true });
// Manually invalidate the cache
ws.invalidateRuntime();
// Customize the cache TTL (default: 55 minutes)
client.workspaces.setTokenTtl(30 * 60 * 1000); // 30 minutesConfiguration options
const client = new Oblien({
// Required
clientId: 'your-client-id',
clientSecret: 'your-client-secret',
// Optional
baseUrl: 'https://api.oblien.com', // Custom base URL (for testing/self-hosted)
});TypeScript support
The SDK is fully typed. All request params and responses have TypeScript interfaces:
import type {
WorkspaceCreateParams,
WorkspaceData,
WorkspaceUpdateParams,
WorkspaceListParams,
ExecRunParams,
ExecTask,
ExecStreamEvent,
NetworkUpdateParams,
TerminalSession,
StatsEvent,
FileListParams,
FileEntry,
} from 'oblien';Error handling
The SDK throws typed errors you can catch and inspect:
import { OblienError } from 'oblien';
try {
await ws.create({ image: 'node-20' });
} catch (err) {
if (err instanceof OblienError) {
console.log(err.code); // 'SANDBOX_LIMIT_REACHED'
console.log(err.status); // 402
console.log(err.message); // 'Workspace limit reached for your plan'
}
}Common error codes are documented in the Errors Reference.
Framework examples
Next.js API route
// app/api/sandbox/route.ts
import Oblien from 'oblien';
const client = new Oblien({
clientId: process.env.OBLIEN_CLIENT_ID!,
clientSecret: process.env.OBLIEN_CLIENT_SECRET!,
});
export async function POST(req: Request) {
const { code } = await req.json();
// Create and bind a scoped handle
const data = await client.workspaces.create({
image: 'node-20',
config: { cpus: 1, memory_mb: 256 },
});
const ws = client.workspace(data.id);
await ws.start();
const rt = await ws.runtime();
const result = await rt.exec.run(
['node', '-e', code],
{ timeoutSeconds: 10 },
);
await ws.delete();
return Response.json(result);
}Express middleware
import Oblien from 'oblien';
const client = new Oblien({
clientId: process.env.OBLIEN_CLIENT_ID!,
clientSecret: process.env.OBLIEN_CLIENT_SECRET!,
});
app.post('/run', async (req, res) => {
const data = await client.workspaces.create({
image: 'python-3.12',
});
const ws = client.workspace(data.id);
await ws.start();
const rt = await ws.runtime();
const result = await rt.exec.run(
['python3', '-c', req.body.code],
);
await ws.delete();
res.json(result);
});Next steps
- API Reference - Full endpoint documentation with SDK + REST examples
- Quickstart - Build something in 5 minutes
- Concepts: Modes - Permanent vs temporary workspaces