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

> Official TypeScript/JavaScript SDK for the Tuturuuu platform

# 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

<CodeGroup>
  ```bash npm theme={null}
  npm install tuturuuu
  ```

  ```bash yarn theme={null}
  yarn add tuturuuu
  ```

  ```bash pnpm theme={null}
  pnpm add tuturuuu
  ```

  ```bash bun theme={null}
  bun add tuturuuu
  ```
</CodeGroup>

## Quick Start

```typescript theme={null}
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

<CardGroup cols={2}>
  <Card title="Bun CLI" icon="terminal">
    Use `ttr` for browser login, workspace discovery, and task workflows
  </Card>

  <Card title="Headless Login" icon="copy">
    Copy a short-lived CLI token from the browser when a local callback is not
    available
  </Card>

  <Card title="Storage Operations" icon="folder">
    Upload, download, list, and delete files and folders
  </Card>

  <Card title="Document Management" icon="file-alt">
    Create, read, update, and delete workspace documents
  </Card>

  <Card title="Signed URLs" icon="link">
    Generate temporary shareable links for files
  </Card>

  <Card title="Image Resizing" icon="image">
    Apply Supabase image transformations during share and download
  </Card>

  <Card title="Analytics" icon="chart-bar">
    Track storage usage, file counts, and limits
  </Card>
</CardGroup>

## 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.

```bash theme={null}
ttr --help
ttr login
ttr upgrade
ttr --version
ttr whoami
ttr host current
ttr host use local --port 7803
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 search "deadline review"
ttr tasks search "deadline review" --mode text
ttr tasks search "deadline review" --mode semantic
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 wallets balance --all
ttr finance transactions --page-size 10
ttr finance transactions create --amount 150000 --wallet <wallet-id> --taken-at 2026-05-09
ttr finance transfers migrate --from-transaction <tx-id> --to-transaction <tx-id> --from-wallet <wallet-id> --to-wallet <wallet-id> --amount 150000 --taken-at 2026-05-09
ttr finance budgets status
ttr calendar events list --start 2026-06-11T00:00:00Z --end 2026-06-12T00:00:00Z
ttr calendar events create "Focus block" --start 2026-06-11T09:00:00+07:00 --duration-minutes 90
ttr calendar schedule preview --window-days 14 --timezone Asia/Ho_Chi_Minh
```

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

Use `ttr host use` to switch between Tuturuuu origins. `production` and `prod`
resolve to `https://tuturuuu.com`; `local` and `localhost` prefer safe local
environment URLs from `TUTURUUU_LOCAL_BASE_URL`, `PORTLESS_URL`, `WEB_APP_URL`,
`NEXT_PUBLIC_WEB_APP_URL`, or `NEXT_PUBLIC_APP_URL`, then fall back to
`https://tuturuuu.localhost`. Pass `--port <port>` for
`http://localhost:<port>`, or `--portless --port <port>` for
`https://tuturuuu.localhost:<port>`. Changing origins clears the saved session
and selected workspace context because both are origin-specific.

`ttr config set-base-url <url>` remains available for compatibility and uses the
same host normalization and context-clearing behavior. 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:

```bash theme={null}
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 host use <target>` to target a local, production, or explicit
URL 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.

The SDK fetch wrapper must attach CLI bearer tokens, refresh sessions, and retry
`401` responses only for relative Tuturuuu API paths or absolute URLs whose
origin exactly matches the configured CLI base URL. Cross-origin absolute URLs
must be delegated without `Authorization` headers.

API routes used by `ttr` must accept the CLI app-session access JWT from the
`Authorization: Bearer ...` header. For `apps/web` routes, prefer
`withSessionAuth` with `targetApp: 'platform'` and
`requiredScope: 'cli:access'` when the route is CLI-only; shared user profile
routes can include the platform target alongside satellite app targets. Shared
API routes should verify the CLI app-session token before falling back to
Supabase session auth and then keep workspace membership checks explicit.
When a handler lives in `packages/apis` and is reused by multiple apps, keep
`@tuturuuu/auth` imports in the app-owned route wrapper and pass a
pre-authenticated context into the shared handler so package builds do not form
an auth/UI/API cycle.

