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

highlevel-cli

v2.1.0

Published

Command-line interface for GoHighLevel, built around the writes that lose data

Readme

highlevel-cli

A command-line interface for GoHighLevel, built around the writes that lose data.

New here? → QUICKSTART.md - nothing to a working command in five minutes.

792 commands. 165 hand-written across 16 groups that guard GoHighLevel's destructive write behaviour, plus 627 generated from the official v3 OpenAPI specs so nothing is missing. Three API layers. And - the part that matters - a write-safety layer built out of the things that have actually gone wrong on live accounts.

Works with one sub-account. Scales to many: every command takes --profile, and the fleet group drives a whole roster from a YAML manifest.

npm install -g highlevel-cli
ghl --dry-run --profile LOCATION_A locations custom-values set --key business_hours --value "8-5"

Every shipped command, grouped by resource, with the curated/generated overlaps flagged: ghl --help for the groups, ghl api --help for the generated tree.


Write safety

This is the reason the project exists, so it goes first.

GoHighLevel has several endpoints that destroy data on a well-formed request. Not on a malformed one - on exactly the request the docs describe. Every item below is a guard around something that already broke a production account.

  • Global --dry-run. Intercepts every POST/PUT/DELETE across all three clients and returns synthetic success. Reads pass through untouched. Put it in front of any command and you get the exact request body you would have sent, on stderr and in the audit log.
  • Audit log. Every write - dry-runs included - appends a JSONL record to ~/.ghl-cli/audit.log: profile, method, path, body, outcome. A replayable answer to "what touched this sub-account, and when".
  • Read-back verification with lag polling. GHL has ~1-2s read-after-write lag on custom values, snippets, and forms. A naive immediate re-read reports a successful write as lost, so verification polls instead of reading once.
  • Fetch-merge on full-replace PUTs. Workflows, calendar slot blocks, domains, trigger links, and the service menu all silently null every field you omit. Partial updates are merged against a fresh read before sending.
  • WorkflowStepWipeError. The shared internal client hard-refuses a workflow-document PUT that carries no workflowData.templates. The rule it enforces was written down first, and violated anyway: a bulk folder sweep blanked the action steps of roughly a hundred live workflows, returning 200 on every call and looking correct until the automations stopped firing. Documentation did not prevent that. A guard on the path every internal write shares does.
  • Calendar slotDuration unit trap. Sending slotDurationUnit: "hours" is silently divided by 60, turning a 2-hour service into 2-minute bookable slots on a live calendar.
  • Confirm gates on every irreversible or batch-destructive write. --yes to script past them, deliberately.
  • 429-only retry with backoff and Retry-After. 5xx bubbles up rather than being retried blind, and POST /locations/ - billable, no rollback - never auto-retries.
  • Profile isolation via contextvars, not globals. A raw threading.Thread does not copy context, so an unwrapped thread lands your write on the wrong sub-account.

What this does not protect you from

Guards that stop at a known boundary are worth more than guards you believe are total, so:

  • ghl api is raw. The generated tree sends exactly the body you give it: no fetch-merge, no read-back. On GoHighLevel's full-replace PUTs that is a data-loss command. Where a curated equivalent exists, it is the one to use - ghl calendars update merges, ghl api calendars update-calendar does not.
  • WorkflowStepWipeError catches a blanked step list, not a shortened one. A PUT carrying one of three existing steps passes the guard and deletes the other two, because the client cannot know what the document held before. Read the workflow first.
  • --profile does not scope --experimental deletes. Several internal endpoints (DELETE /forms/{id} among them) take no location, so the id you pass decides the account. The profile is an audit label there, not a boundary.
  • Pagination can end early. paginate() stops when a page yields no cursor, and reaching --max-pages ends it silently. Treat a result at the page limit as partial.
  • doctor proves the credentials resolve, not that they are sufficient. A pit- token is opaque - no expiry, no scope list - and the live test reads one endpoint.

Why this and not the official MCP

HighLevel ships an official MCP server with 36 tools: Calendar (2), Contacts (8), Conversations (3), Locations (2), Opportunities (4), Payments (2), Blogs (7), Email templates (2), Social (6).

What that list contains zero of: workflows, snippets, forms, custom values, pipeline writes, reputation, snapshots, sub-account provisioning, the service menu, AI agents, billing, and analytics. There is also no --dry-run, no audit trail, and no fetch-merge - so a partial update is a live data-loss event rather than a caught mistake.

HighLevel has published a roadmap toward 250+ tools, so treat the endpoint gap as being on a clock. The write-safety layer is the part that does not erode.


