Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.tuturuuu.com/llms.txt

Use this file to discover all available pages before exploring further.

Tuturuuu SDK

Official TypeScript/JavaScript SDK for interacting with the Tuturuuu platform. Provides type-safe access to workspace storage, files, documents, and agent-friendly task workflows.

Installation

npm install tuturuuu

Quick Start

import { TuturuuuClient } from 'tuturuuu';

// Initialize the client with your API key
const client = new TuturuuuClient('ttr_your_api_key');

// List files in workspace
const files = await client.storage.list({
  path: 'documents',
  limit: 50
});

// Upload a file
const file = new File(['content'], 'example.txt');
const result = await client.storage.upload(file, {
  path: 'documents'
});

// Create a document
const doc = await client.documents.create({
  name: 'My Document',
  content: { text: 'Hello World' }
});

Features

Bun CLI

Use ttr for browser login, workspace discovery, and task workflows

Headless Login

Copy a short-lived CLI token from the browser when a local callback is not available

Storage Operations

Upload, download, list, and delete files and folders

Document Management

Create, read, update, and delete workspace documents

Signed URLs

Generate temporary shareable links for files

Image Resizing

Apply Supabase image transformations during share and download

Analytics

Track storage usage, file counts, and limits

CLI

The SDK package includes a native Bun-powered CLI. ttr is the primary command, and tuturuuu plus tutur3u are aliases for the same binary.
ttr --help
ttr login
ttr upgrade
ttr --version
ttr whoami
ttr workspaces
ttr workspaces use
ttr boards
ttr boards use
ttr lists use
ttr tasks use
ttr tasks --board <board-id>
ttr tasks --compact
ttr tasks create "Add Tuturuuu CLI"
ttr tasks create --list <list-id> --name "Write release notes"
ttr tasks done <task-id>
ttr tasks close <task-id>
ttr tasks update <task-id> --json-payload '{"completed":true}'
ttr finance wallets
ttr finance transactions --page-size 10
ttr finance transactions create --amount 150000 --wallet <wallet-id> --taken-at 2026-05-09
ttr finance budgets status
ttr login opens the browser and creates a Tuturuuu-managed CLI app-session JWT pair through apps/web. The terminal and browser confirmation page show the signed-in account email when the web session exposes it. The CLI stores that session and selected workspace, board, list, task, label, and project in the OS app config directory. personal is the default workspace after login and when no workspace has been selected: If you target a local web origin with ttr config set-base-url, prefer a browser-safe host such as http://localhost:7803. The CLI normalizes 0.0.0.0 and unspecified IPv6 origins to localhost so the browser login flow does not open a blocked address. On Dockerized production deployments, the web auth start route also rewrites wildcard bind origins such as 0.0.0.0:7803 to the forwarded public host or the configured app URL (WEB_APP_URL, NEXT_PUBLIC_WEB_APP_URL, NEXT_PUBLIC_APP_URL, COOLIFY_URL, or COOLIFY_FQDN) before sending users to /login. If those environment values are unset and no public forwarded host is available, https://tuturuuu.com is the default fallback.
  • macOS: ~/Library/Application Support/Tuturuuu/config.json
  • Linux: $XDG_CONFIG_HOME/tuturuuu/config.json or ~/.config/tuturuuu/config.json
  • Windows: %APPDATA%\Tuturuuu\config.json
