Documentation

Tooldrop docs

Build, run, deploy, share, and operate agent-built internal tools. Everything here is public and written for both humans and coding agents: CLI commands, SDK calls, HTTP contracts, and the rules every tool must follow.

Getting started

Ship the first tool#

A tool is a small app deployed behind the Tooldrop gateway. Start from the scaffold, let Claude Code or Codex build inside the contract, then deploy a version the workspace can open.

  1. 1

    Install the CLI from your platform and sign in with the browser-approved device flow.

  2. 2

    Scaffold a tool. The scaffold ships with the SDK, gateway guard, AGENTS.md, and CLAUDE.md.

  3. 3

    Set required env vars, run locally, validate the contract, and deploy a version.

quickstart
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"

Getting started

Core concepts#

Six things Tooldrop owns so your agent-built app doesn't have to.

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.

Convert existing apps

Tooldrop Convert#

A migration plan for teams bringing an existing page, dashboard, or internal app into Tooldrop without carrying over the old auth, secret, database, scheduler, or shell assumptions.

01

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.

02

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.

03

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.

04

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.

05

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.

Tooldrop Convert prompt
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.

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.

Convert existing apps

Convert auth#

Tooldrop owns sign-in and workspace access. The converted app keeps only workflow-specific permissions.

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 existing apps

Convert data and env#

Secrets become Tooldrop env vars, app-owned state moves to `td.storage`, and existing business systems stay behind server-side drivers or APIs.

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 LIMITs to exploratory reads, parameterize queries, and confirm static egress or relay setup for IP-allowlisted hosts.

Convert existing apps

Convert cron jobs#

Scheduled work should be explicit in `tooldrop.json`, implemented as `/api/cron/*` handlers, and observable from the app's Scheduled jobs page.

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.

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

Convert existing apps

Convert webhooks#

Inbound webhook callbacks should be explicit in `tooldrop.json`, implemented as `/api/webhooks/*` handlers, and observable from the app's Webhooks page.

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.

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

Reference

CLI commands#

Everything builders and agents run day to day. Install once, then work from inside the tool directory.

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.

Reference

SDK#

The tool runtime APIs. Server helpers come from @/lib/tooldrop/server, client helpers from @/lib/tooldrop/client.

td.user()server

Returns the signed-in viewer from the gateway JWT: { id, email, name, role }.

process.env.KEYserver

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

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.

tdStorageclient

Client helper for Tool Storage calls through the app's /api/td/* route.