Install

Needs GoHighLevel sub-account access (agency access for the fleet and agency commands). It does not need Python - the released binaries carry their own.

npm

npm install -g highlevel-cli        # then just `ghl`
npx highlevel-cli doctor            # or run it without installing

The npm package carries a prebuilt binary, so this needs Node only to install - the tool itself has no runtime dependency once it is on disk. npm picks the right binary for your platform from the os/cpu fields; there is no postinstall script, so npm ci --ignore-scripts works too.

Direct download

One file, no runtime, nothing else installed. Pick your platform from the latest release:

curl -L -o ghl https://github.com/chowdhuryrz/highlevel-cli/releases/latest/download/ghl-macos-arm64
chmod +x ghl && sudo mv ghl /usr/local/bin/
ghl --version

Swap ghl-macos-arm64 for ghl-macos-x86_64, ghl-linux-x86_64, or ghl-windows-x86_64.exe. Each release ships a .sha256 beside every binary.

With Python, if you would rather manage it that way

Needs Python 3.10+. Not on PyPI yet, so these install from the repo:

uv tool install git+https://github.com/chowdhuryrz/highlevel-cli    # puts `ghl` on PATH — like `npm i -g`
pipx install git+https://github.com/chowdhuryrz/highlevel-cli       # same isolation, if you already use pipx

Plain pip install works only inside a virtualenv you control. Outside one it drops this package and its dependencies into whichever environment is active - on a current macOS that is usually an externally-managed-environment refusal, and where it succeeds it can break an unrelated project sharing the interpreter.

No uv? curl -LsSf https://astral.sh/uv/install.sh | sh

From a clone

git clone https://github.com/chowdhuryrz/highlevel-cli && cd highlevel-cli
./install.sh

Then:

  1. Copy .env.example.env and fill in at minimum GHL_API_KEY_DEFAULT and GHL_LOCATION_ID_DEFAULT. DEFAULT is just the profile name used when you pass no --profile; any name works. The CLI reads the nearest .env from your working directory upward, and real environment variables always win over it.
  2. Audit it: ghl doctor - enumerates every token, decodes the JWTs, checks expiry, and smoke-tests one read per profile.
  3. Smoke-test: ghl --json locations get
  4. Optional: ghl extension unpacks a small Chrome extension that grabs your Firebase credentials, which only the --experimental commands need. Everything else runs on the token from step 1.

.env is yours and is gitignored. Nothing in this repo writes to it.


The three API layers

| Layer | Host | Auth | Covers | |---|---|---|---| | Public | services.leadconnectorhq.com | per-profile PIT (GHL_API_KEY_<PROFILE>) | reads plus most location writes | | Internal | backend.leadconnectorhq.com and parts of services | Firebase JWT from GHL_FIREBASE_REFRESH_TOKEN | everything the web UI can do: workflow writes, snippets, pipelines, form and custom-value deletes, reputation, stats, wallet | | Agency | services.leadconnectorhq.com | agency PIT (GHL_AGENCY_API_KEY) | snapshots, sub-account create, location-token mint |

A Private Integration Token cannot write workflows, pipelines, snippets, or domains, and cannot read reviews, stats, or wallet at all. Those are the --experimental surfaces: they run against undocumented internal endpoints that GoHighLevel may change without notice. Read SECURITY.md before you rely on them.

Per-endpoint detail, including full reproductions of every internal route - method, path, headers, auth layer, request and response shape - is in docs/api-capabilities.md. That document is the most useful thing in this repo.


Usage

# default profile
ghl contacts list --limit 5
ghl --json workflows list

# switch sub-account
ghl --profile LOCATION_A --json contacts list
GHL_PROFILE=LOCATION_B ghl --json forms list

# preview any write without sending it
ghl --dry-run --profile LOCATION_A locations custom-values set --key business_hours --value "..."

# internal API (needs GHL_FIREBASE_REFRESH_TOKEN)
ghl --experimental --profile LOCATION_A --json workflows get <workflowId>

# across a roster of sub-accounts
ghl fleet inventory --profiles ALL --scope all --out /tmp/inventory.json
ghl --dry-run fleet deploy --manifest fleet/locations.example.yaml --profile LOCATION_A

# agency
ghl agency snapshot list
ghl agency location create-from-snapshot --name "..." --timezone America/New_York

More recipes in docs/cli-cookbook.md.

Shell completion

# bash - add to ~/.bashrc
eval "$(_GHL_COMPLETE=bash_source ghl)"

