# ToolDrop Docs

> ToolDrop is a platform for agent-built internal tools called tools. It gives them workspace sign-in, a gateway, Tool Storage, encrypted app environment variables, deployment, version history, rollback, and public share/remix links.

Use these docs when building, editing, deploying, or operating a ToolDrop tool. Agents should prefer this Markdown version over the rendered HTML page when they need compact context.

## Agent entry points

- Web index: https://tooldrop.app/llms.txt
- Markdown docs: https://tooldrop.app/docs.md
- Full single-file context: https://tooldrop.app/llms-full.txt
- Human docs: https://tooldrop.app/docs
- CLI docs: run `tooldrop docs` inside a tool
- MCP docs: run `tooldrop mcp`, then call `td_docs` before making changes
- Local scaffold files: read `AGENTS.md`, `CLAUDE.md`, `QUAD.md`, and `design.md` when present

## Quickstart

```bash
npm install -g https://tooldrop.app/cli.tgz
tooldrop login --url https://tooldrop.app

tooldrop init revenue-dashboard
cd revenue-dashboard

tooldrop env set DATABASE_URL --description "Read replica for reporting" --tag postgres
tooldrop dev
tooldrop validate
tooldrop deploy -m "first version"
# Optional before risky deploys: fail fast on local production build errors.
tooldrop deploy --preflight-build -m "first version"
```

## Core concepts

### Gateway

Open tools at /a/<slug> or /<workspace>/<slug>. The hidden gateway path proxies the deployed tool and signs x-td-user for the app.

### Env vault

Builders store API keys, database URLs, OAuth credentials, and MCP endpoints as encrypted app env vars.

### App database

Each tool gets app-owned key/value storage behind `td.storage`. Free apps use Tool Storage (small caps); on Pro an app can be upgraded to a managed database — the same protocol with far larger caps plus a provisioned DATABASE_URL for advanced SQL. Pro editors browse and update records from the app database page, API, or CLI. Managed Postgres apps cannot be downgraded to Tool Storage because SQL tables cannot be preserved there.

### Versions

Every deploy keeps source, author, status, message, and rollback metadata. Rollback repoints traffic to an existing ready version. On Pro, --staging deploys build without going live so editors can preview (?sbv=N) and promote when ready.

### Scheduled jobs

Pro workspaces can declare recurring jobs in tooldrop.json. ToolDrop generates the scheduler wrapper, records runs, and shows logs at the app level.

### Webhooks

