Skip to content

TypeScript SDK

The official TypeScript SDK for the UnboundBytes API provides a type-safe, ergonomic way to interact with all platform features.

Terminal window
npm install @unboundbytes/sdk
import { UnboundBytesClient } from '@unboundbytes/sdk';
const client = new UnboundBytesClient({
auth: {
type: 'bearer',
token: 'your-auth-token'
}
});
// List devices
const devices = await client.listDevices('ten_xxx');
interface ClientConfig {
baseUrl?: string; // API base URL (default: https://api.unboundbytes.com)
auth: AuthConfig; // Authentication configuration
timeout?: number; // Request timeout in ms (default: 30000)
maxRetries?: number; // Max retry attempts (default: 3)
retryDelay?: number; // Initial retry delay in ms (default: 1000)
}
// Bearer token (web applications)
type BearerAuth = {
type: 'bearer';
token: string | (() => Promise<string>);
expiresAt?: number;
refreshHandler?: () => Promise<{ token: string; expiresAt?: number }>;
};
// HMAC (agents)
type HMACAuth = {
type: 'hmac';
agentId: string;
tenantId: string;
sharedSecret: string;
};
// API Key (scripts)
type APIKeyAuth = {
type: 'apikey';
apiKey: string;
tenantId: string;
};
// Health check
const health = await client.healthCheck();
// Returns: { status: 'healthy' | 'degraded' | 'unhealthy', timestamp: string }
// Initiate pairing
const pairing = await client.initiatePairing({
device_fingerprint: 'unique-device-id',
device_name: 'My Server',
platform: 'linux'
});
// Returns: { pairing_code: string, expires_at: string, portal_url: string }
// Prepare activation (portal only)
const activation = await client.preparePairingActivation(
'ABC123', // pairing code
'ten_xxx' // tenant ID
);
// Returns: { activation_token: string, expires_at: string }
// Activate pairing
const result = await client.activatePairing({
pairing_code: 'ABC123',
activation_token: 'token_xxx',
user_email: 'user@example.com'
});
// Returns: { device_id, agent_id, shared_secret, tenant_id, fingerprint }
// Check pairing status
const status = await client.checkPairingStatus('device-fingerprint');
// Returns: { status: 'pending' | 'activated' | 'paired' | 'expired' }
// List devices
const devices = await client.listDevices('ten_xxx', {
page: 1,
pageSize: 20
});
// Returns: Device[]
// Get device details
const device = await client.getDevice('dev_xxx');
// Returns: Device
// Delete device
await client.deleteDevice('dev_xxx');
// Send heartbeat
await client.sendHeartbeat('dev_xxx', {
cpu: 25.5,
memory: 4096,
disk: 50000,
timestamp: new Date().toISOString()
});
// Poll command queue
const commands = await client.pollCommands('dev_xxx');
// Returns: Command[]
// Queue a command
const result = await client.queueCommand('dev_xxx', {
commandId: 'cmd_xxx',
type: 'restart-app',
issuedAt: new Date().toISOString(),
payload: { appId: 'app_xxx' },
requiresAck: true
}, 'idempotency-key');
// Returns: { commandId: string, status: string }
// Acknowledge command
await client.acknowledgeCommand('dev_xxx', 'cmd_xxx', {
status: 'completed',
output: 'App restarted successfully'
});
// List deployments
const deployments = await client.listDeployments('ten_xxx', {
page: 1,
pageSize: 20
});
// Returns: Deployment[]
// Create deployment
const deployment = await client.createDeployment('ten_xxx', {
appId: 'app_xxx',
deviceId: 'dev_xxx',
configuration: { port: 8080 }
});
// Returns: Deployment
// Get deployment
const deployment = await client.getDeployment('ten_xxx', 'dep_xxx');
// Returns: Deployment
// List all backups for tenant
const backups = await client.listBackups('ten_xxx');
// Returns: Backup[]
// List backups for device
const deviceBackups = await client.listDeviceBackups('dev_xxx');
// Returns: Backup[]
// Create backup metadata
await client.createBackup('dev_xxx', {
backupId: 'bak_xxx',
appId: 'app_xxx',
status: 'completed',
size: 1024000,
checksum: 'sha256:xxx'
});
// Restore backup
await client.restoreBackup('dev_xxx', 'bak_xxx', {
restoreType: 'full',
overwriteExisting: true
});
// List tunnels
const tunnels = await client.listTunnels('dev_xxx');
// Returns: Tunnel[]
// Create tunnel
const tunnel = await client.createTunnel('dev_xxx', {
appId: 'app_xxx',
hostname: 'myapp.example.com'
});
// Returns: Tunnel
// Get tunnel credentials
const credentials = await client.getTunnelCredentials('dev_xxx', 'tun_xxx');
// Returns: TunnelCredentials
// Delete tunnel
await client.deleteTunnel('dev_xxx', 'tun_xxx');
// List apps
const apps = await client.listApps('ten_xxx');
// Returns: App[]
// Create app
const app = await client.createApp({
appId: 'app_xxx',
appName: 'My Application',
tenantId: 'ten_xxx',
configuration: { image: 'nginx:latest' }
});
// Returns: App
// Get app details
const app = await client.getApp('app_xxx');
// Returns: App
// Get tenant dashboard summary
const summary = await client.getTenantDashboard('ten_xxx');
// Returns: { devices: number, apps: number, deployments: number, backups: number }

