CLI
@naturali/cli gives every API operation a command, generated from the
OpenAPI specs. An endpoint that exists in the contract is
reachable from the shell; one that doesn't, isn't.
pnpm add -g @naturali/cli
export NATURALI_TOKEN=nat_sk_...
naturali list-agents --project-id proj_V1StGXR8Z5jdHi6B
The CLI always talks to https://api.naturali.ai — the only naturali.ai API
origin, and not configurable.
Configuration
Credentials and the default project both come from the environment:
| Variable | Purpose |
|---|---|
NATURALI_TOKEN | Bearer credential — a nat_sk_… API key or a session JWT. Required. |
NATURALI_PROJECT | Default --project-id for any command that takes one, so you don't have to repeat it. An explicit --project-id still wins. |
export NATURALI_TOKEN=nat_sk_...
export NATURALI_PROJECT=proj_V1StGXR8Z5jdHi6B
naturali list-agents
Discovering commands
A command is its operationId in kebab-case — createAgent → create-agent.
naturali list-commands # every command, with its description
naturali create-agent --help # a command's flags, grouped, plus its docs link
--help lists each flag with its type, whether it is required, and whether the
value lands in the URL path, the query string, or the request body — read
straight from the spec.
Flags
Flag names are the spec's parameter names. Kebab-case is the convention used
throughout these docs, and snake_case and camelCase are accepted too:
--project-id, --project_id and --projectId are one flag.
naturali list-agents --project-id proj_V1StGXR8Z5jdHi6B --limit 10
naturali create-agent \
--project-id proj_V1StGXR8Z5jdHi6B \
--provider-id aip_V1StGXR8Z5jdHi6B \
--name support-triage \
--tool-bindings '[{"tool_id":"tool_V1StGXR8Z5jdHi6B"}]'
| Value kind | How to pass it |
|---|---|
| String | --name support-triage |
| Number | --temperature 0.7 |
| Boolean | --force — a bare flag means true |
| Object | --tool-choice '{"type":"tool","name":"get_weather"}' |
| Array | repeat the flag (--capabilities a --capabilities b) or pass JSON (--tool-bindings '[{"tool_id":"tool_a"}]') |
An array-typed field always reaches the API as a list, even with a single value.
Every path parameter a command needs must be passed explicitly — a command that leaves one out stops and names it rather than guessing:
$ naturali get-agent --agent-id agent_V1StGXR8Z5jdHi6B
Missing required path parameter(s): project_id.
Provide with --project-id <value>.
Output
The response body goes to stdout as formatted JSON, ready for jq:
naturali list-agents --project-id proj_V1StGXR8Z5jdHi6B | jq -r '.data[].id'
A non-2xx response prints the error envelope
to stderr and exits 1, so a failing command fails a shell script:
{
"status": 403,
"error": { "code": "access_denied", "message": "Not your project" }
}
Scripting example
Create an agent, open a session, and ask it something:
set -euo pipefail
export NATURALI_TOKEN=nat_sk_...
PROJECT_ID=proj_V1StGXR8Z5jdHi6B
AGENT_ID=$(naturali create-agent \
--project-id "$PROJECT_ID" \
--provider-id aip_V1StGXR8Z5jdHi6B \
--name support-triage | jq -r '.id')
SESSION_ID=$(naturali create-session \
--project-id "$PROJECT_ID" \
--agent-id "$AGENT_ID" | jq -r '.id')
naturali add-session-message \
--project-id "$PROJECT_ID" \
--agent-id "$AGENT_ID" \
--session-id "$SESSION_ID" \
--message 'What is our refund window?'
Versioning
@naturali/cli shares its version with the API and with
@naturali/sdk — one number, released together. The CLI is generated
from the API's OpenAPI specs and bundles the SDK at build time, so
@naturali/cli@0.28.0 speaks to API 0.28.0 and pulls in no separate client at
runtime.
A consequence worth knowing: a bump can mean the API gained a command, not that
anything in the CLI's own behaviour changed. naturali --help is generated from
the specs the release was built from, so it is always the authority on what the
version you installed can do.