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

@masonlandcattle/servicetitan-sdk

v0.9.6

Published

ServiceTitan API SDK for Node.js and TypeScript with retries, rate limiting, pagination, and broad tenant API coverage.

Readme

ServiceTitan SDK (Node.js / TypeScript)

A pragmatic ServiceTitan API client with:

  • Robust auth (Client Credentials)
  • Retries with exponential backoff + jitter
  • Honors Retry-After, handles 429/5xx
  • Built-in rate limiting with queued waiting in the client transport
  • URL builder and pagination helpers (getAll)
  • Broad tenant API namespace coverage across the current ServiceTitan developer portal
  • Exported TypeScript helper types for better IntelliSense when using the SDK

The package supports both:

  • a namespaced client via createClient(...)
  • direct functional imports per namespace when preferred

Release status

Current release focus:

  • Synced the SDK to the current ServiceTitan tenant API namespace set
  • Corrected stale routes, verbs, and path segments across multiple resources
  • Added missing documented resource modules and helpers
  • Expanded exported TypeScript helper types to improve IntelliSense

Recommended release posture for the current line:

  • 0.9.x is the stabilization phase after the broad OpenAPI alignment pass
  • the current public surface is intended to be much closer to the local OpenAPI specs than earlier releases
  • future breaking changes should mostly be reserved for upstream ServiceTitan API changes or clear correctness fixes

Repo notes

If you are editing this SDK with Codex or another coding agent, use AGENTS.md as the repo-specific guide.

The short version:

  • local OpenAPI JSON files are the source of truth
  • breaking changes are acceptable when the schema and SDK disagree
  • prefer namespace-specific types over generic placeholders
  • keep src/types/resources.ts for shared base helpers only
  • run npm run build after changes

Install

npm install @masonlandcattle/servicetitan-sdk

Configure

Set env vars or pass options:

  • TENANT_ID
  • APP_KEY
  • CLIENT_ID
  • SECRET_KEY
  • Optional: ENVIRONMENT = production | development

Usage (namespaced client)

import { createClient } from "@masonlandcattle/servicetitan-sdk";

const st = createClient({
  tenantId: process.env.TENANT_ID!,
  appKey: process.env.APP_KEY!,
  clientId: process.env.CLIENT_ID!,
  clientSecret: process.env.SECRET_KEY!,
  environment: (process.env.ENVIRONMENT as any) || "production",
  retries: 3,
  apiRateLimitPerSecond: 45,
  maxConcurrent: 20,
});

// List jobs (single page)
const page = await st.jpm.listJobs({ page: 1, pageSize: 100 });

// List all jobs server-side (aggregates pages)
const allJobs = await st.jpm.listJobs({ jobStatus: "Scheduled" }, { all: true, pageSize: 500 });

// Create a job note
await st.jpm.createJobNote(123456, { text: "Hello from SDK", pinToTop: true });

// Materials (with get-all)
const materials = await st.pricebook.listMaterials({}, { all: true, pageSize: 500 });

Important behaviors

  • Local OpenAPI JSON files in ServiceTitanOpenAPIJson are the source of truth for this SDK.
  • Breaking changes are allowed when the documented ServiceTitan contract and the previous SDK surface disagree.
  • Requests are throttled inside src/client.ts, so callers do not need to add their own queueing to use helpers like client.request(...) or client.getAll(...).
  • Normal tenant API traffic is queued at 45 requests per second per tenant by default. You can lower it or raise it during client creation, but the SDK clamps apiRateLimitPerSecond to 55 max.
  • Reporting runs made through st.reporting.getReportData(...) or st.reporting.startReportDataQuery(...) are additionally queued at 5 runs per minute for the same tenant + report category + report id.
  • When a limit is exceeded, the SDK waits in an in-memory queue instead of failing immediately. This means large getAll() calls or bursts of individual requests may take longer to complete under load.
  • If ServiceTitan still responds with 429, the client honors Retry-After when present and retries with exponential backoff as a fallback.
  • Export feeds generally use { from, includeRecentChanges } continuation params and return { data, hasMore, continueFrom }.
  • Binary telecom media endpoints return ArrayBuffer.
  • options.all is only used on endpoints with safe paginated data plus hasMore behavior.

Known limits

  • Webhook verification helpers are not included yet. The public docs available in this repo workflow are not specific enough to safely implement the exact signature contract.
  • Some nested model fields are still intentionally broad where the upstream schema is weak or highly open-ended.
  • This package is an API SDK, not an application framework. Queueing, persistence, workflow orchestration, and webhook hosting should stay in the consuming app.
  • ServiceTitan may change upstream schemas over time. When that happens, the local OpenAPI JSON files should be updated first, then the SDK should be realigned to match.

Docs map

  • README.md: install, usage, examples, and package behavior
  • AGENTS.md: repo-specific engineering rules for Codex and contributors
  • docs/architecture.md: structure, boundaries, and extension patterns

