CLI Setup

Install the command-line tool for creating apps.


Prerequisites

For Python Apps: uv

The CLI uses uv for Python environment management:

bash
1# macOS / Linux2curl -LsSf https://astral.sh/uv/install.sh | sh34# Windows5powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

For Node.js Apps: Node.js

Node.js v20 or later:

bash
1# macOS / Linux (via fnm)2curl -fsSL https://fnm.vercel.app/install | bash3fnm install 2245# Or via nvm6curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash7nvm install 22

Hardware

App TypeDevelopment Environment
CPU appsAny machine
GPU appsRequires NVIDIA CUDA GPU

Install CLI

bash
1curl -fsSL https://cli.inference.sh | sh

This covers macOS, Linux, and Git Bash on Windows. In Windows PowerShell:

powershell
1irm https://cli.inference.sh/install.ps1 | iex

Both install to ~/.local/bin and add it to your PATH. Open a new terminal afterwards. On Windows, pass JSON input from a file (belt app run <app> --input input.json) because PowerShell strips the quotes from inline JSON.

Or via package managers:

bash
1brew install inference-sh/tap/belt       # macOS / Linux2npx @inferencesh/belt                    # Node.js3scoop bucket add inference https://github.com/inference-sh/scoop-bucket && scoop install belt  # Windows

Command name

The same CLI is available as belt, infsh, and inferencesh (package installs may expose one or all of these names). Subcommands are identical:

bash
1belt version2infsh version    # same CLI

Examples in this documentation use belt. Either prefix works.


Verify

bash
1belt version

Update CLI

bash
1belt update    # or: infsh update

Shell installer (curl … | sh): belt update downloads and replaces the binary in place.

Homebrew or Scoop: the CLI detects package-manager installs and refuses to self-update over a managed binary. Run the manager's command instead:

bash
1brew upgrade belt    # Homebrew2scoop update belt    # Scoop

If you installed with brew install inference-sh/tap/belt or scoop install belt, belt update prints the matching command when an upgrade is available.

Auto-update runs in the background on most commands. It is skipped for belt version, belt update, and help flags. Set INFSH_NO_AUTOUPDATE=1 to disable automatic upgrades.

Dev builds (version dev, typical of go run or local builds) skip self-update so a release binary does not overwrite your working tree. belt update prints skipped: dev build. Pass --force to check the manifest and upgrade anyway:

bash
1belt update --force

Login

bash
1belt login2belt login --key-stdin < key.txt          # non-interactive: skip browser flow (CI, scripts)3printf %s "$KEY" | belt login --key-stdin

Opens a browser for device authorization in interactive terminals.

Headless device auth: in non-interactive shells (no TTY, CI set, BELT_NON_INTERACTIVE=1, or --no-input), belt login without --key prints an approval URL instead of opening a browser. After you approve, the browser page shows a 6-digit code. Run belt login --code <code> (-c) on the same machine to finish. The link and the code expire 5 minutes after belt login ran. The CLI keeps the PKCE verifiers of up to five recent unclaimed runs and tries them newest first, so a code from an earlier belt login on the same machine still works inside that window. If the claim fails, run belt login again for a fresh link.

For scripts and CI, pipe an API key with --key-stdin (or --key -) or set INFERENCE_API_KEY instead. --key <key> still works but warns: a key on the command line lands in shell history and the process list.

The CLI, the SDKs and curl examples all read INFERENCE_API_KEY (see Authentication). INFSH_API_KEY still works but is deprecated: belt prints a warning once per run, and when both are set and differ, INFERENCE_API_KEY wins.

Credentials are stored in ~/.inferencesh/config.json with 0600 permissions (owner read/write only). Other local belt data (skill cache, command history, hook logs, remote machine keys, and similar) defaults to ~/.belt/ and paths under ~/.inferencesh/. Set BELT_CONFIG_DIR to use one directory for all of it — config, cache, history, and remote identity — so separate agents or shells on the same machine never share login or cached skills. See Environment Variables.

A belt login session token slides on use like a browser session: regular CLI use extends server-side expiry (seven-day idle window) without changing the stored token, so you are not logged out a fixed week after login while you keep using belt. Short-lived sessions from device approval (custom expires_in under seven days) are not extended. See Device authorization — Sliding lifetime.

After a successful login, the CLI clears the skill cache and notice cooldowns under that layout (~/.belt/cache and ~/.belt/notices by default), so cached API responses start fresh for the new account.


Logout

bash
1belt logout

Clears the stored credentials in ~/.inferencesh/config.json: API key, session token, anonymous session token, and any workspace override from belt auth switch. Also clears ~/.belt/cache and ~/.belt/notices.

Everything else under ~/.belt and ~/.inferencesh is kept, including command history (~/.belt/history), agent session ledgers (~/.belt/sessions), the hook log (~/.belt/hooks.log), and CLI logs.

INFERENCE_API_KEY in the environment is not cleared. Unset it separately.

Logout removes credentials from your machine only. To revoke a session on the server, use settings → personal → sessions or POST /oauth/revoke (see Device authorization: Revoke). API keys stay valid until you revoke them with belt keys revoke or delete them in settings → workspace → api keys.

When no credential is stored, the command prints not logged in and exits 0.


Reset

bash
1belt reset

Clears all local belt state: the credentials in ~/.inferencesh/config.json, ~/.belt/cache, ~/.belt/notices, ~/.belt/sessions, ~/.belt/hooks.log, the pending headless login (~/.inferencesh/pending_auth.json), and the log directories ~/.inferencesh/logs and ~/.inferencesh/logs-staging.

Command history (~/.belt/history) is kept.

Reset does not revoke server-side sessions or delete API keys. For credential-only cleanup, use Logout.


Check your account

bash
1belt me2belt me --json    # same payload as GET /me — for scripts and automation

Shows your username and workspace. When a workspace override is active, belt me prints a hint to use belt auth switch.

With --json, stdout is compact JSON for the GET /me response (user and team objects). Use --pjson for indented output. See Workspaces API: get current user. On failure, stdout is a JSON error object (same as other --json / --pjson commands); stderr still has the formatted message.

Logged in? belt auth status (alias whoami) prints the account and workspace the stored credential resolves to, and exits non-zero (auth-required) when there is no credential or the server rejects it. With --json: {"logged_in", "email", "team", "credential", "endpoint"}.

bash
1belt auth status --json || printf %s "$KEY" | belt login --key-stdin

Balance: belt balance is a top-level alias for belt me balance (agents often try belt balance first). It prints your workspace's current balance and nudges you to top up when below $5. Use --json for {"balance": <microcents>, "balance_dollars": <float>}.

bash
1belt balance2belt me balance --json

Requires billing:read scope (included in standard API keys). See Billing API.


Switch workspaces

If you belong to multiple workspaces, switch the active workspace for all CLI commands:

bash
1belt auth switch              # interactive team picker2belt auth switch my-org       # switch directly by team username3belt auth sw my-org           # alias

The CLI stores the override in ~/.inferencesh/config.json and sends it as the X-Team-ID header on API requests. Switching back to your default workspace clears the override. Each successful switch also clears ~/.belt/cache and ~/.belt/notices, the same as login.


Temporary token for scripts

bash
1belt auth token

Mints a short-lived API key from your login and prints it to stdout with no trailing newline — for subshells, curl, and shell scripts. The login credential itself stays in ~/.inferencesh/config.json.

bash
1curl -H "Authorization: Bearer $(belt auth token)" https://api.inference.sh/me2export INFERENCE_API_KEY=$(belt auth token --ttl 8h)

The key is pinned to the active workspace (belt auth switch) and cannot act in any other workspace, even ones you belong to.

FlagEffect
--ttl 1hKey lifetime, 1m to 24h. Default 15m, or token_ttl in config.json.
--scope apps:readRestrict the key to a scope. Repeatable. Without it the key gets the same scopes as the login.
--long-livedPrint the stored login credential instead of minting a key.
--jsonOutput token, expires_at, and scopes.

The key is a regular API key with a short expiry — see temporary keys. It cannot create other keys, so a leaked one cannot renew itself. While it is live it shows in your API keys list, where you can revoke it; once expired it is removed.

Works from a belt login session. Creating keys is held by your own sign-in, so an API key login cannot. When not logged in, errors go to stderr and stdout stays empty.

For a long-lived personal or workspace key, use belt keys or the web app.


Manage API keys

Create, list, and revoke API keys in the active workspace (belt auth switch). Requires belt login or INFERENCE_API_KEY, with the same capabilities as the REST API: every member manages their own personal keys; workspace owners and admins also manage workspace keys and every member's keys.

bash
1belt keys create laptop2belt keys create ci --workspace --scope apps:execute --ttl 90d3belt keys list4belt keys revoke <id> -y
CommandPurpose
belt keys create <name>Create a key and print the full secret once
belt keys listList keys you may see (masked values, scope, creator, last use, expiry)
belt keys revoke <id>Revoke by ID from belt keys list (destructive; confirm or -y / --yes)

Personal keys (the default) act as you in the active workspace and stop working when you leave it. Workspace keys (--as workspace or --workspace) act as the workspace and survive their creator; only workspace owners and admins can create them. See API keys.

FlagEffect
--as user|workspaceWho the key acts as (user is default)
--workspaceShorthand for --as workspace
--scope <scope>Restrict permissions (repeatable). Without it the key gets the same scopes as your login
--ttl 720h or 90dExpiration; omit for a key that never expires
--jsonJSON on create and list