For headless agent environments, use copy-token mode:
ttr login --copy
Open the printed URL in an authenticated browser, copy the returned CLI token, and paste it into the terminal. Set TUTURUUU_CONFIG to use a custom config file, or use ttr config set-base-url <url> to target a local or staging web origin. The CLI checks the npm registry at most once per hour and writes a notice to stderr when a newer tuturuuu version is available. This keeps --json output machine-readable for agents. Use ttr upgrade to run bun i -g tuturuuu. Use --no-update-check on a single command, or set TUTURUUU_DISABLE_UPDATE_CHECK=1, to disable the check. CLI sessions are dedicated Tuturuuu gateway JWTs, separate from normal browser and Supabase Auth sessions. The CLI stores a short-lived app-session access JWT plus a longer-lived refresh JWT, refreshes shortly before access-token expiry, updates the saved config with the rotated refresh token, and retries once after a 401 response. Refresh JWTs only carry the CLI refresh scope, so they cannot be used directly as API bearer tokens. If refresh fails because the user no longer exists or the refresh JWT has expired, run ttr login again. API routes used by ttr must accept the CLI app-session access JWT from the Authorization: Bearer ... header. For apps/web routes, prefer withSessionAuth(..., { allowAppSessionAuth: true }); shared API routes should verify the app-session token before falling back to Supabase session auth and then keep workspace membership checks explicit. SDK releases are published automatically from .github/workflows/release-sdk-package.yaml when a push to production changes packages/sdk/package.json or the release workflow itself. The same workflow can also be dispatched manually to catch up a version that already landed on production. Manual dispatches from any other ref are rejected before dependency installation, packaging, or npm trusted publishing can run. The workflow runs the SDK tests, skips publication if the exact package version already exists on npm, prepares a tarball with npm pack outside the trusted-publish job, and then publishes that tarball through npm trusted publishing. Only the final publish-npm job requests id-token: write, so GitHub Actions mints the short-lived OIDC token after dependency installation, package builds, and version checks have already finished. That job downloads the prepared artifact, verifies its package name and version, confirms the bundled npm CLI supports trusted publishing, and runs npm publish ./<tarball> --ignore-scripts without an NPM_TOKEN so npm treats the downloaded artifact as a local file path. The job targets the GitHub environment sdk-release-production; keep that environment restricted to the production branch in repository settings, and keep the npm trusted publisher bound to the same environment name so a branch-selected workflow cannot mint a matching npm publish identity. Do not add bun install, npm install -g, package builds, package lifecycle scripts, or repository secrets to the SDK release workflow. Because the tuturuuu package is public and tutur3u/platform is a public repository, npm also generates and attaches build provenance attestations automatically. The SDK imports @tuturuuu/types and @tuturuuu/internal-api through package exports that point at their git-ignored dist/ directories. The release workflow must build those two workspace packages in the non-OIDC build and prepare jobs before running SDK tests or creating the npm pack tarball; otherwise a clean Actions checkout cannot resolve the SDK imports. The matching npm-side trusted publisher must stay configured at https://www.npmjs.com/package/tuturuuu/access with these exact values:
  • Provider: GitHub Actions
  • Organization or user: tutur3u
  • Repository: platform
  • Workflow filename: release-sdk-package.yaml
  • Environment name: sdk-release-production
If the npm-side configuration drifts (for example after a repo rename or after moving the publish step into another workflow file), npm publish will fail with ENEEDAUTH. Update the trusted publisher entry first; do not paper over the failure by reintroducing a long-lived NPM_TOKEN. After confirming a trusted-publisher release works end to end, the npm package settings should keep Require two-factor authentication and disallow tokens enabled so the package cannot be published with classic tokens.

Scoped Help

Use scoped help when you know the area but not the exact flags. Help commands do not require login, do not read the saved config, and do not run the update check.
ttr --help
ttr upgrade --help
ttr finance --help
ttr finance transactions --help
ttr tasks --help
ttr tasks create --help
ttr tasks done --help
ttr tasks close --help
ttr tasks update --help
ttr workspaces --help
ttr help tasks create
Global help shows the command map. Group help explains defaults and shared flags. Action help focuses on a single operation, including required ids, payload flags, and practical examples.

Agent-Friendly Discovery

For autonomous engineering workflows, start by discovering the current account and selected context:
ttr whoami
ttr whoami --json
ttr workspaces --json --no-update-check
ttr tasks --json --no-update-check
ttr whoami shows login state, account email, current workspace, selected board/list/task/label/project ids, base URL, config path, and the Tuturuuu CLI session label. Use --json --no-update-check when another agent is going to parse stdout. The v1 task surface covers workspaces, boards, lists, tasks, labels, projects, relationships, moves, and bulk task updates. Read-oriented groups list by default, so ttr tasks and ttr workspaces are equivalent to their explicit list forms. ttr boards shows active, unarchived boards by default; use ttr boards --archived, ttr boards --deleted, or ttr boards --all when an agent needs archived or deleted board inventory. Unscoped ttr tasks starts from the personal workspace and lists personal tasks plus open tasks assigned to the user in other accessible workspaces; selecting a board/list or passing --workspace scopes the result back to that context. ttr tasks shows open tasks by default by excluding rows with completed_at or closed_at and by limiting task-list statuses to not_started and active. That default hides tasks in documents, review, done, and closed lists, and tasks from archived boards; use --all, --include-archived, --documents, --review, --done, --closed, --include-documents, --include-review, --include-done, or --include-closed to adjust that filter. Results are paginated at 50 tasks by default; use --page/--page-size or --limit/--offset to page through larger task sets. Human-readable task lists show a footer with the total task count and current page/max page. Add --compact to task lists when an agent only needs the task title, task list name, and per-task workspace name. Task lists are ordered by priority and due date, with prettier due dates and configured task-list colors in table output. Human-readable tables use smooth box borders, respect the terminal width, and wrap long cell content so task titles stay readable instead of overflowing narrow panes. Add --json when another agent or script needs machine-readable output. tasks create, boards create, and lists create accept a quoted positional name as a shorthand for --name. Task CRUD accepts either the task UUID or the board identifier shown in the UI, such as VHP-12; prefixed identifiers resolve within the selected workspace even when another list is selected, and matching human-readable output keeps that prefixed identifier visible. Marking a task completed stamps completed_at so Tuturuuu moves it to the first done list; pass --list <done-list-id> or include list_id in --json-payload to choose another done destination. Use ttr tasks done [task-id] as the quick shortcut. Use ttr tasks close [task-id] to stamp closed_at; pass --list <closed-list-id> to choose a specific closed destination.

