Documentation Index
Fetch the complete documentation index at: https://docs.useterse.ai/llms.txt
Use this file to discover all available pages before exploring further.
The terse CLI has one scaffold command and a shared workflow CLI.
terse init scaffolds a new TypeScript project. The rest of the CLI auto-detects a Terse TypeScript project from the current directory and loads workflows from the default entry file unless you pass --entry-file.
| Language | Project markers | Default entry file | Generated file |
|---|
| TypeScript | package.json, tsconfig.json | src/terse.jobs.ts (falls back to src/index.ts when loading an existing project) | src/terse.generated.ts |
The CLI scaffolds, generates helpers for, and runs TypeScript workflows on Node.js only. There is no Python project layout or Python SDK for workflows.
terse test and terse deploy discover every workflow registered with createJob() in the entry file’s module graph, including files reached by side-effect imports. Keep workflow name values unique.
Getting started
terse init [project-name]
Scaffolds a new TypeScript project and runs the full setup flow: install dependencies, authenticate, create a Terse project, review integrations, and generate helpers.
If you omit project-name, the CLI scaffolds into the current directory. If you run terse init in an existing npm project, the CLI tells you to use terse attach instead.
What it does in order:
- Creates the target directory and scaffolds
package.json, tsconfig.json, src/terse.jobs.ts, .env.example, and .gitignore
- Detects
pnpm or npm and installs dependencies
- Runs
terse login
- Creates a Terse project and writes
terse.config.json
- Reviews your current integrations
- Runs
terse generate
terse attach
Links an existing repo to Terse in self-hosted mode.
Use terse attach when you already have an application repo and want Terse to sync workflows to infrastructure you control instead of uploading source to Terse hosting.
What it does:
- Authenticates with Terse
- Creates a Terse project if the repo is not already linked
- Writes
terse.config.json with self-hosted mode enabled
- Reviews existing integrations and, in an interactive terminal, lets you connect more
- Runs
terse generate when the current directory matches a supported project layout
Before you run terse deploy, set remoteServerUrl in terse.config.json to the URL where your Terse SDK server is running.
{
"projectId": "proj_123",
"name": "my-app",
"selfHosted": true,
"remoteServerUrl": "https://your-app.example.com"
}
If your app keeps workflow definitions outside the default entry file, use --entry-file with terse test and terse deploy.
terse test [job-name]
Fetches sample events and runs a local workflow against one selected event.
terse test
terse test my-job
If you only have one workflow, the CLI selects it automatically. If you have more than one, the CLI prompts you to choose unless you pass job-name.
Options:
| Flag | Description | Default |
|---|
-v, --verbose | Show agent stream output during the run | Enabled |
--entry-file <path> | Override the default workflow entry file | Provider default |
How it works:
- Loads your local workflow registry from the entry file
- Fetches sample events from Terse for integration triggers such as GitHub, Slack, HeyReach, or Attio, and stored webhook payloads when your workflow has already received HTTP traffic
- Synthesizes local sample events for cron triggers (and other cases without stored samples)
- If you use a webhook trigger but no deliveries are stored yet, prints a
curl example against your webhook URL instead of opening an empty picker—run terse deploy first so the CLI can show the URL, send a test POST, then run terse test again
- Prompts you to choose one sample event
- Executes the workflow locally
terse test list [job-name]
Lists sample events for a workflow and assigns each one a content-addressed id.
terse test list
terse test list my-job --json
Use this command when you want a stable handle for a sample event without opening the interactive picker. The id stays the same as long as the underlying trigger payload stays the same. With --json, the output includes webhookEndpoints (URLs you can POST to) when your workflow has a webhook trigger—useful for scripting alongside stored samples.
Options:
| Flag | Description |
|---|
--json | Emit JSON, including the full serialized event for each id |
--entry-file <path> | Override the default workflow entry file |
The CLI caches fetched sample events locally for one hour. terse test show and terse test run --id reuse that cache when they can.
terse test show <id> [job-name]
Shows the contents of one sample event.
terse test show 3f4d1c8a9b12
terse test show 3f4d1c8a9b12 my-job --json
Options:
| Flag | Description |
|---|
--json | Emit JSON instead of the rendered text view |
--entry-file <path> | Override the default workflow entry file |
The CLI looks up the id in the local cache first, then refetches the workflow’s current sample events if needed.
terse test run [job-name]
Runs a workflow against an explicit sample event.
terse test run --id 3f4d1c8a9b12
terse test run my-job --event-file fixture.json
Pass exactly one of --id, --event, or --event-file.
Options:
| Flag | Description |
|---|
--id <id> | Run a cached or refetched sample event from terse test list |
--event <json> | Inline serialized trigger event JSON |
--event-file <path> | Path to a JSON file containing the serialized trigger event |
-v, --verbose | Show agent stream output during the run |
--entry-file <path> | Override the default workflow entry file |
terse deploy
Packages your local project and syncs workflows to Terse. Learn more about how deployment works.
terse deploy
terse deploy --entry-file src/server.ts
Options:
| Flag | Description |
|---|
--entry-file <path> | Override the default workflow entry file |
What happens:
- New workflows are created on the platform
- Existing workflows are updated
- Workflows removed from your code are removed from Terse
- For each workflow that includes a webhook trigger, the CLI prints a Webhook URL line after deploy so you can wire up external systems without opening the app
Deployment modes:
- Hosted: the CLI zips the current project directory, uploads it, and Terse hosts the workflow for you
- Self-hosted: the CLI reads
remoteServerUrl from terse.config.json and configures Terse to call your own server instead of uploading source
Build with workspace context
terse integrate
Interactive integration management from the terminal.
The interactive flow fetches your current integrations, shows provider-specific status, lets you connect, disconnect, or refresh one integration at a time, and reruns terse generate after any changes.
Connection types:
| Type | Flow | Integrations |
|---|
| OAuth | Opens a browser for authorization and waits for the connection to complete | GitHub, Slack, Gmail, Linear, Notion, WorkOS |
| Form | Prompts for credentials or account details in the terminal | Datadog, PostHog, Snowflake, LaunchDarkly, Attio |
terse integrate list
Lists integrations and their connection status.
terse integrate list
terse integrate list --status connected --json
Options:
| Flag | Description |
|---|
--status <status> | Filter to connected or disconnected |
--json | Emit machine-readable JSON |
terse integrate describe <type>
Shows the current status, installation type, required fields, and any setup URL for one integration type.
terse integrate describe snowflake
terse integrate describe slack --json
Use this command to inspect the schema before you build a connect command.
Options:
| Flag | Description |
|---|
--json | Emit machine-readable JSON |
terse integrate connect <type>
Connects or refreshes one integration without using the interactive picker.
terse integrate connect snowflake --field account=my-account --field username=alice --fields-stdin <<<'{"password":"..."}'
terse integrate connect slack
Options:
| Flag | Description |
|---|
--field <key=value> | Repeatable form field values |
--fields-stdin | Read a JSON object of additional field values from stdin |
-f, --force | Re-run the install even if the integration is already connected |
--json | Emit machine-readable JSON |
Use --fields-stdin for secrets so passwords and tokens do not end up in shell history.
For form-based integrations, connect submits the provided field values immediately. For OAuth integrations, connect opens the authorization URL in your default browser and exits 2 with a handoff payload ({ "handoff": "oauth", "url", "waitCommand" } in --json mode, or an ACTION REQUIRED line otherwise). Run the printed waitCommand (e.g. terse integrate wait slack) to block until authorization completes.
terse integrate disconnect <type>
Disconnects one integration.
terse integrate disconnect snowflake
terse integrate disconnect slack --json
Options:
| Flag | Description |
|---|
--json | Emit machine-readable JSON |
terse integrate wait <type>
Polls until an OAuth integration finishes connecting.
terse integrate connect slack
terse integrate wait slack --timeout 300
Use this after terse integrate connect <type> for OAuth integrations such as Slack or GitHub.
Options:
| Flag | Description |
|---|
--timeout <seconds> | Timeout in seconds. Defaults to 300 and caps at 900 |
--json | Emit machine-readable JSON |
The list, describe, connect, disconnect, and wait subcommands do not rerun code generation for you. After a connection change, run terse generate.
terse generate
Fetches your active integrations, their workspace resources, and the current tool definitions from Terse, then writes generated helpers for your local project.
terse generate
terse generate
Outputs:
terse generate writes src/terse.generated.ts.
What the generated file contains:
| Category | Examples |
|---|
| Resource constants | Repos.MyOrg.MyRepo, SlackChannel.Engineering, AttioList.Pipeline.NewDeals |
| Trigger builders | Triggers.github.onPROpened(), Triggers.slack.onMessage(), Triggers.heyReach.onMessageReplyReceived(), Triggers.schedule.cron(), Triggers.webhook.onRequest<BodyType>() |
| Skill constructors | Skills.github(), Skills.slack(), Skills.attio() |
| Deterministic tool wrappers | toolbox.slack.sendMessage() (no TerseAgent, not limited by skills), agent.tools.slack.sendMessage() (same tools, limited by skills) |
Each integration generates typed helpers from real workspace data such as repositories, Slack channels and members, lists, projects, and tool definitions, so your workflows reference actual resources instead of raw ids.
TypeScript projects also get Triggers.webhook from src/terse.generated.ts. Pass a type argument to Triggers.webhook.onRequest<YourBodyType>() so onTrigger and filter infer WebhookTrigger<YourBodyType> for event.body. See Webhook trigger.
Run terse generate again after you connect or disconnect an integration, after resources change inside a connected workspace, or after you upgrade terse-cli.
Observability
terse listen [job-name]
Streams live trigger events for a deployed workflow and executes the matching local workflow on your machine.
terse listen
terse listen my-workflow
What it does:
- Opens an authenticated SSE stream to the backend for your project/workflow
- Prints each forwarded event as it arrives
- Runs your current local workflow code against that serialized trigger
Requirements:
- You’re authenticated (
terse login) or TERSE_API_KEY is set
- The workflow is deployed in this project (
terse deploy)
terse.config.json exists with a valid projectId
Options:
| Flag | Description | Default |
|---|
-v, --verbose | Show agent stream output during the run | Enabled |
--entry-file <path> | Override the default workflow entry file | Provider default |
Common errors:
- 401/403 Not authenticated: run
terse login
- 404 Workflow not deployed: run
terse deploy first
Press Ctrl-C to stop listening.
terse replay [run-id]
Fetches the stored trigger event for one past run and re-executes the matching local workflow on your machine with verbose output.
The CLI resolves the local workflow by the deployed workflow name stored on that run, then executes your current local code against the saved trigger payload.
terse history [job-name]
Lists past runs for a deployed workflow or fetches the full chat history for a single run.
terse history my-job
terse history my-job --json --triggers
terse history --run-id run_abc123
When you pass job-name, the CLI matches your local workflow name to the deployed workflow on Terse. When you pass --run-id, the CLI skips workflow lookup and fetches that run directly.
Options:
| Flag | Description |
|---|
--json | Print JSON instead of a table |
--limit <n> | Max runs to return. Default 20, max 100 |
--page <n> | Page number, 1-indexed |
--status <list> | Comma-separated statuses: success, failed, cancelled, skipped, in_progress, awaiting_approval |
--since <iso> | Only runs at or after this ISO timestamp |
--until <iso> | Only runs at or before this ISO timestamp |
--query <q> | Free-text search across trigger, decision, and event fields |
--triggers | Also fetch the input trigger event JSON for each listed run |
--events | Also fetch the full model event stream for each listed run |
--run-id <id> | Show full chat events for one run instead of listing runs for a workflow |
Use --triggers when you want the serialized input event for each run. Use --events when you want the full stored conversation, including the trigger payload.
terse dashboard
Opens the Terse web app in your default browser.
If TERSE_FRONTEND_URL is set, the CLI opens that URL instead of production.
Authentication
terse login
Authenticates with Terse using a device authorization flow and saves your API key to a user-level config file.
How it works:
- Requests a device code from WorkOS
- Opens your browser to a verification page
- Polls until you complete the browser authorization
- Exchanges the token with the Terse backend for an API key
- Saves the API key so later CLI commands can reuse it
If a valid API key is already saved, the CLI asks whether you want to log in again with a different account.
terse logout
Removes the saved API key from your user config.
Help
terse docs
Opens the Terse documentation site in your default browser.
If TERSE_DOCS_URL is set, the CLI opens that URL instead of the public docs.
API key resolution
Commands that call the Terse API resolve credentials in this order:
TERSE_API_KEY in the current process environment
- The API key saved by
terse login
Those values are user API tokens (the same kind you create in the Terse app). Hosted executions use separate project-scoped tokens that Terse injects for you; you do not paste those into .env.
After terse init or terse login, you usually do not need a project .env file for the CLI because authentication is stored per user on your machine.
Your runtime still needs TERSE_API_KEY in its environment. Set that variable anywhere your Node process runs, such as a local .env file, a container secret, or your hosting provider’s secret store.