SDK version and changelog PRs are generated by
`.github/workflows/release-please.yaml` from Conventional Commits on
`production`. Release Please tracks the SDK in `release-please-config.json` as
the `sdk` component, records the current version in
`.release-please-manifest.json`, and updates `packages/sdk/package.json` in the
combined monorepo release PR. SDK releases are then published automatically from
`.github/workflows/release-sdk-package.yaml` when that version bump lands on
`production` or when the release workflow itself changes. The same publish
workflow can 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, waits for publishable Tuturuuu workspace dependencies to
be visible on npm, prepares a tarball with `npm pack` outside the
trusted-publish job, and then publishes that tarball through
[npm trusted publishing](https://docs.npmjs.com/trusted-publishers). 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, runs `npm publish ./<tarball> --ignore-scripts`, and polls `npm view`
until the exact SDK version is visible without using an `NPM_TOKEN`. 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 package manifest inside the
tarball must keep repository metadata aligned with the provenance repository:
`repository.url` is `https://github.com/tutur3u/platform`, and
`repository.directory` points at the package directory.

`.github/workflows/sdk-version-bump.yaml` is retired and no longer generates
patch-version PRs. Do not restore checksum or package-only bump automation for
the SDK; release-please must remain the version/changelog source of truth.

The SDK imports `@tuturuuu/devbox`, `@tuturuuu/types`, and
`@tuturuuu/internal-api` through package exports that point at their git-ignored
`dist/` directories. The release workflow must build those 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 workflow then runs
`node scripts/ci/prepare-npm-package-manifest.js packages/sdk` before
`npm pack` so the published manifest contains concrete npm versions instead of
local `workspace:` protocol ranges. Keep matching npm release workflows for the
SDK runtime workspace dependencies so consumers can install the published SDK.
When release-please bumps the SDK and one of those dependencies in the same
production push, the SDK workflow waits for the dependency versions before it
packs and publishes. If a dependency release workflow for the same SHA fails,
the SDK wait fails immediately instead of waiting for npm visibility until
timeout.

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, missing
first-publish setup, package access drift, or after moving the publish step into
another workflow file), `npm publish` can fail with `ENEEDAUTH`, `E404`, or
permission text. Update the npm package access or 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.

### Devbox Setup

Use `ttr box setup` on self-hosted runner machines before offloading expensive
checks, Supabase workflows, or E2E suites. The command clones or reuses
`https://github.com/tutur3u/platform.git` at `~/Documents/tuturuuu`, runs
`bun install --frozen-lockfile`, starts the local Supabase stack with
`bun sb:start`, reads `supabase status -o json`, and upserts local Supabase
connection values into ignored `apps/*/.env.local` files with secret values
redacted from CLI output.

```bash theme={null}
ttr box doctor
ttr box setup
ttr box setup --dir .
ttr box repair --dir .
ttr box repair --dir . --dry-run
```

`ttr box doctor` stays read-only. Use `ttr box setup --yes` only when the host
should install detected missing prerequisites through its package manager.
Use `ttr box repair --dir .` after a CLI upgrade to repair an existing runner
service without registering a new runner token.

### 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.

```bash theme={null}
ttr --help
ttr upgrade --help
ttr box --help
ttr calendar --help
ttr calendar events --help
ttr finance --help
ttr finance transactions --help
ttr tasks --help
ttr tasks search --help
ttr tasks create --help
ttr task-templates --help
ttr task-templates import --help
ttr task-templates export --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:

```bash theme={null}
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.

Use `ttr tasks search <query>` for ranked task search. Search defaults to
`--mode hybrid`, accepts `--mode text` for PostgreSQL full-text search only and
`--mode semantic` for embedding similarity only, and supports
`--query`/`--q`, `--limit`/`--match-count`, `--threshold`/`--match-threshold`,
`--workspace`/`--ws`, `--compact`, and `--json`. Search output preserves API
relevance order and shows the score when available. Existing list filtering
with `ttr tasks --q <query>` is unchanged and remains the lightweight list text
filter.