Finance CLI

Finance commands use the selected workspace and the same authenticated internal APIs as the web app. Use --workspace or --ws to target a different workspace, and use --json-payload when a script needs to send fields that do not have dedicated flags yet. Finance list output is paginated by default; use --page/--page-size or --limit/--offset to move through larger result sets.
# Wallet CRUD.
ttr finance wallets
ttr finance wallets --page 2 --page-size 10
ttr finance wallets get <wallet-id>
ttr finance wallets create "Cash" --currency VND --balance 0 --type STANDARD
ttr finance wallets update <wallet-id> --name "Operating Cash"
ttr finance wallets delete <wallet-id>

# Transaction CRUD and reporting helpers.
ttr finance transactions --page-size 10
ttr finance transactions --limit 25 --offset 50
ttr finance transactions get <transaction-id>
ttr finance transactions create --amount 150000 --wallet <wallet-id> --taken-at 2026-05-09
ttr finance transactions update <transaction-id> --category <category-id>
ttr finance transactions delete <transaction-id>
ttr finance transactions export --wallets <wallet-id> --start 2026-05-01 --end 2026-05-31
ttr finance transactions stats --start 2026-05-01 --end 2026-05-31

# Categories, budgets, and recurring transactions.
ttr finance categories create "Travel" --expense --color blue
ttr finance budgets create "Marketing" --amount 1000000 --period monthly --start-date 2026-05-01
ttr finance budgets status
ttr finance recurring create "Rent" --amount 5000000 --wallet <wallet-id> --frequency monthly --start-date 2026-05-01
ttr finance recurring upcoming --days-ahead 30
Common task workflows:
# List open tasks in the selected workspace.
ttr tasks

# List active boards only; pass --archived, --deleted, or --all for other states.
ttr boards

# Compact human output: task title, task list, and workspace.
ttr tasks --compact

# Machine-readable output for agents.
ttr tasks --json --no-update-check

# Create from the selected board/list, prompting when needed.
ttr tasks create "Add Tuturuuu CLI"

# Create directly into a known list.
ttr tasks create --list <list-id> --name "Write release notes"

# Move with the keyboard picker.
ttr tasks move

# Move directly to a done list.
ttr tasks move VHP-12 --list <done-list-id>

# Quick mark done and use the first done list on the task board when available.
ttr tasks done VHP-12

# Quick mark done with an explicit done destination.
ttr tasks done <task-id> --list <done-list-id>

# Quick mark closed and use the first closed list on the task board when available.
ttr tasks close VHP-12

# Quick mark closed with an explicit closed destination.
ttr tasks close <task-id> --list <closed-list-id>

# Mark completed and let Tuturuuu choose the default done list.
ttr tasks update VHP-12 --json-payload '{"completed":true}'

# Mark completed and choose a specific done destination.
ttr tasks update <task-id> --list <done-list-id> --json-payload '{"completed":true}'
For terminal workflows, omit an id from use, get, update, delete, or move commands to pick a workspace, board, list, task, label, or project with the keyboard. The interactive picker shows one-based indexes, colored badges such as [FREE] Tuturuuu and [PRO] Personal, task-list color swatches, the selected row, and muted metadata. Use up/down or j/k to move, space/enter to select, and escape/q to cancel. Interactive selection is disabled for --json output. Use ttr -v or ttr --version to print the installed CLI version.