Use belt auth token for short-lived keys in scripts (24 hours at most, always personal). Use belt keys create for CI deploy keys, shared automation, or named keys you rotate yourself.


Doctor commands

belt auth doctor, belt app doctor, and belt plugin doctor share the same automation contract:

  • Without --check, each command is a diagnostic report and exits 0 even when it finds problems — useful for humans reading the output.
  • With --check, the command exits non-zero when the report is unhealthy — for CI gates and agents.
  • With --json (or --pjson), stdout is a JSON report with a top-level ok boolean.
bash
1belt auth doctor --check --json --no-input2belt app doctor --check --json3belt plugin doctor --check --json

Authentication (belt auth doctor)

bash
1belt auth doctor2belt auth doctor --check --json --no-input

Config being present is not the same as credentials being accepted — a stored session expires, an API key is revoked, or the endpoint may be unreachable. belt auth doctor runs four checks:

CheckWhat it verifies
configWhether a config file exists and was loaded
credentialWhich credential type would be sent (session token or API key) — never prints the secret
pathWhether belt, infsh, and inferencesh on PATH resolve to this binary. Warns when another binary shadows the CLI, for example the launcher left by the old PyPI inferencesh package.
apiWhether the credential is accepted by the API (GET /me); skipped when no credential is configured

With --check, any failed check sets a non-zero exit code. The exit code reflects the first failing check:

  • Credential or auth failures → exit 4 (auth_required)
  • Network or endpoint unreachable → exit 8 (retryable)
  • API rate limit → exit 7 (rate_limited)

Use --json for machine-readable output:

json
1{2  "ok": false,3  "endpoint": "https://api.inference.sh",4  "checks": [5    {"name": "config", "status": "ok", "detail": "/home/user/.inferencesh/config.json"},6    {"name": "credential", "status": "fail", "detail": "none configured", "hint": "run 'belt login', or set INFERENCE_API_KEY"},7    {"name": "api", "status": "skipped", "detail": "no credential to test"}8  ]9}

Check statuses: ok, warn, fail, skipped. Failed checks include a hint with suggested next steps. The path check only warns, so it never fails --check. See Troubleshooting: Stale CLI on PATH.

App health (belt app doctor)

bash
1belt app doctor2belt app doctor fix3belt app doctor --check --json

Run from your app directory before deploy. The doctor checks inf.yml, required files (inference.py / src/inference.js, dependency manifests), Python package layout (__init__.py, relative imports), Node ESM config, GPU dependencies, stale schemas, and other common issues. Errors block belt app deploy; warnings are shown but do not stop the build.

belt app doctor fix auto-fixes issues marked fixable (relative imports, missing __init__.py, GPU accelerate dependency, ESM type: module, and similar).

With --check, only errors gate the exit code — warnings are reported but do not fail the run. When errors are found, --check exits 10 (config): local project setup is missing or invalid (for example no inf.yml, a bad import, or an uninitialised project). Warnings alone still exit 0. With --json, stdout is:

json
1{2  "ok": false,3  "issues": [4    {5      "id": "missing-init",6      "message": "missing __init__.py — has sibling modules (utils.py) but no package init",7      "fixable": true,8      "severity": "error",9      "fix_description": "create __init__.py with `from .inference import App`"10    }11  ]12}

Issue fields: id, message, severity (error or warning), fixable, and optional fix_description / extra. → Deploying

Plugin health (belt plugin doctor)

bash
1belt plugin doctor2belt plugin doctor --fix3belt plugin doctor --check --json

Checks belt CLI availability, authentication, agent runtime installs, plugin cache and hook configuration (Claude Code / Codex), hook shim format, and recent activity in ~/.belt/hooks.log. Use --fix to auto-repair stale caches, reinstall the belt plugin, and patch hook scripts that still contain embedded logic instead of the thin shim format.

With --check, any failed check exits non-zero. With --json, stdout is:

json
1{2  "ok": false,3  "checks": [4    {"name": "belt", "ok": true, "detail": "/usr/local/bin/belt"},5    {"name": "auth", "ok": false, "detail": "not logged in"},6    {"name": "plugin", "ok": false, "detail": "belt not found in installed plugins"}7  ]8}

Check names vary by detected agent runtime (for example plugin, hooks, shims, cursor, pi-ext). → Coding agents · Troubleshooting


What you can do

Development

CommandPurpose
belt app initCreate a new app
belt app init <name> --template apiCreate a vendor API wrapper: one function per upstream operation, an httpx client with retries, a secret for the key, test_requests/ per function, a pricing.md skeleton and a README with the finishing steps (needs engine 1.19+, belt 1.22+)
belt app init --openapi <url-or-file>Generate vendor API apps from an OpenAPI 2.0/3.x document: a hand-editable <vendor>/openapi.plan.yaml, then one api-template app per group of operations with Pydantic models and operation descriptors in _generated.py, a thin inference.py, inf.yml declaring the vendor's credential, and test_requests/ from the spec's examples. Rerun with the plan file to regenerate. --vendor <slug> overrides the slug, --openapi-header "Name: value" authenticates the download. Needs uv for the model generator and belt 1.25+ (→ Creating an app)
belt app testTest locally
belt app deployDeploy to inference.sh (--skip-validation uploads the committed schema files without running the local engine, for machines where it cannot run)

Running Apps

CommandPurpose
belt app runRun an app in the cloud
belt app estimatePredict run cost without dispatching a task
belt app sampleGenerate sample input (file fields use ./your-file.jpg placeholders)

For multi-function apps, belt app sample defaults to the resolved default function (run, explicit default_function, or the sole function). Use -f / --function for other entry points. belt app get lists all functions with run/sample hints.

When an app is missing required secrets or credentials, belt app run (and belt agent run) prints categorized setup hints instead of a generic API error — for example belt secrets set <KEY> (prompts for the value) or belt credentials connect <provider>. MCP requirements suggest belt mcp connect <slug>. When a requirement names no provider, the hint is connect at: https://app.inference.sh/vaults. The same requirement shape is returned by POST /run as HTTP 412 — see Check requirements.

JSON input — belt app run, belt mcp run, and belt flow run resolve input from three sources, in priority order: --input / -i flag → positional argument → piped stdin (when stdin is not a TTY). Each source accepts inline JSON ('{"prompt": "a cat"}'), @filename, or a path to an existing JSON file.

bash
1belt app run namespace/my-app '{"prompt": "a cat"}'          # positional2echo '{"prompt": "a cat"}' | belt app run namespace/my-app   # stdin3belt mcp run linear:list_issues '{"project": "INF"}'         # positional4belt flow run abc123 --input '{"text":"hello"}' -f           # flag (unchanged)

Omit input entirely when the app, tool, or flow does not require it.

Filter output (--jq) — belt app run and belt mcp run accept --jq with a jq expression to filter JSON on stdout without piping to an external jq binary (the CLI embeds the same pure-Go engine GitHub CLI uses). String results are printed unquoted (like jq -r); other values are compact JSON, one per line.

bash
1belt mcp run linear:list_issues --jq '.issues[] | {id, title}'2belt app run okaris/flux -i '{"prompt": "a cat"}' --jq '.output.url'3belt app run okaris/flux -i '{"prompt": "a cat"}' --no-wait --jq '.id'   # task id only

belt app run --jq evaluates against the task object (id, status, status_text, output, error, session_id, created_at, updated_at). App outputs live under .output — for example .output.url for a generated file URL. --jq suppresses progress lines on stdout (same as --json). Use --json or --pjson when you need the full task or tool payload instead.

belt app run exits 1 (error) when the final task status is failed or cancelled. With --json or --jq, stdout still contains the task JSON, so check $? or the status field. See Exit codes.

