npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@saleshandy/saleshandy-cli

v0.1.15

Published

Official CLI for SalesHandy Open API

Readme

@saleshandy/saleshandy-cli

Official CLI for the SalesHandy Open API. Manage prospects, sequences, email accounts, tasks, and more from your terminal.

npm

Install

npm install -g @saleshandy/saleshandy-cli

Requires Node.js 18 or later.

Quick Start

1. Get your API key

Go to SalesHandy API Keys Settings and generate an API key.

2. Authenticate

saleshandy auth login
# Prompts for your API key interactively

# Or pass it directly:
saleshandy auth login --api-key YOUR_API_KEY

3. Run commands

saleshandy prospects list
saleshandy sequences list --json
saleshandy tasks list --limit 5

Authentication

Login and profiles

The CLI stores your API key locally at ~/.config/saleshandy/config.json.

# Default profile
saleshandy auth login --api-key YOUR_API_KEY

# Named profile (for multiple accounts)
saleshandy auth login --api-key ANOTHER_KEY --profile client-a

# Use a specific profile for a command
saleshandy prospects list --profile client-a

# Switch default profile
saleshandy auth switch client-a

# See all profiles
saleshandy auth status

# Remove a profile
saleshandy auth logout
saleshandy auth logout --profile client-a

Environment variable

You can skip login entirely by setting the env var. This is useful for CI/CD:

export SALESHANDY_API_KEY=your_key
saleshandy prospects list

Per-command override

saleshandy prospects list --api-key YOUR_KEY

Precedence: --api-key flag > SALESHANDY_API_KEY env var > saved profile.

Output Formats

Every command supports three output formats:

# Table (default) — human-readable
saleshandy prospects list

# JSON — for scripts and piping
saleshandy prospects list --json

# CSV — for spreadsheets
saleshandy prospects list --csv

Pipe JSON output to jq for filtering:

saleshandy prospects list --json | jq '.[].email'

Export to a CSV file:

saleshandy prospects list --csv > contacts.csv

Disable colored output:

saleshandy prospects list --no-color

Getting Help

# List all topics
saleshandy --help

# List commands in a topic
saleshandy prospects --help

# See flags, args, and examples for a specific command
saleshandy prospects list --help
saleshandy analytics sequence-stats --help

Every command has --help. When in doubt, add --help to see exactly what flags and arguments are required.

Common Patterns

Pagination

All list commands support --limit and --page:

saleshandy prospects list --limit 50 --page 1
saleshandy prospects list --limit 50 --page 2

Sorting

List commands support --sort (asc/desc) and --sort-by:

saleshandy prospects list --sort desc --sort-by createdAt
saleshandy tasks list --sort-order ASC --sort-by priority

Note: Valid --sort-by values differ per resource. Run <command> --help to see available options.

Passing IDs

Many commands require IDs (sequence IDs, prospect IDs, etc.). IDs in SalesHandy are hashed strings like gOwE5l64wb. You get these from list commands:

# Step 1: List sequences to find the ID
saleshandy sequences list
# Output shows ID column: gOwE5l64wb

# Step 2: Use that ID in another command
saleshandy analytics sequence-stats --sequence-id gOwE5l64wb

Comma-separated IDs

Some commands accept multiple IDs. Pass them comma-separated:

saleshandy sequences status-update --sequence-ids gOwE5l64wb,aB3xK9mP2w --status pause
saleshandy tasks bulk-skip --task-ids id1,id2,id3

JSON body via --file

Import and connect commands accept a JSON file:

saleshandy prospects import --file prospects.json
saleshandy email-accounts connect --file accounts.json
saleshandy enrichment contact --file leads.json

Async operations (import, enrichment, connect)

Some operations are async. They return a request ID you can poll:

# Start an import
saleshandy prospects import --file data.json
# Output: ✓ Prospect import has started. Request ID: req_abc123

# Check status
saleshandy prospects import-status req_abc123

# For enrichment, use --wait to poll automatically:
saleshandy enrichment status req_abc123 --wait

Command Reference

auth — Authentication

saleshandy auth login                          # Interactive login
saleshandy auth login --api-key KEY            # Non-interactive
saleshandy auth login --api-key KEY --profile staging
saleshandy auth logout                         # Remove current profile
saleshandy auth logout --profile staging       # Remove named profile
saleshandy auth status                         # Show all profiles
saleshandy auth switch staging                 # Switch active profile

prospects — Manage Prospects

saleshandy prospects list                                    # List all prospects
saleshandy prospects list --search "[email protected]"           # Search by email/name
saleshandy prospects list --limit 50 --page 2                # Paginate
saleshandy prospects list --include-custom-fields             # Include custom fields
saleshandy prospects import --file prospects.json            # Import from file
saleshandy prospects import-status REQUEST_ID                # Check import status
saleshandy prospects verification-status --emails "[email protected],[email protected]"
saleshandy prospects attribute --attribute-id ID --prospect-id ID

# Update prospect status in a sequence
saleshandy prospects status update \
  --prospect-id ID --sequence-id ID --step-id ID --status paused