Resources and quick examples

Below is a brief tour of the main resource namespaces. Each list function supports { all?: boolean; pageSize?: number } to fetch all pages server‑side.

Initialize once and reuse:

import { createClient } from "@masonlandcattle/servicetitan-sdk";
const st = createClient({
  tenantId: process.env.TENANT_ID!,
  appKey: process.env.APP_KEY!,
  clientId: process.env.CLIENT_ID!,
  clientSecret: process.env.SECRET_KEY!,
});

Accounting (st.accounting)

  • Invoices, Payments, GL Accounts, Inventory Bills, AP Credits/Payments, Tax Zones, etc.
const invoices = await st.accounting.listInvoices({ customerId: 1234 }, { all: true });
const invoicesById = await st.accounting.listInvoicesByIds([111, 222, 333]);
await st.accounting.markInvoicesAsExported([111, 222]);
const gl = await st.accounting.listGlAccounts({ type: "Asset" }, { all: true });

CRM (st.crm)

  • Customers, Contacts, Locations, Leads, Bookings.
const customers = await st.crm.listCustomers({ search: "Acme" }, { all: true });
const contacts = await st.crm.listContacts({ updatedAfter: "2024-01-01" }, { all: true });
const locations = await st.crm.listLocations({ customerId: 1234 }, { all: true });

Dispatch (st.dispatch)

  • Teams, Zones, Arrival Windows, Technician Shifts, Appointment Assignments.
const teams = await st.dispatch.listTeams({}, { all: true });
const windows = await st.dispatch.listArrivalWindows({ date: "2025-01-01" }, { all: true });

Equipment Systems (st.equipmentSystems)

  • Installed Equipment, installed equipment systems, and metadata.
const eq = await st.equipmentSystems.listInstalledEquipment({ search: "Carrier" }, { all: true });
const systems = await st.equipmentSystems.listEquipmentSystems({}, { all: true });
await st.equipmentSystems.addEquipmentToSystem(123, { equipmentIds: [98765] });

Findings (st.findings)

  • Findings, finding assets, and finding attachments.
import type {
  FindingCreatePayload,
  FindingListResponse,
} from "@masonlandcattle/servicetitan-sdk";

const findings = await st.findings.listFindings({ page: 1, pageSize: 50 });
const assets = await st.findings.listFindingAssets({ page: 1, pageSize: 50 });

const findingPayload: FindingCreatePayload = {
  name: "Loose wire",
  summary: "Panel inspection issue",
  locationId: 1234,
};

const created = await st.findings.createFinding(findingPayload);
const attachment = await st.findings.createFindingAttachment(created.id!, {
  fileName: "inspection-photo.jpg",
  contentType: "image/jpeg",
  url: "https://example.com/inspection-photo.jpg",
});

Useful Findings exports for IntelliSense:

  • FindingCreatePayload
  • FindingUpdatePayload
  • FindingListParams
  • FindingListResponse
  • FindingAssetListParams
  • FindingAssetListResponse
  • FindingSummary
  • FindingAsset
  • FindingAttachment

Forms (st.forms)

  • Forms, Jobs + Forms, Submissions.
const forms = await st.forms.listForms({}, { all: true });

Inventory (st.inventory)

  • Purchase Orders (+ types/markups/requests), Receipts, Transfers, Returns, Trucks, Vendors, Warehouses.
const pos = await st.inventory.listPurchaseOrders({ status: "Open" }, { all: true });
const receipts = await st.inventory.listReceipts({ dateFrom: "2025-01-01" }, { all: true });

JPM (Jobs/Projects) (st.jpm)

  • Jobs, Appointments, appointment summaries, job equipment attachments, Projects, WBS, types/statuses.
const jobs = await st.jpm.listJobs({ jobStatus: "Scheduled" }, { all: true });
const appts = await st.jpm.listAppointments({ startsOnOrAfter: "2025-01-01" }, { all: true });
const jobEquipment = await st.jpm.attachJobEquipment(123456, { equipmentIds: [98765] });

Marketing (st.marketing) and Marketing Ads (st.marketingAds)

  • Campaigns/Categories/Costs; Ads Attributions, Performance, and Capacity Warnings.
const campaigns = await st.marketing.listCampaigns({}, { all: true });
await st.marketingAds.createWebLeadFormAttribution({
  leadId: 42,
  webSessionData: {
    landingPageUrl: "https://example.com/landing",
    referrerUrl: "https://google.com",
    utmSource: "google",
    utmMedium: "cpc",
    utmCampaign: "spring-promo",
  },
});

Marketing Reputation (st.marketingReputation)

  • Reviews.
const reviews = await st.marketingReputation.listReviews({ createdOnOrAfter: "2025-01-01" }, { all: true });

Memberships (st.memberships)

  • Memberships, Types, Recurring Service Types/Events/Services, Invoice Templates.
