@rowvyn/servicetitan-cli
v0.3.6
Published
ServiceTitan CLI
Downloads
1,714
Maintainers
Readme
ServiceTitan CLI
A first-party quality CLI for the ServiceTitan API.
Built by Rowvyn — See the case study →
ServiceTitan CLI brings customer, job, invoice, dispatch, reporting, and operational intelligence workflows into a single st binary. It is built for developers, operators, and AI agents that need clean output, secure auth, fast scripting, and a reliable escape hatch when the named commands do not cover a niche endpoint yet.
Why
ServiceTitan has no official CLI. The REST API is powerful but requires manual token management, pagination handling, and response parsing. This CLI:
- Handles OAuth 2.0 authentication with secure OS keychain storage
- Paginates automatically with
--all - Formats output as tables, JSON, or CSV
- Provides intelligence commands that combine multiple API calls into operational insights
- Supports AI agent workflows with compact structured output
Requirements
- Node.js >= 20
- macOS, Linux, or Windows
- Linux only:
libsecret-1-devis required for secure credential storage# Debian/Ubuntu sudo apt-get install -y libsecret-1-dev # Fedora sudo dnf install -y libsecret-devel
Installation
The package installs a single binary: st.
npm
Linux note: keytar depends on libsecret. Install libsecret-1-dev before the npm global install on Ubuntu/Debian runners and workstations.
npm install -g @rowvyn/servicetitan-cli
st --versionHomebrew
brew tap montrellcruse/servicetitan-cli
brew install servicetitan-cli
st --versionDirect download
Standalone binaries are not currently published. Use the npm package or Homebrew formula instead.
Quick Start
Authentication
The first run is usually under a minute: authenticate once, then use named profiles for production and integration tenants.
$ st auth login
Profile name: acme
Environment: production
Client ID: cid_live_01JQ7Q2W9GQK1Q8D
Client Secret: ********************************
App Key: ak_live_01JQ7Q8X74A8K4X1
Tenant ID: 985798691
✓ Authenticated profile "acme" and set it as the default profile.$ st auth status
Name Environment TenantId Credentials Default
acme production 985798691 yes true
acme-int integration 985798691 yes falseYou can also print the current bearer token for one-off debugging:
st auth tokenOptional but useful on day one:
st completion installCommand Overview
Every command supports --profile, --output table|json|csv, --compact, and --no-color. Most list commands also support --limit, --page, --fields, and --all for autopagination.
| Command Group | Description |
|--------------|-------------|
| st auth | Login, logout, switch profiles, print token, whoami |
| st customers | List, get, create, update customers and contacts |
| st jobs | Book, list, get, update, cancel, and complete jobs |
| st invoices | List and get invoice records |
| st estimates | List, get, sell, and unsell estimates |
| st leads | List, get, convert, and dismiss leads |
| st bookings | List, get, accept, and dismiss booking requests |
| st memberships | List memberships, types, and recurring services |
| st appointments | List and get job appointments |
| st dispatch | View dispatch board, capacity, teams, zones, and assign technicians |
| st techs | List and get technician profiles |
| st employees | List and get employee records |
| st locations | List, get, create, and update service locations |
| st pricebook | Browse services, materials, equipment, and categories |
| st reporting | List and run ServiceTitan reports |
| st payroll | View payrolls, gross pay, timesheets, and activity codes |
| st timesheets | List technician timesheets and activity types |
| st calls | Browse telecom call records |
| st inventory | View purchase orders, trucks, vendors, and warehouses |
| st business-units | List and get business unit records |
| st job-types | List and get job type definitions |
| st revenue | Revenue rollup with period shortcuts (day/week/month/year/ytd) |
| st snapshot | Daily ops briefing — jobs, revenue MTD, pipeline at a glance |
| st api | Raw API access (GET/POST/PUT/DELETE) with full auth and output rendering |
| st completion | Install shell tab completion for bash, zsh, or fish |
Commands
auth
Manage ServiceTitan profiles and credentials.
$ st auth login --profile branch-ops --env integration
Client ID: cid_test_01JQ7R4Q6A8G1H2N
Client Secret: ********************************
App Key: ak_test_01JQ7R6VDQW3M4TY
Tenant ID: 985798691
✓ Authenticated profile "branch-ops".$ st auth status
Name Environment TenantId Credentials Default
acme production 985798691 yes true
branch-ops integration 985798691 yes false$ st auth logout --profile branch-ops
✓ Removed profile "branch-ops".customers
Browse CRM customers, inspect a single record, and create or update customers safely.
$ st customers list --help
USAGE
$ st customers list [--output table|json|csv] [--profile <value>] [--color] [--compact] [--search <value>] [--active] [--page <value>] [--limit <value>] [--all] [--fields <value>]
FLAGS
--search=<value> Customer search string
--active Only include active customers
--page=<value> Page number to fetch (1-based)
--limit=<value> Maximum number of customers to return
--all Fetch all customer pages
--fields=<value> Comma-separated fields to include$ st customers list --search "martinez" --limit 3
Id Name Phone Email Active Created
403219 Martinez, Elena 602-555-0182 [email protected] true 2023-09-14T16:22:09Z
417004 Martinez, Carlos 602-555-0199 [email protected] true 2024-02-08T11:05:44Z
422771 Martinez Family HOA 480-555-0117 [email protected] true 2024-11-21T08:39:02Z$ st customers get 403219
Field Value
Id 403219
Name Martinez, Elena
Phone 602-555-0182
Email [email protected]
Active true
Created 2023-09-14T16:22:09Z
Address 1842 S Desert Willow Dr
City Mesa
State AZ
Zip 85209$ st customers create --name "Harper Family" --phone "480-555-0104" --email "[email protected]" --city "Gilbert" --state "AZ" --zip "85295" --dry-run
[DRY RUN] POST https://api.servicetitan.io/crm/v2/tenant/985798691/customers
Body:
{
"name": "Harper Family",
"phone": "480-555-0104",
"email": "[email protected]",
"address": {
"city": "Gilbert",
"state": "AZ",
"zip": "85295"
}
}jobs
Inspect the operational core of the tenant: job queues, schedules, totals, and safe write operations.
$ st jobs list --help
USAGE
$ st jobs list [--output table|json|csv] [--profile <value>] [--color] [--compact] [--status <value>] [--date <value>] [--date-range <value>] [--page <value>] [--limit <value>] [--all] [--fields <value>]
FLAGS
--status=<value> Comma-separated job statuses
--date=<value> Exact date to filter by (YYYY-MM-DD)
--date-range=<value> Date range to filter by (YYYY-MM-DD..YYYY-MM-DD)
--page=<value> Page number to fetch (1-based)
--limit=<value> Maximum number of jobs to return
--fields=<value> Comma-separated fields to include$ st jobs list --status Scheduled,InProgress --date 2026-03-26 --limit 4
Id Status Customer Type Scheduled Total
845102 Scheduled Johnson Family Precision Tune-Up 2026-03-26T09:00:00Z 189
845118 InProgress Parkview Dental RTU Cooling Repair 2026-03-26T10:30:00Z 1240
845131 Scheduled Bell Residence Water Heater Replace 2026-03-26T13:00:00Z 0
845144 Scheduled Copper State Suites Plumbing Inspection 2026-03-26T15:30:00Z 0$ st jobs get 845118
Field Value
Id 845118
Status InProgress
Customer Parkview Dental
Type RTU Cooling Repair
Scheduled 2026-03-26T10:30:00Z
Total 1240
Summary Rear rooftop package unit not cooling
Business Unit Commercial HVAC
Technician Ava Thompson
Created 2026-03-25T18:02:14Z$ st jobs book --customer 403219 --type 117 --date 2026-03-29 --tech 52 --priority urgent --summary "No cooling - upstairs system down" --location 550912 --business-unit 14 --dry-run
[DRY RUN] POST https://api.servicetitan.io/jpm/v2/tenant/985798691/jobs
Body:
{
"customerId": 403219,
"jobTypeId": 117,
"scheduledDate": "2026-03-29",
"technicianId": 52,
"priority": "Urgent",
"summary": "No cooling - upstairs system down",
"locationId": 550912,
"businessUnitId": 14
}invoices
Review receivables, balances, and invoice detail from accounting.
$ st invoices list --help
USAGE
$ st invoices list [--output table|json|csv] [--profile <value>] [--color] [--compact] [--status paid|unpaid|void] [--page <value>] [--limit <value>] [--all] [--fields <value>]
FLAGS
--status=<option> Invoice status filter (paid, unpaid, void)
--page=<value> Page number to fetch (1-based)
--limit=<value> Maximum number of invoices to return
--all Fetch all invoice pages
--fields=<value> Comma-separated fields to include$ st invoices list --status unpaid --limit 3
Id Status Customer Total Balance Created
992410 unpaid Bell Residence 412.50 412.50 2026-03-23T19:12:05Z
992417 unpaid Parkview Dental 1240 620 2026-03-24T17:44:18Z
992422 unpaid Copper State Suites 1880 1880 2026-03-25T20:11:40Z$ st invoices get 992417
Field Value
Id 992417
Status unpaid
Customer Parkview Dental
Total 1240
Balance 620
Created 2026-03-24T17:44:18Z
Job Id 845118
Invoice Number INV-104982
Due Date 2026-04-08$ st invoices list --fields id,customer,total,balance --output csv
id,customer,total,balance
992410,Bell Residence,412.5,412.5
992417,Parkview Dental,1240,620
992422,Copper State Suites,1880,1880techs
List technicians and inspect the people behind the dispatch board.
$ st techs list --help
USAGE
$ st techs list [--output table|json|csv] [--profile <value>] [--color] [--compact] [--active] [--page <value>] [--limit <value>] [--all] [--fields <value>]
FLAGS
--active Only include active technicians
--page=<value> Page number to fetch (1-based)
--limit=<value> Maximum number of technicians to return
--all Fetch all technician pages
--fields=<value> Comma-separated fields to include$ st techs list --active --limit 4
Id Name Phone Email Active
52 Ava Thompson 602-555-0125 [email protected] true
61 Luis Ortega 602-555-0141 [email protected] true
77 Mia Campbell 480-555-0132 [email protected] true
84 Jordan Reeves 480-555-0154 [email protected] true$ st techs get 52
Field Value
Id 52
Name Ava Thompson
Phone 602-555-0125
Email [email protected]
Active true
Business Unit Residential HVAC
Employee Id EMP-1042$ st techs list --fields id,name,email --output json
[
{
"id": 52,
"name": "Ava Thompson",
"email": "[email protected]"
}
]memberships
Track active agreement revenue, filter by customer, and inspect membership types.
$ st memberships list --limit 3
Id Type Customer Status Start End Recurring
51012 Comfort Club Gold Harper Family Active 2025-07-12 2026-07-11 true
51027 Plumbing Peace Plan Bell Residence Active 2025-10-02 2026-10-01 true
51043 Commercial Priority Parkview Dental Active 2026-01-15 2027-01-14 true$ st memberships get 51012
Field Value
Id 51012
Type Comfort Club Gold
Customer Harper Family
Status Active
Start 2025-07-12
End 2026-07-11
Recurring true
Customer Id 403219
Location Id 550912
Price 29
Billing Frequency Monthly
Created 2025-07-12T15:20:43Z$ st memberships types --active
Id Name Duration Price Active
901 Comfort Club Gold 12 29 true
902 Plumbing Peace Plan 12 24 true
930 Commercial Priority 12 149 trueestimates
Review open and sold estimates without leaving the terminal.
$ st estimates list --status open --limit 3
Id Status Customer Job Total Created
730118 open Harper Family 845102 6850 2026-03-24T18:10:11Z
730141 open Copper State Suites 845144 12400 2026-03-25T21:02:49Z
730155 open Bell Residence 845131 980 2026-03-26T16:27:38Z$ st estimates get 730118
Field Value
Id 730118
Status open
Customer Harper Family
Job 845102
Total 6850
Created 2026-03-24T18:10:11Z
Name 3-ton heat pump replacement
Sold On
Dismissed On
Created By Ava Thompson$ st estimates list --job 845102 --fields id,status,total --output json
[
{
"id": 730118,
"status": "open",
"total": 6850
}
]leads
Monitor lead flow and safely convert or dismiss leads.
$ st leads list --status open --limit 3
Id Status Customer Campaign Created
210411 open Rivera Residence Google Local Services 2026-03-26T14:05:10Z
210419 open Mesa Animal Clinic Spring Tune-Up Email 2026-03-26T14:28:31Z
210427 open Parkview Dental Referral Program 2026-03-26T15:02:44Z$ st leads get 210411
Field Value
Id 210411
Status open
Customer Rivera Residence
Campaign Google Local Services
Created 2026-03-26T14:05:10Z
Phone 480-555-0178
Email [email protected]
Assigned To Intake Team
Source website$ st leads convert 210411 --dry-run
[DRY RUN] POST https://api.servicetitan.io/crm/v2/tenant/985798691/leads/210411/convert
Body:
{}bookings
Triage inbound booking requests from the tenant-facing bookings API.
$ st bookings list --status open --page 2 --limit 3
Id Status Customer Source Created
310511 Open Rivera Residence Google Local Services 2026-03-26T14:05:10Z
310512 Open Mesa Animal Clinic Website Widget 2026-03-26T14:28:31Z
310513 Open Parkview Dental Referral Program 2026-03-26T15:02:44Z$ st bookings get 310511
Field Value
Id 310511
Status Open
Customer Rivera Residence
Source Google Local Services
Created 2026-03-26T14:05:10Z
Phone 480-555-0178
Email [email protected]
Address 1842 S Desert Willow Dr
Notes Customer prefers text updates$ st bookings dismiss 310511 --reason "Duplicate inquiry" --dry-run
[DRY RUN] PATCH https://api.servicetitan.io/crm/v2/tenant/985798691/bookings/310511
Body:
{
"status": "Dismissed",
"reason": "Duplicate inquiry"
}pricebook
Search the catalog your field teams actually sell from: services, materials, and equipment.
$ st pricebook services --search "tune" --limit 3
Id Name Price Duration Active
117 Precision Tune-Up 189 90 true
118 Premium AC Tune-Up 249 120 true
212 Commercial RTU Tune-Up 425 180 true$ st pricebook materials --search "capacitor" --limit 3
Id Name Price Unit Cost Active
4412 Dual Run Capacitor 45/5 189 42 true
4418 Dual Run Capacitor 40/5 179 39 true
4426 Start Capacitor Kit 129 24 true$ st pricebook equipment --active --fields id,name,price --output json
[
{
"id": 8801,
"name": "3 Ton Heat Pump 15.2 SEER2",
"price": 6850
}
]dispatch
Use dispatch-focused views for appointments, capacity, and assignment changes.
$ st dispatch board --date 2026-03-26
Appointment Job Tech Start End Status
401992 845102 Ava Thompson 2026-03-26 09:00 2026-03-26 10:30 Scheduled
401997 845118 Luis Ortega 2026-03-26 10:30 2026-03-26 12:00 InProgress
402011 845144 Mia Campbell 2026-03-26 15:30 2026-03-26 17:00 Scheduled$ st dispatch capacity --date 2026-03-26
Business Unit Available Scheduled
Residential HVAC 6 18
Commercial HVAC 3 7
Plumbing 4 9$ st dispatch assign --appointment 402011 --tech 84 --dry-run
[DRY RUN] POST https://api.servicetitan.io/dispatch/v2/tenant/985798691/appointment-assignments
Body:
{
"appointmentId": 402011,
"technicianId": 84
}reporting
Run ServiceTitan reports directly from the reporting API without leaving your shell.
$ st reporting list
Category Report Id
operations Revenue by Technician 175
operations Completed Jobs by Day 214
marketing Leads by Campaign 308$ st reporting run --category operations --report 175 --from 2026-03-01 --to 2026-03-31 --output json
{
"data": [
{
"technician": "Ava Thompson",
"completedRevenue": 84210.44,
"completedJobs": 96
},
{
"technician": "Luis Ortega",
"completedRevenue": 73122.18,
"completedJobs": 88
}
]
}$ st reporting run --category operations --report 214 --limit 10 --output csv
date,completedJobs,avgTicket
2026-03-20,18,612.44
2026-03-21,14,588.12
2026-03-22,11,540.09revenue
Use the intelligence layer when you want business answers, not raw endpoint spelunking.
$ st revenue --help
USAGE
$ st revenue [--output table|json|csv] [--profile <value>] [--color] [--compact] [--period day|week|month|year|ytd] [--from <value>] [--to <value>]
FLAGS
--period=<option> [default: month] Revenue period
--from=<value> Start date (YYYY-MM-DD)
--to=<value> End date (YYYY-MM-DD)
--output=<option> Output format
--compact Trim output for scripts and AI agents$ st revenue --period month
Revenue Summary — March 2026
Metric Value
Total Revenue $248,421.72
Total Jobs 312
Avg Job Value $796.22
Date Range Mar 1 - Mar 26, 2026$ st revenue --period ytd --output json
{
"avg_job_value": 802.51,
"from": "2026-01-01",
"period": "ytd",
"to": "2026-03-26",
"total_jobs": 2316,
"total_revenue": 1850603.16
}$ st revenue --from 2026-03-01 --to 2026-03-15 --compact
{"avg_job_value":784.37,"from":"2026-03-01","period":"month","to":"2026-03-15","total_jobs":177,"total_revenue":138833.49}snapshot
Get the daily operations briefing your team cares about first thing in the morning.
$ st snapshot
Snapshot — March 26, 2026
Operations
Metric Value
Jobs Today 22
Jobs This Week 87
Revenue MTD $248,421.72
Pipeline
Metric Value
Open Estimates 14
Active Memberships 1287
Open Leads 7$ st snapshot --date 2026-03-25 --output json
{
"date": "2026-03-25",
"jobs_today": 19,
"jobs_this_week": 81,
"revenue_mtd": 240181.54,
"open_estimates": 13,
"active_memberships": 1285,
"open_leads": 8
}$ st snapshot --compact
{"date":"2026-03-26","jobs_today":22,"jobs_this_week":87,"revenue_mtd":248421.72,"open_estimates":14,"active_memberships":1287,"open_leads":7}api
For anything not covered yet by a named command, call the raw API directly while still reusing auth, profiles, and output rendering.
$ st api get /crm/v2/tenant/{tenant}/customers --params "page=1,pageSize=2" --output json
[
{
"id": 403219,
"name": "Martinez, Elena"
},
{
"id": 417004,
"name": "Martinez, Carlos"
}
]$ st api post /crm/v2/tenant/{tenant}/customers --body '{"name":"Greenway Fitness","phone":"602-555-0190","email":"[email protected]"}' --yes
Field Value
Id 430221
Name Greenway Fitness
Phone 602-555-0190
Email [email protected]$ st api delete /crm/v2/tenant/{tenant}/tags/118 --yes
✓ DELETE request succeeded.completion
Install shell completion when you want faster CLI navigation and flag discovery.
$ st completion install --shell zsh
✓ Installed zsh completion at ~/.zshrcFor AI Agents
The CLI includes first-class support for AI agent consumption:
# Enable compact output
export ST_AGENT_MODE=1
# Get revenue data as JSON
st revenue --from 2025-01-01 --to 2025-12-31 --json
# Daily ops snapshot
st snapshot --json
# Pipe to your agent
st customers list --all --json | your-agent processThe intelligence layer exists to minimize overhead for agents. A generic MCP integration often needs large tool schemas, endpoint descriptions, and follow-up discovery calls before the model can answer a simple ops question. The CLI avoids that by pushing the business question into the command name itself.
$ st revenue --period ytd --compact
{"avg_job_value":802.51,"from":"2026-01-01","period":"ytd","to":"2026-03-26","total_jobs":2316,"total_revenue":1850603.16}
$ st snapshot --compact
{"date":"2026-03-26","jobs_today":22,"jobs_this_week":87,"revenue_mtd":248421.72,"open_estimates":14,"active_memberships":1287,"open_leads":7}These payloads are zero-schema output:
- No endpoint discovery round-trip
- No large tool definition injected into the prompt
- No empty arrays, null-heavy objects, or audit metadata when
--compactorST_AGENT_MODE=1is set - A single command produces exactly the slice of business context an agent needs
$ st jobs list --status Scheduled --limit 2 --output json | jq '.[].id'
845102
845131
$ st snapshot --output json | jq '{date, jobs_today, revenue_mtd}'
{
"date": "2026-03-26",
"jobs_today": 22,
"revenue_mtd": 248421.72
}Environment Variables
| Variable | Description | Default |
|----------|-------------|---------|
| ST_PROFILE | Override the active profile | First profile |
| ST_OUTPUT | Default output format: table, json, or csv | table |
| ST_AGENT_MODE | Set to 1 for compact JSON output optimized for AI agent consumption | 0 |
| ST_TIMEZONE | IANA timezone for date-aware queries (e.g., America/New_York) | System timezone |
| ST_NO_COLOR | Disable colorized terminal output | — |
| ST_ENVIRONMENT | Override stored environment at runtime | — |
| ST_TENANT_ID | Override tenant ID at runtime | — |
| ST_APP_KEY | Override app key at runtime | — |
| ST_CONFIG_DIR | Use a non-default config directory | ~/.config/st |
| ST_CLIENT_ID | ServiceTitan OAuth client ID (overrides stored credentials) | — |
| ST_CLIENT_SECRET | ServiceTitan OAuth client secret (overrides stored credentials) | — |
| ST_TIMEOUT | Global request timeout in milliseconds | 30000 |
Security note: When
ST_CLIENT_IDandST_CLIENT_SECRETare set, they take precedence over credentials stored in the OS keychain. Use environment variables for CI/CD pipelines; usest auth loginfor interactive use.
Example overrides:
ST_PROFILE=acme-int st jobs list --status Scheduled
ST_OUTPUT=json st customers list --limit 5
ST_ENVIRONMENT=integration st revenue --period monthArchitecture / How It Works
See ARCHITECTURE.md for an overview of the project structure, module layout, auth flow, and output rendering pipeline.
Output Formats
Choose the renderer that matches the job:
--output tablefor interactive terminal work.--output jsonforjq, scripts, agents, and downstream tooling.--output csvfor exports into spreadsheets or BI pipelines.
Field projection keeps large responses focused:
st jobs list --date-range 2026-03-01..2026-03-31 --fields id,status,customer,total --output csvCompact mode removes dead weight from responses:
st techs list --fields id,name,email --output json --compactCommon combinations:
st customers list --output json
st invoices list --fields id,customer,total,balance --output csv
st revenue --period ytd --compactEscape Hatch
Named commands cover the common workflows. When you need something obscure or newly released, use the raw API verbs:
st api get /crm/v2/tenant/{tenant}/customers --params "page=1,pageSize=50"
st api post /crm/v2/tenant/{tenant}/customers --body '{"name":"Acme Mechanical"}' --yes
st api put /crm/v2/tenant/{tenant}/customers/403219 --body '{"phone":"602-555-0199"}' --yes
st api delete /crm/v2/tenant/{tenant}/tags/118 --yesMutation verbs prompt before sending unless --yes is passed. Use --dry-run to print the resolved method, path, and JSON body without making the request.
The escape hatch still gives you:
- Stored auth and profile selection.
{tenant}substitution against the active profile.- Table, JSON, or CSV output formatting when the response is record-like.
Configuration
Profiles live in ~/.config/st/config.json by default. Secrets do not: client credentials are stored separately in the OS keychain. The config file tracks the default profile, output settings, color preference, compact mode, and non-secret profile metadata such as tenantId, appKey, and environment.
Typical setup:
st auth login --profile acme-prod --env production
st auth login --profile acme-int --env integration
st --profile acme-int snapshotSupported environments:
productionfor live tenant access.integrationfor sandbox and pre-production workflows.
Contributing
Issues and pull requests are welcome at github.com/montrellcruse/servicetitan-cli. If you are working on the CLI locally, the standard validation loop is:
npm run typecheck
npm run lint
npm testSee CONTRIBUTING.md for full contribution guidelines, branch conventions, and commit format.
Security
For security disclosures, see SECURITY.md.
License
MIT