Pro workspaces can declare public webhook endpoints in tooldrop.json. ToolDrop publishes stable platform URLs, forwards deliveries into app /api/webhooks/* handlers, and records delivery status plus logs.

### App logs

ToolDrop automatically records app 5xx gateway failures and Pro workspaces can browse/search app log lines written with td.log(), td.warn(), and td.error(). Editors can also read app, cron, and webhook logs through the CLI, HTTP API, or MCP.

### Share links

A /s/<token> page starts invite-only, then can opt into guest viewer access or source remixing without env values.

### Agent contract

The scaffold carries AGENTS.md and CLAUDE.md so coding agents know auth, secrets, storage, UI, and deployment rules.

## ToolDrop Convert

ToolDrop Convert is the migration plan for an existing page, dashboard, or internal app that was not built on the ToolDrop contract. Convert one useful route or workflow first, then repeat the pattern.

1. **Inventory the current app.** List framework, routes, auth/session code, env vars, database writes, file/blob storage, cron or queue workers, external APIs, and any pages that recreate workspace chrome.
2. **Choose the first route.** Convert one useful page or workflow first. Prefer a stable read-only route, then move writes, background jobs, and larger data changes after the ToolDrop contract is passing.
3. **Add the ToolDrop contract.** Add `tooldrop.json`, declare required `env` keys, set framework/build/output fields when detection is not enough, and run with `tooldrop dev` so identity, env vars, base path, and storage match production.
4. **Replace platform-owned concerns.** Remove local auth, secret files, deployment dashboards, global nav, account/team settings, and host scheduler config. ToolDrop owns those surfaces; the tool keeps only the app workflow.
5. **Validate, stage, then deploy.** Run `tooldrop validate`, fix every contract failure, use `tooldrop deploy --staging` for risky changes on Pro, then publish with a human deploy message.

### Agent prompt for a conversion

```text
You are converting an existing app into a ToolDrop tool.

First read AGENTS.md or run tooldrop docs.
Then make a narrow conversion plan:
1. Inventory routes, auth, env vars, data writes, crons, and local/global chrome.
2. Convert one page or workflow at a time.
3. Remove app-owned auth and use td.user() / useTDUser().
4. Move secrets to ToolDrop env vars and app-owned state to td.storage.
5. Convert scheduled work to /api/cron/* plus tooldrop.json crons.
6. Convert inbound webhooks to /api/webhooks/* plus tooldrop.json webhooks.
7. Run tooldrop validate before deploy.

Do not deploy until the human has reviewed the converted workflow.
```

### Convert auth

- Delete app-owned login, logout, signup, session-cookie, next-auth, Clerk, Auth0, or custom JWT flows unless the human explicitly needs the tool to administer those systems.
- Read the current viewer with server-side `td.user()` or client-side `useTDUser()` inside `TDProvider`; do not infer identity from browser storage or query params.
- Move app-specific authorization into small domain checks, for example an allowed email/domain list, a role table read from an external system, or records stored in `td.storage`.
- Keep the scaffold `proxy.ts`, `lib/tooldrop/`, and `/api/td/*` bridge intact. If the source app has middleware, merge only app-specific redirects and leave ToolDrop identity checks in place.
- Remove auth screens from the converted page. The viewer reaches the tool through the ToolDrop workspace session and should land directly in the workflow.

### Convert data and env

- Move secrets from `.env`, code constants, deployment settings, or old platform config into ToolDrop env vars with `tooldrop env set KEY --description ... --tag ...`, and declare required keys in `tooldrop.json`.
- Keep external business systems external. Use normal server-side drivers or SDKs with `process.env.DATABASE_URL`, API keys, or base URLs; never expose those values to client components.
- Move app-owned preferences, saved filters, drafts, notes, review states, and small CRUD records into `td.storage`; do not add SQLite, an ORM, or new production tables for that state.
- For existing local JSON, SQLite, or browser-local data, write a small import script or route that reads records and calls `td.storage.set()` / `tooldrop db set` with namespaced keys.
- When the converted app outgrows Tool Storage, upgrade the app to a managed database on Pro before bringing a new provider. The same `td.storage` calls keep working and a managed `DATABASE_URL` can support direct SQL.
- For existing production databases, start read-only when possible, add `LIMIT`s to exploratory reads, parameterize queries, and confirm static egress or relay setup for IP-allowlisted hosts.

### Convert cron jobs

- Convert host scheduler files, worker entrypoints, and external cron callbacks into Next.js `GET` route handlers under `/api/cron/*` when the tool uses the Next scaffold.
- Declare each schedule in `tooldrop.json` with `name`, `path`, and cron `schedule`; deploy live when changing schedules because scheduler config follows the live version.
- Cron handlers run without a human viewer. Do not call `td.user()`; use server-side env vars, external APIs, `td.storage`, and idempotent job state such as `sync:last-run`.
- Use `td.cron.log()` for progress lines that should appear on the app's Scheduled jobs page, and return concise JSON or text because the response is saved with the run.
- Copy the latest scaffold `proxy.ts` if validation reports an old cron bypass marker.

### Convert webhooks

- Convert third-party webhook receivers into Next.js route handlers under `/api/webhooks/*` when the tool uses the Next scaffold.
- Declare each public endpoint in `tooldrop.json` with `name`, `path`, and optional `methods`; live deploys publish stable `/webhooks/<deploy-slug>/<endpoint>` URLs.
- Webhook handlers run without a human viewer. Do not call `td.user()`; verify provider signatures with server-side env vars before trusting payloads.
- Use `td.webhook.log()` for delivery-visible notes, and return concise JSON or text because the response is saved with the delivery.
- Copy the latest scaffold `proxy.ts` if validation reports an old webhook bypass marker.

### Conversion deploy checks

- Open the converted route through `tooldrop dev`, not plain framework dev, and verify base-path-safe links, loading, empty, and error states.
- Run `tooldrop validate` before every deploy and fix contract failures instead of bypassing them.
- Run a local production build or `tooldrop deploy --preflight-build` after changing framework config, env use, cron routes, or server-only imports.
- Use a staged deploy on Pro for high-risk conversions, then promote only after an editor previews the version.
- Write a deploy message that names the converted workflow, for example `Converted invoice review to ToolDrop auth and storage`.

## Agent rules

1. **Never write auth code.** No Clerk, next-auth, custom login pages, or session cookies inside a tool. Read identity with td.user().
2. **Never hardcode secrets.** Declare required keys in tooldrop.json, set app values with tooldrop env set, use tooldrop env set --global only for workspace-wide shared values, and read them server-side through process.env. Before asking the human for a credential, check workspace-global vars (tooldrop env ls --global / td_list_env global:true) and reuse a matching key — its value is injected into dev, previews, and deploys automatically. Do not use tooldrop.json connections.
3. **Use Tool Storage for app-owned state.** Preferences, workflow state, saved filters, notes, and small records belong in td.storage.
4. **Deploy with a real message.** Every deploy becomes version history. Use a short human sentence that says what changed.
5. **Keep hosting details behind ToolDrop.** When talking to viewers or builders, say ToolDrop hosting, remote build, or deployed runtime; do not name the underlying infrastructure provider.

## Data model

- Tool Storage is the default lightweight database for a tool. It is platform-managed, scoped by tool, and accessed through `td.storage`.
- For shared records, key by record type, for example task:<id>, note:<id>, or approval:<orderId>.
- For per-user records, include `me.id`, for example prefs:<userId> or saved-view:<userId>:<viewId>.
- For small collections, store one record per key and discover them with `td.storage.list("task:")`.
- Pro editors can inspect and update those records outside the tool with the app database page, `tooldrop db`, or `/api/apps/:slug/storage`. Treat those writes like production data changes.
- Do not add SQLite, an ORM, or new tables just to save preferences, drafts, notes, review flags, saved filters, or small CRUD records.
- Use server-side environment variables for external services: declare required database URLs, API keys, OAuth credentials, or MCP endpoints in `tooldrop.json`, set them with `tooldrop env set`, and read them with `process.env.KEY`.
- ToolDrop does not provide named database connections. Do not add `tooldrop.json` `connections` or call `td.query("main-db", ...)`; external databases are ordinary server-side drivers configured by env vars.
- Use a direct `DATABASE_URL` only when the deployed runtime can reach that host. Public TLS Postgres/MySQL URLs work with `pg`, `mysql2`, or provider SDKs; private IPs, localhost tunnels, Unix sockets, Cloud SQL sockets, and VPC-only endpoints require a connector, proxy, static egress allowlist, or narrow HTTP API.
- Sizing rule of thumb: Tool Storage caps at 5000 keys, 256KB per value, and 50MB per app — preferences, saved views, and small CRUD records. When data outgrows that, is unbounded (event streams, sync pipelines, imported datasets), or needs SQL (joins, rollups, indexes), the first choice on Pro is to upgrade the app to a managed database (app database page, or `tooldrop db upgrade`): the same `td.storage` calls keep working with far larger caps, records copy over, and a fully-managed `DATABASE_URL` is injected for direct SQL. Otherwise bring your own — a free-tier Neon/Supabase Postgres set with `tooldrop env set DATABASE_URL`.
- Managed tier: upgrading an app provisions a database it owns. `td.storage` transparently uses it with the larger caps, and (where the platform has a database provider configured) `process.env.DATABASE_URL` points at that same managed Postgres for advanced/relational/chart data — no signup, connection string, or allowlist. Managed Postgres apps stay on the managed tier so SQL tables and deployment database state are not lost.

## App database access

Every tool has an app database, accessed in app code through `td.storage`. Free apps use Tool Storage; on Pro an app can be upgraded to a **managed database** — the same `td.storage` protocol with far larger caps, plus a fully-managed `DATABASE_URL` for direct SQL. Existing records copy over on upgrade, so no tool code changes. On Pro, editors can also inspect and update records from the workspace UI, CLI, MCP, or HTTP API. Managed Postgres apps cannot be downgraded to Tool Storage because SQL tables and deployment database state cannot be preserved there.

```bash
tooldrop db ls --app revenue-dashboard --prefix task:
tooldrop db get task:123 --app revenue-dashboard
tooldrop db set task:123 '{"title":"Review contract","done":false}' --app revenue-dashboard
tooldrop db rm task:123 --app revenue-dashboard

tooldrop db info --app revenue-dashboard        # current tier, engine, usage
tooldrop db upgrade --app revenue-dashboard     # provision a managed database (Pro)
tooldrop db downgrade --app revenue-dashboard   # only for key/value-only managed stores
```

The HTTP API uses the same CLI bearer auth as other app endpoints:

```http
GET /api/apps/:slug/storage?prefix=task:&limit=100&values=0
GET /api/apps/:slug/storage?key=task:123
POST /api/apps/:slug/storage
DELETE /api/apps/:slug/storage

GET /api/apps/:slug/database        # current tier + managed database status
POST /api/apps/:slug/database       # upgrade to a managed database (Pro)
DELETE /api/apps/:slug/database     # downgrade KV-only managed storage to Tool Storage
```

These endpoints require edit access to the app plus the Pro `databaseAccess` feature (upgrading additionally needs `managedDatabase`). Runtime tool code should still use `td.storage`; the app database API is for human and agent operations around an app's stored records and storage tier.

## External databases and env vars

ToolDrop stores encrypted env vars and injects them into server-side runtime. It does not create named database connections or network tunnels for the app. Use `process.env.DATABASE_URL` with a normal driver such as `pg`, `mysql2`, or a provider SDK, and keep all secret reads in server code.

- Declare database URLs and credentials in `tooldrop.json` under `env`, then set them with `tooldrop env set`.
- Do not use `tooldrop.json` `connections` or `td.query("main-db", ...)`; those are outside the current ToolDrop contract.
- Direct public TLS Postgres/MySQL endpoints usually work with one `DATABASE_URL` when the deployed runtime can reach the host.
- Private IPs, `localhost` proxies, Unix sockets, Cloud SQL socket paths, and VPC-only endpoints need deployment infrastructure too: connector, IAM/service account, proxy, VPN, static egress allowlist, or a narrow HTTP API.
- Keep app-owned preferences, saved filters, review flags, and small records in `td.storage`, even when the tool reads from an external database.

## Unblocking an IP-allowlisted database

If `DATABASE_URL` is correct but every query times out (`connect ETIMEDOUT`), the database probably only accepts connections from IP addresses on its allowlist. ToolDrop solves this with **one permanent egress IP**: the database admin allowlists that single ToolDrop address once, and every tool — local `tooldrop dev` and deployed — reaches the database through it. Your own machine's IP is never involved.

The ToolDrop egress IP for this platform is `8.235.38.133`.

1. **Allowlist the ToolDrop egress IP — once.** All ToolDrop database traffic — `tooldrop dev` on any machine and every deployed tool — reaches your database from one permanent platform IP. Have your database admin add that single address to the allowlist, labeled `tooldrop-egress`. It never changes, and it is the only entry ToolDrop ever needs. Your own machine's IP is never involved.
2. **Point `DATABASE_URL` at the relay address.** Your workspace admin registers the database with the ToolDrop egress relay and gives you a relay address (host and port). Use that as the host in `DATABASE_URL` with your normal database credentials: `tooldrop env set DATABASE_URL`.
3. **Query with the normal driver.** Nothing else changes: server-only code, the usual driver (`pg`, `mysql2`, or a provider SDK), parameterized queries, and a read replica with a read-only user when possible. The connection is end-to-end between your driver and the database — the relay only forwards packets, so credentials and TLS still terminate at the database.

How the database admin adds the ToolDrop IP, per provider:

- **Google Cloud SQL:** Console → SQL → the instance → Connections → Networking → Authorized networks → Add network. Name: `tooldrop-egress`, Network: the ToolDrop IP as `x.x.x.x/32`, then Save. Applies in about a minute.
- **AWS RDS:** The instance's security group → Edit inbound rules → add a rule for the database port with source `x.x.x.x/32`, and put `tooldrop-egress` in the rule's Description.
- **Azure SQL:** The server's Networking page → Firewall rules → add a rule named `tooldrop-egress`.

## CLI commands

- `tooldrop login --url <platform-url>`: Connect a local machine to the workspace with the browser-approved device flow.
- `tooldrop init <name>`: Scaffold a new tool with Next.js, the ToolDrop SDK, proxy guard, AGENTS.md, and CLAUDE.md.
- `tooldrop dev [-p 4711]`: Run the tool locally behind the same gateway contract used in production.
- `tooldrop validate`: Check tooldrop.json, framework settings, gateway files, SDK files, and obvious secret leaks.
- `tooldrop deploy -m "what changed"`: Run the local typecheck when available, package source, create a version, build on ToolDrop hosting, and publish through the workspace gateway.
- `tooldrop deploy --preflight-build`: Run npm run build locally before uploading so build errors fail fast instead of waiting on the remote builder.
- `tooldrop deploy --staging`: Build a preview version that stays off the live URL. Editors preview it at ?sbv=N and promote it from the app's settings page (Pro).
- `tooldrop env ls|get|set|pull|rm`: Manage encrypted app env vars and metadata. Values are explicit to retrieve and injected server-side.
- `tooldrop env ... --global`: Manage workspace-global env vars. Writes require admin or Clerk org:env:manage; app env vars override globals on conflicts.
- `tooldrop db ls|get|set|rm|info|upgrade|downgrade`: Inspect and update an app's database from the CLI, or upgrade its storage tier to a managed database (Pro, editor access required). Downgrade is only for key/value-only managed stores; managed Postgres stays managed.
- `tooldrop logs [app|errors|cron|webhook|all]`: Read app logs, gateway/runtime errors, scheduled job run logs, and public webhook delivery logs from the CLI (Pro for app log browsing).
- `tooldrop apps|versions|rollback|clone`: Discover apps, inspect version history, repoint live traffic, or download source for more work.
- `tooldrop share|install`: Turn on a public share page, opt into guest join/remix access, or remix a shared source bundle from another ToolDrop workspace.
- `tooldrop docs|mcp`: Print the agent guide or expose ToolDrop tools to Claude Code, Codex, and other MCP clients.

## SDK reference

Server import: `import { td } from "@/lib/tooldrop/server"`

Client import: `import { TDProvider, useTDUser, tdStorage } from "@/lib/tooldrop/client"`

- `td.user()` (server): Returns the signed-in viewer from the gateway JWT: { id, email, name, role }.
- `process.env.KEY` (server): Read platform env vars server-side after declaring them in tooldrop.json and setting values with the CLI. Env vars configure normal drivers and SDKs; they are not named database handles.
- `td.storage` (server): Per-app key/value storage for preferences, saved views, notes, review states, and records. Backed by Tool Storage on Free, or the app's managed database (far larger caps) on Pro — the same calls either way.
- `td.cron.log()` (server): Write a log line to the current scheduled job run. No-ops outside scheduled job requests.
- `td.webhook.log()` (server): Write a log line to the current public webhook delivery. No-ops outside webhook handler requests.
- `td.log() / td.warn() / td.error()` (server): Write app-level log lines visible from the app Logs page, CLI, API, and MCP. Do not log secrets, payload bodies, or customer data you would not show an editor.
- `TDProvider + useTDUser()` (client): Expose the current ToolDrop user to client components through the tool's /api/td/user bridge.
- `tdStorage` (client): Client helper for Tool Storage calls through the app's /api/td/* route.

### Server example

```ts
import { td } from "@/lib/tooldrop/server";

const me = await td.user();
const databaseUrl = process.env.DATABASE_URL;

await td.storage.set(`prefs:${me.id}`, { density: "compact" });
const prefs = await td.storage.get(`prefs:${me.id}`);
```

## App logs

App log browsing is a Pro feature. ToolDrop automatically records gateway-level app failures such as unreachable deployments and 5xx responses. Server code can also write explicit app log lines with `td.log()`, `td.warn()`, and `td.error()`.

```ts
import { td } from "@/lib/tooldrop/server";

export async function GET() {
  await td.log("Starting report refresh");
  try {
    // run app work here
    await td.log("Report refresh finished");
    return Response.json({ ok: true });
  } catch (err) {
    await td.error(err instanceof Error ? err.message : "Report refresh failed");
    return Response.json({ error: "refresh failed" }, { status: 500 });
  }
}
```

Editors can inspect logs in the app's Logs page, with `tooldrop logs errors --app <slug>`, through MCP `td_logs`, or via the HTTP API:

```http
GET /api/apps/:slug/logs?type=app&level=error&source=gateway&limit=50
GET /api/apps/:slug/logs?type=cron&limit=25
GET /api/apps/:slug/logs?type=webhook&limit=25
GET /api/apps/:slug/logs?type=all&limit=25
```

Logs store metadata and messages only. Do not log request bodies, webhook payloads, cookies, env values, API tokens, or sensitive customer data.

## Scheduled jobs

Scheduled jobs are a Pro feature for Next.js tools. Declare jobs in `tooldrop.json`; ToolDrop generates the scheduler config and wrapper route during live deploys.

```json
{
  "slug": "revenue-dashboard",
  "name": "Revenue Dashboard",
  "crons": [
    {
      "name": "daily-sync",
      "path": "/api/cron/daily-sync",
      "schedule": "0 12 * * *"
    }
  ]
}
```

Each job path must be an `/api/cron/*` GET route. Scheduled routes run without a human viewer, so do not call `td.user()`; use server-side env vars, external APIs, and `td.storage` instead. Return concise JSON or text because the response is saved with the run.

```ts
import { td } from "@/lib/tooldrop/server";

export async function GET() {
  await td.cron.log("Starting daily sync");

  // Read secrets from process.env and store lightweight state in td.storage.
  const previous = await td.storage.get("sync:last-run");
  await td.storage.set("sync:last-run", {
    previous,
    finishedAt: new Date().toISOString(),
  });

  await td.cron.log("Daily sync finished");
  return Response.json({ ok: true });
}
```

Run `tooldrop validate` before deploying. The current `proxy.ts` scaffold includes the scheduled-job bypass marker; older tools should copy the latest proxy from a fresh scaffold before adding crons. Deploy without `--staging` when schedules are present because scheduler config follows the live deployment.

## Webhooks

Webhooks are a Pro feature for Next.js tools. Declare endpoints in `tooldrop.json`; ToolDrop publishes stable platform URLs and forwards each delivery into the app's matching `/api/webhooks/*` handler.

```json
{
  "slug": "revenue-dashboard",
  "name": "Revenue Dashboard",
  "webhooks": [
    {
      "name": "stripe-events",
      "path": "/api/webhooks/stripe",
      "methods": ["POST"]
    }
  ]
}
```

Webhook handlers run without a human viewer, so do not call `td.user()`. Verify provider signatures with server-side env vars before trusting payloads, then use `td.storage` for app-owned state or external APIs configured by env vars.

```ts
import { td } from "@/lib/tooldrop/server";

export async function POST(request: Request) {
  const body = await request.text();
  const signature = request.headers.get("stripe-signature");

  // Verify provider signatures with a server-side env var before trusting body.
  if (!signature || !process.env.STRIPE_WEBHOOK_SECRET) {
    await td.webhook.log("Missing Stripe signature", "warn");
    return Response.json({ error: "missing signature" }, { status: 400 });
  }

  await td.webhook.log("Received Stripe event");
  await td.storage.set("stripe:last-event", {
    bytes: body.length,
    receivedAt: new Date().toISOString(),
  });

  return Response.json({ ok: true });
}
```

Run `tooldrop validate` before deploying. The current `proxy.ts` scaffold includes the webhook bypass marker; older tools should copy the latest proxy from a fresh scaffold before adding webhooks. Deploy without `--staging` when webhook endpoint config changes because public URLs follow the live deployment.

## MCP tools

Add the ToolDrop MCP server to Claude Code, Codex, or another MCP client:

```bash
claude mcp add tooldrop -- tooldrop mcp
```

- `td_docs`
- `td_whoami`
- `td_list_apps`
- `td_logs`
- `td_list_env`
- `td_env_set`
- `td_database_list`
- `td_database_get`
- `td_database_set`
- `td_database_delete`
- `td_database_tier`
- `td_validate`
- `td_deploy`
- `td_versions`
- `td_rollback`
- `td_clone`
- `td_share`
- `td_install`

## API authentication modes

- **CLI bearer:** Stored in ~/.tooldrop.json after login and used by the CLI plus MCP server.
- **Workspace session:** Browser session checked by the platform gateway before a tool is opened.
- **App token:** Runtime token issued to deployed tools or tooldrop dev for storage calls.
- **Public token:** Share-link token embedded in /s/<token>; never grants env values or private app data.

## API reference

### CLI and workspace
- `GET /api/me` (CLI bearer): Return the current user and platform URL.
- `GET /api/apps` (CLI bearer): List apps visible to the signed-in user.
- `POST /api/apps` (CLI bearer): Register or sync a tool before dev or deploy.
- `GET /api/apps/:slug` (CLI bearer): Return app metadata, env metadata, and recent versions.
- `POST /api/apps/:slug/dev` (Builder): Issue local dev credentials, env values, and the gateway signing secret.
- `POST /api/apps/:slug/deploy` (Editor): Upload source files and create a new deploy version. Pass staging: true to build without going live (Pro).
- `GET /api/apps/:slug/versions` (Viewer): List version history and the currently served version.
- `POST /api/apps/:slug/rollback` (Editor): Activate an existing ready version — roll back to an older one or promote a staged one.
- `GET /api/apps/:slug/bundle` (Builder): Download source for clone or handoff. Optional ?version=N.
- `GET /api/apps/:slug/logs` (Editor + Pro): Read app logs, gateway errors, cron run logs, and webhook delivery logs. Query: ?type=app|errors|cron|webhook|all&level=error&source=gateway&q=text&limit=50.
### App database
- `GET /api/apps/:slug/storage` (Editor + Pro): List Tool Storage records. Query: ?prefix=task:&limit=100&values=0. Values are included by default unless values=0.
- `GET /api/apps/:slug/storage?key=KEY` (Editor + Pro): Read one Tool Storage record by key.
- `POST /api/apps/:slug/storage` (Editor + Pro): Create or replace one JSON-serializable value: { key, value }.
- `DELETE /api/apps/:slug/storage` (Editor + Pro): Delete one record: { key }.
- `GET /api/apps/:slug/database` (Editor + Pro): Report the app's storage tier, managed database status, and usage stats.
- `POST /api/apps/:slug/database` (Editor + Pro): Upgrade to a managed database (needs managedDatabase). Provisions it and copies existing records over.
- `DELETE /api/apps/:slug/database` (Editor + Pro): Downgrade key/value-only managed storage to Tool Storage. Managed Postgres databases are refused because SQL tables cannot be preserved.
### Environment and sharing
- `GET /api/apps/:slug/env` (Editor): List env metadata. Add ?values=1 to explicitly include decrypted values.
- `POST /api/apps/:slug/env` (Editor): Set or rotate one encrypted env var with description and tags.
- `DELETE /api/apps/:slug/env` (Editor): Remove an env var from the vault.
- `GET /api/settings/variables` (Workspace member): List workspace-global env metadata visible to the caller. Add ?values=1 to include decrypted values the caller can use.
- `POST /api/settings/variables` (Admin or org:env:manage): Set or rotate one workspace-global env var: { key, value, description, scope, allowedUsers }.
- `DELETE /api/settings/variables` (Admin or org:env:manage): Remove one workspace-global env var: { key }.
- `GET /api/apps/:slug/share` (Viewer): Return the active share link and stats if one exists.
- `POST /api/apps/:slug/share` (Editor): Create, update, or regenerate the public share link. New links default to no guest join and no remix.
- `DELETE /api/apps/:slug/share` (Editor): Revoke the active share link.
### Public share links
- `GET /api/share/:token` (Public token): Public metadata for unfurls and share pages. Never returns source or env values.
- `GET /api/share/:token/bundle` (Public token): Download live source for remix when allowRemix is enabled.
- `POST /api/share/:token/join` (Signed-in user): Grant viewer access through a join-enabled share link.
### Tool runtime
- `ANY /a/:deploySlug/*` (Workspace session): Gateway proxy that signs x-td-user and serves the current tool version.
- `ANY /webhooks/:deploySlug/:endpoint` (Public endpoint): Public webhook receiver that records delivery status, forwards to the generated wrapper, and returns the app handler response.
- `GET /api/tooldrop-cron/:job` (Host secret): Generated scheduled-job wrapper that records start, finish, status, and log lines before invoking the tool's /api/cron/* handler.
- `ANY /api/tooldrop-webhook/:endpoint` (Platform secret): Generated webhook wrapper that forwards public deliveries into the tool's /api/webhooks/* handler.
- `POST /api/connector` (App token): Runtime bridge for Tool Storage operations plus cron, webhook, and app log reporting. Tools call this through the SDK.
- `POST /api/cli/device/start` (Public): Start CLI browser login by creating a short-lived device code.
- `GET /api/cli/device/poll?code=...` (Public code): Poll for browser approval and return a one-time CLI token.

## Security and operations

- Tools never own authentication. Direct deployment URLs are guarded by proxy.ts and should reject requests without x-td-user.
- Scheduled jobs enter through a generated wrapper protected by CRON_SECRET. Put job handlers under /api/cron/* and do not expose them to browsers.
- Webhooks enter through public /webhooks/<deploy-slug>/<endpoint> URLs, then a generated wrapper protected by TOOLDROP_WEBHOOK_SECRET. Put handlers under /api/webhooks/* and verify provider signatures inside the handler.
- App logs capture metadata only: level, source, message, path, method, status, duration, request id, and optional user attribution. Do not log secrets, request bodies, cookies, env values, tokens, or sensitive customer data.
- Uploaded bundles exclude .env files, local state, build output, node_modules, and logs. Env values remain on the platform.
- Tool Storage is intentionally small: 5000 keys per app, 256KB per value, and 50MB total per app. A Pro app upgraded to a managed database keeps the same protocol with far larger caps.
- Public remix links publish source code when enabled. They never publish env values, data, credentials, or member lists.

## Common failure modes

- If `td.user()` says there is no identity, run the app with `tooldrop dev` or open it through the platform gateway.
- If a deployed tool cannot reach a service named by an environment variable, update the env value or the service allowlist for the deployed runtime.
- If queries time out with a correct `DATABASE_URL`, the database likely allowlists IPs: confirm the allowlist has the ToolDrop egress IP (labeled `tooldrop-egress`) and that `DATABASE_URL` points at the relay address from your workspace admin. See "Unblocking an IP-allowlisted database" above.
- If a secret appears in the browser, move the read into a server component or route handler and remove any `NEXT_PUBLIC_` prefix.
- If deploy reports a stale base, clone the latest version or deploy with `--force` only when overwriting is intentional.
- If a change is risky, deploy with `--staging` (Pro): the build stays off the live URL until an editor previews (`?sbv=N`) and promotes it.
- If validation fails, fix each contract violation before deploy; tools never ship their own auth stack.

## Public web resources

- Human docs: https://tooldrop.app/docs
- Agent index: https://tooldrop.app/llms.txt
- Full agent context: https://tooldrop.app/llms-full.txt
- Sitemap: https://tooldrop.app/sitemap.xml
- Robots: https://tooldrop.app/robots.txt