# Status options: replied, finished, paused, unsubscribed, active

# Tags
saleshandy prospects tags list
saleshandy prospects tags list --search "VIP"
saleshandy prospects tags assign --prospects '[{"prospectId":"ID","tagId":"ID"}]'
saleshandy prospects tags unassign --data '[{"prospects":["ID"]}]'

sequences — Manage Sequences

saleshandy sequences list                                        # List all
saleshandy sequences list --search "Growth" --limit 10           # Filter
saleshandy sequences status-update --sequence-ids ID1,ID2 --status pause

# Create a sequence (optionally attach email account + schedule in one call)
saleshandy sequences create --title "Q2 Outreach"
saleshandy sequences create --title "Q2 Outreach" --email-account-id EA_ID --schedule-id SCH_ID

# Email accounts on a sequence
saleshandy sequences email-accounts list --sequence-id ID
saleshandy sequences email-accounts add --sequence-id ID --email-account-ids ID1,ID2
saleshandy sequences email-accounts remove --sequence-id ID --email-account-ids ID1

# Import prospects into a sequence
saleshandy sequences prospects-import --file data.json

# Add existing contacts to a sequence step
saleshandy sequences contacts-add --sequence-id ID --step-id ID --contact-ids ID1,ID2

# Steps — list + get + create
saleshandy sequences steps list --sequence-id ID
saleshandy sequences steps get --sequence-id ID --step-id ID
saleshandy sequences steps create \
  --sequence-id ID --type 1 --absolute-days 1 --variants-file ./variants.json

# Variants — add + update
saleshandy sequences steps variants create \
  --sequence-id ID --step-id ID --type 1 --payload-file ./payload.json
saleshandy sequences steps variants update \
  --sequence-id ID --step-id ID --variant-id ID --status 0
saleshandy sequences steps variants update \
  --sequence-id ID --step-id ID --variant-id ID --attachment-ids "[]"   # clear attachments

# Settings — get + update (codes 1–14, see docs)
saleshandy sequences settings get --sequence-id ID
saleshandy sequences settings get --sequence-id ID --code 6
saleshandy sequences settings update --sequence-id ID --set 6=1 --set 5=1
saleshandy sequences settings update --sequence-id ID --schedule-id SCH_ID

# Priority / distribution
saleshandy sequences priority-distribution update --sequence-id ID --preset 3
# Presets: 1=Prioritise Follow-Ups, 2=Prioritise New Prospects,
#          3=Balanced Sending, 4=Aggressively Prioritise New Prospects

Sample variants.json for sequences steps create --type 1 (Email):

[
  {
    "payload": {
      "subject": "Hello {{firstName}}",
      "content": "<p>Hi {{firstName}}, quick note.</p>",
      "preheader": "Quick note"
    },
    "attachmentIds": ["aX2vY9wZ1q"]
  }
]

Sample payload.json for sequences steps variants create --type 2 (LinkedIn Connection Request):

{ "connectionNote": "Hi {{firstName}}, would love to connect." }

schedules — Sending schedules

saleshandy schedules list
saleshandy schedules list --json
saleshandy schedules create \
  --name "Weekdays 9-5 EST" --timezone America/New_York \
  --time-slots-file ./time-slots.json
saleshandy schedules create \
  --name "Default" --timezone Asia/Kolkata \
  --time-slots-file ./time-slots.json --is-default

Sample time-slots.json (must contain exactly 7 entries):

[
  { "day": 0, "slots": [] },
  { "day": 1, "slots": [{ "start": { "hour": 9, "minute": 0 }, "end": { "hour": 17, "minute": 0 } }] },
  { "day": 2, "slots": [{ "start": { "hour": 9, "minute": 0 }, "end": { "hour": 17, "minute": 0 } }] },
  { "day": 3, "slots": [{ "start": { "hour": 9, "minute": 0 }, "end": { "hour": 17, "minute": 0 } }] },
  { "day": 4, "slots": [{ "start": { "hour": 9, "minute": 0 }, "end": { "hour": 17, "minute": 0 } }] },
  { "day": 5, "slots": [{ "start": { "hour": 9, "minute": 0 }, "end": { "hour": 17, "minute": 0 } }] },
  { "day": 6, "slots": [] }
]

attachments — Upload files for email variants

saleshandy attachments upload --file ./proposal.pdf
saleshandy attachments upload --file ./doc.docx --json

The returned attachmentId is used in the attachmentIds field of an Email variant payload (see sequences steps create examples above).

email-accounts — Email Accounts

saleshandy email-accounts list                                    # List all
saleshandy email-accounts list --search "gmail"                   # Filter
saleshandy email-accounts connect --file accounts.json            # Connect via SMTP/IMAP
saleshandy email-accounts connect-status REQUEST_ID               # Check connection
saleshandy email-accounts reconnect --email-account-ids ID1,ID2
saleshandy email-accounts bulk-update --email-account-ids ID1,ID2 --is-paused

enrichment — Lead Finder

# Enrich contacts
saleshandy enrichment contact --linkedin-urls "https://linkedin.com/in/person"
saleshandy enrichment contact --file contacts.json --reveal-phone
saleshandy enrichment contact --file contacts.json --webhook-url https://example.com/hook