const memberships = await st.memberships.listMemberships({ customerId: 1234 }, { all: true });

Payroll (st.payroll)

  • Timesheets (+ non-job, per-job), Timesheet Codes, Payrolls, Adjustments, Job Splits, Location Labor Rates.
const codes = await st.payroll.listTimesheetCodes({}, { all: true });
const payrolls = await st.payroll.listPayrolls({ createdOnOrAfter: "2025-01-01" }, { all: true });

Pricebook (st.pricebook)

  • Materials, Services, Equipment, Images, Discounts & Fees, Categories, Client-Specific Pricing.
const materials = await st.pricebook.listMaterials({ updatedAfter: "2025-01-01" }, { all: true });

Reporting (st.reporting)

  • Report Categories and mappings, Dynamic Value Sets, and async report data queries.
const cats = await st.reporting.listReportCategories({}, { all: true });
const result = await st.reporting.startReportDataQuery("operations", 123, {
  parameters: [{ name: "From", value: "2026-01-01" }],
});

if ("token" in result) {
  const polled = await st.reporting.getReportDataQuery(result.token);
}

Sales Estimates (st.salesEstimates)

  • Estimates, Estimate Items, Estimate Templates, Proposal Templates, and Proposal Types.
const ests = await st.salesEstimates.listEstimates({ jobNumber: "131179" }, { all: true });
const templates = await st.salesEstimates.listEstimateTemplates({ active: "True" }, { all: true });
await st.salesEstimates.updateEstimateItems(123, {
  skuId: 456,
  membershipDurationBillingId: 789,
});

Scheduling Pro (st.schedulingPro)

  • Routers, Schedulers.
const schedulers = await st.schedulingPro.listSchedulers({}, { all: true });

Service Agreements (st.serviceAgreements)

  • Agreements and export endpoints, including agreement custom field values.
const agreements = await st.serviceAgreements.listServiceAgreements({}, { all: true });

Settings (st.settings)

  • Employees, Technicians, Business Units, Tag Types, User Roles.
const techs = await st.settings.listTechnicians({}, { all: true });

Task Management (st.taskManagement)

  • Tasks and Client-Side Data.
const tasks = await st.taskManagement.listTasks({ status: "Open" }, { all: true });

Telecom (st.telecom)

  • Calls + media.
const calls = await st.telecom.listCalls({ createdOnOrAfter: "2025-01-01" }, { all: true });

Timesheets V2 (st.timesheetsV2)

  • Activities, Types, Categories.
const activities = await st.timesheetsV2.listActivities({}, { all: true });

Job Bookings (st.jbce)

  • Call Reasons.
const reasons = await st.jbce.listCallReasons({}, { all: true });

Customer Interactions (st.customerInteractions)

  • Technician Ratings.
const ratings = await st.customerInteractions.listTechnicianRatings({ createdOnOrAfter: "2025-01-01" }, { all: true });

Type helpers

The package exports helper types from the root for stronger autocomplete and payload guidance. Examples:

import type {
  CrmExportCustomer,
  CreateEstimateRequest,
  EquipmentSystem,
  CreateWebLeadFormAttributionRequest,
  Contact,
  ContactMethodCreatePayload,
  CustomerInteractionsExportResponse,
  FindingCreatePayload,
  Invoice,
  JpmExportJob,
  MarketingAdsPerformanceRecord,
  ReportDataPendingResponse,
  SalesEstimatesExportResponse,
  TelecomExportCall,
} from "@masonlandcattle/servicetitan-sdk";

Many resource methods now use typed params and payload helpers directly, so editor IntelliSense is much better than a generic Record<string, unknown> workflow.

Alternate import style (functional)

import { ServiceTitanClient, CRM } from "@masonlandcattle/servicetitan-sdk";
const st = new ServiceTitanClient({ /* creds */ });
const customers = await CRM.listCustomers(st, { createdOnOrAfter: "2024-01-01" }, { all: true });

Local development / testing without publishing

Run local example scripts directly against the source using tsx.

  1. Set environment variables:

    • TENANT_ID
    • APP_KEY
    • CLIENT_ID
    • SECRET_KEY
  2. Execute examples:

npm run ex:crm
npm run ex:dispatch
npm run ex:equip
npm run ex:namespaced
npm run ex:crm-export
npm run ex:telecom-media

These scripts import from ../src, so there's no need to publish/install.

API Shape

/{category}/v2/tenant/{TENANT_ID}/{subject}

Use client.buildPath({ category, subject, idOrSubpath }) and client.request(method, path, { params, data }). To fetch every page server-side, pass { all: true, pageSize?: number } to supported list functions.

Contributing / Codex

If you are updating the SDK with Codex or another coding agent, read AGENTS.md first. It captures the repo conventions used for the OpenAPI alignment work.

Publish

npm run build
npm publish --access public

Not affiliated with ServiceTitan. Respect rate limits and terms.