kusty CLInew
kusty is a command-line interface for the Kustomer API built for AI agents and scripts. Every command returns a stable JSON envelope and a numbered exit code. Install it, connect an org with an API key or OAuth, and make your first calls.
kusty is the Kustomer API as a command-line tool. It's designed to be driven by models as much as by people: output is JSON by default, every error has a stable code and a hint, exit codes tell you what went wrong without parsing anything, and nothing ever stops to wait for input. The same binary also runs as an MCP server, so Claude Code, Cursor, Codex and Gemini CLI can call every Kustomer endpoint as a tool.
Install
kusty is a single Go binary. To build it from source you need Go 1.26 and make. From a checkout of the kusty source, build the public profile and install it to ~/.local/bin:
make install BUILD_PROFILE=publicMake sure ~/.local/bin is on your PATH, then check the install:
kusty versionTurn on shell completion with kusty completions zsh (or bash, fish, powershell), for example kusty completions zsh > "${fpath[1]}/_kusty".
Connect an org
kusty targets orgs through profiles. A profile names an org and says how to authenticate to it. You can set one up interactively with kusty config, or with flags.
The quickest way to start. Create an API key in Settings › Security › API Keys with the roles you need, then:
kusty profile add prod --org-subdomain acme --default
export KUSTY_API_KEY=eyJhbGciOi...
kusty auth whoami--org-subdomain acme points at https://acme.api.kustomerapp.com. You can pass your dashboard URL instead with --tenant-url https://acme.kustomerapp.com.
With OAuth, everyone authenticates as themselves, with their own permissions, instead of sharing a key. An admin sets it up once per org, then each user logs in.
Admin, once: with an API key that has the org.admin.apps role, register kusty's private app and mint its client secret:
export KUSTY_API_KEY=eyJhbGciOi... # key with org.admin.apps
kusty app register # registers and installs the private "Kusty CLI" app
kusty app secret # prints the OAuth client ID and secret
kusty app status # confirms it's installedShare the tenant URL, client ID, client secret and scopes with your team. kusty config shows them as a copy-paste bundle.
Each user: run kusty config, create a profile with your tenant URL and the OAuth credentials, then:
kusty auth login # opens your browser to sign in
kusty auth whoamikusty config is a terminal UI that walks through the same steps. It's the only interactive part of kusty; scripts and agents use the profile, app and auth commands instead.
Sessions and refresh
kusty auth login runs the OAuth authorization-code flow through a local callback on port 53682, 53683 or 53684. The resulting tokens are stored per profile in a file only you can read, in sessions/ next to the config file.
Access tokens last 24 hours. kusty renews them from the refresh token automatically, including inside a long-running MCP server, so you shouldn't need to log in again for weeks.
By default kusty requests the scopes org.permission.customer, org.permission.conversation, org.permission.message, org.permission.user and offline_access (needed for refresh). Set oauth_scopes on a profile to request others.
Profiles and the config file
Profiles live in ~/.config/kusty/config.toml, or wherever KUSTY_CONFIG points.
default_profile = "prod"
[profiles.prod]
org_subdomain = "acme"
[profiles.sandbox]
org_subdomain = "acme-sandbox"
api_key_env = "SANDBOX_KUSTY_API_KEY"api_key_envmakes a profile read its key from a different variable, so you can keep a key per org in your shell profile.- Settings resolve in this order: command-line flag, then
KUSTY_*environment variable, then profile, then built-in default. - For credentials, an API key wins over an OAuth session. An admin profile that holds the OAuth client credentials for sharing still uses its own key.
kusty profile use sandbox # change the default
kusty --profile sandbox api GET /v1/users/current # one command
KUSTY_PROFILE=sandbox kusty auth whoami # one shell
kusty context show # which profile, org and credential would be used, offlinekusty context show doesn't touch the network: it reports the profile, org, base URL, credential_source (env:KUSTY_API_KEY, oauth, flag, …) and whether a credential is present. Put it at the top of scripts. kusty auth whoami proves the credential works by calling /v1/users/current.
Make requests
There are two ways to call the API.
Named commands cover every operation in the API reference, one per operation, with path and query parameters as flags:
kusty customers get-by-email --email sam@example.com
kusty conversations list --pageSize 10 --output table
kusty evaluations runevaluation --evaluationId 6716c1d2c4e5f60012ab36eaRun kusty <resource> --help to list a resource's subcommands, or see the command reference.
kusty api sends any request to any path, which is handy for endpoints you already know from the API reference:
kusty api GET /v1/customers --limit 5
kusty api PATCH /v1/customers/6716a3a9c4e5f60012ab34c1 --field name="Sam Rivera"
kusty api POST /v1/customers --data @customer.json
echo '{"name":"Sam Rivera"}' | kusty api POST /v1/customers --data @-
kusty api GET /v1/customers --all | jq -r '.data.attributes.name'--data wins if you pass both --data and --field. Field paths use dots, with emails.0.email for array items and -1 to append (tags.-1=vip).
The output contract
Every command writes exactly one thing to stdout: a success envelope, a stream of JSON lines, or an error envelope.
{
"schema_version": "1",
"data": { "type": "customer", "id": "6716a3a9c4e5f60012ab34c1", "attributes": { "name": "Sam Rivera" } },
"meta": { "request_id": "9f2c41d8…", "duration_ms": 142, "next_cursor": "…" },
"warnings": []
}{
"schema_version": "1",
"error": {
"code": "AUTH_REQUIRED",
"message": "no API key configured",
"hint": "set SANDBOX_KUSTY_API_KEY or pass --api-key",
"retryable": false
}
}datais the JSON:APIdatafrom Kustomer's response, so.data.attributes.nameworks the same as with the raw API.- The default format is
jsonl, one envelope per line, whether you're in a terminal, a pipe, or an agent. Use--output jsonfor pretty JSON,yaml, ortableto read results yourself. --fields id,attributes.namekeeps only the paths you list, which saves tokens when an agent needs one value.- Error envelopes come with a non-zero exit code. The
hintnames the specific fix, such as the environment variable the active profile expects. - Human-readable diagnostics go to stderr; structured logs go to a JSON-lines file at
$XDG_STATE_HOME/kusty/kusty.log. Every request'srequest_idappears in both the log andmeta.
Safety rails
Release notes
kusty whats-new prints kusty's release notes, newest first, with no login needed. Narrow them with a topic, --product cli|mcp, --since 2026-10-01 or --limit 5.