For real-time command communication:

import { UnboundBytesClient } from '@unboundbytes/sdk';
const client = new UnboundBytesClient({
auth: {
type: 'hmac',
agentId: 'agt_xxx',
tenantId: 'ten_xxx',
sharedSecret: 'secret'
}
});
const ws = client.createWebSocketClient(
client.config.auth as HMACAuth,
'dev_xxx',
{
onCommand: async (command) => {
console.log('Received:', command);
// Process command...
await client.acknowledgeCommand('dev_xxx', command.commandId, {
status: 'completed'
});
},
onConnect: () => console.log('Connected'),
onDisconnect: () => console.log('Disconnected'),
onError: (error) => console.error('Error:', error)
}
);
ws.connect();
import {
APIError,
NetworkError,
RateLimitError,
AuthenticationError,
ValidationError
} from '@unboundbytes/sdk';
try {
await client.getDevice('dev_xxx');
} catch (error) {
if (error instanceof APIError) {
console.error('API Error:', error.code, error.message);
console.error('Status:', error.statusCode);
console.error('Details:', error.details);
console.error('Request ID:', error.requestId);
} else if (error instanceof RateLimitError) {
console.error('Rate limited. Retry after:', error.retryAfter);
} else if (error instanceof NetworkError) {
console.error('Network error:', error.message);
console.error('Cause:', error.cause);
} else if (error instanceof AuthenticationError) {
console.error('Auth error:', error.message);
} else if (error instanceof ValidationError) {
console.error('Validation error:', error.message);
console.error('Fields:', error.fields);
}
}
import type {
Device,
Deployment,
Backup,
Tunnel,
Command,
App,
ClientConfig,
BearerAuth,
HMACAuth,
APIKeyAuth
} from '@unboundbytes/sdk';

For advanced use cases, import the auto-generated types:

import type { paths, components, operations } from '@unboundbytes/sdk';
// Path-level types
type DevicesPath = paths['/v1/devices'];
type GetDevicesOp = DevicesPath['get'];
// Component schemas
type DeviceSchema = components['schemas']['Device'];
// Operation types
type InitiatePairingOp = operations['initiatePairing'];
type InitiatePairingRequest = InitiatePairingOp['requestBody'];
type InitiatePairingResponse = InitiatePairingOp['responses']['201'];

The SDK automatically retries failed requests with exponential backoff:

  • Retryable errors: 5xx errors, 429 (rate limit), network errors
  • Non-retryable errors: 4xx errors (except 429)
  • Backoff: Exponential with jitter (1s, 2s, 4s, … up to 10s max)
  • Rate limits: Respects Retry-After header

Configure retry behavior:

const client = new UnboundBytesClient({
auth: { type: 'bearer', token: 'xxx' },
maxRetries: 5, // Default: 3
retryDelay: 2000 // Default: 1000ms
});