# Enrich companies
saleshandy enrichment company --domains "acme.com,startup.io"
saleshandy enrichment company --file companies.json

# Check status and get results
saleshandy enrichment status REQUEST_ID
saleshandy enrichment status REQUEST_ID --wait    # Polls until complete
saleshandy enrichment result REQUEST_ID

# Check credits and rate limits
saleshandy enrichment credits
saleshandy enrichment rate-limits

unified-inbox — Emails

saleshandy unified-inbox unread-count
saleshandy unified-inbox outcomes --limit 10
saleshandy unified-inbox emails list --limit 20 --search "acme"
saleshandy unified-inbox emails list --sentiment positive --start-date 2025-01-01
saleshandy unified-inbox emails thread THREAD_ID
saleshandy unified-inbox emails get THREAD_ID EMAIL_ID

# Reply to an email
saleshandy unified-inbox emails reply \
  --thread-id ID --email-id ID \
  --to "[email protected]" --content "Thanks for reaching out!"

analytics — Reports

# Sequence stats (pass sequence ID from `sequences list`)
saleshandy analytics sequence-stats --sequence-id gOwE5l64wb

# Consolidated stats across multiple sequences
saleshandy analytics consolidated-stats \
  --sequence-ids ID1,ID2 --start-date 2025-01-01 --end-date 2025-12-31

# Email account stats
saleshandy analytics email-account-stats --email-id ID

# Team stats
saleshandy analytics team-stats \
  --user-ids ID1,ID2 \
  --start-date 2025-01-01T00:00:00.000+00:00 \
  --end-date 2025-12-31T23:59:59.000+00:00 \
  --count-by absolute --type prospect \
  --order-by total --limit 10 --page 1

tasks — Task Management

saleshandy tasks list                                    # List all tasks
saleshandy tasks list --status overdue --limit 10        # Filter by status
saleshandy tasks list --sequence-id ID                   # Filter by sequence
saleshandy tasks get TASK_ID                             # Task details
saleshandy tasks counts                                  # Counts by status
saleshandy tasks assignees                               # List assignees

# Actions
saleshandy tasks complete TASK_ID
saleshandy tasks skip TASK_ID
saleshandy tasks snooze TASK_ID --snooze-until "2025-12-25T09:00:00.000Z"
saleshandy tasks note-update TASK_ID --note "Called, no answer"

# Bulk actions
saleshandy tasks bulk-skip --task-ids ID1,ID2,ID3
saleshandy tasks bulk-snooze --task-ids ID1,ID2 --snooze-until "2025-12-25T09:00:00.000Z"
saleshandy tasks bulk-status BULK_ACTION_ID

dnc — Do Not Contact

saleshandy dnc list                                      # List DNC lists
saleshandy dnc items DNC_LIST_ID                         # Items in a list
saleshandy dnc add --dnc-list-id ID --items "[email protected],baddomain.com"
saleshandy dnc search --search "bad.com" --type domain

fields — Custom Fields

saleshandy fields list --system-fields     # Include system fields
saleshandy fields list --no-system-fields  # Custom fields only

clients — Client Management

saleshandy clients list
saleshandy clients list --search "Acme"
saleshandy clients assign --resource-type sequence --client-id ID --resource-ids ID1,ID2
saleshandy clients assign --resource-type emailAccount --client-id ID --resource-ids ID1

user — Team Info

saleshandy user team-members
saleshandy user team-members --json

warmup — Email Warmup

saleshandy warmup report EMAIL_ACCOUNT_ID 2025-03-15

Global Flags

These flags work on every command:

| Flag | Description | |------|-------------| | --json | Output as JSON | | --csv | Output as CSV | | --profile <name> | Use a named auth profile | | --api-key <key> | Override API key for this command | | --no-color | Disable colored output | | --help | Show help |

Shell Autocomplete

Set up tab completion:

saleshandy autocomplete

Follow the printed instructions for your shell (bash/zsh/fish).

FAQ

Q: How do I find the ID I need to pass to a command? A: Run the list command for that resource first. For example, saleshandy sequences list shows sequence IDs, then use that ID with saleshandy analytics sequence-stats --sequence-id <ID>.

Q: A command says a flag is required but I don't know the value. A: Run <command> --help to see the description and valid options for each flag. For IDs, list the parent resource first.

Q: How do I pass JSON data? A: Use --file path/to/file.json for import/connect/enrichment commands. For tag operations, pass inline JSON as a string: --prospects '[{"prospectId":"ID","tagId":"ID"}]'

Q: Can I use this in CI/CD? A: Yes. Set SALESHANDY_API_KEY as an environment variable and use --json for machine-readable output. Example: SALESHANDY_API_KEY=xxx saleshandy prospects list --json

Q: How do I check the status of an async operation? A: Import, enrichment, and connect operations return a request ID. Use the corresponding *-status command: saleshandy prospects import-status <requestId> or saleshandy enrichment status <requestId> --wait

Links

License

MIT - see LICENSE