Client Initialization

Basic Usage

const client = new TuturuuuClient('ttr_your_api_key');

With Custom Configuration

const client = new TuturuuuClient({
  apiKey: 'ttr_your_api_key',
  baseUrl: 'https://tuturuuu.com/api/v1', // optional
  timeout: 30000 // optional, default 30s
});

Storage API

List Files

Lists files and folders in the workspace drive.
const files = await client.storage.list({
  path: 'documents',          // folder path (optional)
  search: 'report',           // search term (optional)
  limit: 50,                  // max results (optional, default 100)
  offset: 0,                  // pagination offset (optional)
  sortBy: 'created_at',       // sort field (optional)
  sortOrder: 'desc'           // sort direction (optional)
});

console.log(files.data);       // Array of StorageObject
console.log(files.pagination); // Pagination info

Upload File

Uploads a file to the workspace drive.
const file = new File(['content'], 'document.pdf');

const result = await client.storage.upload(file, {
  path: 'documents',  // destination folder (optional)
  upsert: true        // overwrite if exists (optional)
});

console.log(result.data.path);     // Relative path
console.log(result.data.fullPath); // Full storage path

Download File

Downloads a file as a Blob.
const blob = await client.storage.download('documents/report.pdf');

// Save to disk (Node.js)
const buffer = Buffer.from(await blob.arrayBuffer());
await fs.writeFile('report.pdf', buffer);

// Create download link (Browser)
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'report.pdf';
a.click();
Apply an image transform during download:
const blob = await client.storage.download('images/photo.png', {
  transform: {
    width: 320,
    height: 180,
    resize: 'cover',
    quality: 80
  }
});

Delete Files

Deletes one or more files or folders.
const result = await client.storage.delete([
  'documents/old-report.pdf',
  'images/screenshot.png'
]);

console.log(result.data.deleted); // Number of files deleted
console.log(result.data.paths);   // Array of deleted paths

Create Folder

Creates a new folder in the workspace drive.
const result = await client.storage.createFolder(
  'documents',  // parent path
  'reports'     // folder name
);

console.log(result.data.path); // "documents/reports"

Share File

Generates a signed URL for temporary file sharing.
const result = await client.storage.share('documents/report.pdf', {
  expiresIn: 3600 // 1 hour in seconds (default 3600, max 604800)
});

console.log(result.data.signedUrl);  // Temporary URL
console.log(result.data.expiresAt);  // ISO timestamp
console.log(result.data.expiresIn);  // Seconds until expiration
Apply an image transform before generating the signed URL:
const result = await client.storage.share('images/photo.png', {
  expiresIn: 3600,
  transform: {
    width: 320,
    height: 180,
    resize: 'contain',
    quality: 85
  }
});

Get Analytics

Retrieves storage usage statistics for the workspace.
const analytics = await client.storage.getAnalytics();

console.log(analytics.data.totalSize);       // Total bytes used
console.log(analytics.data.fileCount);       // Number of files
console.log(analytics.data.storageLimit);    // Storage limit in bytes
console.log(analytics.data.usagePercentage); // Usage percentage
console.log(analytics.data.largestFile);     // Largest file info
console.log(analytics.data.smallestFile);    // Smallest file info

Documents API

List Documents

Lists documents in the workspace with optional filters.
const docs = await client.documents.list({
  search: 'meeting',    // search in names (optional)
  limit: 20,            // max results (optional, default 50)
  offset: 0,            // pagination offset (optional)
  isPublic: false       // filter by visibility (optional)
});

console.log(docs.data);       // Array of Document
console.log(docs.pagination); // Pagination info

Create Document

Creates a new document in the workspace.
const doc = await client.documents.create({
  name: 'Meeting Notes',
  content: {
    text: 'Discussion points...',
    attendees: ['Alice', 'Bob']
  },
  isPublic: false // optional, default false
});

console.log(doc.data.id);      // Document ID
console.log(doc.data.name);    // Document name
console.log(doc.data.content); // JSONB content

Get Document

Retrieves a document by its ID.
const doc = await client.documents.get('document-id-123');

console.log(doc.data.name);
console.log(doc.data.content);
console.log(doc.data.created_at);

Update Document

Updates an existing document.
const doc = await client.documents.update('document-id-123', {
  name: 'Updated Meeting Notes',     // optional
  content: { text: 'New content' },  // optional
  isPublic: true                     // optional
});

