Mitc.ConnectWiseCli 0.1.0
cw — ConnectWise PSA CLI
cw is a personal-productivity command-line tool for ConnectWise PSA. It wraps a curated slice of the ConnectWise REST API, covering the things a billable team member needs daily: browsing projects, searching and updating tickets (both project tickets and service tickets), viewing your own time, and logging new time entries.
Everything the CLI does is authenticated as you — via your API key pair — and is therefore constrained by your ConnectWise security role. No separate service account is required.
Getting Your ConnectWise API Keys
API keys are generated from inside ConnectWise under your own member account. You need three pieces of information before running cw config set:
| Piece | Where to get it |
|---|---|
| Public key | ConnectWise → My Account → API Keys → New → copy the Public Key |
| Private key | Same dialog — copy the Private Key immediately; ConnectWise never shows it again |
| Client ID | The integration ID registered at the ConnectWise Developer Network. M&I already has one; ask your manager if you don't have it |
Your security role must permit API key generation. If the API Keys tab is missing from My Account, ask your ConnectWise administrator to enable it for your role.
Authentication uses HTTP Basic auth in the form company+publicKey:privateKey, sent with every request alongside a clientId header. There is no username/password login flow and no token refresh — every call is self-contained.
Installation
dotnet publish -c Release -o ./publish
Add the publish directory to your PATH, or create an alias cw pointing to ConnectWiseCli.exe.
Configuration
Run cw config set once to create the config file:
cw config set \
--client-id "your-connectwise-client-id" \
--company "mitcs" \
--public-key "AbCdEf1234" \
--private-key "xYzSecretKey" \
--member "skuehlshelby"
Optional time-logging defaults (can be set now or updated later):
cw config set \
--client-id "your-connectwise-client-id" \
--company "mitcs" \
--public-key "AbCdEf1234" \
--private-key "xYzSecretKey" \
--member "skuehlshelby" \
--billable "Billable" \
--work-type-id 12 \
--work-role-id 7
All config set options:
| Option | Required | Description |
|---|---|---|
--instance |
No | ConnectWise hostname (default: clearit.mitcs.com) |
--client-id |
Yes | ConnectWise Developer Network integration ID |
--company |
Yes | ConnectWise company short name (e.g. mitcs) |
--public-key |
Yes | API key public portion |
--private-key |
Yes | API key private portion |
--member |
Yes | Your ConnectWise member identifier (e.g. skuehlshelby) |
--billable |
No | Default billable option for time entries (e.g. Billable, DoNotBill) |
--work-type-id |
No | Default work type ID for time entries |
--work-role-id |
No | Default work role ID for time entries |
Config file location
The config is stored at:
%APPDATA%\ConnectWiseCli\config.json
It is plain JSON and is human-editable. It contains your private API key in plain text. Do not commit it to source control.
To inspect the current config (private key is always redacted in output):
cw config show
Global Flags
These flags apply to every command and must be placed before the subcommand:
| Flag | Description |
|---|---|
--json |
Emit raw JSON instead of formatted tables. Useful for piping to jq or other tools |
-v / --verbose |
Enable debug logging, including redacted HTTP request/response details |
Examples:
cw --json projects list --mine
cw -v tickets search --query "Sierra Nevada" --board "Internet Solutions Projects"
Commands
config
cw config set
Create or update the config file. All options are described in the Configuration section above.
cw config show
Display the current configuration. The private key is always shown as ********.
projects
cw projects list
List projects. Without options, lists all projects your security role can see.
cw projects list --mine
cw projects list --board "Internet Solutions Projects"
cw projects list --company "County of Glenn" --status "Open"
| Option | Description |
|---|---|
--mine |
Filter to projects where you are the manager |
--board |
Filter by board name |
--company |
Filter by company name |
--status |
Filter by status name (e.g. Open, Closed) |
--conditions |
Raw ConnectWise conditions string (overrides the other filters) |
Output columns: Id, Name, Company, Status, Budget (hours), Actual (hours).
cw projects get <id>
Show full details for a single project.
cw projects get 1042
Output fields: Id, Name, Company, Board, Status, Manager, Budget Hours, Actual Hours.
tickets
cw tickets search
Search across project tickets and service tickets. By default, searches both types.
cw tickets search --query "email migration" --board "Internet Solutions Projects"
cw tickets search --mine --type service --status "In Progress"
cw tickets search --company "AM King" --conditions "summary contains \"server\""
| Option | Description |
|---|---|
--query |
Free-text search on ticket summary |
--type |
project, service, or both (default: both) |
--mine |
Filter to tickets assigned to you |
--company |
Filter by company name |
--board |
Filter by board name |
--status |
Filter by status name |
--conditions |
Raw ConnectWise conditions string (overrides the other filters) |
Output columns: Id, Type, Summary, Company, Status.
cw tickets get <id>
Show full details for a single ticket. Tries service tickets first, then project tickets.
cw tickets get 88421
Output fields: Id, Type, Summary, Company, Board, Status, Owner, Budget Hours, Actual Hours.
cw tickets update <id>
Update fields on a ticket. At least one of --status, --assign, --summary, or --set must be provided.
cw tickets update 88421 --status "In Progress" --assign "skuehlshelby"
cw tickets update 88421 --summary "CSU Chico – email migration Phase 2"
cw tickets update 88421 --set "priority/name=High" --set "impact/name=Medium"
| Option | Description |
|---|---|
--type |
project or service. If omitted, resolved by fetching the ticket first |
--status |
New status name |
--assign |
Assignee member identifier (e.g. skuehlshelby) |
--summary |
New summary text |
--set |
Raw JSON Patch path=value pair (repeatable). E.g. --set "priority/name=High" |
cw tickets time <id>
List all time entries logged against a ticket.
cw tickets time 88421
Output columns: Id, Member, Start, Hours, Billable, Notes.
time
cw time list
List your own time entries. Defaults to the current calendar month.
cw time list
cw time list --from 2026-06-01 --to 2026-06-30
cw time list --unbilled
| Option | Description |
|---|---|
--from |
Start date, inclusive (format: yyyy-MM-dd). Defaults to first day of current month |
--to |
End date, exclusive (format: yyyy-MM-dd). Defaults to first day of next month |
--unbilled |
Show only entries not yet on an invoice |
Output columns: Id, Start, Hours, Charge (type + ticket id), Billable, WIP, Notes.
cw time add
Log a time entry against a ticket.
cw time add --ticket 88421 --start 9:00 --end 11:30 --notes "Deployed new Integral Ag build to staging"
cw time add --ticket 88421 --start 13:00 --end 14:00 --date 2026-06-20 --billable DoNotBill
| Option | Required | Description |
|---|---|---|
--ticket |
Yes | Ticket ID to log against |
--start |
Yes | Start time (e.g. 9:00, 13:30) |
--end |
Yes | End time (e.g. 11:30, 17:00) |
--date |
No | Date in yyyy-MM-dd format. Defaults to today |
--notes |
No | Time entry notes |
--type |
No | project or service. If omitted, resolved by fetching the ticket first |
--billable |
No | Billable option (e.g. Billable, DoNotBill). Defaults to config value |
--work-type-id |
No | Work type ID. Defaults to config value |
--work-role-id |
No | Work role ID. Defaults to config value |
Hooks
cw can run a shell command after certain write operations succeed. Configure hooks in config.json under a hooks object:
{
"hooks": {
"time.added": "my-time-notifier.exe",
"ticket.updated": "my-ticket-webhook.bat"
}
}
Each value is a command line executed via cmd /c. The event context is serialized as JSON and piped to the command's standard input.
Supported events
| Event | Fired after | Context (stdin JSON) |
|---|---|---|
time.added |
cw time add succeeds |
The full TimeEntry object returned by the ConnectWise API |
ticket.updated |
cw tickets update succeeds |
{ "id": int, "type": string, "status": string, "assign": string, "summary": string } |
Failure behavior
Hooks are best-effort: if the hook command exits with a non-zero code or throws an exception, cw logs a warning and continues normally. A hook failure never blocks or fails the command that fired it.
Error Handling
- If no config file exists, commands that require ConnectWise access will print a clear message directing you to run
cw config set. - API errors surface as a problem message including the HTTP status and ConnectWise error code.
- Run with
-vto see the full request/response cycle (private key is never logged).
| Version | Downloads | Last updated |
|---|---|---|
| 0.4.13 | 0 | 7/14/2026 |
| 0.4.12 | 1 | 7/13/2026 |
| 0.4.11 | 1 | 7/10/2026 |
| 0.4.10 | 1 | 7/9/2026 |
| 0.4.9 | 2 | 7/6/2026 |
| 0.4.8 | 1 | 7/2/2026 |
| 0.4.7 | 1 | 7/1/2026 |
| 0.4.6 | 1 | 6/25/2026 |
| 0.4.5 | 1 | 6/25/2026 |
| 0.4.4 | 1 | 6/23/2026 |
| 0.4.3 | 1 | 6/23/2026 |
| 0.4.2 | 1 | 6/23/2026 |
| 0.4.1 | 1 | 6/23/2026 |
| 0.4.0 | 0 | 6/23/2026 |
| 0.3.1 | 0 | 6/23/2026 |
| 0.3.0 | 0 | 6/23/2026 |
| 0.2.1 | 0 | 6/23/2026 |
| 0.2.0 | 1 | 6/23/2026 |
| 0.1.0 | 1 | 6/22/2026 |