@thegraphitelab/n8n-nodes-servicetitan
v0.12.0
Published
n8n custom node for ServiceTitan — TGL internal use.
Readme
@thegraphitelab/n8n-nodes-servicetitan
n8n custom node package for ServiceTitan. Built by The Graphite Lab, published publicly on npmjs.org under the @thegraphitelab scope. Primary consumer is TGL's Railway-hosted n8n fleet; the package is openly browsable.
Current version: 0.6.0
Installation
In each n8n instance, go to Settings → Community Nodes → Install and enter:
@thegraphitelab/n8n-nodes-servicetitanAccept the disclaimer and install.
Required env var
Set on the n8n service (both primary and worker if running queue mode):
TGL_SERVICETITAN_APP_KEY=<TGL ServiceTitan ST-App-Key>This is the TGL-owned ST-App-Key from our ServiceTitan developer portal app — same across all TGL clients, sourced from env so the value never sits in client-visible credential fields. See the repo root README for the broader rationale.
What ships today
Credential: ServiceTitan API
- OAuth 2.0
client_credentialsgrant ST-App-Keyheader sourced fromTGL_SERVICETITAN_APP_KEY- Environment switch:
Integration (Sandbox)vsProduction preAuthenticationcaches anexpirableaccessTokenin the credential data (15min TTL)- Test endpoint:
/settings/v2/tenant/{tenantId}/user-roles?page=1&pageSize=1
UI fields (per-client, set when creating the credential in n8n):
- Environment —
Integration (Sandbox)orProduction - Tenant ID — from ServiceTitan → Settings → API Application Access
- Client ID — OAuth client ID, per-client
- Client Secret — OAuth client secret, per-client
Action node: ServiceTitan
Picker label: ServiceTitan. Internal n8n name: tglServiceTitan.
Resource + Operation pattern.
| Resource | Operations | |---|---| | Customer | Get by ID |
The Customer.Get response includes both raw customFields[] and a flattened customFieldsByName sibling object (see Custom fields below).
Trigger node: ServiceTitan Trigger
Picker label: ServiceTitan Trigger. Sits next to the action node in n8n's node picker when the user searches "ServiceTitan".
Two-level Resource + Event picker inside the node, matching the action node's pattern. Resource determines which Event options show.
| Resource | Events | |---|---| | Job | Created or Modified |
Polling-based with modifiedOnOrAfter cursor and a boundary-id dedup set to avoid duplicate emissions on overlap windows.
Architecture
Resource-config table pattern. The trigger and action nodes share:
GenericFunctions.ts—serviceTitanApiRequest, pagination helpers, customFields flatteningresources/<resource>/— per-resource folder, one file per operation, declarative-style routingtriggerResources.ts— resource-config table that drives the trigger's two-level Resource + Event picker
Adding a new resource or trigger event is a localized change — see CONTRIBUTING.md at the repo root.
Custom fields behavior
ServiceTitan returns custom fields as an array of {typeId, name, value} objects. That shape is awkward to consume in downstream n8n nodes — every workflow has to write a JS-code step to find a field by name.
This package returns three shapes on every entity that has custom fields, so consumers pick what fits:
customFields— raw array of{typeId, name, value}, untouched. Use this if your workflow already depends on the ST shape, or you need both name + typeId together.custom_fields_by_name— flat map: keys are field names, values are the raw values. Use{{ $json.custom_fields_by_name["Lockbox Code"] }}to read directly.custom_fields_by_type_id— flat map: keys are stringified typeIds, values are the raw values. Use this when you want to address a field by ID (more stable than name across tenant renames).
All three coexist on every entity. None is destructive of the others.
Roadmap
Phase 1 (current): refactored framework that scales, shipping the existing Customer.Get + Jobs trigger through it.
Phase 3: port the rest of TGL's ServiceTitan Zapier operations resource-by-resource. Reference source: /Users/jacobnolley/projects/advanced_servicetitan_zapier_app (the live Zapier custom app).
Resources to port (non-exhaustive):
- Jobs (more operations beyond the trigger)
- Customers (full CRUD)
- Estimates
- Appointments
- Invoices
- Projects
- Payments
- Calls
- Leads
- Locations
- Technicians
- Vendors
- Employees
- Bookings
Each resource lands as a minor bump; each new operation on an existing resource is a patch.
Local dev
# From this directory
pnpm devBoots embedded n8n at http://localhost:5678 with this package pre-loaded and hot reload. ServiceTitan sandbox credentials go in the local n8n credential store (~/.n8n).
Reference
- Repo root:
../../README.md - Recipes for adding resources, trigger events, new packages:
../../CONTRIBUTING.md - Source-of-truth Zapier app being ported from:
/Users/jacobnolley/projects/advanced_servicetitan_zapier_app