console.log(doc.data); // Updated document

Delete Document

Deletes a document permanently.
await client.documents.delete('document-id-123');

Search Documents

Searches documents by name (alias for list() with search).
const results = await client.documents.search('meeting notes', {
  limit: 10,
  isPublic: false
});

console.log(results.data); // Matching documents

CMS Delivery

The SDK also exposes the external-project delivery client that powers Tuturuuu CMS preview surfaces and branded external apps.
const delivery = await client.externalProjects.getDelivery('workspace-id');

if (delivery.loadingData?.adapter === 'yoola') {
  console.log(delivery.loadingData.featuredArtwork?.title);
  console.log(delivery.loadingData.singletonSections.about?.bodyMarkdown);
}

const yoolaLoadingData =
  await client.externalProjects.getYoolaLoadingData('workspace-id');
console.log(yoolaLoadingData.artworkCategories);
For the Yoola adapter, artworkCategories comes from the configured gallery taxonomy on singleton-sections/gallery (profile_data.categoryOptions) rather than being inferred from every artwork entry. External app admin dashboards can auto-create and bind the workspace studio before their first diff/apply when the workspace ID is configured and the app token has external-projects:* or manage scope:
await client.externalProjects.sync.setup('workspace-id', { manifest });

Public Folder Asset Sync

External apps can keep seed media in their local public/ folder and still publish it through Tuturuuu Drive. Mark each manifest asset with metadata.publicPath, metadata.localAssetPath, metadata.sourcePublicPath, or a relative sourceUrl.
import {
  linkExternalProjectPublicFolderAssets,
} from 'tuturuuu';
import { uploadExternalProjectPublicFolderAssets } from 'tuturuuu/external-projects/public-assets';

const linkedManifest = linkExternalProjectPublicFolderAssets(manifest);

const sync = await uploadExternalProjectPublicFolderAssets(
  client.externalProjects,
  'workspace-id',
  manifest,
  { publicDir: './public' }
);

console.log(sync.uploaded.length);
console.log(linkedManifest.content.entries[0]?.assets?.[0]?.storagePath);
The linker writes deterministic storage paths under external-projects/{adapter}/{collectionSlug}/{entrySlug}/{filename}. The upload helper uses the existing external-project signed upload URL route, so production Supabase keys are not needed in external apps.

TanStack Query

For React apps, keep the SDK as the delivery boundary and TanStack Query as the cache owner:
import { useQuery } from '@tanstack/react-query';
import {
  getEpmDeliveryQueryOptions,
  getYoolaLoadingDataQueryOptions,
} from 'tuturuuu';

const deliveryQuery = useQuery({
  ...getEpmDeliveryQueryOptions(client.externalProjects, 'workspace-id'),
  staleTime: 60_000,
});

const yoolaLoadingQuery = useQuery({
  ...getYoolaLoadingDataQueryOptions(
    client.externalProjects,
    'workspace-id'
  ),
  staleTime: 60_000,
});
In external Next.js apps, prefer hydrating the delivery query once near the root layout and deriving page-level view models from that shared cache instead of letting each route perform its own SDK fetch.

Error Handling

The SDK provides specific error classes for different scenarios:
import {
  AuthenticationError,
  AuthorizationError,
  NotFoundError,
  ConflictError,
  RateLimitError,
  NetworkError,
  ValidationError
} from 'tuturuuu';

try {
  await client.storage.upload(file);
} catch (error) {
  if (error instanceof AuthenticationError) {
    // Invalid or expired API key
    console.error('Authentication failed:', error.message);
  } else if (error instanceof AuthorizationError) {
    // Insufficient permissions
    console.error('Forbidden:', error.message);
  } else if (error instanceof NotFoundError) {
    // Resource not found
    console.error('Not found:', error.message);
  } else if (error instanceof ConflictError) {
    // Resource already exists
    console.error('Conflict:', error.message);
  } else if (error instanceof RateLimitError) {
    // Rate limit exceeded
    console.error('Rate limited:', error.message);
  } else if (error instanceof NetworkError) {
    // Network or timeout error
    console.error('Network error:', error.message);
  } else if (error instanceof ValidationError) {
    // Invalid input
    console.error('Validation error:', error.message);
  }
}

Error Properties

All error classes include:
  • message - Human-readable error description
  • code - Machine-readable error code
  • statusCode - HTTP status code
  • name - Error class name
