@formo/cli
v1.0.0
Published
Formo API CLI — query profiles and analytics data
Readme
@formo/cli
Command-line interface for the Formo API. Manage wallet profiles, alerts, dashboards, charts, contracts, segments, and run analytics SQL — directly from your terminal or via AI agents.
Installation
npm install -g @formo/cli
# or use without installing:
npx @formo/cliAuthentication
Save your API key locally:
formo login <apiKey>Or set the FORMO_API_KEY environment variable — it takes precedence over the saved config:
export FORMO_API_KEY=formo_abc123Get your API key from Settings → API Keys in the Formo dashboard.
Auth commands
formo login [apiKey]
Save your API key to ~/.config/formo/config.json. Validates the key against the API and stores the workspace context.
formo login formo_abc123formo logout
Remove the saved API key and clear authentication state.
formo logoutformo status
Show current authentication state, workspace, and project ID.
formo statusformo profiles
Wallet profile commands.
profiles get <address>
Fetch a single wallet profile by address or ENS name.
| Option | Description |
|---|---|
| --expand | Comma-separated fields: apps, chains, tokens, labels |
formo profiles get 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
formo profiles get vitalik.eth --expand labels,chainsprofiles search
Search wallet profiles with filters, sorting, and pagination. Returns a PaginatedResponse<Profile>.
| Option | Description |
|---|---|
| --address | Filter by wallet address |
| --page | Page number (1-indexed, default 1) |
| --size | Page size (default 100, max 1000) |
| --order-by | last_onchain, first_onchain, net_worth_usd, updated_at, tx_count, first_seen, last_seen, num_sessions, revenue, volume, points |
| --order-dir | asc or desc |
| --expand | Comma-separated fields to expand |
| --conditions | JSON array of FilterCondition objects (see below) |
| --logic | Combine conditions with and (default) or or |
formo profiles search --size 10
formo profiles search --order-by net_worth_usd --order-dir desc --size 5
formo profiles search --page 2 --size 20
formo profiles search --conditions '[{"field":"users.net_worth_usd","op":"gt","value":10000}]' --size 20
formo profiles search --conditions '[{"field":"users.net_worth_usd","op":"gt","value":10000},{"field":"users.volume","op":"gt","value":1000}]' --logic or --size 20
formo profiles search --conditions '[{"field":"chains.1.balance","op":"gt","value":1000}]' --size 20profiles update <address>
Merge-update identity properties on a wallet profile.
| Option | Description |
|---|---|
| --properties | JSON object of properties to merge |
Allowed property keys: user_id, display_name, email, farcaster, discord, twitter, telegram, instagram, website, github, linkedin, facebook, tiktok, youtube, reddit, avatar, description, location, ens, lens, basenames, linea. Unknown keys are rejected server-side.
formo profiles update 0xd8dA... --properties '{"display_name":"Vitalik","twitter":"VitalikButerin"}'
formo profiles update vitalik.eth --properties '{"email":"[email protected]"}'Requires
profiles:writescope.
profiles labels create <address>
Upsert one or more labels on a wallet profile. Provide either a single label via --tag-id or a batch via --labels.
| Option | Description |
|---|---|
| --tag-id | Label identifier (e.g. vip, airdrop_eligible) |
| --value | Optional label value (e.g. tier name, country code) |
| --chain-id | Optional chain identifier the label applies to |
| --labels | JSON array of UserLabelInput objects for batch upsert |
formo profiles labels create 0xd8dA... --tag-id vip
formo profiles labels create 0xd8dA... --tag-id tier --value gold --chain-id 1
formo profiles labels create 0xd8dA... --labels '[{"tag_id":"vip"},{"tag_id":"airdrop_eligible","chain_id":"1"}]'profiles labels delete <address>
Delete a label from a wallet profile.
| Option | Description |
|---|---|
| --tag-id | Label identifier to delete (required) |
| --chain-id | Optional chain identifier to scope the deletion |
formo profiles labels delete 0xd8dA... --tag-id vip
formo profiles labels delete 0xd8dA... --tag-id tier --chain-id 1Requires
profiles:writescope.
formo alerts
Project alert commands. Requires alerts:read (list/get) or alerts:write (create/update/delete/toggle).
alerts list
List all alerts for the project.
alerts get <alertId>
Get a single alert by ID.
alerts create
| Option | Description |
|---|---|
| --name | Alert name |
| --trigger-type | Trigger type (e.g. event, threshold) |
| --trigger-filters | JSON array of trigger filter objects |
| --recipient | JSON array of recipient objects |
| --secret | Webhook secret |
formo alerts create --name "High value tx" --trigger-type event \
--trigger-filters '[{"name":"event","operator":"equals","value":"transaction"}]' \
--recipient '[{"type":"email","value":["[email protected]"]}]'alerts update <alertId>
Same options as create. Replaces the alert configuration.
alerts delete <alertId>
Delete an alert.
alerts toggle <alertId> --status <active|paused>
Toggle an alert between active and paused.
formo alerts toggle alert_abc123 --status pausedformo boards
Dashboard board commands. Requires boards:read / boards:write.
boards list
List all boards for the project.
boards get <boardId>
Get a single board by ID.
boards create
| Option | Description |
|---|---|
| --name | Board name |
| --description | Optional board description |
formo boards create --name "Revenue Metrics" --description "Weekly revenue tracking"boards update <boardId>
| Option | Description |
|---|---|
| --name | New board name |
| --description | New board description |
boards delete <boardId>
Delete a board.
formo charts
Chart commands. Charts live inside a board. Requires charts:read / charts:write.
charts list --board-id <boardId>
List all charts in a board.
charts get <chartId> --board-id <boardId>
Get a single chart by ID.
charts create --board-id <boardId> --body '<json>'
Create a chart from a JSON config string.
charts update <chartId> --board-id <boardId> --body '<json>'
Update a chart.
charts delete <chartId> --board-id <boardId>
Delete a chart.
formo contracts
Smart contract commands. Requires contracts:read / contracts:write.
contracts list
List all tracked contracts. Returns { data: Contract[], deploy: { last_deployed_at, diff }, total, page, size, has_more }.
contracts create
| Option | Description |
|---|---|
| --address | Contract address (0x…) |
| --chain | Chain ID (e.g. 1, 137) |
| --name | Human-readable contract name |
| --abi | Contract ABI as a JSON string |
| --events | Events configuration as a JSON string |
formo contracts create --address 0x1f9840a85d5af5bf1d1762f925bdaddc4201f984 --chain 1 \
--name "UNI Token" --abi '[{"type":"event","name":"Transfer","inputs":[]}]' \
--events '{"Transfer":true}'contracts update <chain> <address>
| Option | Description |
|---|---|
| --name | Updated contract name |
| --abi | Updated ABI |
| --events | Updated events config |
contracts delete <chain> <address>
Remove a tracked contract.
formo segments
User segment commands. Requires segments:read / segments:write.
segments list
List all user segments.
segments create
| Option | Description |
|---|---|
| --title | Segment title |
| --filter-sets | JSON array of filter set strings defining the segment |
segments delete <segmentId>
Delete a user segment.
formo query
query run "<sql>"
Run a SQL query against your Formo analytics data. Returns { data, total, limit, offset, has_more }.
formo query run "SELECT count(*) FROM events"
formo query run "SELECT address, net_worth_usd FROM wallet_profiles ORDER BY net_worth_usd DESC LIMIT 10"Requires
query:readscope.
formo analytics
Pre-built analytics pipes — the same data that powers the Formo dashboard — without writing SQL. Each pipe is a subcommand: formo analytics <pipe>.
Pipes: kpis, event_timeseries, funnel, flow, frequency, lifecycle, retention, revenue_overview, revenue_by_metric, revenue_timeseries, volume_by_metric, top_chains, top_events, top_locations, top_pages, top_sources, top_wallets
| Option | Description |
|---|---|
| --date-from | Inclusive start date YYYY-MM-DD (default: 7 days before --date-to) |
| --date-to | Inclusive end date YYYY-MM-DD (default: today) |
| --filters | JSON array of [{field,op,value}]. Use in/notIn with a pipe-delimited value (e.g. "chrome\|firefox") |
| --params | JSON object of pipe-specific params merged into the query (e.g. {"limit":10,"group_by":"device"}) |
formo analytics kpis
formo analytics kpis --date-from 2026-04-01 --date-to 2026-04-30 --params '{"group_by":"device"}'
formo analytics funnel --date-from 2026-04-01 --date-to 2026-04-30 --params '{"steps":[{"type":"event","event":"page","name":"page::0","filters":[]},{"type":"track","event":"connect","name":"connect::1","filters":[]}],"window_seconds":86400}'
formo analytics top_wallets --date-from 2026-04-01 --date-to 2026-04-30 --params '{"limit":10}'
formo analytics retention --filters '[{"field":"location","op":"equals","value":"US"}]'Requires
query:readscope. Runformo analytics <pipe> --helpfor the pipe-specific params accepted via--params.
formo import
import wallets
Bulk-import wallet addresses into the project via the events API.
| Option | Description |
|---|---|
| --addresses | JSON array of wallet address strings |
| --write-key | Project write SDK key |
formo import wallets --addresses '["0xabc...","0xdef..."]' --write-key write_key_xyzFilterCondition reference
profiles search --conditions accepts a JSON array of filter condition objects:
[
{ "field": "users.net_worth_usd", "op": "gt", "value": 10000 },
{ "field": "chains.1.balance", "op": "gte", "value": 1000 }
]The
fieldmust be a typed path. A bare name likenet_worth_usdis silently ignored by the API (no error, no filtering — the search returns everything). Always prefix the field with its type.
| Field | Type | Description |
|---|---|---|
| field | string | Typed path (see prefixes below) |
| op | string | eq, neq, gt, gte, lt, lte, in, nin |
| value | any | Value to compare against |
| scope | string | (token filters only) any or protocol |
| appId | string | (token filters with scope: protocol) e.g. aave-v3 |
| Prefix | Examples |
|---|---|
| users. | users.net_worth_usd, users.volume, users.revenue, users.points, users.device, users.location, users.lifecycle, users.ens, users.farcaster |
| chains. | chains.balance (any chain), chains.1.balance (Ethereum) |
| apps. | apps.uniswap-v3.balance |
| tokens. | tokens.0xA0b8…48.balance |
| labels. | labels.coinbase.verified_account |
Combine multiple conditions with --logic and (default) or --logic or.
Output formats
Every command supports the standard incur output flags:
| Flag | Description |
|---|---|
| --format <toon\|json\|yaml\|md\|jsonl> | Output format (default: toon) |
| --json | Shorthand for --format json |
| --verbose | Include the full envelope (ok, data, meta) |
| --filter-output <keys> | Filter output by key paths (e.g. data,meta.duration) |
Every list endpoint returns a PaginatedResponse<T> envelope: { data: [...], total, page, size, has_more }. Every error follows: { error: { code, message, doc_url, param?, details? } } — branch on error.code, not message.
Development
# Install dependencies
pnpm install
# Run in development mode
pnpm dev
# Build TypeScript
pnpm build
# Lint
pnpm lint
# Run tests (requires TEST_TOKEN in .env)
pnpm test