SDK Guide
The Storage Brain TypeScript SDK provides a type-safe client for uploading files, managing workspaces, and querying file metadata.
Installation
npm install @marlinjai/storage-brain-sdkThe SDK ships ESM and CommonJS builds with full TypeScript type definitions.
Creating a Client
import { StorageBrain } from '@marlinjai/storage-brain-sdk';
const storage = new StorageBrain({
apiKey: 'sk_live_your_api_key_here',
});Configuration Options
| Option | Type | Default | Description |
|---|---|---|---|
apiKey | string | (required) | Tenant API key (sk_live_... or sk_test_...) |
baseUrl | string | Production URL | API base URL |
timeout | number | 30000 | Request timeout in milliseconds |
maxRetries | number | 3 | Number of retry attempts for failed requests |
workspaceId | string | -- | Default workspace ID for all operations |
Uploading Files
const file = await storage.upload(myFile, {
context: 'expense-receipts',
tags: { department: 'finance', year: '2026' },
onProgress: (percent) => {
console.log(`Upload progress: ${percent}%`);
},
});
console.log(file.id); // "a1b2c3d4-..."
console.log(file.url); // Download URL
console.log(file.metadata); // { [key: string]: unknown } | nullThe upload method handles the full upload flow:
- Requests a presigned URL from the API (handshake)
- Uploads the file content directly to storage
- Returns the final file metadata
Upload Options
| Option | Type | Required | Description |
|---|---|---|---|
context | string | No | Optional free-form string for categorization (max 100 characters) |
tags | Record<string, string> | No | Key-value pairs for categorization |
onProgress | (percent: number) => void | No | Progress callback (0-100) |
webhookUrl | string | No | URL to notify after upload completes |
signal | AbortSignal | No | Signal for cancelling the upload |
workspaceId | string | No | Workspace to upload into (overrides client default) |
Cancelling an Upload
const controller = new AbortController();
// Start upload
const uploadPromise = storage.upload(file, {
context: 'documents',
signal: controller.signal,
});
// Cancel after 5 seconds
setTimeout(() => controller.abort(), 5000);
try {
const result = await uploadPromise;
} catch (error) {
if (error instanceof UploadError) {
console.log('Upload cancelled');
}
}Workspace Management
Workspaces partition files within a tenant. Each workspace has a name, slug, and optional quota.
Create a Workspace
const workspace = await storage.createWorkspace({
name: 'Marketing',
slug: 'marketing',
quotaBytes: 100 * 1024 * 1024, // 100 MB
metadata: { team: 'growth' },
});
console.log(workspace.id); // "ws-uuid-..."
console.log(workspace.slug); // "marketing"List Workspaces
const workspaces = await storage.listWorkspaces();
// Workspace[]Get a Workspace
const workspace = await storage.getWorkspace('ws-uuid-...');Update a Workspace
const updated = await storage.updateWorkspace('ws-uuid-...', {
name: 'Marketing Team',
quotaBytes: 200 * 1024 * 1024,
});Delete a Workspace
Deleting a workspace soft-deletes all files within it and releases quota.
await storage.deleteWorkspace('ws-uuid-...');Scoping a Client to a Workspace
Use withWorkspace() to create a new client instance that automatically scopes all uploads and file listings to a specific workspace. The workspace ID is sent as an X-Workspace-Id header on every request.
const marketingStorage = storage.withWorkspace(workspace.id);
// Upload goes into the marketing workspace
const file = await marketingStorage.upload(myFile, {
context: 'campaign-assets',
});
// List only returns files in the marketing workspace
const result = await marketingStorage.listFiles();You can also pass workspaceId per-call to override the client default:
const file = await storage.upload(myFile, {
workspaceId: 'specific-workspace-id',
});Retrieving Files
Get a Single File
const file = await storage.getFile('a1b2c3d4-e5f6-7890-abcd-ef1234567890');
console.log(file.originalName); // "receipt.jpg"
console.log(file.sizeBytes); // 245000
console.log(file.workspaceId); // "ws-uuid" or null
console.log(file.metadata); // { [key: string]: unknown } | nullList Files
const result = await storage.listFiles({
limit: 50,
context: 'expense-receipts',
fileType: 'application/pdf',
workspaceId: 'ws-uuid-...',
});
console.log(result.files); // FileInfo[]
console.log(result.total); // Total matching files
console.log(result.nextCursor); // Pagination cursor or nullPagination
let cursor: string | undefined;
do {
const result = await storage.listFiles({ limit: 100, cursor });
for (const file of result.files) {
console.log(file.originalName);
}
cursor = result.nextCursor ?? undefined;
} while (cursor);List Files Options
| Option | Type | Default | Description |
|---|---|---|---|
limit | number | 20 | Results per page (1-100) |
cursor | string | -- | Pagination cursor from previous response |
context | string | -- | Filter by context string |
fileType | string | -- | Filter by MIME type |
workspaceId | string | -- | Filter by workspace |
Signed Download URLs
Generate time-limited URLs for unauthenticated file downloads. Useful for sharing files externally or embedding in emails.
const signed = await storage.getSignedUrl('file-uuid', 3600); // 1 hour
console.log(signed.url); // Full URL with HMAC token
console.log(signed.expiresAt); // ISO 8601 expiration
console.log(signed.expiresIn); // Seconds until expiryThe expiresIn parameter defaults to 3600 seconds (1 hour) and accepts values from 60 to 86400 seconds.
Deleting Files
await storage.deleteFile('a1b2c3d4-e5f6-7890-abcd-ef1234567890');This performs a soft delete. The file metadata is marked as deleted but not immediately removed from storage.
Tenant Information
Check Quota
const quota = await storage.getQuota();
console.log(quota.quotaBytes); // 524288000 (500 MB)
console.log(quota.usedBytes); // 10485760
console.log(quota.availableBytes); // 513802240
console.log(quota.usagePercent); // 2Get Tenant Info
const tenant = await storage.getTenantInfo();
console.log(tenant.id); // "tenant-uuid"
console.log(tenant.name); // "My App"
console.log(tenant.allowedFileTypes); // ["image/jpeg", "image/png", ...]Error Handling
The SDK provides specific error classes for different failure scenarios:
import {
StorageBrain,
StorageBrainError,
AuthenticationError,
QuotaExceededError,
InvalidFileTypeError,
FileTooLargeError,
FileNotFoundError,
NetworkError,
UploadError,
ValidationError,
} from '@marlinjai/storage-brain-sdk';
try {
await storage.upload(file, { context: 'documents' });
} catch (error) {
if (error instanceof AuthenticationError) {
// Invalid or expired API key (HTTP 401)
} else if (error instanceof QuotaExceededError) {
// Storage quota exceeded (HTTP 403)
console.log(error.quotaBytes, error.usedBytes);
} else if (error instanceof InvalidFileTypeError) {
// File type not allowed (HTTP 400)
} else if (error instanceof FileTooLargeError) {
// File exceeds 100 MB limit (HTTP 400)
} else if (error instanceof FileNotFoundError) {
// File does not exist (HTTP 404)
} else if (error instanceof NetworkError) {
// Connection failure or timeout
} else if (error instanceof UploadError) {
// Upload failed or was cancelled
} else if (error instanceof ValidationError) {
// Request validation failed (HTTP 400)
console.log(error.errors); // [{ path: "fileName", message: "..." }]
}
}All error classes extend StorageBrainError, which has the following properties:
| Property | Type | Description |
|---|---|---|
message | string | Human-readable error message |
code | string | Machine-readable error code |
statusCode | number or undefined | HTTP status code (if from API) |
details | object or undefined | Additional error context |
Retry Behavior
The SDK automatically retries requests that fail with server errors (5xx) or network issues. Client errors (4xx) are not retried.
Retry configuration:
- Max attempts: 3 (configurable via
maxRetries) - Backoff: Exponential (1s, 2s, 4s, capped at 10s)
TypeScript Types
The SDK exports all types for use in your application:
import type {
StorageBrainConfig,
UploadOptions,
FileInfo,
ListFilesOptions,
ListFilesResult,
QuotaInfo,
TenantInfo,
UploadHandshake,
SignedUrlInfo,
Workspace,
CreateWorkspaceInput,
UpdateWorkspaceInput,
AllowedMimeType,
FileMetadata,
} from '@marlinjai/storage-brain-sdk';Constants
Useful constants are also exported:
import {
ALLOWED_MIME_TYPES, // ['image/jpeg', 'image/png', ...]
} from '@marlinjai/storage-brain-sdk';