# zsh - add to ~/.zshrc
eval "$(_GHL_COMPLETE=zsh_source ghl)"

# fish
_GHL_COMPLETE=fish_source ghl > ~/.config/fish/completions/ghl.fish

Completes group names, command names and flags, including the generated api tree.

Global flags

| Flag | Effect | |---|---| | --json | machine-readable output (use this for agents and pipes) | | --profile <NAME> | pick a sub-account profile from .env (default DEFAULT) | | --location-id <ID> | use this location ID instead of the profile's (the token still comes from the active profile) | | --experimental | enable the internal-API commands | | --dry-run | intercept every POST/PUT/DELETE, log the intended request, return synthetic success. Reads still hit the server. |

Command groups

--exp marks commands that additionally require --experimental.

| Group | Commands | |---|---| | contacts | list, get, create, update, delete, search, add-tag, remove-tag, notes (5), tasks (6) | | conversations | list, get, create, update, delete, messages, get-email, send | | opportunities | list, get, create, update, delete, pipelines, pipeline-create --exp, pipeline-update --exp, pipeline-delete --exp | | calendars | list, get, create, update, delete, slots, appointments, book, groups | | service-menu | services, categories, addons, locations, resources, booking-options, config (25 commands) | | workflows | list, get --exp, create --exp, create-step --exp, create-n8n --exp, delete --exp, duplicate --exp, move --exp, add-opp-action --exp, lint, enroll, remove | | snippets --exp | list, get, create, update, move, folders, folder-create, folder-delete | | forms | list, submissions, delete --exp, delete-submissions --exp | | locations | get, update, search, tags, custom-fields, custom-values (list/get/create/set/import/delete --exp/folder-create/folder-get/folder-delete), business-profile --exp | | links | list, get, create, update, delete | | reputation | widgets (list/get/create/update/delete) --exp | | domains | list, get, add, update, delete, check, validate | | ai | agents, bot --exp, kb, faqs, deploy-template | | users | list, get | | agency | snapshot (list/get/last-push), location (create-from-snapshot/mint-token) | | fleet | inventory, deploy, verify, generate-manifest | | doctor | pre-flight .env and token audit | | api | the generated long tail - 627 operations across 42 resources (see below) |

ghl api - the generated long tail

Every group above except api is hand-written. Five more (payments, invoices, social, emails, documents) were removed once ghl api shipped: they were thin wrappers over the same endpoints, and the generated tree exposes far more of each - 45 social-planner operations against the 3 they had, 42 invoice operations against 4. Everything else GoHighLevel documents is reachable through ghl api, which builds its Click command tree at runtime from ghl_cli/data/api_index.json - 627 operations across 42 resources, compiled straight from GoHighLevel's official v3 OpenAPI specs.

ghl api --help                          # 42 resources
ghl api surveys --help                  # operations on one resource
ghl api surveys get-surveys
ghl --dry-run api surveys get-surveys   # honoured here too

Generated commands send Version: v3, go through the same public-API client as everything else, and therefore inherit --dry-run and the ~/.ghl-cli/audit.log record for free. Regenerate the index against newer specs with python scripts/build_api_index.py.

They are raw endpoint wrappers. No fetch-merge on full-replace PUTs, no read-back verification, no confirm gates - none of the Write safety machinery above. That machinery is per-endpoint knowledge and it only exists in the curated groups.

So: where both exist, use the curated command. A generated resource can never shadow a curated group - api is registered last and lives under its own namespace - but the reverse mistake is easy to make by hand. Where a curated group and a generated resource share a name, the curated one wins - it is registered first and owns the name. Reach for ghl api for the endpoints nothing else covers, and for reads, where there is nothing to destroy.

Profiles

Every command takes --profile <NAME>, which resolves GHL_API_KEY_<NAME> and GHL_LOCATION_ID_<NAME> out of the environment. Add a sub-account by adding two lines to .env - there is no registry to update. fleet commands operate over the whole set from a YAML manifest; see fleet/locations.example.yaml.


Working with AI agents

The CLI is one bash tool instead of a several-hundred-tool MCP server, --json on every command, and --help at each level - so an agent discovers only what it needs, when it needs it. See QUICKSTART.md for the pattern, and give your agent --dry-run before its first write.


Development

python3 -m venv .venv && .venv/bin/pip install -e '.[dev]'
.venv/bin/python -m pytest tests/     # 285 tests, well under a second
.venv/bin/python -m ruff check .

See CONTRIBUTING.md.


License

MIT - see LICENSE. Portions derive from an earlier project; see NOTICE.