SalesNexus CLI — manage your CRM from the command line
Project description
SalesNexus CLI (snx)
A command-line interface for the SalesNexus CRM. Designed for humans and AI agents — output auto-switches to JSON when piped.
Version: 0.1.1 | API: SalesNexus Public API v1 | Python: ≥ 3.10
Table of Contents
- Installation
- Quick Start
- Environments
- Authentication & Configuration
- Global Options
- Output Formats
- Command Reference
- Pagination
- Custom Fields
- Batch Operations
- AI Agent Integration Guide
- Error Handling & Exit Codes
- Rate Limits
- Environment Variables
- Shell Completions
- Development
- Troubleshooting
- License
Installation
From source (recommended for now)
cd salesnexus-cli
pip install -e .
With dev dependencies (for testing)
pip install -e ".[dev]"
Verify installation
snx --version
# snx 0.1.1
Note: If
snxis not on your PATH after install, add the Python Scripts directory:
- Windows:
%LOCALAPPDATA%\...\Python3XX\Scripts- macOS/Linux:
~/.local/bin
Quick Start
# 1. Save your API key
snx auth login --api-key sn_live_AbCdEfGh.01234567890123456789012345678901
# 2. Verify connection
snx ping
# ✓ Connected as john@acme.com (account 42)
# 3. List your contacts
snx contacts list
# 3b. List recent call transcripts
snx transcripts list --page-size 5
# 4. Create a contact
snx contacts create --first-name "Jane" --last-name "Doe" --email "jane@acme.com" --company "Acme Corp"
# 5. Get JSON output (for scripting or AI agents)
snx contacts list --json
Environments
SalesNexus provides two public API environments:
| Environment | Base URL | Description |
|---|---|---|
| Production | https://api.salesnex.us |
Live data. This is the default. |
| Beta | https://api-beta.salesnex.us |
Pre-release features. Data may be reset. |
The CLI defaults to production. To use beta, pass --base-url when logging in:
# Production (default — no --base-url needed)
snx auth login --api-key sn_live_...
# Beta
snx auth login --api-key sn_live_... --profile beta --base-url https://api-beta.salesnex.us
You can maintain profiles for both environments simultaneously:
snx auth switch default # → production
snx auth switch beta # → beta
Important: API keys are environment-specific. A production key will not work on beta, and vice versa.
Authentication & Configuration
API Key
You need a SalesNexus API key to use the CLI. API keys have the format:
sn_live_XXXXXXXX.YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
├─────┘ └──────────────────────────────┘
prefix secret (32 chars)
Generate API keys from the SalesNexus web app under Settings → API Keys, or ask your account administrator.
Profiles
The CLI supports named profiles (like AWS CLI), stored in ~/.salesnexus/config.toml:
active_profile = "default"
[profiles.default]
api_key = "sn_live_AbCdEfGh.01234567890123456789012345678901"
base_url = "https://api.salesnex.us"
[profiles.beta]
api_key = "sn_live_XyZwVuTs.98765432109876543210987654321098"
base_url = "https://api-beta.salesnex.us"
Credential Precedence
The CLI resolves credentials in this order (highest priority first):
| Priority | Source | Example |
|---|---|---|
| 1 (highest) | CLI flags | --api-key sn_live_... |
| 2 | Environment variables | SALESNEXUS_API_KEY=sn_live_... |
| 3 (lowest) | Config file profile | ~/.salesnexus/config.toml |
The profile is selected by: --profile flag → SALESNEXUS_PROFILE env var → active_profile in config.
Managing Profiles
# Save a profile
snx auth login --api-key sn_live_AbCdEfGh.01234567890123456789012345678901
# Save a profile for the beta environment
snx auth login --api-key sn_live_XyZw... --profile beta --base-url https://api-beta.salesnex.us
# List all profiles
snx auth list
# Switch active profile
snx auth switch staging
# Check current status
snx auth status
# Remove a profile
snx auth logout --profile staging
Global Options
These flags are available on every command:
| Flag | Short | Description |
|---|---|---|
--json |
Force JSON output. Auto-enabled when stdout is not a TTY (piped). | |
--csv |
Force CSV output. | |
--profile NAME |
-p |
Use a specific config profile for this command. |
--api-key KEY |
Override API key for this command (hidden from help). | |
--base-url URL |
Override base URL for this command (hidden from help). | |
--version |
-v |
Print version and exit. |
--install-completion |
Install shell completion for your current shell. | |
--help |
Show help for any command. |
Examples
# Use a different profile for one command
snx --profile staging contacts list
# Force JSON output
snx --json contacts get 123
# CSV output piped to a file
snx --csv contacts list --all > contacts.csv
# One-off API key (e.g. in CI)
snx --api-key sn_live_... ping
Transcripts
Read call transcripts created automatically by integrated calling providers.
List transcripts
snx transcripts list
snx transcripts list --contact-id 123
snx transcripts list --provider RingCentral --status completed
snx transcripts list --search "pricing objection" --all --json
Supported filters:
--page--page-size--contact-id--user-id--provider--status--from--to--search--all
Get one transcript
snx transcripts get 987
snx --json transcripts get 987
List output includes summary columns such as transcript ID, contact ID, provider, status, duration, and creation date. get returns the full transcript payload including transcriptText and recordingUrl when available.
Output Formats
The CLI supports three output formats:
Table (default in interactive terminals)
When you run snx in a terminal, you get rich formatted tables:
┏━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ id ┃ firstName ┃ lastName ┃ email ┃ company ┃
┡━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ 101 │ Jane │ Doe │ jane@acme.com │ Acme Corp │
│ 102 │ John │ Smith │ john@widgets.io │ Widgets Inc │
└─────┴───────────┴──────────┴────────────────────┴───────────────┘
Page 1 — 2 of 48 total
For single records (get commands), a key-value table is shown:
┃ id ┃ 101 ┃
┃ firstName ┃ Jane ┃
┃ lastName ┃ Doe ┃
┃ email ┃ jane@acme.com ┃
┃ company ┃ Acme Corp ┃
┃ ... ┃ ┃
JSON (default when piped / for AI agents)
When stdout is not a TTY (e.g., piped to another program or called by an AI agent via subprocess), the CLI automatically outputs JSON. You can also force it with --json.
List commands return:
{
"data": [
{
"id": 101,
"firstName": "Jane",
"lastName": "Doe",
"email": "jane@acme.com",
"company": "Acme Corp",
...
}
],
"totalItems": 48,
"page": 1,
"pageSize": 20
}
Get commands return the raw object:
{
"id": 101,
"firstName": "Jane",
"lastName": "Doe",
"email": "jane@acme.com",
"phone": "555-0101",
"company": "Acme Corp",
"title": "VP Sales",
"address": "123 Main St",
"city": "Houston",
"state": "TX",
"zip": "77001",
"country": "US",
"managerUserId": 5,
"createdAt": "2025-06-15T10:30:00Z",
"updatedAt": "2026-02-20T14:22:00Z",
"customFields": {
"lead_source": "Trade Show",
"industry": "Manufacturing"
}
}
CSV
Use --csv for spreadsheet-compatible output:
id,firstName,lastName,email,company
101,Jane,Doe,jane@acme.com,Acme Corp
102,John,Smith,john@widgets.io,Widgets Inc
Command Reference
ping
Verify API connectivity and show the current authenticated user.
snx ping
# ✓ Connected as john@acme.com (account 42)
snx --json ping
# {"message": "pong", "user": "john@acme.com", "accountId": 42}
auth
Manage API key profiles.
snx auth login
Save an API key to a named profile.
| Option | Short | Required | Default | Description |
|---|---|---|---|---|
--api-key |
-k |
Yes | API key (sn_live_...). |
|
--base-url |
-u |
No | https://api.salesnex.us |
API base URL. See Environments. |
--profile |
-p |
No | default |
Profile name to save. |
snx auth login --api-key sn_live_AbCdEfGh.01234567890123456789012345678901
# Profile 'default' saved. Base URL: https://api.salesnex.us
snx auth login -k sn_live_XyZw... -p beta -u https://api-beta.salesnex.us
# Profile 'beta' saved. Base URL: https://api-beta.salesnex.us
snx auth status
Show the active profile and verify connectivity.
| Option | Short | Default | Description |
|---|---|---|---|
--profile |
-p |
Active profile | Profile to check. |
snx auth status
# Profile: default
# Base URL: https://api.salesnex.us
# API Key: sn_live_AbCd...8901
# ✓ Connected as john@acme.com (account 42)
snx auth switch PROFILE
Set a different profile as active.
snx auth switch beta
# Active profile set to 'beta'.
snx auth list
List all saved profiles.
snx auth list
# ┏━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━┓
# ┃ Name ┃ Base URL ┃ API Key ┃ Active ┃
# ┡━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━┩
# │ default │ https://api.salesnex.us │ sn_live_AbCd...8901 │ ✓ │
# │ beta │ https://api-beta.salesnex.us │ sn_live_XyZw...1098 │ │
# └─────────┴────────────────────────────────┴─────────────────────┴────────┘
snx auth logout
Remove a saved profile.
| Option | Short | Default | Description |
|---|---|---|---|
--profile |
-p |
default |
Profile to remove. |
snx auth logout --profile beta
# Profile 'beta' removed.
contacts
Full CRUD for contacts, including search, custom fields, and batch operations.
snx contacts list
List contacts with optional search and pagination.
| Option | Short | Default | Description |
|---|---|---|---|
--page |
1 |
Page number. | |
--page-size |
20 |
Results per page (max 100). | |
--search |
-s |
Search by name, email, company, etc. | |
--all |
-a |
false |
Fetch all pages automatically. |
# Basic list
snx contacts list
# Search for contacts
snx contacts list --search "Acme"
# Get page 3 with 50 results
snx contacts list --page 3 --page-size 50
# Fetch ALL contacts (auto-paginates)
snx contacts list --all --json
JSON output:
{
"data": [
{
"id": 101,
"firstName": "Jane",
"lastName": "Doe",
"email": "jane@acme.com",
"phone": "555-0101",
"company": "Acme Corp",
"title": "VP Sales",
"city": "Houston",
"state": "TX",
"customFields": {}
}
],
"totalItems": 1,
"page": 1,
"pageSize": 20
}
snx contacts get ID
Get a single contact with all fields.
snx contacts get 101
# JSON
snx --json contacts get 101
JSON output:
{
"id": 101,
"firstName": "Jane",
"lastName": "Doe",
"email": "jane@acme.com",
"phone": "555-0101",
"company": "Acme Corp",
"title": "VP Sales",
"address": "123 Main St",
"city": "Houston",
"state": "TX",
"zip": "77001",
"country": "US",
"managerUserId": 5,
"createdAt": "2025-06-15T10:30:00Z",
"updatedAt": "2026-02-20T14:22:00Z",
"customFields": {
"lead_source": "Trade Show",
"industry": "Manufacturing"
}
}
snx contacts create
Create a new contact.
| Option | Short | Required | Description |
|---|---|---|---|
--first-name |
-f |
Yes | First name. |
--last-name |
-l |
No | Last name. |
--email |
-e |
No | Email address. |
--phone |
No | Phone number. | |
--company |
-c |
No | Company name. |
--title |
No | Job title. | |
--address |
No | Street address. | |
--city |
No | City. | |
--state |
No | State. | |
--zip |
No | ZIP / postal code. | |
--country |
No | Country. | |
--manager-id |
No | Assigned manager user ID. | |
--custom-field |
-F |
No | Custom field as key=value (repeatable). |
# Minimal
snx contacts create --first-name "Jane"
# Full
snx contacts create \
--first-name "Jane" \
--last-name "Doe" \
--email "jane@acme.com" \
--phone "555-0101" \
--company "Acme Corp" \
--title "VP Sales" \
--city "Houston" \
--state "TX" \
--zip "77001" \
--custom-field "lead_source=Trade Show" \
--custom-field "industry=Manufacturing"
Custom fields that don't exist yet are auto-created by the API as Character type.
snx contacts update ID
Update an existing contact. Only pass the fields you want to change.
snx contacts update 101 --email "new@acme.com" --title "Director of Sales"
# Update custom fields
snx contacts update 101 --custom-field "lead_source=Referral"
snx contacts delete ID
Delete a contact (soft delete). Prompts for confirmation unless --yes is passed.
snx contacts delete 101
# Delete contact 101? [y/N]: y
# Contact 101 deleted.
# Skip confirmation
snx contacts delete 101 --yes
snx contacts batch-update
Bulk update contacts by IDs or lookup.
| Option | Short | Required | Description |
|---|---|---|---|
--ids |
One of --ids or --lookup-id |
Comma-separated contact IDs. | |
--lookup-id |
One of --ids or --lookup-id |
Lookup ID for selection. | |
--field |
-F |
Yes | Field update as key=value (repeatable). |
# Update by IDs
snx contacts batch-update --ids "101,102,103" --field "status=Active" --field "source=Web"
# Update by lookup (saved search)
snx contacts batch-update --lookup-id 7 --field "status=Inactive"
JSON output:
{
"successCount": 3,
"deletedCount": 0,
"opportunitiesDeletedCount": 0,
"failedIds": []
}
snx contacts batch-delete
Bulk delete contacts. Prompts for confirmation unless --yes.
snx contacts batch-delete --ids "101,102,103" --yes
snx contacts batch-delete --lookup-id 7 --yes
opps (opportunities)
Manage sales opportunities (deals).
snx opps list
List opportunities with optional filters.
| Option | Short | Default | Description |
|---|---|---|---|
--page |
1 |
Page number. | |
--page-size |
20 |
Results per page (max 100). | |
--goal-id |
-g |
Filter by goal. | |
--stage-id |
Filter by pipeline stage. | ||
--contact-id |
-c |
Filter by contact. | |
--all |
-a |
false |
Fetch all pages. |
snx opps list
snx opps list --goal-id 5
snx opps list --contact-id 101 --json
snx opps list --all
JSON output:
{
"data": [
{
"id": 200,
"title": "Acme Enterprise Deal",
"contactId": 101,
"goalId": 5,
"currentStageId": 12,
"ownerUserId": 3,
"amount": 50000.00,
"currency": "USD",
"createdAt": "2026-01-10T08:00:00Z",
"updatedAt": "2026-02-25T16:30:00Z",
"customFields": {},
"opportunityContacts": [
{ "contactId": 101, "role": "Decision Maker" }
]
}
],
"totalItems": 1,
"page": 1,
"pageSize": 20
}
snx opps get ID
snx opps get 200
snx opps create
Create a new opportunity.
| Option | Short | Required | Description |
|---|---|---|---|
--contact-id |
-c |
Yes | Primary contact ID. |
--goal-id |
-g |
Yes | Goal (pipeline group) ID. |
--stage-id |
No | Initial stage ID. | |
--owner-id |
No | Owner user ID. | |
--title |
-t |
No | Opportunity title. |
--amount |
No | Deal amount (decimal). | |
--currency |
No | Currency code (e.g., USD). |
|
--custom-field |
-F |
No | Custom field key=value (repeatable). |
snx opps create \
--contact-id 101 \
--goal-id 5 \
--title "Acme Enterprise Deal" \
--amount 50000 \
--currency USD \
--custom-field "priority=High"
snx opps update ID
snx opps update 200 --stage-id 13 --amount 75000
snx opps delete ID
snx opps delete 200 --yes
snx opps batch-update
snx opps batch-update --ids "200,201" --field "priority=Low"
snx opps batch-delete
snx opps batch-delete --ids "200,201" --yes
tasks
Manage tasks assigned to users, linked to contacts/opportunities.
snx tasks list
| Option | Short | Default | Description |
|---|---|---|---|
--page |
1 |
Page number. | |
--page-size |
20 |
Results per page (max 200). | |
--scope |
own |
own (your tasks) or all. |
|
--start-date |
Filter from date (YYYY-MM-DD). | ||
--end-date |
Filter to date (YYYY-MM-DD). |
snx tasks list
snx tasks list --scope all --start-date 2026-02-01 --end-date 2026-02-28
JSON output (uses ApiResponse envelope):
{
"data": [
{
"id": 500,
"title": "Follow up call",
"details": "Discuss proposal",
"type": "Call",
"priority": "High",
"status": "Pending",
"dateFrom": "2026-02-26T10:00:00Z",
"dateTo": "2026-02-26T10:30:00Z",
"assignedToUserId": 3,
"contactId": 101,
"opportunityId": null,
"completedAt": null
}
],
"totalItems": 1,
"page": 1,
"pageSize": 20
}
snx tasks get ID
snx tasks get 500
snx tasks get 500 --scope all # if task belongs to another user
snx tasks create
| Option | Short | Required | Default | Description |
|---|---|---|---|---|
--title |
-t |
Yes | Task title. | |
--details |
-d |
No | Description. | |
--date-from |
No | Start date (ISO 8601). | ||
--date-to |
No | End date. | ||
--type |
No | Task type (e.g. Call, Email, Meeting). | ||
--priority |
No | Priority (e.g. High, Normal, Low). | ||
--color |
No | Color. | ||
--assigned-to |
No | Current user | Assigned user ID. | |
--contact-id |
-c |
No | Linked contact. | |
--opportunity-id |
-o |
No | Linked opportunity. | |
--group-id |
-g |
No | Linked group. |
snx tasks create \
--title "Follow up call" \
--details "Discuss proposal" \
--type "Call" \
--priority "High" \
--date-from "2026-02-27T10:00:00" \
--date-to "2026-02-27T10:30:00" \
--contact-id 101
snx tasks update ID
snx tasks update 500 --status "Completed"
snx tasks update 500 --title "Updated title" --priority "Low"
snx tasks delete ID
snx tasks delete 500 --yes
notes
Manage notes attached to contacts or opportunities.
snx notes list
| Option | Short | Description |
|---|---|---|
--contact-id |
-c |
Filter by contact. |
--opportunity-id |
-o |
Filter by opportunity. |
--page |
Page number (default: 1). | |
--page-size |
Results per page (default: 20). |
snx notes list --contact-id 101
snx notes list --opportunity-id 200
JSON output:
{
"data": [
{
"id": 800,
"contactId": 101,
"opportunityId": null,
"noteText": "Called, interested in demo",
"createdOn": "2026-02-25T14:00:00Z",
"createdBy": 3
}
]
}
snx notes get ID
snx notes get 800
snx notes create
| Option | Short | Required | Description |
|---|---|---|---|
--text |
-t |
Yes | Note text. |
--contact-id |
-c |
One of these | Contact ID. |
--opportunity-id |
-o |
required | Opportunity ID. |
snx notes create --contact-id 101 --text "Called, interested in enterprise plan"
snx notes create --opportunity-id 200 --text "Sent revised proposal"
snx notes update ID
snx notes update 800 --text "Updated: Called, requesting demo next week"
snx notes delete ID
snx notes delete 800 --yes
goals
Manage goals (pipeline groups with stages). Goals contain pipelines, which contain stages.
snx goals list
| Option | Default | Description |
|---|---|---|
--page |
1 |
Page number. |
--page-size |
50 |
Results per page. |
--sort-by |
Sort field. | |
--sort-desc |
false |
Sort descending. |
snx goals list
snx goals get ID
Returns the goal with all its pipelines and stages:
snx --json goals get 5
{
"id": 5,
"name": "Enterprise Sales",
"description": "High-value B2B deals",
"createdBy": 1,
"createdAt": "2025-03-01T00:00:00Z",
"updatedAt": "2026-01-15T12:00:00Z",
"pipelines": [
{
"id": 10,
"goalId": 5,
"name": "Default Pipeline",
"description": "",
"isDefault": true,
"stages": [
{ "id": 11, "pipelineId": 10, "name": "Prospecting", "order": 1, "slaHours": 72, "isConversion": false },
{ "id": 12, "pipelineId": 10, "name": "Qualification", "order": 2, "slaHours": 48, "isConversion": false },
{ "id": 13, "pipelineId": 10, "name": "Proposal", "order": 3, "slaHours": 168, "isConversion": false },
{ "id": 14, "pipelineId": 10, "name": "Closed Won", "order": 4, "slaHours": null, "isConversion": true }
]
}
]
}
snx goals create
| Option | Short | Required | Description |
|---|---|---|---|
--name |
-n |
Yes | Goal name. |
--description |
-d |
No | Goal description. |
snx goals create --name "SMB Sales" --description "Small and mid-market deals"
snx goals update ID
snx goals update 5 --name "Enterprise Sales (Updated)"
snx goals delete ID
snx goals delete 5 --yes
fields
Manage custom fields for contacts and opportunities.
snx fields list
| Option | Short | Default | Description |
|---|---|---|---|
--entity |
-e |
contact |
Entity type: contact or opportunity. |
snx fields list
snx fields list --entity opportunity
JSON output:
{
"data": [
{
"id": 30,
"name": "lead_source",
"label": "Lead Source",
"type": 0,
"isSystem": false,
"isReadOnly": false,
"isRequired": false,
"isDropDown": true,
"multiSelect": false,
"defaultValue": "",
"options": ["Web", "Referral", "Trade Show", "Cold Call"]
}
]
}
Field type mapping:
| Type Code | Name | Description |
|---|---|---|
0 |
character |
Free text |
1 |
currency |
Money amount |
2 |
date |
Date |
3 |
numeric |
Number |
4 |
phone |
Phone number |
5 |
time |
Time |
6 |
checkbox |
Boolean |
7 |
percentage |
Percentage |
8 |
image |
Image URL |
snx fields create
| Option | Short | Required | Default | Description |
|---|---|---|---|---|
--entity |
-e |
No | contact |
contact or opportunity. |
--name |
-n |
Yes | Internal field name. | |
--label |
-l |
No | Display label. | |
--type |
-t |
No | character |
Field type (see table above). |
--required |
No | false |
Mark as required. | |
--dropdown |
No | false |
Make it a dropdown. | |
--multi-select |
No | false |
Allow multiple values. | |
--default |
No | Default value. | ||
--options |
No | Comma-separated dropdown options. |
# Simple text field
snx fields create --name "lead_source" --label "Lead Source"
# Dropdown field with options
snx fields create \
--name "industry" \
--label "Industry" \
--dropdown \
--options "Technology,Manufacturing,Healthcare,Finance,Other"
# Opportunity currency field
snx fields create --entity opportunity --name "projected_revenue" --type currency
# Multi-select field
snx fields create --name "interests" --label "Interests" --dropdown --multi-select \
--options "Product A,Product B,Product C"
templates
Manage email templates (campaigns).
snx templates list
| Option | Default | Description |
|---|---|---|
--sort-by |
Sort field. | |
--sort-desc |
false |
Sort descending. |
--mode |
Filter: bulk or triggered. |
snx templates list
snx templates list --mode bulk
snx templates get ID
snx --json templates get 42
{
"id": 42,
"name": "February Newsletter",
"objective": "Monthly product updates",
"status": "sent",
"mode": "bulk",
"scheduleAt": "2026-02-15T09:00:00Z",
"segmentId": 7,
"trackingHost": "track.salesnex.us",
"createdAt": "2026-02-10T10:00:00Z",
"updatedAt": "2026-02-15T09:05:00Z"
}
snx templates stats ID
Get send/open/click statistics for a template.
snx --json templates stats 42
{
"sent": 1250,
"opens": 487,
"clicks": 134,
"bounces": 12,
"unsubscribes": 3,
"complaints": 0
}
snx templates create
| Option | Short | Required | Default | Description |
|---|---|---|---|---|
--name |
-n |
Yes | Template name. | |
--objective |
No | Campaign objective. | ||
--mode |
No | bulk |
bulk or triggered. |
|
--segment-id |
No | Target segment ID. |
snx templates create --name "March Newsletter" --mode bulk --segment-id 7
snx templates update ID
snx templates update 42 --name "February Newsletter (Final)" --status "draft"
snx templates delete ID
snx templates delete 42 --yes
reports
Manage saved reports.
snx reports list
snx reports list
snx reports get ID
snx --json reports get 15
{
"id": 15,
"title": "Q1 Pipeline Summary",
"specJson": "{\"type\":\"pipeline\",\"goalId\":5}",
"narrativeJson": null,
"isPublic": true,
"createdAt": "2026-01-05T10:00:00Z",
"lastRunAt": "2026-02-25T08:00:00Z"
}
snx reports create
| Option | Short | Required | Description |
|---|---|---|---|
--title |
-t |
Yes | Report title. |
--spec |
No | Report spec as JSON string. | |
--public |
No | Make report visible to all users. |
snx reports create --title "Q1 Pipeline Summary" --public
snx reports update ID
snx reports update 15 --title "Q1 Pipeline Summary (Final)" --public
snx reports delete ID
snx reports delete 15 --yes
lookups
Manage lookups (saved searches), segments (filter conditions), and layouts (column configs).
Lookups
snx lookups list # List all lookups
snx lookups get 7 # Get a specific lookup
snx lookups create --name "Hot Leads" # Create a lookup
JSON output for snx lookups get:
{
"id": 7,
"name": "Hot Leads",
"segmentId": 12,
"layoutId": 3,
"segment": { "id": 12, "name": "..." },
"layout": { "id": 3, "name": "..." }
}
Segments (lookups segments)
Segments define filter conditions for contact lists.
snx lookups segments list # List all segments
snx lookups segments get 12 # Get a segment
snx lookups segments create --name "Active in Texas" --spec '{"conditions": [...]}'
snx lookups segments update 12 --name "Active in TX (Updated)"
Segment condition operators: equals, not_equals, gt, ge, lt, le, contains, not_contains, starts_with, ends_with, at, is_blank
Layouts (lookups layouts)
Layouts configure which columns appear in a lookup view.
snx lookups layouts list
snx lookups layouts get 3
snx lookups layouts create --name "Compact View" --spec '{"columns": [...]}'
snx lookups layouts update 3 --name "Compact View (v2)"
docs (documents)
Manage documents attached to contacts, groups, or opportunities.
snx docs list
| Option | Short | Default | Description |
|---|---|---|---|
--page |
1 |
Page number. | |
--page-size |
20 |
Results per page. | |
--contact-id |
-c |
Filter by contact. | |
--group-id |
-g |
Filter by group. | |
--opportunity-id |
-o |
Filter by opportunity. | |
--scope |
own |
own or all. |
|
--all |
-a |
false |
Fetch all pages. |
snx docs list --contact-id 101
snx docs get ID
snx --json docs get 300
{
"id": 300,
"userId": 3,
"contactId": 101,
"groupId": null,
"opportunityId": null,
"description": "Signed contract",
"originalFileName": "contract-2026.pdf",
"mime": "application/pdf",
"size": 245760,
"tags": "contract,signed",
"sourceUrl": "https://...",
"sourceType": "url",
"createdAt": "2026-02-20T10:00:00Z"
}
snx docs create
Create a document by importing from a URL.
| Option | Short | Required | Description |
|---|---|---|---|
--url |
Yes | URL to import. | |
--description |
-d |
No | Document description. |
--contact-id |
-c |
No | Attach to contact. |
--group-id |
-g |
No | Attach to group. |
--opportunity-id |
-o |
No | Attach to opportunity. |
--tags |
No | Comma-separated tags. |
snx docs create --url "https://example.com/proposal.pdf" --contact-id 101 --description "Q1 Proposal" --tags "proposal,q1"
snx docs delete ID
snx docs delete 300 --yes
forms
Manage web forms for lead capture.
snx forms list
snx forms list
JSON output:
{
"data": [
{
"id": 50,
"name": "Contact Us",
"slug": "contact-us",
"status": "published",
"isWiretap": false,
"createdAt": "2025-12-01T10:00:00Z"
}
]
}
snx forms get ID
snx forms get 50
snx forms create
| Option | Short | Required | Description |
|---|---|---|---|
--name |
-n |
Yes | Form name. |
--slug |
No | URL slug. | |
--redirect-url |
No | Redirect URL after submission. | |
--settings |
No | Settings as JSON string. |
snx forms create --name "Contact Us" --slug "contact-us" --redirect-url "https://acme.com/thanks"
snx forms update ID
snx forms update 50 --name "Contact Us (v2)"
snx forms delete ID
snx forms delete 50 --yes
snx forms embed ID
Get the HTML embed code snippet for a published form.
snx forms embed 50
snx forms unpublish ID
Take a form offline.
snx forms unpublish 50
users
List account users (read-only).
snx users list
snx users list
JSON output:
{
"data": [
{
"id": 3,
"username": "john@acme.com",
"email": "john@acme.com",
"isActive": true,
"securityLevel": "Admin"
}
]
}
snx users get ID
snx users get 3
Pagination
Most list commands are paginated. Two mechanisms are available:
Manual pagination
snx contacts list --page 1 --page-size 50
snx contacts list --page 2 --page-size 50
Auto-pagination (--all)
Use --all to automatically fetch every page and return the complete dataset:
snx contacts list --all --json
This iterates with pageSize=100 until all records are retrieved. Useful for exports and AI agent workflows.
Limits:
--page-sizemax is 100 for most endpoints, 200 for tasks.
Custom Fields
Contacts and opportunities support custom fields. These appear in the customFields object in JSON output.
Setting custom fields on create/update
Use --custom-field (or -F) one or more times:
snx contacts create --first-name "Jane" \
--custom-field "lead_source=Web" \
--custom-field "industry=Tech" \
--custom-field "score=85"
Auto-creation
If a custom field name doesn't exist yet, the SalesNexus API automatically creates it as a Character type field. To create fields with specific types (dropdown, currency, etc.) first use snx fields create.
Reading custom fields
Custom fields are included in all get and list JSON output:
{
"id": 101,
"firstName": "Jane",
"customFields": {
"lead_source": "Web",
"industry": "Tech",
"score": "85"
}
}
Batch Operations
Contacts and opportunities support batch (bulk) operations.
Selection modes
| Mode | Flag | Description |
|---|---|---|
| By IDs | --ids "1,2,3" |
Explicit comma-separated list. |
| By Lookup | --lookup-id 7 |
All contacts matching a saved lookup/search. |
Batch update
# Update specific contacts
snx contacts batch-update --ids "101,102,103" --field "status=Active"
# Update all contacts in a lookup
snx contacts batch-update --lookup-id 7 --field "status=Inactive" --field "source=Archive"
Batch delete
snx contacts batch-delete --ids "101,102,103" --yes
snx contacts batch-delete --lookup-id 7 --yes
Response
{
"successCount": 3,
"deletedCount": 3,
"opportunitiesDeletedCount": 0,
"failedIds": []
}
AI Agent Integration Guide
The SalesNexus CLI is purpose-built for use by AI coding agents (GitHub Copilot, Claude Code, Cursor, Windsurf, etc.). Here's how to integrate it.
Auto-JSON detection
When an AI agent runs snx via subprocess, stdout is not a TTY, so the CLI automatically outputs JSON — no --json flag needed. Status messages (like "Contact 101 created.") go to stderr, keeping stdout clean for parsing.
Recommended agent workflow
# 1. Check connectivity
snx ping
# 2. Discover the account structure
snx goals list # pipelines & stages
snx fields list # available fields
snx users list # team members
# 3. Search for contacts
snx contacts list --search "Acme Corp"
# 4. Get full details
snx contacts get 101
# 5. Create/update records
snx contacts create --first-name "New" --last-name "Lead" --email "lead@co.com"
snx notes create --contact-id 101 --text "AI: Identified as high-value lead"
# 6. Bulk operations
snx contacts batch-update --ids "101,102" --field "status=Qualified"
Composing commands (bash)
# Create a contact and immediately add a note
ID=$(snx contacts create --first-name "Jane" --email "jane@co.com" | jq -r '.id')
snx notes create --contact-id "$ID" --text "Auto-created by agent"
# Export all contacts to CSV
snx --csv contacts list --all > contacts_export.csv
# Find contacts without email and update them
snx contacts list --all | jq -r '.data[] | select(.email == null) | .id' | while read id; do
snx contacts update "$id" --custom-field "needs_email=true"
done
# Get open opportunity value
snx opps list --all | jq '[.data[].amount // 0] | add'
Composing commands (PowerShell)
# Create a contact and capture ID
$result = snx contacts create --first-name "Jane" --email "jane@co.com" | ConvertFrom-Json
snx notes create --contact-id $result.id --text "Auto-created"
# List contacts as objects
$contacts = snx contacts list --all | ConvertFrom-Json
$contacts.data | Where-Object { $_.state -eq "TX" } | Format-Table
# Export to CSV
snx --csv contacts list --all | Out-File contacts.csv
Using in CI/CD
# GitHub Actions example
env:
SALESNEXUS_API_KEY: ${{ secrets.SALESNEXUS_API_KEY }}
steps:
- run: pip install salesnexus-cli
- run: snx ping
- run: snx contacts list --all --json > contacts.json
MCP server note
SalesNexus also provides an MCP (Model Context Protocol) server for deeper AI integration with 47 tools and 34 resources. The CLI wraps the public REST API (stable, versioned), while MCP is used for real-time AI chat workflows. Both can complement each other.
Error Handling & Exit Codes
| Exit Code | Meaning |
|---|---|
0 |
Success. |
1 |
Error (API error, auth failure, bad input, etc.). |
Error messages
Errors print to stderr in a consistent format:
Error: HTTP 401: Invalid or expired API key. Run `snx auth login` to configure.
Error: HTTP 404: Resource not found.
Error: HTTP 429: Rate limit exceeded. Try again later.
Error: HTTP 409: Field 'lead_source' already exists.
The CLI automatically retries on transient failures (HTTP 429, 502, 503, 504) with exponential backoff, up to 3 attempts.
JSON error output
When --json is active, errors still print to stderr (not stdout), so your JSON parsing pipeline won't break.
Rate Limits
The SalesNexus API enforces rate limits:
| Limit | Value |
|---|---|
| Requests per hour | 10,000 per account |
| Queue depth | 2 requests |
When rate-limited, the CLI waits using the Retry-After header and retries automatically.
Environment Variables
| Variable | Description |
|---|---|
SALESNEXUS_API_KEY |
API key (overrides config file). |
SALESNEXUS_BASE_URL |
Base URL (overrides config file). Default: https://api.salesnex.us. Set to https://api-beta.salesnex.us for beta. |
SALESNEXUS_PROFILE |
Profile name to use (overrides active_profile in config). |
NO_COLOR |
Disable colored output (respected by rich). |
Shell Completions
Install tab completion for your shell:
# Auto-detect shell
snx --install-completion
# Or show the completion script without installing
snx --show-completion
Supports: bash, zsh, fish, PowerShell.
After installing, restart your shell or source the config file. Then:
snx con<TAB> → snx contacts
snx contacts cr<TAB> → snx contacts create
snx contacts create --<TAB> → --first-name --last-name --email ...
Development
Setup
cd salesnexus-cli
pip install -e ".[dev]"
Run tests
pytest
Project structure
salesnexus-cli/
├── pyproject.toml # Package config, "snx" entry point
├── README.md # This file
├── src/salesnexus_cli/
│ ├── __init__.py # Version
│ ├── main.py # Typer app, global options, command registration
│ ├── config.py # ~/.salesnexus/config.toml profile management
│ ├── client.py # httpx wrapper (auth, retry, errors)
│ ├── output.py # Dual formatter: rich tables / JSON / CSV
│ ├── pagination.py # --all auto-pagination helper
│ └── commands/
│ ├── auth.py # login, status, switch, list, logout
│ ├── contacts.py # CRUD + batch + search
│ ├── opportunities.py # CRUD + batch
│ ├── tasks.py # CRUD + date filters
│ ├── notes.py # CRUD
│ ├── goals.py # CRUD (pipelines & stages)
│ ├── fields.py # list + create (contact & opp)
│ ├── templates.py # CRUD + stats
│ ├── reports.py # CRUD
│ ├── lookups.py # lookups + segments + layouts
│ ├── documents.py # list + get + create-from-url + delete
│ ├── forms.py # CRUD + embed + unpublish
│ ├── users.py # list + get (read-only)
│ └── ping.py # connectivity check
└── tests/
Adding a new command
- Create
src/salesnexus_cli/commands/mycommand.py. - Define a
typer.Typer()app with commands. - Register in
main.py:app.add_typer(mycommand.app, name="mycommand").
Troubleshooting
"snx" is not recognized
The Python Scripts directory is not on your PATH. Add it:
- Windows:
$env:PATH += ";$env:LOCALAPPDATA\...\Python3XX\Scripts" - macOS/Linux:
export PATH="$HOME/.local/bin:$PATH"
Or run via: python -m salesnexus_cli.main
"No API key configured"
Run snx auth login --api-key sn_live_... to save your key, or set SALESNEXUS_API_KEY environment variable.
"Invalid or expired API key"
Your API key may have expired or been revoked. Generate a new one from SalesNexus Settings → API Keys.
"Rate limit exceeded"
The CLI automatically retries, but if you're seeing this persistently, you're exceeding 10,000 requests/hour. Space out your requests or contact support.
Timeout errors
The default timeout is 30 seconds. For large exports (--all with many pages), this should be sufficient per request. If you experience timeouts, check your network connection.
SSL/TLS errors
If you're behind a corporate proxy, you may need to set SSL_CERT_FILE or REQUESTS_CA_BUNDLE environment variables, or use --base-url with your proxy URL.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file salesnexus_cli-0.1.1.tar.gz.
File metadata
- Download URL: salesnexus_cli-0.1.1.tar.gz
- Upload date:
- Size: 46.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b62e1a0f3ac816a90308f379a3839e3a96df8f59b8c9b18de748e08705b9009c
|
|
| MD5 |
5229ab311eb150e997ec9a7df8884dba
|
|
| BLAKE2b-256 |
a0fb36be6ce0cc18133cb4e8459d05ac0d2fec53a17cd5067f1cb5e8dc169edb
|
Provenance
The following attestation bundles were made for salesnexus_cli-0.1.1.tar.gz:
Publisher:
publish.yml on salesnexus2/salesnexus-cli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
salesnexus_cli-0.1.1.tar.gz -
Subject digest:
b62e1a0f3ac816a90308f379a3839e3a96df8f59b8c9b18de748e08705b9009c - Sigstore transparency entry: 1066450239
- Sigstore integration time:
-
Permalink:
salesnexus2/salesnexus-cli@d6a79fc2fecf4dea7293faba45edb9085386b9f3 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/salesnexus2
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d6a79fc2fecf4dea7293faba45edb9085386b9f3 -
Trigger Event:
release
-
Statement type:
File details
Details for the file salesnexus_cli-0.1.1-py3-none-any.whl.
File metadata
- Download URL: salesnexus_cli-0.1.1-py3-none-any.whl
- Upload date:
- Size: 42.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9a0b30aff5aca72f70f4472d59d8da7c63828e91fab0a5cd39697d92fec00dae
|
|
| MD5 |
2d21c6cc415c97ab5fab6003670c1a31
|
|
| BLAKE2b-256 |
414186c1cd646131977b9361b7fa58ec9dd589e0b1da8bd80568286f7f4a7e6b
|
Provenance
The following attestation bundles were made for salesnexus_cli-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on salesnexus2/salesnexus-cli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
salesnexus_cli-0.1.1-py3-none-any.whl -
Subject digest:
9a0b30aff5aca72f70f4472d59d8da7c63828e91fab0a5cd39697d92fec00dae - Sigstore transparency entry: 1066450259
- Sigstore integration time:
-
Permalink:
salesnexus2/salesnexus-cli@d6a79fc2fecf4dea7293faba45edb9085386b9f3 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/salesnexus2
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d6a79fc2fecf4dea7293faba45edb9085386b9f3 -
Trigger Event:
release
-
Statement type: