Skip to main content

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

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 snx is 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-size max 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

  1. Create src/salesnexus_cli/commands/mycommand.py.
  2. Define a typer.Typer() app with commands.
  3. 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

salesnexus_cli-0.1.1.tar.gz (46.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

salesnexus_cli-0.1.1-py3-none-any.whl (42.7 kB view details)

Uploaded Python 3

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

Hashes for salesnexus_cli-0.1.1.tar.gz
Algorithm Hash digest
SHA256 b62e1a0f3ac816a90308f379a3839e3a96df8f59b8c9b18de748e08705b9009c
MD5 5229ab311eb150e997ec9a7df8884dba
BLAKE2b-256 a0fb36be6ce0cc18133cb4e8459d05ac0d2fec53a17cd5067f1cb5e8dc169edb

See more details on using hashes here.

Provenance

The following attestation bundles were made for salesnexus_cli-0.1.1.tar.gz:

Publisher: publish.yml on salesnexus2/salesnexus-cli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for salesnexus_cli-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 9a0b30aff5aca72f70f4472d59d8da7c63828e91fab0a5cd39697d92fec00dae
MD5 2d21c6cc415c97ab5fab6003670c1a31
BLAKE2b-256 414186c1cd646131977b9361b7fa58ec9dd589e0b1da8bd80568286f7f4a7e6b

See more details on using hashes here.

Provenance

The following attestation bundles were made for salesnexus_cli-0.1.1-py3-none-any.whl:

Publisher: publish.yml on salesnexus2/salesnexus-cli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page