try {
  await client.storage.delete([]);
} catch (error) {
  if (error instanceof ValidationError) {
    console.log(error.message);    // "At least one path is required"
    console.log(error.code);       // "VALIDATION_ERROR"
    console.log(error.statusCode); // 400
    console.log(error.name);       // "ValidationError"
  }
}

TypeScript Support

The SDK is fully typed with TypeScript:
import type {
  StorageObject,
  Document,
  ListStorageOptions,
  UploadOptions,
  ShareOptions,
  CreateDocumentData,
  UpdateDocumentData,
  ListDocumentsOptions
} from 'tuturuuu';

// Type-safe options
const options: ListStorageOptions = {
  path: 'documents',
  sortBy: 'created_at',
  sortOrder: 'desc'
};

// Type-safe document data
const docData: CreateDocumentData = {
  name: 'My Doc',
  content: { text: 'Content' },
  isPublic: false
};

API Key Management

Creating an API Key

  1. Log in to your Tuturuuu workspace
  2. Navigate to SettingsAPI Keys
  3. Click “Create API Key”
  4. Set a descriptive name
  5. Assign a Workspace Role (determines permissions)
  6. Optionally set an expiration date
  7. Copy the generated key (starts with ttr_)
  8. Store securely (you won’t see it again)

Best Practices

Never commit API keys to version control! Use environment variables or secret management services.
# .env
TUTURUUU_API_KEY=ttr_your_api_key_here
// app.ts
const client = new TuturuuuClient(process.env.TUTURUUU_API_KEY!);

Permissions

API keys inherit permissions from their assigned Workspace Role. The role determines what operations the key can perform. Required Permissions:
OperationRequired Role Permission
Storage Operations (list, upload, download, delete, folders, share, analytics)manage_drive
Document Operations (list, create, get, update, delete, search)manage_documents
API Key Managementmanage_api_keys (Owner/Admin only)
Note: Permissions are managed through workspace roles, not individual API keys. To modify what an API key can do, update its assigned role’s permissions in SettingsRoles.

Security Best Practices

CRITICAL: Never expose API keys in client-side code! API keys must remain server-side only.

❌ Insecure (NEVER do this)

// BAD: Exposes API key to browser!
'use client'; // Client component

const client = new TuturuuuClient(process.env.NEXT_PUBLIC_API_KEY);
// ❌ Anyone can inspect network requests and steal your key!
Create server-side API routes that proxy SDK calls:
// app/api/storage/list/route.ts (SERVER-SIDE)
import { TuturuuuClient } from 'tuturuuu';
import { NextResponse } from 'next/server';

const client = new TuturuuuClient(process.env.TUTURUUU_API_KEY);

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);
  const path = searchParams.get('path') || '';

  const files = await client.storage.list({ path, limit: 50 });
  return NextResponse.json(files);
}
Then call from your client component:
// app/components/FileList.tsx (CLIENT COMPONENT - SAFE)
'use client';

export function FileList() {
  const [files, setFiles] = useState([]);

  useEffect(() => {
    fetch('/api/storage/list?path=documents')
      .then(res => res.json())
      .then(setFiles);
  }, []);

  return <div>{/* Render files */}</div>;
}

Node.js/Express Backend

// server.js
import { TuturuuuClient } from 'tuturuuu';
import express from 'express';

const client = new TuturuuuClient(process.env.TUTURUUU_API_KEY);
const app = express();

app.get('/api/files', async (req, res) => {
  const files = await client.storage.list();
  res.json(files);
});

app.listen(3000);

Serverless Functions (Netlify, Vercel, AWS Lambda)

// netlify/functions/storage.ts
import { TuturuuuClient } from 'tuturuuu';

export const handler = async (event) => {
  const client = new TuturuuuClient(process.env.TUTURUUU_API_KEY);
  const files = await client.storage.list();

  return {
    statusCode: 200,
    body: JSON.stringify(files),
  };
};

Security Checklist

Always use the SDK in server-side contexts:
  • Next.js API Routes (App Router or Pages Router)
  • Node.js backend servers
  • Serverless functions
  • Server Components (React Server Components)