`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.

Task templates are single-task starters, separate from board templates.
Workspace templates are managed with
`ttr task-templates list/show/create/update/delete/use` and can be imported
from or exported to local markdown files. Local templates live under
`.tuturuuu/task-templates/*.md`, use YAML frontmatter for fields such as `key`,
`name`, `task_name`, `priority`, `label_ids`, `assignee_ids`, and
`project_ids`, and use the markdown body as the task description. Markdown
descriptions from `--description-format markdown` and local template bodies
support GFM pipe tables and are stored as TipTap/Yjs table content for the web
editor. `ttr tasks create --template <key-or-path>` resolves workspace keys or
local markdown paths; explicit flags such as `--name`, `--list`, `--priority`,
`--labels`, and `--projects` override template defaults.

```markdown theme={null}
---
key: qa-handoff
name: QA handoff
task_name: Verify release handoff
priority: high
---

| Field | Value |
| --- | --- |
| Owner | Platform |
| Environment | Staging |
```

### 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.

Finance API routes that back `ttr finance` should resolve access through
`getFinanceRouteContext` from `@tuturuuu/apis/finance/request-access`, with
app-owned routes passing `resolveFinanceRouteAuthContext(request)` from
`apps/web/src/lib/finance-route-auth.ts` when they need CLI app-session bearer
token support. Keep CLI token verification in the app layer so `@tuturuuu/apis`
does not depend on `@tuturuuu/auth`; `getFinanceRouteContext` still keeps the
pre-authenticated actor, browser Supabase sessions, `personal` workspace
resolution, and finance permission checks on the same path.

```bash theme={null}
# Wallet CRUD.
ttr finance wallets
ttr finance wallets --page 2 --page-size 10
ttr finance wallets get <wallet-id>
ttr finance wallets balance <wallet-id>
ttr finance wallets balance --all
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

# Transfer creation, update, and id-preserving migration.
ttr finance transfers create --from-wallet <wallet-id> --to-wallet <wallet-id> --amount 150000 --taken-at 2026-05-09
ttr finance transfers update --from-transaction <tx-id> --to-transaction <tx-id> --from-wallet <wallet-id> --to-wallet <wallet-id> --amount 150000 --taken-at 2026-05-09
ttr finance transfers migrate --from-transaction <tx-id> --to-transaction <tx-id> --from-wallet <wallet-id> --to-wallet <wallet-id> --amount 150000 --taken-at 2026-05-09

# Categories, budgets, and recurring transactions.
ttr finance categories create "Travel" --expense --color blue --description "Trips and commuting"
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
```

### Calendar CLI

Calendar commands use the selected workspace and the same authenticated calendar
APIs as the web app. Use `--workspace` or `--ws` to target a different
workspace, and use `--json` when scripts need the raw structured response.
Human output renders resource-specific tables for events, calendars, categories,
accounts, provider calendars, connections, sources, and schedulable tasks.

Event time input is exact ISO datetime only. `events create` requires `--start`
and exactly one of `--end` or `--duration-minutes`; `events update --duration-minutes` also requires `--start`. Source flags map to provider
payloads: use `--source-provider tuturuuu --calendar <workspace-calendar-id>`
for workspace calendars, or `--source-provider google|microsoft --connection <connection-id>` for connected provider calendars. `calendar calendars reset`
is destructive and requires `--yes`.

```bash theme={null}
# Event CRUD.
ttr calendar events list --start 2026-06-11T00:00:00Z --end 2026-06-12T00:00:00Z
ttr calendar events get <event-id>
ttr calendar events create "Focus block" --start 2026-06-11T09:00:00+07:00 --duration-minutes 90
ttr calendar events update <event-id> --title "Focus block" --locked true
ttr calendar events delete <event-id>

# Default source and workspace calendar administration.
ttr calendar sources list
ttr calendar sources use --source-provider tuturuuu --calendar <calendar-id>
ttr calendar calendars create "Team" --color BLUE
ttr calendar calendars update <calendar-id> --name "Team Calendar"
ttr calendar calendars reset --yes

# Categories and provider connections.
ttr calendar categories create "Focus" --color BLUE
ttr calendar categories reorder --json-payload '{"categories":[{"id":"...","position":0}]}'
ttr calendar accounts list
ttr calendar auth google
ttr calendar auth microsoft
ttr calendar provider-calendars list --account <account-id>
ttr calendar connections create --calendar-id primary --calendar-name "Primary" --account <account-id>

# Scheduling.
ttr calendar schedule status
ttr calendar schedule tasks --q roadmap
ttr calendar schedule preview --window-days 14 --timezone Asia/Ho_Chi_Minh
ttr calendar schedule apply --mode safe-apply --scope impacted-only --window-days 14 --timezone Asia/Ho_Chi_Minh
```

Common task workflows:

```bash theme={null}
# 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

# Ranked full-text, semantic, or hybrid search.
ttr tasks search "deadline review"
ttr tasks search "deadline review" --mode hybrid --limit 20 --threshold 0.25
ttr tasks search --query "deadline review" --mode text --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"

# Create and use task templates.
ttr task-templates list
ttr task-templates create "Bug report" --key bug-report --title "Investigate bug" --priority high
ttr task-templates export bug-report --file .tuturuuu/task-templates/bug-report.md
ttr task-templates import .tuturuuu/task-templates/bug-report.md
ttr tiptap parse --text "| Field | Value |\n| --- | --- |\n| Owner | Platform |" --format markdown --output json
ttr tasks create --template bug-report --list <list-id> --name "Investigate checkout bug"
ttr tasks create "QA handoff" --description-file table.md --description-format markdown

# 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. Picker labels, descriptions, badges, and color names derived from
platform data must visibly escape terminal control characters before rendering.
Use `ttr -v` or `ttr --version` to print the installed CLI version.

## Client Initialization

### Basic Usage

```typescript theme={null}
const client = new TuturuuuClient('ttr_your_api_key');
```

### With Custom Configuration

```typescript theme={null}
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.

```typescript theme={null}
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.

```typescript theme={null}
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.

```typescript theme={null}
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:

```typescript theme={null}
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.

```typescript theme={null}
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.

```typescript theme={null}
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.

```typescript theme={null}
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:

```typescript theme={null}
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.

```typescript theme={null}
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.

```typescript theme={null}
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.

```typescript theme={null}
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.

```typescript theme={null}
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.

```typescript theme={null}
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.

```typescript theme={null}
await client.documents.delete('document-id-123');
```

### Search Documents

Searches documents by name (alias for `list()` with search).

```typescript theme={null}
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.

```typescript theme={null}
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:

```typescript theme={null}
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`.

```typescript theme={null}
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}`. External apps can upload those assets through signed upload URLs, or through the external-project asset multipart route when they need the app server to stream the file with a server-owned bearer token. 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:

```typescript theme={null}
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:

```typescript theme={null}
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

```typescript theme={null}
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:

```typescript theme={null}
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 **Settings** → **API 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

<Warning>
  **Never commit API keys to version control!** Use environment variables or
  secret management services.
</Warning>

```bash theme={null}
# .env
TUTURUUU_API_KEY=ttr_your_api_key_here
```

```typescript theme={null}
// 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:**

| Operation                                                                      | Required 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 Management                                                             | `manage_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 **Settings** → **Roles**.

## Security Best Practices

<Warning>
  **CRITICAL: Never expose API keys in client-side code!** API keys must remain
  server-side only.
</Warning>

### ❌ Insecure (NEVER do this)

```typescript theme={null}
// 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!
```

### ✅ Secure (Recommended patterns)

#### Next.js App Router (Recommended)

Create server-side API routes that proxy SDK calls:

```typescript theme={null}
// 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:

```typescript theme={null}
// 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

```typescript theme={null}
// 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)

```typescript theme={null}
// 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

<AccordionGroup>
  <Accordion title="✅ DO: Use server-side code">
    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)
  </Accordion>

  <Accordion title="✅ DO: Use environment variables">
    Store API keys in environment variables:

    ```bash theme={null}
    # .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!
  </Accordion>

  <Accordion title="✅ DO: Add .env.local to .gitignore">
    Ensure secrets never get committed: `gitignore # .gitignore .env.local
          .env*.local `
  </Accordion>

  <Accordion title="❌ DON'T: Use NEXT_PUBLIC_ prefix">
    This exposes the variable to the browser: `bash # BAD - Exposed to client!
          NEXT_PUBLIC_TUTURUUU_API_KEY=ttr_key `
  </Accordion>

  <Accordion title="❌ DON'T: Import SDK in client components">
    Never import the SDK in files with `'use client'` directive.
  </Accordion>

  <Accordion title="❌ DON'T: Hardcode API keys">
    Never commit keys to version control:

    ```typescript theme={null}
    // BAD
    const client = new TuturuuuClient('ttr_1234567890abcdef');
    ```
  </Accordion>
</AccordionGroup>

### Complete Example

See the [external app example](https://github.com/tutur3u/platform/tree/main/apps/external) 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

| Operation            | Limit        | Window     |
| -------------------- | ------------ | ---------- |
| Storage Upload       | 20 requests  | per minute |
| Storage Download     | 50 requests  | per minute |
| Signed Upload URLs   | 30 requests  | per minute |
| All Other Operations | 100 requests | per 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

```typescript theme={null}
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:

```typescript theme={null}
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 **Settings** → **Workspace Settings** → **Secrets**
2. Add one or more of these secrets:

| Secret Name               | Description                 | Example Value              |
| ------------------------- | --------------------------- | -------------------------- |
| `RATE_LIMIT_WINDOW_MS`    | Time window in milliseconds | `60000` (1 minute)         |
| `RATE_LIMIT_MAX_REQUESTS` | Max requests per window     | `200` (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.

<Note>
  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.
</Note>

### Best Practices

<Tip>
  **Implement exponential backoff** when handling rate limits to avoid hammering
  the API with retries.
</Tip>

```typescript theme={null}
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;
    }
  }
}
```

<Tip>
  **Monitor rate limit headers** to track your usage and avoid hitting limits:

  ```typescript theme={null}
  // 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));
  }

  ```
</Tip>

## Examples

<CardGroup cols={2}>
  <Card title="Basic Usage" icon="play" href="https://github.com/tutur3u/platform/blob/main/packages/sdk/examples/basic-usage.ts">
    Complete example of all SDK operations
  </Card>

  <Card title="Error Handling" icon="triangle-exclamation" href="https://github.com/tutur3u/platform/blob/main/packages/sdk/examples/error-handling.ts">
    Comprehensive error handling patterns
  </Card>
</CardGroup>

## Support

<CardGroup cols={3}>
  <Card title="GitHub" icon="github" href="https://github.com/tutur3u/platform">
    Source code and issues
  </Card>

  <Card title="Discord" icon="discord" href="https://discord.gg/tuturuuu">
    Community support
  </Card>

  <Card title="Docs" icon="book" href="https://docs.tuturuuu.com">
    Full documentation
  </Card>
</CardGroup>

## Changelog

The package version is managed by Release Please, and the full, authoritative
changelog is generated per release. For the complete history (currently shipping
the `0.10.x` line), see the maintained changelog rather than this page:

<CardGroup cols={2}>
  <Card title="CHANGELOG.md" icon="github" href="https://github.com/tutur3u/platform/blob/main/packages/sdk/CHANGELOG.md">
    Full per-release changelog (source of truth)
  </Card>

  <Card title="npm" icon="npm" href="https://www.npmjs.com/package/tuturuuu">
    Published versions and release tags
  </Card>
</CardGroup>

**Highlights since the initial release:**

* Storage operations (list, upload, download, delete, folders, share, analytics)
* Document operations (CRUD, search)
* `ttr` CLI with workspace, host switching, and session-based auth
* Finance commands (wallets, transactions, transfers, tags, wallet checkpoints)
* Calendar commands
* External projects support and remote devbox tooling
* Comprehensive error handling, full TypeScript types, and Zod validation

<Note>
  Do not treat any version number quoted in this page as exact — the SDK is on a
  fast release cadence. Always check the linked changelog or npm for the current
  version.
</Note>

## License

MIT
