Perspective CLI
perspective is a command-line tool for the Perspective REST API. It is designed to be driven equally well by people, shell scripts, CI jobs and coding agents such as Claude Code:
- JSON in, JSON out. Every command prints JSON on stdout — pretty-printed at a terminal, compact when piped — so it composes with
jq. - Machine-readable errors. Errors are printed as JSON on stderr, with a stable exit code.
- No HTTP client to write. Workspaces, funnels, contacts and metrics are one command each, with pagination and date windows handled for you.
- Agent-ready. Ships an Agent Skill (
SKILL.md) that teaches coding agents how to use it safely.
Install
Requires Node.js 22.12 or newer.
npm install -g @perspective-software/cli # or: pnpm add -g @perspective-software/cli
perspective --version
To try it without installing, use npx @perspective-software/cli <command>.
Quick start
# 1. Store your API key (prompts for it)
perspective auth login
# 2. Find a funnel ID
perspective funnels list --status live
# 3. Use it
perspective contacts list <funnelId> --limit 10
perspective metrics kpi <funnelId> conversion_rate --days 30
See Authentication for API keys and CI, and Commands for the full reference.
Output
Every command that reads or writes API data prints a { "data": … } or { "data": …, "meta": … } envelope. A command that maps to one API request prints the response unchanged (Pagination). A few commands combine or reshape responses, and keep the same envelope:
| Command | Output |
|---|---|
funnels list | data is a flat list of funnels: id, name, status, workspaceId, workspaceName. |
contacts list --all | data holds the contacts from every page; meta is { "total": n }. |
metrics kpi with several KPIs or all | data is a list with one KPI result per item, in the order given, each with its type. |
auth and skill commands print their own small objects, such as { "ok": true, … }; see the Command Reference.
perspective funnels list | jq -r '.data[] | "\(.id)\t\(.name)"'
Use --pretty or --no-pretty to override the terminal detection.
To keep output small, --fields keeps only the fields you list. It works on every command, keeps meta, and dotted paths reach into nested objects. A missing field comes back as null, so every item has the same keys:
perspective contacts list <funnelId> --all --fields id,email,status,properties.city
Rate limits
The API allows 100 requests per minute. When the CLI gets HTTP 429, it waits as long as the Retry-After header says (at most a minute) and retries, up to 3 times, before it fails with exit code 6.
Errors
Errors go to stderr in one shape:
{
"error": {
"code": "forbidden",
"message": "User has no access to campaign",
"status": 403,
"hint": "Either the funnel ID is wrong or not in a workspace this key can access (check `perspective funnels list`), or the key lacks the permission this endpoint needs (crm:read, crm:write, metrics:read)."
}
}
status is the HTTP status when the error came from the API.
Exit codes
| Code | error.code | Meaning |
|---|---|---|
0 | — | Success |
1 | error | Any other API error, or an unreadable credentials file |
2 | usage | Invalid flags or arguments, or HTTP 400 |
3 | unauthenticated | Missing or invalid API key (HTTP 401) |
4 | forbidden | The funnel ID is wrong or outside your workspaces, or the API key lacks the permission (HTTP 403) |
5 | not_found | Unknown funnel or contact (HTTP 404) |
6 | rate_limited | Rate limit hit (HTTP 429) — see Errors |
7 | network | The API could not be reached or did not answer within 30 seconds |
Exit codes are stable across releases, so scripts can branch on them.
Global options
| Option | Environment variable | Description |
|---|---|---|
--api-key <key> | PERSPECTIVE_API_KEY | API key |
--fields <list> | — | Only these fields of each result, comma-separated; dots nest |
--base-url <url> | PERSPECTIVE_BASE_URL | API origin, default https://api.perspective.co |
--pretty / --no-pretty | — | Force indented / compact JSON |
| — | PERSPECTIVE_CONFIG_DIR | Where the API key is stored, default ~/.config/perspective |