Output files — the task result prints to stdout (progress and status lines go to stderr), so belt app run … | jq and > out.json work. With --json, files lists every file the app produced (path and url, found from the app's output schema). --urls prints only those URLs, one per line. --download (bare form: current directory) or --download=DIR saves each file as <task>-<field>-<n>.<ext> and adds local_path to the JSON entries.

bash
1belt app run okaris/flux -i '{"prompt": "a cat"}' --urls2belt app run okaris/flux -i '{"prompt": "a cat"}' --download=./out3belt app run okaris/flux -i '{"prompt": "a cat"}' --json --jq '.files[].url'

Batch — --batch prompts.jsonl (or - for stdin) runs one task per line and writes one JSONL result per input (index, task_id, status, output, error, submit_error, attempts). --concurrency N (default 4) bounds how many tasks are in flight; --retry N re-submits an input after a transient submit error (5xx, 429, network) or a provider-side task failure, with a 2s·attempt backoff.

bash
1belt app run pruna/p-image --batch prompts.jsonl --concurrency 8 --retry 1 > results.jsonl

Cost estimation — belt app estimate reports the predicted cost of a run without dispatching a task. belt app run --estimate (-e) does the same when you already have a run command written. Both call POST /store/apps/{appId}/estimate with the same input handling as belt app run (inline JSON, @file, positional, or stdin). Apps with fixed per-call pricing can be estimated with no input; input-dependent pricing needs the intended input JSON.

bash
1belt app estimate okaris/flux --input input.json2belt app estimate okaris/flux --input '{"prompt":"a cat"}'3belt app run okaris/flux --estimate --input '{"prompt":"a cat"}'4belt app estimate okaris/flux --input input.json --json --select microcents,confidence

Human-readable output shows estimated cost with a confidence level:

confidenceDisplay
exactSingle dollar amount (all fees are input-based)
rangeMin–max dollar range (from the app's estimate CEL expression)
unknownunknown until the app runs plus depends on: fields when pricing needs post-execution data

With --json, stdout is the full EstimateCostResponse (confidence, microcents, min, max, depends_on, pricing_description, estimate_error). → Estimate app cost · Usage-based pricing

Managing Apps

CommandPurpose
belt app listList your deployed apps (--search, --limit, --cursor, --json; belt app my / belt app list aliases)
belt app storeBrowse the public app store (--featured, --category, --new, --limit, --cursor, --json, --save)
belt app searchSearch apps by name or description
belt suggest <query>Unified discovery (apps, skills, knowledge, docs)
belt app getGet app details
belt app pullDownload or sync an app

Programmatic listing:

bash
1belt app list --json                         # your deployed apps (requires login)2belt app list --search "flux" --json3belt app list --limit 50 --cursor <next>     # cursor pagination (see hints in table output)4belt app store --json                        # public store with cursor pagination metadata5belt app store search "video" --category video --json6belt app store --save store-snapshot.json    # write JSON to file instead of stdout

belt app list --json prints the full cursor-list response from POST /apps/list (items as AppDTO[], plus next_cursor, has_next, …). belt app store --json prints the same cursor-list shape from POST /store/apps/list (items as PublicAppStoreDTO[]) — see List approved apps. In table output, both commands print next page: / prev page: hints with the --cursor value when more results exist. --save writes the store payload to a file (always pretty-printed) and prints a confirmation line to stdout.

belt app pull syncs app code and metadata from inference.sh:

bash
1belt app pull              # sync current app (from inf.yml in cwd)2belt app pull --meta       # sync only the listing into inf.yml (no code)3belt app pull <app-id>     # download app by ID to namespace/name/4belt app pull --all        # sync every local app (finds inf.yml recursively)5belt app pull --all --meta # sync only the listing for every local app6belt app pull <app-id> --force   # overwrite existing directory without prompt

A pull merges into inf.yml; it does not rewrite the file (CLI 1.19.11 and later):

  • The listing comes down from the platform: title, description, category and images. Use --meta after editing an app's images or description on its page.
  • Everything else stays as you wrote it, including comments, key order and keys such as default_function, secrets and credentials. These reach the platform when you deploy.
  • A value the platform does not have, such as an empty image or no title, leaves the local one in place.
  • A full pull also takes the version's metadata and fills in keys the deployed inf.yml lacks.

An app pulled into a new directory gets namespace and name in its inf.yml, so deploy and run commands resolve the correct namespace/name ref.

If the name in inf.yml answers with a different app on the platform, because the app was renamed or redirected, the pull stops with an error and changes nothing.

When syncing an existing directory, the CLI removes old app files before extracting the new archive but preserves local environment and VCS paths: .git, .env, .infsh, .venv, venv, node_modules, and __pycache__.

belt app get shows schemas, pricing, and run/sample hints for an app ref (namespace/name, namespace/name@version-id, or app ID). With --json, stdout is {"app": <AppDTO>, "pricing": ...} from GET /apps/{ref}. When the ref (or a platform alias on it) pins a function, app.resolved_function names that entry point — see Get App.

bash
1belt app get okaris/flux2belt app get okaris/flux@abc123 --versions   # version history3belt app get okaris/flux --json              # includes status fields for scripts

When lifecycle status is not active, human-readable output shows a banner after the description:

StatusDisplayRun hints
deprecated⚠ deprecated (warning) plus optional status_messageShown — app still accepts runs
maintenance⚙ under maintenance (warning) plus optional status_messageOmitted — the banner already indicates runs are unavailable
retired✕ retired (error) plus optional status_messageOmitted — the banner already indicates runs are unavailable

belt app run against a maintenance or retired app returns API errors (503 app_maintenance, 410 app_retired) — see Tasks API. Use belt app get before scripting runs to check status and the owner's message (for example, a replacement app ref).

belt suggest calls the same discovery API as GET /suggest. Results include a suggested CLI command or URL for each hit:

bash
1belt suggest "flux image"2belt suggest "web search" --json     # JSON for agent hooks3belt suggest "deploy" --agent        # directive framing for agent hooks

With no arguments, belt suggest reads stdin (hook JSON or plain text). CLI args take precedence over stdin.

Human-readable output groups results by category (app, skill, knowledge, and so on). Each section header shows the category and a command template (for example belt skill use <name>). Result lines include an optional inline [tag], the name, and description. After a cross-collection search, stderr may suggest scoped follow-ups such as belt app suggest "...".

Hook mode (--json): Returns hookSpecificOutput.additionalContext with one line per match: - [type] name — description, then run: <command> on the next line. Results are ranked by score and deduped server-side. Hook stdin is JSON with a prompt field (or plain text). When transcript_path is present, belt extracts assistant text from the current turn (since the last user message, up to 1000 characters) and sends it as the context field to POST /suggest for semantic ranking — keyword matching still uses prompt only. Belt also sends scope signals from the current directory (git: remote, lang:, module:, and similar tags) on every suggest call so knowledge captured in the same repo or language ranks higher. API failures are silent in --json mode so hooks do not pollute agent stderr.

Use --agent to frame hook output as a directive to the agent. Descriptions in suggest results are always one line of at most 200 characters; load the resource to read the rest. Hook integrations installed via belt plugin install <agent> call belt suggest --json automatically.

belt suggest also sends scope tags from the current working directory (git remote, module name, language, path) so project-relevant knowledge ranks higher in results. Entries uploaded with belt know upload carry the same tags unless you used --global.

Use belt app search when you only need apps. Use belt suggest for cross-collection discovery (skills, knowledge, docs, and apps).

→ Search concept · Search API

Secrets

CommandPurpose
belt secrets listList all secrets (--json)
belt secrets set <key>Create or update a secret. The value is read from stdin (or --value-stdin); on a terminal it is prompted without echo. A value argument still works but lands in shell history. Flags: --provider, --provider-name, --provider-website, --as / --team
belt secrets attach <key> <provider>Link an existing secret to a provider's credential
belt secrets detach <key>Turn a linked secret back into a plain secret
belt secrets get <key>Get a secret (masked; --json)
belt secrets delete <key>Delete a secret

Connectors (MCP)

CommandPurpose
belt mcp listBrowse MCP connector servers (--category, --featured, --json)
belt mcp search <query>Search servers by name or description (same filters as list)
belt mcp get <slug>Show server details and connection status
belt mcp connect <slug>Connect OAuth for a server (--as, --team, see Credentials (CLI))
belt mcp disconnect <slug>Disconnect an MCP server (confirms; --yes to skip)
belt mcp refresh <slug>Re-run discovery and enrichment on a server
belt mcp tools <slug>List tools on a connected server (slug:tool to show one tool's schema; --json)
belt mcp run <slug>:<tool>Call a connector tool from the terminal (<slug> alone lists tools, same as mcp tools)

belt mcp run and belt mcp tools use slug:tool format — the first colon splits the server slug from the tool name (for example linear:list_issues). Pass only the slug to belt mcp run to list available tools instead of calling one. If you mistype an MCP subcommand, the CLI prints this format hint. After belt mcp list, stderr may show tips for belt mcp search, get, and connect.

belt mcp run <slug>:<tool> --help fetches that tool's schema (description, required fields, JSON Schema, and a sample belt mcp run … --input '…' line) before the usual usage text — the same detail as belt mcp tools <slug>:<tool>. Tool input is -i / --input, a positional JSON argument, or stdin; mistyped flags such as --args print the parse error on stderr plus a reminder instead of exiting silently. When the server returns is_error: true, stderr repeats the server's message when present and suggests belt mcp tools <slug>:<tool> for the schema; stdout still holds the full JSON payload. When the tool result is only one page of a larger list (next_cursor, has_more, pageInfo, and similar), stderr also hints how to pass the cursor in --input for the next page — see Using tools — Call a tool.

With --json, belt mcp tools prints the tool surface from the connected MCP server: an array of name, description, and inputSchema objects when you pass only the slug, or a single tool object when you pass slug:tool. Tool names, descriptions, and schemas are authored by the MCP server (third-party content), so combine --wrap-untrusted when piping this output to an LLM — see Global automation flags.

bash
1belt mcp tools linear --json2belt mcp tools linear:list_issues --json3belt mcp tools linear --json --select name,description   # projection for agents

Some MCP tools need extra input mid-call (Multi Round-Trip Request / MRTR). In an interactive terminal, belt mcp run prompts for missing fields (form mode) or opens a browser for OAuth-style steps (URL mode), then retries automatically. In non-interactive mode (CI, no TTY, or BELT_NON_INTERACTIVE=1), the CLI prints the input_required JSON envelope to stdout and exits with code 2 — scripts must parse input_requests and call the API again with inputResponses. See Using tools — Interactive elicitation.

→ Connectors overview · Browsing connectors · Using tools

Credentials (CLI)

List, connect, and inspect OAuth, service account, and MCP credentials for your workspace:

CommandPurpose
belt credentials listList connected credentials (--limit, --cursor, --json; table output prints pagination hints). Table columns: company, slug (the provider / credential slug), sign-in (OAuth, API key, HTTP), status, owner (you, the workspace, the organization, or the platform), keys (an API key's linked secret keys). The table also lists providers whose OAuth app is set up but not connected yet; --json --apps adds them to the JSON as apps (--json alone stays a bare array)
belt credentials get <provider>Show one provider slug and its saved credential (--json for scripts). Table mode merges GET /credentials/configs with GET /credentials/{provider}: a provider block (name, company when it differs from the provider name, slug, sign-in, website, docs, declared keys) and a credential block (owner, status, connection times, linked keys or OAuth accounts)
belt credentials connect <provider>Connect OAuth, API-key, or service-account providers (--as, --team)
belt credentials disconnect <provider>Remove a workspace connection
bash
1belt credentials list2belt credentials list --limit 50 --cursor <next>   # when stderr hints show more pages3belt credentials get google4belt credentials get mcp --json   # includes credential id for agent tools5belt credentials connect google6belt credentials connect slack7belt credentials disconnect google

belt creds is an alias for the same command group.

belt credentials connect looks up the provider in GET /credentials/configs. OAuth providers open your browser and poll for up to two minutes. API key providers prompt for each declared secret field (sensitive fields are masked), save each as the provider's credential (the same as belt secrets set <KEY> --provider <provider>), and report connected when the server marks the credential connected. Service accounts connect directly when the provider supports it. If the provider is already connected, the CLI prints the account and suggests disconnecting first. Unknown providers list available options; for MCP servers, use belt mcp connect <slug> instead.

Connection scope: Pass --as user|team|org|platform (or --team as shorthand for --as team) to set who owns the credential. The flag sets connection_scope on the connect request. Omit both flags to use the provider default. Invalid --as values are rejected before any API call. To change who owns an existing connection without reconnecting, use PUT /credentials/{provider}/scope — see Change connection scope.

bash
1belt credentials connect google --as team      # team admin: shared team OAuth2belt mcp connect linear --as user              # personal MCP authorization (default for MCP)

Google OAuth and the managed service account are separate providers (google vs google-sa). Connect the service account from vaults → credentials (google-sa slug) or with belt credentials connect google-sa.

Non-interactive OAuth (CI, scripts): When stderr is not a TTY, CI is set, or BELT_NON_INTERACTIVE=1, belt credentials connect prints the authorization URL but does not open a browser or poll. Open the URL manually, then verify with belt credentials get <provider>. For API key providers in the same conditions, the CLI prints belt secrets set <KEY> --value-stdin --provider <provider> < value-file lines instead of prompting.

Provider keys match the REST API: google, google-sa, gcp, slack, mcp, and so on. MCP servers all sit under mcp; the server URL is the credential's account identifier, and GET /credentials/mcp?identifier={server_url} names one. Use belt credentials get --json when you need the credential id for agent connector tools.

→ Credentials API · Connectors overview

Vaults (CLI)

Inspect workspace vaults, the containers that group credentials and secrets. In the web app they are under vaults.

CommandPurpose
belt vaults listList workspace vaults (ls alias, --json)
belt vaults show [vault-id]Show a vault's credentials and secrets

vault is an alias for vaults (for example belt vault show). Both commands are read-only.

bash
1belt vaults list2belt vaults show                    # default vault3belt vaults show vault_abc123       # specific vault by ID4belt vaults show --json

belt vaults show with no argument resolves the workspace's default vault (GET /vaults/default). It prints two tables: credentials (provider, type, scope, status, display name) and secrets (key, value, description). Secret values are masked.

With --json:

  • belt vaults list prints a VaultDTO[] array (id, name, description, is_default, credential_count, secret_count, ...)
  • belt vaults show prints {"vault_id", "name", "credentials": [...], "secrets": [...]}

To connect credentials or set secrets, use belt credentials connect, belt secrets set, or the Vault UI.

→ Vaults API · Credentials overview · Environment secrets

Flows (CLI)

Create, edit, run, and publish flows from the terminal — same APIs as Flows and Flow Runs.

Flow refs — Commands that target a saved flow accept namespace/name or a flow ID (full flow_… ID or short ID). Resolution matches belt agent get and belt agent run: namespaced refs call GET /flows/{namespace}/{name}; IDs call GET /flows/{id}.

CommandPurpose
belt flow create --name <name>Create an empty flow (--json for raw response)
belt flow listList your flows (ls alias; --json)
belt flow get <ref>Show flow metadata (--json)
belt flow describe <ref>Show the full graph with node I/O fields and connections
belt flow duplicate <ref>Copy a flow (dup / cp aliases)
belt flow delete <ref>Delete a flow
belt flow run <ref>Start a flow run (--input JSON, -f to stream progress)
belt flow runsList recent flow runs (--limit, --json)
belt flow run-get <run-id>Get run status and output (-f to follow until complete)
belt flow cancel <run-id>Cancel a running flow
belt flow publish <ref>Deploy the flow as a standalone app

Graph editing — belt flow edit applies draft mutations via POST /flows/{id}/actions. The first argument is the same <ref> as belt flow get:

CommandPurpose
belt flow edit node add <ref> <name> --app <app-ref>Add an app node (resolves namespace/name to app ID)
belt flow edit node rm <ref> <name>Remove a node
belt flow edit node inspect <ref> <name>Show input/output fields with types and defaults
belt flow edit node set-input <ref> <name> <key> [value]Set a static value or --from node:key connection
belt flow edit node clear-input <ref> <name> <key>Clear an input value or connection
belt flow edit edge add <ref> <source> <target>Add a structural edge between nodes
belt flow edit edge rm <ref> <source> <target>Remove an edge
belt flow edit output set <ref> <field> --from <node>:<key>Map a node output to the flow output
belt flow edit output rm <ref> <field>Remove an output mapping
belt flow edit set-schema <ref> --input '...' --output '...'Set flow-level input/output JSON Schema (type must be "object"; see Flows API)
belt flow edit undo <ref>Undo your most recent graph edit (server-side action log; see Undo and redo)
belt flow edit redo <ref>Redo your most recently undone edit
belt flow edit actions <ref> [json]Apply raw action JSON (escape hatch for uncovered mutations)
bash
1belt flow get myteam/voice-pipeline         # by namespace/name2belt flow get abc123                        # by short ID3belt flow create --name voice-pipeline4belt flow edit node add myteam/voice-pipeline tts --app elevenlabs/tts5belt flow edit node set-input myteam/voice-pipeline tts text "hello world"6belt flow edit node set-input myteam/voice-pipeline voice-changer audio --from tts:audio7belt flow edit output set myteam/voice-pipeline audio --from voice-changer:audio8belt flow describe myteam/voice-pipeline9belt flow run myteam/voice-pipeline -f10belt flow publish myteam/voice-pipeline

Use input as the source node in --from to wire a node input to a flow-level input (for example --from input:text). belt flow run -f streams NDJSON progress events with polling fallback when SSE is unavailable.

→ Flows guide · Creating a flow · Flows API · Flow Runs API

Agent integration (CLI)

Wire belt into a local coding agent runtime (discovery hooks and core skills):

CommandPurpose
belt plugin install [agent...]Wire belt into coding agents: hooks, core skills, and a belt section in the agent's instruction file (CLAUDE.md, AGENTS.md, GEMINI.md, steering, rules). No args detects installed agents and sets up all of them; --all covers every supported agent; --scope project writes into the current repo; --rules=false skips the instruction file. Claude Code and Codex go through their plugin marketplaces.
belt plugin uninstall [agent...]Remove hooks and the belt section. No args cleans every agent that has belt installed.
belt plugin promptsList, show, export, and reset the extraction prompts and the rules block. Overrides live in <repo>/.beltsh/prompts/<name>.md (team) or ~/.belt/prompts/<name>.md (personal); {{default}} includes the embedded text.
belt plugin statusList each detected agent and whether belt hooks are installed at user scope, project scope, both, or not at all
belt plugin infoShow belt plugin capabilities and install status
belt plugin doctorCheck belt plugin health (--fix to auto-repair; --check --json for CI and agents)
belt plugin historyImport knowledge from Claude Code conversation history
belt review --agent <name>Extract knowledge from agent sessions (hook command)

belt review is called by Stop and PreCompact hooks installed via belt plugin install <agent>. Claude Code plugin hooks call belt plugin review instead. It reads hook JSON from stdin, evaluates the session transcript, and saves reusable knowledge to your workspace. Each candidate is deduped against similar entries already in your registry (search, then create / update / skip) — see Knowledge API — extraction deduplication.

bash
1belt review --agent cursor --trigger stop        # called by Cursor Stop hook2belt plugin review --trigger stop                # called by Claude Code Stop hook3belt review --agent claude-code --trigger stop   # same pipeline as belt plugin review4belt review --agent codex --dry-run                # preview without saving5belt review --agent cursor --force                 # skip 10-turn gating
FlagPurpose
--agentRuntime: claude-code, codex, cursor, gemini, or windsurf (defaults to claude-code)
--triggerHook that invoked review: stop, precompact, or session-end (default stop)
--forceRun immediately instead of every 10 user turns on Stop
--dry-runPrint extraction results without saving

Claude Code uses fork-based extraction and can auto-publish skills. Other agents use transcript-based extraction through the inference.sh API. Requires belt login.

→ Coding agents — skills directories, hook behavior, and Claude Code details

Agents (CLI)

CommandPurpose
belt agent create <name> [description]Create an agent from flags (no YAML file)
belt agent deploy <path>Create or update an agent from a YAML definition
belt agent pull <ref>Export agent config as YAML (--save / -o to write a file)
belt agent listList your agents
belt agent get <ref>Show agent details (version, tools, skills, context)
belt agent delete <ref>Delete an agent (rm alias)
belt agent run <ref> "<message>"Run an agent from the terminal
belt agent run <ref> "<message>" --chat <id>Continue an existing chat
belt agent run <ref> "<message>" --no-waitReturn chat ID immediately (use belt chat to follow up)
belt agent run <ref> "<message>" --timeout <duration>Max wait for response (default 4m; e.g. 10m)

belt agent create calls POST /agents with a first version. Use it for quick agents from the terminal; use belt agent deploy when you need skills, context fields, output schema, or other YAML-only options.

Redeploying the same agent name creates a new version instead of failing with a name conflict. This is the same upsert behavior as belt app deploy. The namespace is assigned from your active workspace server-side, so the namespace field in YAML is optional. You only need a stable name in the file to update an existing agent.

bash
1belt agent create my-helper "A helpful assistant"2belt agent create todo-bot --model openrouter/claude-sonnet-46 --prompt "You manage Todoist tasks"3belt agent create todo-bot --model openrouter/claude-sonnet-46 \4  --mcp 46e8a4bq...:find-projects \5  --mcp 46e8a4bq...:add-tasks6belt agent create my-bot --json    # raw AgentDTO on stdout
FlagPurpose
--modelCore app ref (e.g. openrouter/claude-sonnet-46)
--promptSystem prompt
--mcpMCP tool as server:tool_name, the server by its slug (repeatable); it runs as each person's own login. List tool names with belt mcp tools <slug>
--jsonOutput the created agent as JSON

On success, the CLI prints namespace/name@version-id for use with belt agent run.

belt agent pull exports an agent version as YAML for local editing and redeploy. Ref format matches belt agent get and belt agent run (namespace/name, namespace/name@version-id, or agent ID). Print to stdout or save with --save / -o:

bash
1belt agent pull myteam/support-bot2belt agent pull myteam/support-bot@abc123 --save agent.yml

YAML from pull is intended to round-trip through belt agent deploy without losing configuration: call and http tool auth, headers, and JSON schemas; client tool input_schema / output_schema; skill skill_id and version_id pins; preload (system-prompt injection vs on-demand skill_get); inline skill content for text-defined skills; the permissions block (today allow_all_tools, copied into each new chat created in the agent's own workspace so unattended runs do not stall on tool approval); and lifecycle hooks, including the builtin learning hooks. Redeploying YAML that omits hooks clears hooks that were configured in the editor or on a prior version. See Skills on agents, Adding skills, and Approval settings.

Lifecycle hooks go under hooks. To turn on the learning hooks (the suggest and learn switches in the agent editor):

yaml
1hooks:2  - { event: agent.turn_start, type: builtin, handler: "belt:suggest" }3  - { event: agent.complete, type: builtin, handler: "belt:extract" }4  - { event: agent.pre_compact, type: builtin, handler: "belt:extract" }

Before belt v1.19.4, belt agent deploy ignored hooks and deployed a version without them. Update the CLI before redeploying an agent that has hooks. See Lifecycle hooks.

belt agent get accepts the same refs as belt agent run — namespace/name, namespace/name@version-id, or agent ID. With --json, stdout is the full AgentDTO from GET /agents/{ref}.

bash
1belt agent get myteam/support-bot2belt agent get myteam/support-bot@latest3belt agent get agent_abc123

Human-readable output uses lowercase labels and lists attached skills with their resolved ref and version pin:

code
1  agent:  myteam/support-bot2  version: abc1233  description: Handles customer support4  system prompt: You are a helpful support agent...5  core app: openrouter/claude-sonnet-4667  tools:8    - search (call): Search the web910  skills:11    - myteam/pricing-rules@ver_xyz: Pricing guidelines1213  context:14    - tenant_id (required): Tenant identifier

Each skill line shows skill_id@version_id when the agent pins a specific skill version; otherwise it falls back to the skill name from the agent definition. System prompts longer than 200 characters are truncated.

With --no-wait, the CLI returns as soon as the run is accepted. Without --json, it prints Chat started: <chat-id> with muted belt chat get / belt chat pending hints. With --json --no-wait, stdout is only {"chat_id": "<id>"} — use this in scripts that poll with belt chat get or belt chat watch.

Without --no-wait, belt agent run streams chat events until the agent finishes, needs approval, or hits --timeout (default 4m). On timeout, the CLI prints the chat ID so you can follow up with belt chat get or belt chat pending. With --json (and no --no-wait), stdout is the full POST /agents/run response after streaming completes. Tool calls print as muted progress lines while they run:

code
1→ calling search...2→ search completed

Failed tools print → {name} failed. The final assistant message is printed when the run completes. After a successful run, the CLI prints a muted continuation hint with the chat ID — use belt chat send <chat-id> "<message>" for follow-ups without starting a new run.

Chat (CLI)

Manage agent conversations and human-in-the-loop approvals from the terminal:

CommandPurpose
belt chat listList chats (--status busy, awaiting_input, waiting, idle, completed); prints a continuation hint below the table
belt chat get <chat-id>Show transcript and pending tool invocations (--last N, --all, --full)
belt chat send <chat-id> "<message>"Send a follow-up and stream the agent's response (same streaming as belt agent run)
belt chat watch <chat-id>Re-attach to a running chat and stream until idle or another approval is needed
belt chat pendingList tool invocations awaiting approval
belt chat approve <invocation-id>Approve HIL tool (--watch / -w to stream the response)
belt chat reject <invocation-id>Reject (--reason "...")
belt chat stop <chat-id>Stop a running chat

When belt agent run stops at awaiting_input, the CLI prints belt chat approve / reject commands for each pending invocation. For the unified interrupt model (widgets, auth gates, hook gates, and tool approvals), use belt interrupt list on the run ID from belt chat get --json — see Interrupt (CLI).

belt chat get shows the last 3 messages by default. Use --last N for more, --all for the full transcript, or --full to disable truncation of message text and tool argument JSON (default truncates at 200 and 100 characters respectively). Pending tool invocations always show full args and an approve: hint.

belt chat send posts your message via POST /chats/{id}/messages and then streams chat events until the agent finishes, needs approval, or the stream times out (4 minutes) — the same SSE path as belt agent run without --no-wait. Tool calls print as muted progress lines (→ calling {name}... / → {name} completed or failed). If the agent stops at awaiting_input, the CLI prints belt chat approve / reject commands. With --json, stdout is the created user message from POST /chats/{id}/messages immediately after the message is accepted, without waiting for the agent to finish — poll with belt chat get --json or run belt chat watch if you need the final transcript in automation.

Use belt chat watch when you did not just send a message — for example after belt chat approve without --watch, or to re-attach to a chat that is already busy.

Interrupt (CLI)

List and resolve interrupt gates on agent runs. Interrupts are the unified gate model when a run pauses for tool approval, widget input, missing credentials, lifecycle hook gates, or other external input (active_run.state is input_required or auth_required). For tool-approval interrupts (reason: tool_approval), belt interrupt resolve … allow is equivalent to belt chat approve; deny is equivalent to belt chat reject.

CommandPurpose
belt interrupt list <run-id>List pending interrupts for an agent run (GET /agent-runs/{id}/interrupts)
belt interrupt resolve <interrupt-id> <allow|deny>Resolve a pending interrupt (POST /interrupts/{id}/resolve)
bash
1# List pending interrupts for a run (run ID from belt chat get --json active_run.id)2belt interrupt list run_abc12334# Allow or deny a specific interrupt5belt interrupt resolve int_abc123 allow6belt interrupt resolve int_abc123 deny78# JSON output for scripting9belt interrupt list run_abc123 --json10belt interrupt resolve int_abc123 allow --json

With --json, list prints an array of InterruptDTO objects; resolve prints the updated interrupt. The table output shows interrupt ID, reason, source, and resource (resource ID).

→ Agents REST API — Interrupt gates · Approval settings · Human-in-the-loop

Skills (CLI)

Refs use namespace/name or namespace/name@version-id (same as the REST API). belt skill use without a version fetches the latest.

CommandPurpose
belt skill listList your uploaded skills (--json; table includes relative updated times)
belt skill upload <path>Publish or update a skill from a directory or SKILL.md (--name, --desc, --prune)
belt skill use <ref>Print a skill's instructions to stdout
belt skill add <ref>Install a skill into your workspace
belt skill files <ref>List supporting files (references, scripts, examples)
belt skill view <ref> <path>View one supporting file
belt skill versions <namespace>/<name>List version history (version ID, date, notes)
belt skill supersede <old> <new>Mark an outdated skill as superseded by a replacement
belt skill delete <ref>Soft-delete a skill (removed from search and suggest)
belt skill storeBrowse the public skill store (--featured, --category, --search, --page)
belt skill store search <query>Search approved store skills

belt skill list --json prints a page-based list envelope: items as SkillDTO[], plus total, page, pages, has_next, and has_previous. Pagination uses --page (20 per page). --search / -q filters by name or description. --save / -o writes the same shape to a file (pretty-printed). Entry fields match List Knowledge items. Use --results-only --select to project fields from items — keys are stable snake_case in release builds.

belt skill use does not inline supplementary files. When a skill has extra files, stderr prints tips for belt skill add and belt skill files.

A skill version holds only the files in that upload. When updating an existing skill, belt skill upload refuses to publish if the upload omits supplementary files the current version has (for example after uploading a lone SKILL.md over a skill with references/). The error lists the paths that would be dropped. Upload the full skill directory, or pass --prune to publish without the missing files after you remove or rename them locally. New skills are not checked.

belt know upload applies the same guard to every upload of type skill (directory, file, or stdin), with the same --prune flag.

belt skill versions requires belt login — it resolves the skill by name, then calls GET /knowledge/{id}/versions. The table marks the current version at the bottom.

belt skill supersede requires belt login. Both arguments are namespace/name refs or skill IDs. The old skill is hidden from belt suggest and other discovery results; the replacement skill is unchanged. Use this when you publish a replacement under a new name instead of versioning in place.

belt skill delete requires belt login. Pass a skill ID or namespace/name ref. The CLI resolves refs to an ID and soft-deletes the entry (same backend path as belt know delete). belt know delete accepts the same ref forms for any knowledge type. The skill is removed from search indexing and no longer appears in suggest results. Destructive: prompts for confirmation unless you pass --yes / -y. To keep a replacement discoverable instead of removing the old skill, use belt skill supersede.

bash
1belt skill versions myteam/code-review2belt skill use myteam/code-review@abc123   # specific version3belt skill supersede myteam/old-review myteam/code-review4belt skill delete myteam/deprecated-skill --yes

→ Skill registry · Delete Skill API · Supersede Skill API · Search concept · Search API · Knowledge API — versions

Knowledge (CLI)

belt know is an alias for belt knowledge. Refs use namespace/name or namespace/name@version-id (same as the REST API).

CommandPurpose
belt know listList entries (--type, --search, --json, pagination)
belt know search <query>Search your knowledge entries
belt know get <ref>Show metadata, file list, and instructions (up to 64KB)
belt know upload <path>Upload a directory, file, or stdin (--type, --name, --global, --prune for skills)
belt know delete <ref>Delete an entry (entry ID or namespace/name)

belt know list --json uses the same page-based envelope as belt skill list --json (items, total, page, pages, has_next, has_previous). Filter with --type and --search / -q; paginate with --page. --save / -o writes the same JSON to a file (pretty-printed).

bash
1belt know list --type observation2belt know get acme/onboarding              # latest version (no login for public entries)3belt know get acme/onboarding@abc123       # specific version4belt know upload ./my-skill/               # directory with SKILL.md → skill5belt know upload ./notes.md --type reference6belt know upload ./notes.md --global       # skip project scope tags

By default, belt know upload attaches project scope tags from the working directory (git remote, module name, language, path) so /suggest can rank the entry higher in matching projects. Pass --global to omit scope for workspace-wide references that should surface everywhere.

When belt know get finds supplementary files (references, scripts, examples), the CLI prints tips for belt skill add and belt skill files so you can install or inspect them locally.

belt know upload auto-collects scope signals from the current directory (git remote, module name, language) and stores them on the new version — see Knowledge — Project scope.

→ Knowledge API · Creating skills

Files (CLI)

belt file (alias belt files) lists, uploads, downloads, and deletes files in your workspace. Use it to save outputs from belt app run or belt mcp run without shelling out to curl.

CommandPurpose
belt file listList your files (ls alias; --json, --limit, --cursor, --type, --search / -q, -l / --long)
belt file download <url-or-id>Download to cwd or -o / --out path (-o - pipes to stdout)
belt file upload <path> [path...]Upload via presigned URL; returns permanent URI (--name for a single file only)
belt file info <id>Show metadata including media dimensions, duration, and codec
belt file delete <id>Delete a file (rm alias; --force / -f or --yes to skip confirmation)
bash
1belt file list --type image --limit 202belt file list --search photo3belt file list -q avatar --type image4belt file download https://files.inference.sh/abc/image.png5belt file download 01abc123def456 -o ./output.png6belt file upload ./photo.jpg --name avatar.jpg7belt file upload *.png8belt file info 01abc123def456 --json9belt file delete 01abc123def456 --force

--type filters by content category (image, video, audio, text) — the CLI matches MIME types like image/%. --search / -q filters by filename substring (case-insensitive). Combine both to narrow results (for example -q avatar --type image). In table output, the type column shows short labels (image, video, audio, or subtypes like json, pdf, docx, csv, text) instead of raw MIME strings. Downloads accept a file URL (for example from task output) or a file ID from belt file list. If the destination filename already exists, download auto-renames with (1), (2) suffixes. Uploads and downloads show live progress (speed, bytes transferred, ETA); batch uploads print a [1/N] counter per file.

belt app run auto-uploads local file paths and data: URIs in --input JSON for schema fields with format: file — use belt file upload when you need a URI outside a task run. See SDK files — automatic upload.

→ Files API · SDK Files

Forms (CLI)

List, inspect, fill, and review submissions for schema-driven forms in your workspace (and public forms from other teams). Requires belt login.

CommandPurpose
belt form listList your team's forms (ls alias; --all includes other teams' public forms; pagination + --json)
belt form get <namespace/name>Show title, status, submit policy, then each question in full (--json for the full FormDTO)
belt form fill <namespace/name> [key=value ...]Answer and submit (--data / --data-file for JSON; interactive prompts for remaining questions)
belt form submissions <namespace/name>List submissions for a form you own (--json, pagination)
bash
1belt form list2belt form get infsh/use_case3belt form fill acme/feedback rating=5 role=dev langs=go,ts4belt form fill acme/feedback --data '{"rating":5}' --no-input5belt form fill acme/feedback --data-file answers.json --json6belt form submissions acme/feedback --limit 50

Refs use namespace/name (your workspace username is the namespace). belt form fill only submits when the form status is open.

Inspect. belt form get prints one block per question (never truncated): key, widget type, required/optional, full prompt text, description, choice labels, validation limits (length, numeric bounds, and similar), examples, and only if visibility rules. Use this before scripting belt form fill so agents see the same constraints the web app enforces.

Answers. Pass key=value pairs, --data (inline JSON or @file), or --data-file. Later sources override earlier ones; repeat a key to collect multiple values for multiple-choice questions. Values are coerced by question type (numbers for ratings, yes/no for yes-no, option labels or list indices for choices). In a terminal, belt prompts for any shown question still missing an answer; optional questions accept Enter to skip. Conditional questions appear and disappear as earlier answers change visibility (same rules as the web app).

Automation. With --no-input or without a TTY, every required visible question must be supplied up front or the command fails. Local checks (missing required answers, bad coercion) and API 422 field errors are reported together in question order. On stderr, each problem is one key: message line; when belt knows the accepted shape, the line adds — expects … (question kind, choice labels, and limits such as length or numeric bounds). A muted footer points at belt form get <ref> for every question with full prompts and limits. With --json, stdout is a single object { "error", "form"?, "missing"?, "fields"? }; each entry in missing or fields includes key, optional title, message, and expects when applicable, so agents can fix every problem without reading the whole form first. Answers to hidden questions are dropped; non-empty answers to questions that are not shown produce a muted warning.

Claims. belt form fill records answers only — it does not print credit amounts or grant rewards. When a bounty program uses the form as proof (proof_type: form), claim credits with belt bounty claim <program> (see below). That command reuses an existing submission or fills the form first in one step.

→ Forms API · Bounties (CLI)

Bounties (CLI)

List active credit programs from GET /bounties and claim them with a proof (form submission or deployed app). Requires belt login. Responses are cached locally for 6 hours (~/.belt/cache/bounty-programs.json).

CommandPurpose
belt bounty listShow each program's name, reward, and how to claim (bounties alias; --json for machine output)
belt bounty claim <program> [key=value ...]Claim a form bounty — fills proof_form when you have not submitted yet (same --data / --data-file / prompts as belt form fill)
belt bounty claim <program> <namespace/app>Claim an app bounty (for example app-builder)
bash
1belt bounty list2belt bounty claim agent-survey3belt bounty claim agent-survey role=dev langs=go,ts --no-input4belt bounty claim agent-survey --data-file answers.json --json5belt bounty claim app-builder acme/my-app

belt bounty list --json returns {name, description, amount_microcents, reward, proof_type, proof_form?, command?, claimed?} per program. Form programs show claimed: true after a successful claim on this machine.

Form programs. Use belt form get <proof_form> to inspect every question before scripting. With --no-input or without a TTY, missing required answers exit 2 and print the same structured errors as belt form fill (expects hints and a belt form get footer). When a saved payment method is required, exit 9 includes billing_url and the exact belt bounty claim … command to rerun; your answers are kept.

App programs. The app must be deployed on inference.sh. belt bounty claim app-builder <namespace/app> is the same as belt app promo submit <namespace/app> (alias). Typical rules: useful app, not a duplicate of an existing listing, public after claim, daily limit per program — see belt app promo help.

belt bounty claim --json prints one object: status is claimed, payment_method_required, already_claimed, or failed; missing form answers use the fill object {error, form, missing, fields} on stdout.

→ Bounties API

App bounties (CLI)

The app-builder program pays workspace credits for useful apps you deploy and make public. Reward amounts and claim limits come from GET /bounties and can change without a CLI release; belt bounty list shows the current offer (belt app promo points there instead of naming an amount).

CommandPurpose
belt bounty listList active programs with reward and the command that claims each one (--json for machine-readable rows)
belt bounty claim app-builder <namespace/app>Claim the app-builder bounty for a deployed app you own
belt app promoShow app-builder rules, examples, and how to claim
belt app promo submit <namespace/app>Same as belt bounty claim app-builder <namespace/app>

App-builder rules (also shown by belt app promo):

  1. The app must be deployed (belt app deploy).
  2. The app must be useful and not duplicate something already on the platform.
  3. Python or JavaScript apps are fine — simple utilities are great candidates.
  4. Submitting agrees to make the app public so other agents can reuse it.
  5. One claim per day for this program (max_per_day on the API) — check belt bounty list for limits on other programs.
bash
1belt bounty list2belt skill use inference-sh/building-inferencesh-apps   # optional starter skill3belt app deploy4belt bounty claim app-builder myteam/my-utility5# same as: belt app promo submit myteam/my-utility

Claims call POST /me/bounty with bounty_id set to the program name and proof_id set to your app ID; the CLI accepts namespace/name and resolves the app. When the program sets requires_payment_method and the team has no saved payment method, the API returns 402 payment_method_required and records nothing; add a card under team billing payments and run the claim again.

Feedback (CLI)

Submit customer-discovery answers and open feedback. Responses are stored on your account via POST /me/survey (requires belt login).

CommandPurpose
belt feedbackList pending survey questions and bounty-linked forms (✓ when already submitted)
belt feedback <question-id> "<answer>"Answer a structured question
belt feedback "<message>"Send open feedback (question_id = open)

Structured question IDs: use_case, discovery, need, testimonial (legacy feedback is still accepted by the API). Which questions are active, and the exact wording and options, come from active bounty programs named survey-<id> and the matching platform form infsh/<id> (first question in the schema). When the API cannot be reached and nothing is cached, the CLI falls back to built-in wording for those four IDs.

bash
1belt feedback                                    # see what's pending2belt feedback discovery "found it on github"3belt feedback use_case "building AI agents for customer support"4belt feedback need "better template discovery"5belt feedback testimonial "fastest way to ship agents with real integrations"6belt feedback "love the speed, wish there were more templates"

Survey answers are stored with POST /me/survey. belt feedback does not claim credits; a survey-<id> bounty is claimed separately with POST /me/bounty (see Bounties). The API rejects placeholder template text and stores nothing.

Bounty forms. Programs with proof_type: form name the form in proof_form (for example infsh/marketplace_waitlist). With belt login, belt feedback lists those programs (excluding single-question survey-<id> entries). Each line shows the title, reward, and belt bounty claim <program>. Lines show ✓ when the program is already claimed on this machine. Use belt bounty list for the same programs in table or JSON form.

Before most commands, the CLI may show one due bounty or survey notice at the top of stderr (the notice block). Programs come from GET /bounties (cached 6 hours); the CLI walks them in notice_priority order and respects each program's notice_cooldown_hours. Survey programs (survey-<id>) use the same wording as belt feedback (from infsh/<id> when available). Form programs use notice_text or earn $X — <description>, ending with belt bounty claim <program>; they keep appearing until claimed on this machine. Single-question surveys use a 1-hour cooldown per question; when every active structured question is answered, an occasional freeform prompt (72-hour cooldown) suggests belt feedback "...". Notices are skipped for belt version, belt update, belt feedback, belt login, belt logout, belt bounty, and belt app promo so scripts and agents parsing those commands get clean output. A logged-out session does not spend survey notice cadence.

Survey prompts are not injected into belt suggest --json hook output — only the stderr notice path above. Submissions include optional metadata: agent (detected runtime, e.g. cursor) and context (your five most recent belt commands, comma-separated).

The CLI records the last 10 commands you run in ~/.belt/history (via the post-command tips hook; skipped for belt feedback itself and when tips are suppressed with INFSH_NO_TIPS=1, --json, --pjson, or --jq, or when stderr is not a terminal). When you submit feedback, the last five entries are sent as context.

Survey prompts and other stderr tips are silent when stderr is not a terminal or CI / BELT_NON_INTERACTIVE is set. Inside a coding agent, only UserKnowledge survey questions (worded for the operator to answer) and bounty forms (intended for the agent to complete with belt bounty claim <program> … --no-input) appear in the notice block; cash bounties and self-answerable surveys are hidden so the agent does not claim rewards on the operator's behalf. INFSH_NO_TIPS=1 disables them everywhere.

belt skill use keeps a copy of each resolved skill under ~/.belt/cache/skills/ and serves the newest cached copy, with a stderr note, when the API is unreachable.

→ Survey API · Forms API · Bounties (CLI) · Coding agents

Remotes (CLI)

Connect a machine to inference as a remote and optionally run commands on it. See Remotes for the security model and workflow.

CommandPurpose
belt remote startRegister this machine and connect the outbound daemon (--name, --exec, --agents, --detach)
belt remote enable / disableLinux (systemd): install or remove a user service that starts the remote at boot (enable accepts the same flags as start)
belt remote listList remotes registered to your team
belt remote tag <id> [tag...]Show, add, or remove policy tags on a remote (--rm to remove; needs manage_policy)
belt remote rm <id>...Remove remote rows from your account (--yes to skip confirm)
belt remote status / logs / stopInspect or stop the local daemon (foreground, detached, or service)
belt remote exec <remote-id> -- <command> [args...]Run a command on a connected remote and stream output (--cwd, --timeout, --pty)
bash
1belt login2belt remote start --name work-laptop --exec    # opt in to command execution3belt remote start --agents                     # opt in to harness agent sessions4belt remote exec remote_abc123 -- ls -la5belt remote exec remote_abc123 -- bash -c "cd /tmp && make test"

belt remote start requires login and the remotes:write scope. It creates a machine identity key in ~/.inferencesh/ (or BELT_CONFIG_DIR when set), probes for installed agent harnesses (from the agentprotocol registry), registers with POST /remotes, and keeps an authenticated WebSocket open to inference. Registration is scoped to your active team (belt auth switch); restart the daemon after switching teams. Press Ctrl-C to disconnect a foreground daemon.

Starting again with different --exec or --agents than the running daemon offers to restart it (agent sessions on that machine end). Decline or run without an interactive terminal and belt tells you to belt remote stop first. Flags belong on belt remote start, not bare belt remote. belt remote list shows account remotes; after seven days without a heartbeat it suggests belt remote rm for stale rows.

Exec opt-in. Command execution is off by default. Start the daemon with --exec so it accepts remote_exec_start frames. Without --exec, belt remote exec gets 403 exec_disabled from the API.

Agent sessions opt-in. Harness sessions are off by default. Start the daemon with --agents so it accepts remote_session_* frames and registers agents_enabled: true. Without --agents, the daemon refuses session opens with agent sessions are disabled on this remote (start it with --agents). This is separate from --exec. See Agent harness sessions.

belt remote exec streams stdout/stderr as output arrives and exits with the remote command's exit code. It is marked destructive (confirmation prompt, --yes in non-interactive mode). Automation should call POST /remotes/{id}/execs and poll GET /execs/{id}/output. See Remotes API.

→ Remotes guide · Remotes API

Tasks (CLI)

CommandPurpose
belt task listList recent tasks (--status, --limit, --cursor, --app)
belt task get <id>Get task status and output (-f / --follow to stream until complete)
belt task logs <id>View execution logs (--type to filter; -f to tail live output)
belt task cost <id>Show cost breakdown in microcents
belt task timings <id>Show timing breakdown by phase
belt task cancel <id>Cancel a running task
belt task files <id>List input and output files with IDs (--role input or output)
belt task delete-files <id>Delete a finished task's files and keep the task (--role to limit the side)
belt task delete <id>Delete a finished task (alias rm; --files also deletes its input and output files)

Cancel a running task before delete or delete-files. belt task files lists only files stored on inference.sh; external https:// URLs in input are not listed.

bash
1belt task files task_abc2belt task files task_abc --role output --json3belt task delete-files task_abc --yes          # destructive — confirm in scripts4belt task delete task_abc --files --yes        # remove task and its platform files

For HTTP equivalents, see Tasks API (list, logs, timings, telemetry, status, delete, and file endpoints). Resource telemetry is available via GET /tasks/:id/telemetry or client.tasks.getTelemetry(id) in the JavaScript SDK.


Automation contract

Belt exposes a machine-readable contract for agents, CI, and scripts. stdout is data; prompts, progress, and diagnostics go to stderr.

belt schema

Print the complete command tree, flags, and automation guarantees as JSON. The output describes the binary that is actually running, so it stays correct as commands change. Schema output is always indented JSON — --json is not required.

bash
1belt schema                              # full contract2belt schema app run                      # one subtree3belt schema --include-hidden             # hidden commands and flags4belt schema --include-inherited          # repeat global flags on every command node5belt schema | jq '.automation.exit_codes'   # exit-code table only

Top-level fields:

FieldPurpose
schema_versionBumped when the document shape changes in a breaking way
buildCLI version string
automation.output_formatstext, json, pjson
automation.exit_codesStable name → numeric code map (same table the process exits with)
automation.global_flagsPersistent flags available on every command
commandRecursive command tree (name, path, aliases, flags, subcommands, runnable, effect, …)

Each command node includes an effect field that tells agents what running it does:

effectMeaning
readonlySafe to call speculatively — only reads remote state
destructiveRemoves or halts something; prompts for confirmation and refuses unattended runs without --yes
unclassifiedNeither read-only nor destructive — treat as potentially mutating

Pass a command path to scope to one subtree. An invalid path exits 2 (usage).

Destructive commands

Commands that remove or halt remote resources prompt for confirmation before acting. Declining the prompt exits 130 (cancelled) without sending a request — the resource is unchanged. In non-interactive mode (--no-input, CI, or BELT_NON_INTERACTIVE=1), destructive commands refuse to run without --yes / -y and exit 2 (usage).

CommandAction
belt agent delete <ref>Delete an agent
belt chat stop <chat-id>Stop a running chat
belt file delete <id>Delete a file
belt flow cancel <run-id>Cancel a flow run
belt flow delete <id>Delete a flow
belt credentials disconnect <provider>Remove a workspace credential
belt know delete <ref>Delete a knowledge entry (ID or namespace/name)
belt skill delete <ref>Delete a skill (ID or namespace/name)
belt mcp disconnect <slug>Disconnect an MCP server
belt remote exec <remote-id> -- <command>Run a command on a connected remote
belt remote rm <id>...Remove remote registrations from your account
belt secrets delete <key>Delete a secret
belt task cancel <id>Cancel a task
belt task delete <id>Delete a finished task
belt task delete-files <id>Delete a finished task's files

Pass --yes / -y to skip the confirmation prompt. belt file delete also accepts --force / -f (predates --yes; same effect).

bash
1belt agent delete myteam/old-bot --yes2belt file delete 01abc123 --force           # equivalent to --yes3belt task cancel task_abc --yes             # confirm unattended cancel

--yes is separate from domain flags like belt task cancel --force, which means hard kill instead of graceful drain — it does not skip the confirmation prompt.

Exit codes

Belt exits with stable numeric codes so automation can branch without parsing stderr. Existing codes keep their meaning; unrecognized failures exit 1 (error).

CodeNameTypical cause
0okSuccess
1errorGeneric or unclassified failure
2usageInvalid command, arguments, or flags; or a destructive command run without --yes when input is disabled
4auth_requiredMissing, expired, or rejected credentials
5not_foundRequested resource does not exist
6permission_deniedAuthenticated but not allowed
7rate_limitedAPI quota or rate limit
8retryableTransient network or server failure
9paymentInsufficient balance or entitlement
10configRequired local setup is missing or invalid (for example belt app doctor --check findings)
130cancelledInterrupted (Ctrl-C), declined confirmation prompt, or context cancellation

Read the live table from belt schema | jq '.automation.exit_codes'. With --json or --pjson active, failures also write a JSON error object to stdout (see below).

Compact help for agents (BELT_HELP=agent)

When BELT_HELP=agent is set, belt help (or running belt with no subcommand) prints a compact automation summary instead of the full grouped command tree — output formats, discovery commands, common reads, and the exit-code table. Individual belt <command> --help output is unchanged.

bash
1BELT_HELP=agent belt help

Global automation flags

FlagPurpose
--no-inputNever prompt; fail instead (for CI and agents). Also implied by BELT_NON_INTERACTIVE=1 and CI.
--wrap-untrustedMark third-party content as untrusted for LLM consumers

With --wrap-untrusted, text belt did not author — MCP tool listings and results, skill instructions, knowledge entries — is framed with <<<EXTERNAL_UNTRUSTED_CONTENT>>> markers and a security notice so an LLM can distinguish data from operator instructions. JSON output uses in-place neutralization plus an externalContent provenance object so the payload stays parseable. Applies to commands that fetch external content (for example belt mcp tools, belt mcp run, belt skill use, belt know get).


JSON output (--json / --pjson)

Many commands accept --json (-j) for machine-readable output on stdout. As of CLI v1.15.13, --json writes compact single-line JSON so it pipes cleanly to jq and other tools. Use --pjson when you want indented JSON for human debugging in the terminal.

For belt app run and belt mcp run, --jq '<expr>' filters output inline (no external jq required) — see Filter output (--jq) above.

Both flags suppress contextual stderr tips (post-command tips only — hook tip: lines inside belt suggest --json output are unchanged). On belt mcp run, --jq suppresses those tips as well so piped or merged streams stay machine-readable. Tips are also skipped when stderr is not a terminal (for example when an agent captures stdout and stderr together). Commands that support --save (for example belt app store --save) always write pretty-printed JSON to the file, regardless of --json or --pjson.

Output projection (--results-only, --select)

Global flags that shrink JSON output for agents and scripts. Both require --json or --pjson — using them without a JSON mode is a usage error (exit 2).

FlagPurpose
--results-onlyUnwrap list envelopes — drop pagination fields (has_next, next_cursor, …) and emit only the payload array
--select <fields>Keep only comma-separated fields (dot paths allowed). Applied per element when the result is an array

Both are applied centrally on every JSON stdout path (including belt mcp run, which holds raw upstream payloads), so list commands support them without per-command opt-in.

bash
1belt know list --json --results-only --select id,name2belt app list --json --results-only --select id,namespace,name3belt skill store --json --results-only --select namespace,name4belt mcp run linear:list_issues --json --select id,title5belt mcp tools linear --json --select name,description6belt task list --json --select id,status

The payload array key is not always items — the skill store uses skills, MCP listings may use servers, and some APIs use results. --results-only unwraps whichever single non-envelope key holds an array. If the object has two payload arrays or a non-array sibling field, it is treated as a result object and left unchanged.

Key matching is normalized (has_next, HasNext, and hasNext are equivalent). --select meta.owner keeps nested fields using the leaf name (owner). Missing fields are omitted rather than emitted as null.

Note: --results-only drops pagination metadata. If you need next_cursor, has_next, or page, do not use --results-only. Page-based list commands (belt know list, belt skill list, belt skill store) expose has_next / has_previous and page / pages instead of cursor fields.

All list commands support JSON output: belt agent list, belt chat list, belt task list, belt flow list, belt flow runs, belt credentials list, belt keys list, belt vaults list, belt app list, belt app store, belt skill list, belt know list, belt file list, belt mcp list, belt secrets list, and others. Detail commands include belt me, belt balance, belt task get, belt chat send (POST /chats/{id}/messages user-message payload — does not wait for the agent to finish), belt app get, belt agent get, belt agent create, belt file info, belt file download, belt file upload, belt file delete, belt mcp tools, belt secrets get, belt vaults show, and belt agent run ({"chat_id": "..."} with --no-wait; full run response otherwise).

bash
1belt task list --json | jq '.[].id'          # compact — pipes cleanly2belt task get task_abc --pjson               # indented — easier to read3belt app store --json --save store.json      # stdout compact; file pretty-printed

On failure with --json or --pjson active, the CLI writes a JSON error object to stdout in addition to the human-readable message on stderr:

json
1{"error": "not logged in or session expired. run 'belt login' to authenticate"}

Parse stdout for automation; ignore stderr unless you need the formatted message. Successful JSON responses use the command-specific schema documented in each command's help (belt task get --help).

Billing and plan limits

Billing and plan-limit errors include upgrade URLs in the error message when the API returns entitlement metadata with upgrade_available: true under the top-level meta of the problem+json response.

Error kindTypical messageWhat to do
Insufficient balanceBilling URL in messageAdd credits at settings → workspace → billing
Plan limitSubscription URL in messageUpgrade at settings → workspace → subscription
Addon entitlementAdd-ons URL in messageSubscribe at settings → workspace → add-ons
Not logged inSession expiredRun belt login

When addon_plan_id is present in entitlement metadata, the CLI links to the add-ons page instead of the subscription page.

Inspect usage programmatically with GET /entitlements/usage — see Entitlements API.

→ Billing API · Subscription API · Troubleshooting — Plan limits


Environment Variables

VariableDescription
INFERENCE_API_KEYAPI key (overrides config file). Same variable the SDKs read
INFSH_API_KEYDeprecated fallback for INFERENCE_API_KEY; prints a warning. INFERENCE_API_KEY wins when both are set
BELT_NON_INTERACTIVEForce non-interactive mode (skip browser login, MCP OAuth polling, spinners, and stdin prompts). Also set automatically when CI is truthy.
BELT_HELPSet to agent for compact automation help instead of the full command tree
INFSH_NO_AUTOUPDATEDisable automatic CLI updates
INFSH_NO_TIPSDisable stderr tips and survey prompts after commands
INFSH_ENGINE_BINPath to a custom private engine binary
BELT_CONFIG_DIRRoot directory for all local belt state instead of the default split (~/.inferencesh for credentials, pending login, remote keys, logs; ~/.belt for cache, history, sessions, hooks). Point each agent or shell at its own directory so logins, skill cache, and remote identity never leak across accounts on one machine.
BELT_HTTP_TIMEOUTOverall timeout per API request (Go duration, default 60s). Streams and downloads have no deadline.
BELT_HTTP_RETRIESRetries of GET requests after transport errors or 5xx (default 2, 0 disables). Writes are never retried.
HTTPS_PROXY, HTTP_PROXY, NO_PROXYHonoured by the CLI, the installers and the npm package.
SSL_CERT_FILE, SSL_CERT_DIRExtra CA certificates for TLS-intercepting proxies.

Shell Completions

bash
1belt completion bash  # or zsh, fish, powershell

Next

→ Coding Agents — Build with AI assistants

→ Creating an App — Manual setup

we use cookies

we use cookies to ensure you get the best experience on our website. for more information on how we use cookies, please see our cookie policy.

by clicking "accept", you agree to our use of cookies.
learn more.