Store API keys in environment variables:
# .env.local (server-side only)
TUTURUUU_API_KEY=ttr_your_key_here
TUTURUUU_BASE_URL=https://tuturuuu.com/api/v1
Never use NEXT_PUBLIC_ prefix for API keys!
Ensure secrets never get committed:
# .gitignore
.env.local
.env*.local
This exposes the variable to the browser:
# BAD - Exposed to client!
NEXT_PUBLIC_TUTURUUU_API_KEY=ttr_key
Never import the SDK in files with 'use client' directive.
Never commit keys to version control:
// BAD
const client = new TuturuuuClient('ttr_1234567890abcdef');

Complete Example

See the external app example for a complete implementation showcasing:
  • ✅ Secure server-side API routes
  • ✅ File upload with progress
  • ✅ Storage analytics dashboard
  • ✅ Proper error handling
  • ✅ Environment variable configuration

Rate Limiting

The API enforces rate limits per API key to ensure fair usage and system stability:

Rate Limits by Operation

OperationLimitWindow
Storage Upload20 requestsper minute
Storage Download50 requestsper minute
Signed Upload URLs30 requestsper minute
All Other Operations100 requestsper minute

Rate Limit Headers

Every API response includes rate limit information in the headers:
  • X-RateLimit-Limit - Maximum requests allowed in the time window
  • X-RateLimit-Remaining - Number of requests remaining in current window
  • X-RateLimit-Reset - Unix timestamp when the rate limit resets
const result = await client.storage.list();

// Access rate limit info from headers (if using direct API calls)
console.log('Limit:', result.headers.get('X-RateLimit-Limit'));
console.log('Remaining:', result.headers.get('X-RateLimit-Remaining'));
console.log('Resets at:', new Date(
  Number.parseInt(result.headers.get('X-RateLimit-Reset')) * 1000
));

Handling Rate Limits

When you exceed the rate limit, the API returns a 429 Too Many Requests response:
try {
  await client.storage.upload(file);
} catch (error) {
  if (error instanceof RateLimitError) {
    console.error('Rate limited! Try again later.');
    console.error('Retry after:', error.retryAfter, 'seconds');

    // Implement exponential backoff
    await new Promise(resolve =>
      setTimeout(resolve, error.retryAfter * 1000)
    );
  }
}

Workspace-Specific Rate Limits

Workspace administrators can configure custom rate limits for their workspace through Workspace Secrets. These override the default limits for all API keys in that workspace. To set custom rate limits:
  1. Navigate to SettingsWorkspace SettingsSecrets
  2. Add one or more of these secrets:
Secret NameDescriptionExample Value
RATE_LIMIT_WINDOW_MSTime window in milliseconds60000 (1 minute)
RATE_LIMIT_MAX_REQUESTSMax requests per window200 (double the default)
Example Configuration:
RATE_LIMIT_WINDOW_MS = 60000
RATE_LIMIT_MAX_REQUESTS = 500
This configuration allows 500 requests per minute for all API keys in the workspace, instead of the default 100.
Workspace-specific rate limits only apply to operations that don’t have explicit per-operation limits. For operations like uploads and downloads, the operation-specific limits still apply.

Best Practices

Implement exponential backoff when handling rate limits to avoid hammering the API with retries.
async function uploadWithRetry(file: File, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await client.storage.upload(file);
    } catch (error) {
      if (error instanceof RateLimitError && attempt < maxRetries - 1) {
        // Exponential backoff: 1s, 2s, 4s
        const delay = Math.pow(2, attempt) * 1000;
        await new Promise(resolve => setTimeout(resolve, delay));
        continue;
      }
      throw error;
    }
  }
}
Monitor rate limit headers to track your usage and avoid hitting limits:
// Check remaining requests after each call
const response = await fetch('https://tuturuuu.com/api/v1/storage/list', {
  headers: { 'Authorization': `Bearer ${apiKey}` }
});

const remaining = response.headers.get('X-RateLimit-Remaining');
const limit = response.headers.get('X-RateLimit-Limit');

console.log(`API calls remaining: ${remaining}/${limit}`);

// Slow down if close to limit
if (Number(remaining) < Number(limit) * 0.1) {
  await new Promise(resolve => setTimeout(resolve, 1000));
}

Examples

Basic Usage

Complete example of all SDK operations

Error Handling

Comprehensive error handling patterns

Support

GitHub

Source code and issues

Discord

Community support

Docs

Full documentation

Changelog

v0.1.0 (2025-10-21)

  • Initial release
  • Storage operations (list, upload, download, delete, folders, share, analytics)
  • Document operations (CRUD, search)
  • Comprehensive error handling
  • Full TypeScript support
  • Zod validation

License

MIT