SStorage Brain

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

The 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

OptionTypeDefaultDescription
apiKeystring(required)Tenant API key (sk_live_... or sk_test_...)
baseUrlstringProduction URLAPI base URL
timeoutnumber30000Request timeout in milliseconds
maxRetriesnumber3Number of retry attempts for failed requests
workspaceIdstring--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 } | null

The upload method handles the full upload flow:

  1. Requests a presigned URL from the API (handshake)
  2. Uploads the file content directly to storage
  3. Returns the final file metadata

Upload Options

OptionTypeRequiredDescription
contextstringNoOptional free-form string for categorization (max 100 characters)
tagsRecord<string, string>NoKey-value pairs for categorization
onProgress(percent: number) => voidNoProgress callback (0-100)
webhookUrlstringNoURL to notify after upload completes
signalAbortSignalNoSignal for cancelling the upload
workspaceIdstringNoWorkspace 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 } | null

List 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 null

Pagination

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

OptionTypeDefaultDescription
limitnumber20Results per page (1-100)
cursorstring--Pagination cursor from previous response
contextstring--Filter by context string
fileTypestring--Filter by MIME type
workspaceIdstring--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 expiry

The 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);   // 2

Get 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:

PropertyTypeDescription
messagestringHuman-readable error message
codestringMachine-readable error code
statusCodenumber or undefinedHTTP status code (if from API)
detailsobject or undefinedAdditional 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';