server example
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}`);

Reference

App database#

Tool Storage is the app-owned database. Pro editors can inspect and update those records from the workspace UI, CLI, MCP, or HTTP API.

Runtime app code should keep using td.storage. The app database surface is for humans and agents operating the records around that app.

Reads and writes require app edit access plus Pro. Listing values, setting keys, and deleting records are all audit logged.

The API lives at /api/apps/:slug/storage. Use values=0 when a list only needs key metadata.

app database CLI
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

Reference

App logs#

Pro workspaces can browse app runtime logs and automatic gateway failures from the app UI, CLI, HTTP API, and MCP.

Tooldrop automatically records app 5xx gateway failures. Server code can add explicit lines with td.log(), td.warn(), and td.error().

CLI and API access use the same app editor permission model. Read app, cron, and webhook logs with tooldrop logs or GET /api/apps/:slug/logs.

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

app logs CLI
tooldrop logs errors --app revenue-dashboard
tooldrop logs app --level warn --source runtime --app revenue-dashboard
tooldrop logs cron --details --app revenue-dashboard
tooldrop logs webhook --json --app revenue-dashboard
server route logging
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 });
  }
}

Scheduled jobs

Run work on a schedule#

Pro workspaces can run recurring work from Next.js tools with platform-owned scheduling, run history, and logs.

Declare each job in tooldrop.json. Live deploys generate the scheduler wrapper and expose runs on the app's Scheduled jobs page.

Job handlers live under /api/cron/*. They run without a human viewer, so use server-side env vars and td.storage instead of td.user().

Use td.cron.log() for messages you want saved with the run.

tooldrop.json
{
  "slug": "revenue-dashboard",
  "name": "Revenue Dashboard",
  "crons": [
    {
      "name": "daily-sync",
      "path": "/api/cron/daily-sync",
      "schedule": "0 12 * * *"
    }
  ]
}
app/api/cron/daily-sync/route.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 });
}

Webhooks

Receive public webhooks#

Pro workspaces can publish stable webhook URLs from Next.js tools with platform-owned delivery history and logs.

Declare each endpoint in tooldrop.json. Live deploys publish stable URLs on the app's Webhooks page.

Handlers live under /api/webhooks/*. They run without a human viewer, so verify provider signatures with server-side env vars before trusting payloads.

Use td.webhook.log() for messages you want saved with the delivery.

tooldrop.json
{
  "slug": "revenue-dashboard",
  "name": "Revenue Dashboard",
  "webhooks": [
    {
      "name": "stripe-events",
      "path": "/api/webhooks/stripe",
      "methods": ["POST"]
    }
  ]
}
app/api/webhooks/stripe/route.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 });
}

Building with agents

Agent guide#

The four rules every coding agent must follow inside a tool, plus the MCP server that teaches them automatically.

01

Never write auth code

No Clerk, next-auth, custom login pages, or session cookies inside a tool. Read identity with td.user().

02

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.

03

Use Tool Storage for app-owned state

Preferences, workflow state, saved filters, notes, and small records belong in td.storage.

04

Deploy with a real message

Every deploy becomes version history. Use a short human sentence that says what changed.

05

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.

agent handoff
You are editing a Tooldrop tool.

First read the local AGENTS.md or run:
  tooldrop docs

If MCP is available, call td_docs before making changes.
Follow the tool contract:
  - no app auth code
  - no hardcoded secrets
  - app-owned state goes in td.storage
  - run tooldrop validate before deploy

MCP tools

Add the Tooldrop MCP server to Claude Code, Codex, or any MCP client:

claude mcp add tooldrop -- tooldrop mcp
td_docstd_whoamitd_list_appstd_logstd_list_envtd_env_settd_database_listtd_database_gettd_database_settd_database_deletetd_database_tiertd_validatetd_deploytd_versionstd_rollbacktd_clonetd_sharetd_install

Building with agents

Data model#

Tool Storage is the default database for a tool. These rules decide what lives there and when to reach for something bigger.

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.

API reference

HTTP contracts#

Every request authenticates with one of four modes. The tables below note which mode each endpoint expects.

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.

CLI and workspace#

10 endpoints
MethodEndpointAuth
GET/api/me

Return the current user and platform URL.

CLI bearer
GET/api/apps

List apps visible to the signed-in user.

CLI bearer
POST/api/apps

Register or sync a tool before dev or deploy.

CLI bearer
GET/api/apps/:slug

Return app metadata, env metadata, and recent versions.

CLI bearer
POST/api/apps/:slug/dev

Issue local dev credentials, env values, and the gateway signing secret.

Builder
POST/api/apps/:slug/deploy

Upload source files and create a new deploy version. Pass staging: true to build without going live (Pro).

Editor
GET/api/apps/:slug/versions

List version history and the currently served version.

Viewer
POST/api/apps/:slug/rollback

Activate an existing ready version — roll back to an older one or promote a staged one.

Editor
GET/api/apps/:slug/bundle

Download source for clone or handoff. Optional ?version=N.

Builder
GET/api/apps/:slug/logs

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.

Editor + Pro

App database#

7 endpoints
MethodEndpointAuth
GET/api/apps/:slug/storage

List Tool Storage records. Query: ?prefix=task:&limit=100&values=0. Values are included by default unless values=0.

Editor + Pro
GET/api/apps/:slug/storage?key=KEY

Read one Tool Storage record by key.

Editor + Pro
POST/api/apps/:slug/storage

Create or replace one JSON-serializable value: { key, value }.

Editor + Pro
DELETE/api/apps/:slug/storage

Delete one record: { key }.

Editor + Pro
GET/api/apps/:slug/database

Report the app's storage tier, managed database status, and usage stats.

Editor + Pro
POST/api/apps/:slug/database

Upgrade to a managed database (needs managedDatabase). Provisions it and copies existing records over.

Editor + Pro
DELETE/api/apps/:slug/database

Downgrade key/value-only managed storage to Tool Storage. Managed Postgres databases are refused because SQL tables cannot be preserved.

Editor + Pro

Environment and sharing#

9 endpoints
MethodEndpointAuth
GET/api/apps/:slug/env

List env metadata. Add ?values=1 to explicitly include decrypted values.

Editor
POST/api/apps/:slug/env

Set or rotate one encrypted env var with description and tags.

Editor
DELETE/api/apps/:slug/env

Remove an env var from the vault.

Editor
GET/api/settings/variables

List workspace-global env metadata visible to the caller. Add ?values=1 to include decrypted values the caller can use.

Workspace member
POST/api/settings/variables

Set or rotate one workspace-global env var: { key, value, description, scope, allowedUsers }.

Admin or org:env:manage
DELETE/api/settings/variables

Remove one workspace-global env var: { key }.

Admin or org:env:manage
GET/api/apps/:slug/share

Return the active share link and stats if one exists.

Viewer
POST/api/apps/:slug/share

Create, update, or regenerate the public share link. New links default to no guest join and no remix.

Editor
DELETE/api/apps/:slug/share

Revoke the active share link.

Editor

Public share links#

3 endpoints
MethodEndpointAuth
GET/api/share/:token

Public metadata for unfurls and share pages. Never returns source or env values.

Public token
GET/api/share/:token/bundle

Download live source for remix when allowRemix is enabled.

Public token
POST/api/share/:token/join

Grant viewer access through a join-enabled share link.

Signed-in user

Tool runtime#

7 endpoints
MethodEndpointAuth
ANY/a/:deploySlug/*

Gateway proxy that signs x-td-user and serves the current tool version.

Workspace session
ANY/webhooks/:deploySlug/:endpoint

Public webhook receiver that records delivery status, forwards to the generated wrapper, and returns the app handler response.

Public endpoint
GET/api/tooldrop-cron/:job

Generated scheduled-job wrapper that records start, finish, status, and log lines before invoking the tool's /api/cron/* handler.

Host secret
ANY/api/tooldrop-webhook/:endpoint

Generated webhook wrapper that forwards public deliveries into the tool's /api/webhooks/* handler.

Platform secret
POST/api/connector

Runtime bridge for Tool Storage operations plus cron, webhook, and app log reporting. Tools call this through the SDK.

App token
POST/api/cli/device/start

Start CLI browser login by creating a short-lived device code.

Public
GET/api/cli/device/poll?code=...

Poll for browser approval and return a one-time CLI token.

Public code

Operations

Unblock an IP-allowlisted database#

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

Tooldrop egress IP

8.235.38.133
label it tooldrop-egress
  1. 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. 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. 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.

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.

Operations

Security guardrails#

Defaults the platform enforces so a quick internal tool never becomes an incident.

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.

Something missing? The Markdown docs carry the same content in one file.