@thegraphitelab/n8n-nodes-servicetitan 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/README.md +103 -25
  2. package/dist/nodes/ServiceTitan/GenericFunctions.d.ts +23 -0
  3. package/dist/nodes/ServiceTitan/GenericFunctions.js +50 -0
  4. package/dist/nodes/ServiceTitan/GenericFunctions.js.map +1 -1
  5. package/dist/nodes/ServiceTitan/ServiceTitanTrigger.node.d.ts +6 -1
  6. package/dist/nodes/ServiceTitan/ServiceTitanTrigger.node.js +106 -77
  7. package/dist/nodes/ServiceTitan/ServiceTitanTrigger.node.js.map +1 -1
  8. package/dist/nodes/ServiceTitan/TglServiceTitan.node.d.ts +10 -1
  9. package/dist/nodes/ServiceTitan/TglServiceTitan.node.js +16 -12
  10. package/dist/nodes/ServiceTitan/TglServiceTitan.node.js.map +1 -1
  11. package/dist/nodes/ServiceTitan/resources/index.d.ts +18 -0
  12. package/dist/nodes/ServiceTitan/resources/index.js +28 -0
  13. package/dist/nodes/ServiceTitan/resources/index.js.map +1 -0
  14. package/dist/nodes/ServiceTitan/servicetitan.dark.svg +64 -3
  15. package/dist/nodes/ServiceTitan/servicetitan.svg +64 -3
  16. package/dist/nodes/ServiceTitan/types/common.d.ts +27 -0
  17. package/dist/nodes/ServiceTitan/types/common.js +9 -0
  18. package/dist/nodes/ServiceTitan/types/common.js.map +1 -0
  19. package/dist/nodes/ServiceTitan/types/customer.d.ts +15 -0
  20. package/dist/nodes/ServiceTitan/types/customer.js +3 -0
  21. package/dist/nodes/ServiceTitan/types/customer.js.map +1 -0
  22. package/dist/nodes/ServiceTitan/types/index.d.ts +3 -0
  23. package/dist/nodes/ServiceTitan/types/index.js +3 -0
  24. package/dist/nodes/ServiceTitan/types/index.js.map +1 -0
  25. package/dist/nodes/ServiceTitan/types/job.d.ts +21 -0
  26. package/dist/nodes/ServiceTitan/types/job.js +3 -0
  27. package/dist/nodes/ServiceTitan/types/job.js.map +1 -0
  28. package/dist/package.json +1 -1
  29. package/package.json +1 -1
package/README.md CHANGED
@@ -1,48 +1,126 @@
1
1
  # @thegraphitelab/n8n-nodes-servicetitan
2
2
 
3
- n8n custom node for ServiceTitan. Internal use only published to GitHub Packages under the `@thegraphitelab` scope.
3
+ 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.
4
4
 
5
- ## Status
5
+ **Current version: 0.6.0**
6
6
 
7
- Day-one scope:
8
- - **Credentials**: OAuth 2.0 client_credentials grant + ST-App-Key header, environment switch (Integration sandbox vs Production)
9
- - **Customer**: Get by ID
7
+ ## Installation
10
8
 
11
- More resources (Job, Invoice, Estimate, Technician, Booking, etc.) get added per the `CONTRIBUTING.md` recipe in the repo root.
9
+ In each n8n instance, go to **Settings Community Nodes Install** and enter:
12
10
 
13
- ## Credentials
11
+ ```
12
+ @thegraphitelab/n8n-nodes-servicetitan
13
+ ```
14
+
15
+ Accept the disclaimer and install.
16
+
17
+ ### Required env var
18
+
19
+ Set on the n8n service (both primary and worker if running queue mode):
20
+
21
+ ```
22
+ TGL_SERVICETITAN_APP_KEY=<TGL ServiceTitan ST-App-Key>
23
+ ```
24
+
25
+ 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](../../README.md#installing-in-n8n) for the broader rationale.
26
+
27
+ ## What ships today
28
+
29
+ ### Credential: ServiceTitan API
30
+
31
+ - OAuth 2.0 `client_credentials` grant
32
+ - `ST-App-Key` header sourced from `TGL_SERVICETITAN_APP_KEY`
33
+ - Environment switch: `Integration (Sandbox)` vs `Production`
34
+ - `preAuthentication` caches an `expirable` `accessToken` in the credential data (15min TTL)
35
+ - Test endpoint: `/settings/v2/tenant/{tenantId}/user-roles?page=1&pageSize=1`
36
+
37
+ UI fields (per-client, set when creating the credential in n8n):
14
38
 
15
- Required UI fields (per-client, set when creating a credential in n8n):
16
39
  - **Environment** — `Integration (Sandbox)` or `Production`
17
- - **Tenant ID** — found in ServiceTitan → Settings → API Application Access
40
+ - **Tenant ID** — from ServiceTitan → Settings → API Application Access
18
41
  - **Client ID** — OAuth client ID, per-client
19
42
  - **Client Secret** — OAuth client secret, per-client
20
43
 
21
- Required env var (set once per n8n instance, on **both** primary and worker services):
22
- - **`TGL_SERVICETITAN_APP_KEY`** — the `ST-App-Key`. TGL-owned application identifier, same across all TGL clients. Sourced from the TGL ServiceTitan developer portal app. Read from the env at request time, not stored per-credential.
44
+ ### Action node: ServiceTitan
45
+
46
+ Picker label: **ServiceTitan**. Internal n8n name: `tglServiceTitan`.
47
+
48
+ Resource + Operation pattern.
49
+
50
+ | Resource | Operations |
51
+ |---|---|
52
+ | Customer | Get by ID |
53
+
54
+ The Customer.Get response includes both raw `customFields[]` and a flattened `customFieldsByName` sibling object (see Custom fields below).
55
+
56
+ ### Trigger node: ServiceTitan Trigger
23
57
 
24
- The credential's "Test" button hits `/settings/v2/tenant/{tenantId}/user-roles?page=1&pageSize=1` to verify the full auth chain works.
58
+ Picker label: **ServiceTitan Trigger**. Sits next to the action node in n8n's node picker when the user searches "ServiceTitan".
25
59
 
26
- ## Auth flow
60
+ Two-level Resource + Event picker inside the node, matching the action node's pattern. Resource determines which Event options show.
27
61
 
28
- 1. n8n calls `preAuthentication` which POSTs `grant_type=client_credentials` to the env-correct token URL
29
- 2. The returned `access_token` (TTL 15min) is cached in the credential data
30
- 3. Each request injects `Authorization: Bearer <token>` AND `ST-App-Key: <appKey>` headers
31
- 4. The `requestDefaults.baseURL` on the node switches between integration and production hosts based on `$credentials.environment`
62
+ | Resource | Events |
63
+ |---|---|
64
+ | Job | Created or Modified |
65
+
66
+ Polling-based with `modifiedOnOrAfter` cursor and a boundary-id dedup set to avoid duplicate emissions on overlap windows.
67
+
68
+ ## Architecture
69
+
70
+ Resource-config table pattern. The trigger and action nodes share:
71
+
72
+ - `GenericFunctions.ts` — `serviceTitanApiRequest`, pagination helpers, customFields flattening
73
+ - `resources/<resource>/` — per-resource folder, one file per operation, declarative-style routing
74
+ - `triggerResources.ts` — resource-config table that drives the trigger's two-level Resource + Event picker
75
+
76
+ Adding a new resource or trigger event is a localized change — see [`CONTRIBUTING.md`](../../CONTRIBUTING.md) at the repo root.
77
+
78
+ ## Custom fields behavior
79
+
80
+ 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.
81
+
82
+ This package returns **both** shapes on every entity that has custom fields:
83
+
84
+ - `customFields` — the raw array, untouched, so anything that depends on the ServiceTitan shape still works
85
+ - `customFieldsByName` — sibling object, keys are field names, values are `{typeId, value}`
86
+
87
+ Downstream workflows can use `{{ $json.customFieldsByName["Field Name"].value }}` directly without a code step. A workflow that already does the manual flattening keeps working because `customFields` is unchanged.
88
+
89
+ ## Roadmap
90
+
91
+ Phase 1 (current): refactored framework that scales, shipping the existing Customer.Get + Jobs trigger through it.
92
+
93
+ Phase 3: port the rest of TGL's ServiceTitan Zapier operations resource-by-resource. Reference source: [`/Users/jacobnolley/projects/advanced_servicetitan_zapier_app`](../../../advanced_servicetitan_zapier_app) (the live Zapier custom app).
94
+
95
+ Resources to port (non-exhaustive):
96
+ - Jobs (more operations beyond the trigger)
97
+ - Customers (full CRUD)
98
+ - Estimates
99
+ - Appointments
100
+ - Invoices
101
+ - Projects
102
+ - Payments
103
+ - Calls
104
+ - Leads
105
+ - Locations
106
+ - Technicians
107
+ - Vendors
108
+ - Employees
109
+ - Bookings
110
+
111
+ Each resource lands as a minor bump; each new operation on an existing resource is a patch.
32
112
 
33
113
  ## Local dev
34
114
 
35
115
  ```bash
116
+ # From this directory
36
117
  pnpm dev
37
- # Opens http://localhost:5678 with an embedded n8n and this node pre-loaded
38
118
  ```
39
119
 
40
- ## Reference
41
-
42
- Endpoints and operation catalog are ported from `The-Graphite-Lab/advanced_servicetitan_zapier_app`.
120
+ Boots 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`).
43
121
 
44
- ## TODO
122
+ ## Reference
45
123
 
46
- - [ ] Port additional resources (Job, Invoice, Estimate, Technician, Customer Note, etc.)
47
- - [ ] Add polling trigger node (`ServiceTitanTrigger`) using `modifiedOnOrAfter` filter
48
- - [ ] Wire usage metering to `api.thegraphitelab.com/usage` (matches the Zapier app's billing layer)
124
+ - Repo root: [`../../README.md`](../../README.md)
125
+ - Recipes for adding resources, trigger events, new packages: [`../../CONTRIBUTING.md`](../../CONTRIBUTING.md)
126
+ - Source-of-truth Zapier app being ported from: `/Users/jacobnolley/projects/advanced_servicetitan_zapier_app`
@@ -5,3 +5,26 @@ import type { IDataObject, IExecuteFunctions, IHttpRequestMethods, ILoadOptionsF
5
5
  * injection from TGL_SERVICETITAN_APP_KEY) runs on every call.
6
6
  */
7
7
  export declare function serviceTitanApiRequest(this: IExecuteFunctions | IPollFunctions | ILoadOptionsFunctions, method: IHttpRequestMethods, path: string, qs?: IDataObject, body?: IDataObject | undefined): Promise<IDataObject>;
8
+ /**
9
+ * Paginates a ServiceTitan list endpoint and returns the flattened set of
10
+ * `data[]` rows across all pages.
11
+ *
12
+ * Mirrors the trigger's pagination contract:
13
+ * - 50-page safety cap (matches ServiceTitanTrigger)
14
+ * - relies on response.hasMore as the loop terminator
15
+ * - threads `page` through the query string; caller supplies the rest
16
+ *
17
+ * Caller is responsible for sort/filter qs. We do not enforce pageSize here
18
+ * because some ST endpoints have endpoint-specific caps.
19
+ */
20
+ export declare function serviceTitanApiRequestAllItems(this: IExecuteFunctions | IPollFunctions | ILoadOptionsFunctions, method: IHttpRequestMethods, path: string, qs?: IDataObject, body?: IDataObject | undefined): Promise<IDataObject[]>;
21
+ /**
22
+ * Returns the same item with a sibling `customFieldsByName` object added,
23
+ * keyed by the customField's `name`, mapping to `{ typeId, value }`.
24
+ *
25
+ * Idempotent and non-destructive:
26
+ * - Original `customFields[]` array is left untouched
27
+ * - Missing or non-array `customFields` returns the input unchanged
28
+ * - Duplicate names: last write wins (ST doesn't normally emit duplicates)
29
+ */
30
+ export declare function flattenCustomFields(item: IDataObject): IDataObject;
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.serviceTitanApiRequest = serviceTitanApiRequest;
4
+ exports.serviceTitanApiRequestAllItems = serviceTitanApiRequestAllItems;
5
+ exports.flattenCustomFields = flattenCustomFields;
4
6
  const ServiceTitanApi_credentials_1 = require("../../credentials/ServiceTitanApi.credentials");
5
7
  /**
6
8
  * Shared ServiceTitan API request helper. Routes through n8n's auth helper so
@@ -25,4 +27,52 @@ async function serviceTitanApiRequest(method, path, qs = {}, body = undefined) {
25
27
  options.body = body;
26
28
  return (await this.helpers.httpRequestWithAuthentication.call(this, 'serviceTitanApi', options));
27
29
  }
30
+ /**
31
+ * Paginates a ServiceTitan list endpoint and returns the flattened set of
32
+ * `data[]` rows across all pages.
33
+ *
34
+ * Mirrors the trigger's pagination contract:
35
+ * - 50-page safety cap (matches ServiceTitanTrigger)
36
+ * - relies on response.hasMore as the loop terminator
37
+ * - threads `page` through the query string; caller supplies the rest
38
+ *
39
+ * Caller is responsible for sort/filter qs. We do not enforce pageSize here
40
+ * because some ST endpoints have endpoint-specific caps.
41
+ */
42
+ async function serviceTitanApiRequestAllItems(method, path, qs = {}, body = undefined) {
43
+ const collected = [];
44
+ let page = 1;
45
+ const maxPages = 50;
46
+ while (page <= maxPages) {
47
+ const response = (await serviceTitanApiRequest.call(this, method, path, { ...qs, page }, body));
48
+ if (Array.isArray(response.data)) {
49
+ collected.push(...response.data);
50
+ }
51
+ if (!response.hasMore)
52
+ break;
53
+ page += 1;
54
+ }
55
+ return collected;
56
+ }
57
+ /**
58
+ * Returns the same item with a sibling `customFieldsByName` object added,
59
+ * keyed by the customField's `name`, mapping to `{ typeId, value }`.
60
+ *
61
+ * Idempotent and non-destructive:
62
+ * - Original `customFields[]` array is left untouched
63
+ * - Missing or non-array `customFields` returns the input unchanged
64
+ * - Duplicate names: last write wins (ST doesn't normally emit duplicates)
65
+ */
66
+ function flattenCustomFields(item) {
67
+ const customFields = item.customFields;
68
+ if (!Array.isArray(customFields))
69
+ return item;
70
+ const byName = {};
71
+ for (const raw of customFields) {
72
+ if (!raw || typeof raw.name !== 'string')
73
+ continue;
74
+ byName[raw.name] = { typeId: raw.typeId, value: raw.value };
75
+ }
76
+ return { ...item, customFieldsByName: byName };
77
+ }
28
78
  //# sourceMappingURL=GenericFunctions.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"GenericFunctions.js","sourceRoot":"","sources":["../../../nodes/ServiceTitan/GenericFunctions.ts"],"names":[],"mappings":";;AAgBA,wDA4BC;AAnCD,+FAA2E;AAE3E;;;;GAIG;AACI,KAAK,UAAU,sBAAsB,CAE3C,MAA2B,EAC3B,IAAY,EACZ,KAAkB,EAAE,EACpB,OAAgC,SAAS;IAEzC,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC;IACjE,MAAM,WAAW,GAAI,WAAW,CAAC,WAAsB,IAAI,aAAa,CAAC;IACzE,MAAM,OAAO,GAAG,IAAA,wCAAU,EAAC,WAAW,CAAC,CAAC;IAExC,MAAM,OAAO,GAAwB;QACpC,MAAM;QACN,GAAG,EAAE,WAAW,OAAO,GAAG,IAAI,EAAE;QAChC,EAAE;QACF,OAAO,EAAE;YACR,MAAM,EAAE,kBAAkB;YAC1B,cAAc,EAAE,kBAAkB;SAClC;QACD,IAAI,EAAE,IAAI;KACV,CAAC;IACF,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAE5C,OAAO,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,6BAA6B,CAAC,IAAI,CAC5D,IAAI,EACJ,iBAAiB,EACjB,OAAO,CACP,CAAgB,CAAC;AACnB,CAAC"}
1
+ {"version":3,"file":"GenericFunctions.js","sourceRoot":"","sources":["../../../nodes/ServiceTitan/GenericFunctions.ts"],"names":[],"mappings":";;AAiBA,wDA4BC;AAcD,wEAwBC;AAWD,kDAWC;AAhGD,+FAA2E;AAG3E;;;;GAIG;AACI,KAAK,UAAU,sBAAsB,CAE3C,MAA2B,EAC3B,IAAY,EACZ,KAAkB,EAAE,EACpB,OAAgC,SAAS;IAEzC,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC;IACjE,MAAM,WAAW,GAAI,WAAW,CAAC,WAAsB,IAAI,aAAa,CAAC;IACzE,MAAM,OAAO,GAAG,IAAA,wCAAU,EAAC,WAAW,CAAC,CAAC;IAExC,MAAM,OAAO,GAAwB;QACpC,MAAM;QACN,GAAG,EAAE,WAAW,OAAO,GAAG,IAAI,EAAE;QAChC,EAAE;QACF,OAAO,EAAE;YACR,MAAM,EAAE,kBAAkB;YAC1B,cAAc,EAAE,kBAAkB;SAClC;QACD,IAAI,EAAE,IAAI;KACV,CAAC;IACF,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAE5C,OAAO,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,6BAA6B,CAAC,IAAI,CAC5D,IAAI,EACJ,iBAAiB,EACjB,OAAO,CACP,CAAgB,CAAC;AACnB,CAAC;AAED;;;;;;;;;;;GAWG;AACI,KAAK,UAAU,8BAA8B,CAEnD,MAA2B,EAC3B,IAAY,EACZ,KAAkB,EAAE,EACpB,OAAgC,SAAS;IAEzC,MAAM,SAAS,GAAkB,EAAE,CAAC;IACpC,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,MAAM,QAAQ,GAAG,EAAE,CAAC;IAEpB,OAAO,IAAI,IAAI,QAAQ,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,CAAC,MAAM,sBAAsB,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,CAAC,CAClE,CAAC;QAE7B,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YAClC,SAAS,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QAClC,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,OAAO;YAAE,MAAM;QAC7B,IAAI,IAAI,CAAC,CAAC;IACX,CAAC;IAED,OAAO,SAAS,CAAC;AAClB,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,mBAAmB,CAAC,IAAiB;IACpD,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC;IACvC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC;QAAE,OAAO,IAAI,CAAC;IAE9C,MAAM,MAAM,GAAuD,EAAE,CAAC;IACtE,KAAK,MAAM,GAAG,IAAI,YAA+B,EAAE,CAAC;QACnD,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ;YAAE,SAAS;QACnD,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC;IAC7D,CAAC;IAED,OAAO,EAAE,GAAG,IAAI,EAAE,kBAAkB,EAAE,MAAM,EAAE,CAAC;AAChD,CAAC"}
@@ -1,5 +1,10 @@
1
- import { type INodeExecutionData, type INodeType, type INodeTypeDescription, type IPollFunctions } from 'n8n-workflow';
1
+ import { type ILoadOptionsFunctions, type INodeExecutionData, type INodePropertyOptions, type INodeType, type INodeTypeDescription, type IPollFunctions } from 'n8n-workflow';
2
2
  export declare class ServiceTitanTrigger implements INodeType {
3
3
  description: INodeTypeDescription;
4
+ methods: {
5
+ loadOptions: {
6
+ getBusinessUnits(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
7
+ };
8
+ };
4
9
  poll(this: IPollFunctions): Promise<INodeExecutionData[][] | null>;
5
10
  }
@@ -14,8 +14,8 @@ class ServiceTitanTrigger {
14
14
  },
15
15
  group: ['trigger'],
16
16
  version: 1,
17
- description: 'Starts the workflow when ServiceTitan records are created or modified',
18
- subtitle: '={{$parameter.event}}',
17
+ description: 'Fires for new ServiceTitan jobs',
18
+ subtitle: '={{$parameter.resource}}: {{$parameter.event}}',
19
19
  defaults: {
20
20
  name: 'ServiceTitan Trigger',
21
21
  },
@@ -31,19 +31,27 @@ class ServiceTitanTrigger {
31
31
  ],
32
32
  properties: [
33
33
  {
34
- displayName: 'Trigger On',
34
+ displayName: 'Resource',
35
+ name: 'resource',
36
+ type: 'options',
37
+ noDataExpression: true,
38
+ default: 'job',
39
+ options: [{ name: 'Job', value: 'job' }],
40
+ },
41
+ {
42
+ displayName: 'Event',
35
43
  name: 'event',
36
44
  type: 'options',
37
45
  noDataExpression: true,
38
- default: 'jobCreatedOrModified',
46
+ default: 'newJob',
47
+ displayOptions: { show: { resource: ['job'] } },
39
48
  options: [
40
49
  {
41
- name: 'Job Created or Modified',
42
- value: 'jobCreatedOrModified',
43
- description: 'Fires for any job whose modifiedOn timestamp has advanced since the last poll',
50
+ name: 'New Job',
51
+ value: 'newJob',
52
+ description: 'Fires for each new job created in ServiceTitan (mirrors the Zapier query_jobs trigger: latest 100 per poll, dedup by job ID)',
44
53
  },
45
54
  ],
46
- description: 'ServiceTitan updates modifiedOn for any field change including internal status churn, so expect more fires than human-meaningful changes — filter downstream',
47
55
  },
48
56
  {
49
57
  displayName: 'Filters',
@@ -51,14 +59,22 @@ class ServiceTitanTrigger {
51
59
  type: 'collection',
52
60
  placeholder: 'Add Filter',
53
61
  default: {},
54
- displayOptions: { show: { event: ['jobCreatedOrModified'] } },
62
+ displayOptions: { show: { resource: ['job'] } },
55
63
  options: [
56
64
  {
57
- displayName: 'Business Unit ID',
65
+ displayName: 'Business Unit Name or ID',
58
66
  name: 'businessUnitId',
59
- type: 'string',
67
+ type: 'options',
68
+ typeOptions: { loadOptionsMethod: 'getBusinessUnits' },
60
69
  default: '',
61
- description: 'Only emit jobs from this business unit',
70
+ description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
71
+ },
72
+ {
73
+ displayName: 'Created On or After',
74
+ name: 'createdOnOrAfter',
75
+ type: 'dateTime',
76
+ default: '',
77
+ description: 'Only jobs created on or after this date/time',
62
78
  },
63
79
  {
64
80
  displayName: 'Customer ID',
@@ -71,15 +87,17 @@ class ServiceTitanTrigger {
71
87
  name: 'includeInactive',
72
88
  type: 'boolean',
73
89
  default: false,
74
- description: 'Whether to include soft-deleted jobs. ServiceTitan bumps modifiedOn on deletion, so leaving this off (default) is the right call for most sync workflows.',
90
+ description: 'Whether to include soft-deleted jobs in the results',
75
91
  },
76
92
  {
77
93
  displayName: 'Job Status',
78
94
  name: 'jobStatus',
79
- type: 'multiOptions',
80
- default: [],
81
- options: JOB_STATUSES.map((s) => ({ name: s, value: s })),
82
- description: 'Only emit jobs with one of these statuses',
95
+ type: 'options',
96
+ default: '',
97
+ options: [
98
+ { name: 'Any', value: '' },
99
+ ...JOB_STATUSES.map((s) => ({ name: s, value: s })),
100
+ ],
83
101
  },
84
102
  {
85
103
  displayName: 'Job Type ID',
@@ -87,92 +105,103 @@ class ServiceTitanTrigger {
87
105
  type: 'string',
88
106
  default: '',
89
107
  },
108
+ {
109
+ displayName: 'Modified Before',
110
+ name: 'modifiedBefore',
111
+ type: 'dateTime',
112
+ default: '',
113
+ description: 'Only jobs whose modifiedOn is before this date/time',
114
+ },
90
115
  ],
91
116
  },
92
117
  ],
93
118
  };
119
+ methods = {
120
+ loadOptions: {
121
+ async getBusinessUnits() {
122
+ const credentials = await this.getCredentials('serviceTitanApi');
123
+ const tenantId = credentials.tenantId;
124
+ const response = (await GenericFunctions_1.serviceTitanApiRequest.call(this, 'GET', `/settings/v2/tenant/${tenantId}/business-units`, { pageSize: 200, active: true }));
125
+ const rows = Array.isArray(response.data) ? response.data : [];
126
+ return rows
127
+ .map((bu) => ({
128
+ name: bu.name ?? `Business Unit ${bu.id}`,
129
+ value: String(bu.id),
130
+ }))
131
+ .sort((a, b) => a.name.localeCompare(b.name));
132
+ },
133
+ },
134
+ };
94
135
  async poll() {
95
136
  const credentials = await this.getCredentials('serviceTitanApi');
96
137
  const tenantId = credentials.tenantId;
97
138
  const staticData = this.getWorkflowStaticData('node');
98
- // If the underlying tenant changed (credential swap), wipe the cursor.
139
+ // Tenant-swap detection: if the credential's tenant changed, wipe the
140
+ // dedup cursor so we don't reuse another tenant's ID watermark.
99
141
  if (staticData.tenantId && staticData.tenantId !== tenantId) {
100
- delete staticData.lastTimeChecked;
101
- delete staticData.boundaryIds;
142
+ delete staticData.highestSeenId;
102
143
  }
103
144
  staticData.tenantId = tenantId;
104
- const now = new Date().toISOString();
105
- // First run: start the watermark at activation time so we don't drain ST history.
106
- const since = staticData.lastTimeChecked ?? now;
107
- const boundaryIds = new Set(staticData.boundaryIds ?? []);
108
- const event = this.getNodeParameter('event', 0);
109
- if (event !== 'jobCreatedOrModified') {
110
- return null;
111
- }
112
145
  const filters = this.getNodeParameter('filters', 0, {});
113
- const baseQs = {
114
- modifiedOnOrAfter: since,
115
- modifiedBefore: now,
116
- sort: '+ModifiedOn',
146
+ // Mirror the Zapier query_jobs param shape: pageSize=100, active filter, plus user filters.
147
+ const qs = {
117
148
  pageSize: 100,
118
- includeTotal: false,
119
149
  active: filters.includeInactive ? 'Any' : 'True',
120
150
  };
121
- if (filters.jobStatus && filters.jobStatus.length > 0) {
122
- baseQs.jobStatus = filters.jobStatus[0];
123
- }
124
151
  if (filters.businessUnitId)
125
- baseQs.businessUnitId = filters.businessUnitId;
126
- if (filters.jobTypeId)
127
- baseQs.jobTypeId = filters.jobTypeId;
152
+ qs.businessUnitId = filters.businessUnitId;
153
+ if (filters.createdOnOrAfter)
154
+ qs.createdOnOrAfter = filters.createdOnOrAfter;
128
155
  if (filters.customerId)
129
- baseQs.customerId = filters.customerId;
130
- const collected = [];
131
- let page = 1;
132
- const maxPages = 50;
156
+ qs.customerId = filters.customerId;
157
+ if (filters.jobStatus)
158
+ qs.jobStatus = filters.jobStatus;
159
+ if (filters.jobTypeId)
160
+ qs.jobTypeId = filters.jobTypeId;
161
+ if (filters.modifiedBefore)
162
+ qs.modifiedBefore = filters.modifiedBefore;
163
+ let response;
133
164
  try {
134
- while (page <= maxPages) {
135
- const response = (await GenericFunctions_1.serviceTitanApiRequest.call(this, 'GET', `/jpm/v2/tenant/${tenantId}/jobs`, { ...baseQs, page }));
136
- const rows = Array.isArray(response.data) ? response.data : [];
137
- for (const job of rows) {
138
- const id = Number(job.id);
139
- if (Number.isFinite(id) && boundaryIds.has(id))
140
- continue;
141
- collected.push(job);
142
- }
143
- if (!response.hasMore)
144
- break;
145
- page += 1;
146
- }
165
+ response = (await GenericFunctions_1.serviceTitanApiRequest.call(this, 'GET', `/jpm/v2/tenant/${tenantId}/jobs`, qs));
147
166
  }
148
167
  catch (error) {
149
- // Bubble up so n8n's poll runner logs it and retries next tick. We do NOT
150
- // advance staticData here — same window will be re-polled.
151
168
  throw new n8n_workflow_1.NodeApiError(this.getNode(), error);
152
169
  }
153
- // Manual ("Fetch Test Event") mode: return one sample without burning the
154
- // watermark, so users can wire downstream nodes safely.
170
+ const rows = Array.isArray(response.data) ? response.data : [];
171
+ const previousHighest = staticData.highestSeenId;
172
+ const maxIdSeen = rows.reduce((m, j) => Math.max(m, Number(j.id) || 0), 0);
173
+ // First activation: seed the cursor at the current highest job ID and emit
174
+ // nothing. Avoids draining the latest 100 jobs into n8n on activation.
175
+ if (previousHighest === undefined) {
176
+ staticData.highestSeenId = maxIdSeen;
177
+ return null;
178
+ }
179
+ // Dedup: only jobs with ID > the watermark. ST job IDs are monotonically
180
+ // increasing, so id > watermark === "newer than what we last saw".
181
+ const newJobs = rows
182
+ .filter((j) => Number.isFinite(Number(j.id)) && Number(j.id) > previousHighest)
183
+ .sort((a, b) => Number(a.id) - Number(b.id)); // ascending — older-new before newer-new
184
+ // Manual mode ("Fetch Test Event"): return one sample without burning the
185
+ // watermark. If there are no new jobs, fall back to the most recent one
186
+ // so the user can wire downstream nodes against a real row.
155
187
  if (this.getMode() === 'manual') {
156
- if (collected.length === 0)
188
+ const sample = newJobs[0] ?? (rows.length > 0
189
+ ? [...rows].sort((a, b) => Number(b.id) - Number(a.id))[0]
190
+ : undefined);
191
+ if (!sample)
157
192
  return null;
158
- return [this.helpers.returnJsonArray(collected.slice(0, 1))];
159
- }
160
- // Advance cursor only on success.
161
- staticData.lastTimeChecked = now;
162
- // Boundary-id Set: ids of any returned job whose modifiedOn equals the new
163
- // cursor, so the next poll's inclusive modifiedOnOrAfter doesn't duplicate-fire.
164
- const newBoundary = [];
165
- for (const job of collected) {
166
- if (typeof job.modifiedOn === 'string' && job.modifiedOn === now) {
167
- const id = Number(job.id);
168
- if (Number.isFinite(id))
169
- newBoundary.push(id);
170
- }
193
+ return [
194
+ this.helpers.returnJsonArray([
195
+ (0, GenericFunctions_1.flattenCustomFields)(sample),
196
+ ]),
197
+ ];
171
198
  }
172
- staticData.boundaryIds = newBoundary;
173
- if (collected.length === 0)
199
+ if (newJobs.length === 0)
174
200
  return null;
175
- return [this.helpers.returnJsonArray(collected)];
201
+ // Advance cursor to the highest ID emitted.
202
+ staticData.highestSeenId = Math.max(previousHighest, ...newJobs.map((j) => Number(j.id)));
203
+ const enriched = newJobs.map((j) => (0, GenericFunctions_1.flattenCustomFields)(j));
204
+ return [this.helpers.returnJsonArray(enriched)];
176
205
  }
177
206
  }
178
207
  exports.ServiceTitanTrigger = ServiceTitanTrigger;
@@ -1 +1 @@
1
- {"version":3,"file":"ServiceTitanTrigger.node.js","sourceRoot":"","sources":["../../../nodes/ServiceTitan/ServiceTitanTrigger.node.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ServiceTitanTrigger.node.js","sourceRoot":"","sources":["../../../nodes/ServiceTitan/ServiceTitanTrigger.node.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,13 @@
1
1
  import { type INodeType, type INodeTypeDescription } from 'n8n-workflow';
2
- export declare class TglServiceTitan implements INodeType {
2
+ /**
3
+ * Action node for ServiceTitan.
4
+ *
5
+ * Filename note: this file MUST stay named `TglServiceTitan.node.ts` because
6
+ * n8n's `node-filename-against-convention` lint rule maps the filename to the
7
+ * `name` field. We keep `name: 'tglServiceTitan'` for collision safety against
8
+ * any future official n8n ServiceTitan node, so the file follows. The exported
9
+ * class is `ServiceTitan` — that's what consumers (and the n8n loader) see.
10
+ */
11
+ export declare class ServiceTitan implements INodeType {
3
12
  description: INodeTypeDescription;
4
13
  }
@@ -1,9 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TglServiceTitan = void 0;
3
+ exports.ServiceTitan = void 0;
4
4
  const n8n_workflow_1 = require("n8n-workflow");
5
- const customer_1 = require("./resources/customer");
6
- class TglServiceTitan {
5
+ const resources_1 = require("./resources");
6
+ /**
7
+ * Action node for ServiceTitan.
8
+ *
9
+ * Filename note: this file MUST stay named `TglServiceTitan.node.ts` because
10
+ * n8n's `node-filename-against-convention` lint rule maps the filename to the
11
+ * `name` field. We keep `name: 'tglServiceTitan'` for collision safety against
12
+ * any future official n8n ServiceTitan node, so the file follows. The exported
13
+ * class is `ServiceTitan` — that's what consumers (and the n8n loader) see.
14
+ */
15
+ class ServiceTitan {
7
16
  description = {
8
17
  displayName: 'ServiceTitan',
9
18
  name: 'tglServiceTitan',
@@ -40,18 +49,13 @@ class TglServiceTitan {
40
49
  name: 'resource',
41
50
  type: 'options',
42
51
  noDataExpression: true,
43
- options: [
44
- {
45
- name: 'Customer',
46
- value: 'customer',
47
- },
48
- ],
52
+ options: resources_1.resourceSelectOptions,
49
53
  default: 'customer',
50
54
  },
51
- ...customer_1.customerOperations,
52
- ...customer_1.customerFields,
55
+ ...resources_1.allResources.flatMap((r) => r.resourceOperations),
56
+ ...resources_1.allResources.flatMap((r) => r.resourceFields),
53
57
  ],
54
58
  };
55
59
  }
56
- exports.TglServiceTitan = TglServiceTitan;
60
+ exports.ServiceTitan = ServiceTitan;
57
61
  //# sourceMappingURL=TglServiceTitan.node.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TglServiceTitan.node.js","sourceRoot":"","sources":["../../../nodes/ServiceTitan/TglServiceTitan.node.ts"],"names":[],"mappings":";;;AAAA,+CAIsB;AAEtB,mDAA0E;AAE1E,MAAa,eAAe;IAC3B,WAAW,GAAyB;QACnC,WAAW,EAAE,cAAc;QAC3B,IAAI,EAAE,iBAAiB;QACvB,IAAI,EAAE;YACL,KAAK,EAAE,uBAAuB;YAC9B,IAAI,EAAE,4BAA4B;SAClC;QACD,KAAK,EAAE,CAAC,WAAW,CAAC;QACpB,OAAO,EAAE,CAAC;QACV,QAAQ,EAAE,oDAAoD;QAC9D,WAAW,EAAE,oCAAoC;QACjD,QAAQ,EAAE;YACT,IAAI,EAAE,cAAc;SACpB;QACD,YAAY,EAAE,IAAI;QAClB,MAAM,EAAE,CAAC,kCAAmB,CAAC,IAAI,CAAC;QAClC,OAAO,EAAE,CAAC,kCAAmB,CAAC,IAAI,CAAC;QACnC,WAAW,EAAE;YACZ;gBACC,IAAI,EAAE,iBAAiB;gBACvB,QAAQ,EAAE,IAAI;aACd;SACD;QACD,eAAe,EAAE;YAChB,OAAO,EACN,oHAAoH;YACrH,OAAO,EAAE;gBACR,cAAc,EAAE,kBAAkB;gBAClC,MAAM,EAAE,kBAAkB;aAC1B;SACD;QACD,UAAU,EAAE;YACX;gBACC,WAAW,EAAE,UAAU;gBACvB,IAAI,EAAE,UAAU;gBAChB,IAAI,EAAE,SAAS;gBACf,gBAAgB,EAAE,IAAI;gBACtB,OAAO,EAAE;oBACR;wBACC,IAAI,EAAE,UAAU;wBAChB,KAAK,EAAE,UAAU;qBACjB;iBACD;gBACD,OAAO,EAAE,UAAU;aACnB;YACD,GAAG,6BAAkB;YACrB,GAAG,yBAAc;SACjB;KACD,CAAC;CACF;AAlDD,0CAkDC"}
1
+ {"version":3,"file":"TglServiceTitan.node.js","sourceRoot":"","sources":["../../../nodes/ServiceTitan/TglServiceTitan.node.ts"],"names":[],"mappings":";;;AAAA,+CAIsB;AAEtB,2CAAkE;AAElE;;;;;;;;GAQG;AACH,MAAa,YAAY;IACxB,WAAW,GAAyB;QACnC,WAAW,EAAE,cAAc;QAC3B,IAAI,EAAE,iBAAiB;QACvB,IAAI,EAAE;YACL,KAAK,EAAE,uBAAuB;YAC9B,IAAI,EAAE,4BAA4B;SAClC;QACD,KAAK,EAAE,CAAC,WAAW,CAAC;QACpB,OAAO,EAAE,CAAC;QACV,QAAQ,EAAE,oDAAoD;QAC9D,WAAW,EAAE,oCAAoC;QACjD,QAAQ,EAAE;YACT,IAAI,EAAE,cAAc;SACpB;QACD,YAAY,EAAE,IAAI;QAClB,MAAM,EAAE,CAAC,kCAAmB,CAAC,IAAI,CAAC;QAClC,OAAO,EAAE,CAAC,kCAAmB,CAAC,IAAI,CAAC;QACnC,WAAW,EAAE;YACZ;gBACC,IAAI,EAAE,iBAAiB;gBACvB,QAAQ,EAAE,IAAI;aACd;SACD;QACD,eAAe,EAAE;YAChB,OAAO,EACN,oHAAoH;YACrH,OAAO,EAAE;gBACR,cAAc,EAAE,kBAAkB;gBAClC,MAAM,EAAE,kBAAkB;aAC1B;SACD;QACD,UAAU,EAAE;YACX;gBACC,WAAW,EAAE,UAAU;gBACvB,IAAI,EAAE,UAAU;gBAChB,IAAI,EAAE,SAAS;gBACf,gBAAgB,EAAE,IAAI;gBACtB,OAAO,EAAE,iCAAqB;gBAC9B,OAAO,EAAE,UAAU;aACnB;YACD,GAAG,wBAAY,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,kBAAkB,CAAC;YACpD,GAAG,wBAAY,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,CAAC;SAChD;KACD,CAAC;CACF;AA7CD,oCA6CC"}
@@ -0,0 +1,18 @@
1
+ import type { INodeProperties, INodePropertyOptions } from 'n8n-workflow';
2
+ /**
3
+ * Aggregator for action-node resources.
4
+ *
5
+ * Adding a new resource means:
6
+ * 1. Create resources/<name>/ with `<name>Operations` + `<name>Fields` exports
7
+ * 2. Add an entry to `allResources` below
8
+ * 3. Add a corresponding option to `resourceSelectOptions`
9
+ *
10
+ * The action node (TglServiceTitan.node.ts) spreads these directly into its
11
+ * `properties[]` and never has to be touched again for a new resource.
12
+ */
13
+ export declare const resourceSelectOptions: INodePropertyOptions[];
14
+ export interface ResourceBundle {
15
+ resourceOperations: INodeProperties[];
16
+ resourceFields: INodeProperties[];
17
+ }
18
+ export declare const allResources: ResourceBundle[];
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.allResources = exports.resourceSelectOptions = void 0;
4
+ const customer_1 = require("./customer");
5
+ /**
6
+ * Aggregator for action-node resources.
7
+ *
8
+ * Adding a new resource means:
9
+ * 1. Create resources/<name>/ with `<name>Operations` + `<name>Fields` exports
10
+ * 2. Add an entry to `allResources` below
11
+ * 3. Add a corresponding option to `resourceSelectOptions`
12
+ *
13
+ * The action node (TglServiceTitan.node.ts) spreads these directly into its
14
+ * `properties[]` and never has to be touched again for a new resource.
15
+ */
16
+ exports.resourceSelectOptions = [
17
+ {
18
+ name: 'Customer',
19
+ value: 'customer',
20
+ },
21
+ ];
22
+ exports.allResources = [
23
+ {
24
+ resourceOperations: customer_1.customerOperations,
25
+ resourceFields: customer_1.customerFields,
26
+ },
27
+ ];
28
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../nodes/ServiceTitan/resources/index.ts"],"names":[],"mappings":";;;AAEA,yCAAgE;AAEhE;;;;;;;;;;GAUG;AAEU,QAAA,qBAAqB,GAA2B;IAC5D;QACC,IAAI,EAAE,UAAU;QAChB,KAAK,EAAE,UAAU;KACjB;CACD,CAAC;AAOW,QAAA,YAAY,GAAqB;IAC7C;QACC,kBAAkB,EAAE,6BAAkB;QACtC,cAAc,EAAE,yBAAc;KAC9B;CACD,CAAC"}
@@ -1,4 +1,65 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" fill="none">
2
- <rect width="64" height="64" rx="12" fill="#4F8AFF"/>
3
- <text x="32" y="42" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif" font-weight="700" font-size="28" fill="#0B1220" text-anchor="middle">ST</text>
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- ServiceTitan wordmark (dark-theme variant — white fill). Same paths as the light variant. -->
3
+ <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="-131 222 585 106">
4
+ <path fill="#FFFFFF" d="M41.8,264.1c-2.7-2.3-6.2-3.9-10.1-3.9c-2.7,0-6.2,1.6-6.2,4.7c0,3.5,3.9,4.7,6.6,5.5l3.9,1.2c8.2,2.3,14.4,6.2,14.4,15.6
5
+ c0,5.8-1.6,11.7-5.8,15.6c-4.7,3.9-10.5,5.5-16.4,5.5c-7.4,0-14.4-2.3-20.3-6.6l6.2-12.1c3.9,3.1,8.2,5.8,13.2,5.8
6
+ c3.5,0,7.4-1.9,7.4-5.8c0-4.3-5.8-5.8-9-6.6c-9.4-2.7-15.6-5.1-15.6-16.4c0-11.7,8.2-19.5,19.9-19.5c5.8,0,12.9,1.9,17.9,4.7
7
+ L41.8,264.1z M83.5,281.7c-0.8-3.9-4.3-6.2-7.8-6.2c-3.9,0-7.4,2.3-8.2,6.2H83.5z M96.4,289.5H67.1c0,5.8,3.1,9,8.6,9
8
+ c3.1,0,5.1-0.8,6.6-3.5h13.2c-2.3,9.7-10.9,13.6-19.9,13.6c-12.9,0-22.6-7.4-22.6-21c0-13.2,9-21,21.8-21c13.6,0,21.4,8.6,21.4,21.8
9
+ V289.5z M113.5,275.8c3.1-5.8,7.4-7.4,12.9-7.4h2.7v13.2c-2.3-1.2-3.9-1.2-5.8-1.2c-7.8,0-9.7,5.1-9.7,11.7v16H99.9v-39.4h13.6
10
+ V275.8z M151.7,288.7l9.4-19.9h15.6l-20.7,39h-9l-20.3-39h15.6L151.7,288.7z M192.2,307.8h-14.4v-39.4h14.4V307.8z M193,255.2
11
+ c0,4.3-3.5,7.8-7.8,7.8c-4.3,0-7.8-3.5-7.8-7.8c0-4.3,3.5-7.8,7.8-7.8C189.5,247.4,193,250.9,193,255.2z M227.7,280.5
12
+ c-2.3-1.6-4.7-2.7-7.4-2.7c-5.5,0-9.7,4.3-9.7,9.7c0,5.8,4.3,9.7,9.7,9.7c2.3,0,4.7-1.2,7-2.7v11.7c-3.5,1.6-6.6,2.3-9.7,2.3
13
+ c-11.7,0-21.4-8.6-21.4-20.6c0-12.5,9.7-21.4,22.2-21.4c3.1,0,6.6,0.8,9.4,2.3V280.5z M261.6,281.7c-0.8-3.9-4.3-6.2-7.8-6.2
14
+ c-3.9,0-7.4,2.3-8.2,6.2H261.6z M274.4,289.5h-29.2c0,5.8,3.1,9,8.6,9c3.1,0,5.1-0.8,6.6-3.5h13.2c-2.3,9.7-10.9,13.6-19.9,13.6
15
+ c-12.9,0-22.6-7.4-22.6-21c0-13.2,9-21,21.8-21c13.6,0,21.4,8.6,21.4,21.8V289.5z M302.1,307.8h-14.4V263h-12.9v-12.5h39.7V263
16
+ h-12.5V307.8z M331.7,307.8h-14v-39.4h14V307.8z M332.5,255.2c0,4.3-3.5,7.8-7.8,7.8s-7.8-3.5-7.8-7.8c0-4.3,3.5-7.8,7.8-7.8
17
+ S332.5,250.9,332.5,255.2z M354.3,307.8h-14v-27.7H336v-11.3h4.3v-12.1h14v12.1h7.8v11.3h-7.8V307.8z M376.1,288.3c0,5.1,3.1,9,9,9
18
+ c5.8,0,9-3.9,9-9c0-4.7-3.1-9-9-9C379.6,279.3,376.1,283.6,376.1,288.3z M407.7,307.8h-14v-4.3c-2.3,3.9-7.4,5.8-11.7,5.8
19
+ c-11.7,0-19.9-9.7-19.9-21s8.2-21,19.5-21c4.7,0,9,1.6,12.1,5.1v-3.9h14V307.8z M426,273.9c3.5-4.7,7.4-6.2,12.9-6.2
20
+ c10.5,0,14.8,6.6,14.8,16.4v24.2h-14v-19.1c0-3.9,0.8-10.1-6.2-10.1c-5.8,0-7.4,4.3-7.4,9v20.3h-14v-39.4h14V273.9z M460.7,266.5
21
+ v1.6h1.2c0.8,0,2.3,0,2.3-0.8c0-0.8-0.8-0.8-1.6-0.8H460.7z M464.9,272.3h-1.2l-1.6-3.1h-1.6v3.1h-0.8v-7h2.7c1.2,0,2.3,0.4,2.3,1.9
22
+ c0,1.2-0.8,1.9-1.9,1.9L464.9,272.3z M457.2,268.8c0,2.7,2.3,5.1,5.1,5.1c2.7,0,5.1-2.3,5.1-5.1c0-2.7-2.3-5.1-5.1-5.1
23
+ C459.5,263.8,457.2,265.7,457.2,268.8z M456,268.8c0-3.5,2.7-6.2,6.2-6.2c3.5,0,6.2,2.7,6.2,6.2c0,3.5-2.7,6.2-6.2,6.2
24
+ C458.7,274.7,456,271.9,456,268.8z M-121.8,317.1c0,0,5.8,2.3,5.8,4.7c0,0-0.4,1.9-2.3,1.9c-1.9,0-4.7-3.1-4.7-4.7
25
+ S-121.8,317.1-121.8,317.1z M-128,287.9C-128.4,287.9-128.4,287.5-128,287.9c-0.4-0.4-0.4-0.4-0.4-0.4c-1.2,0.4-3.1,1.9-3.1,4.7
26
+ c0,10.5,28.4,11.7,44,30.8c1.6,1.9,4.3,3.1,7,3.1c3.5,0,7-0.4,10.1-0.8c0.4,0,0.8-0.4,0.8-0.8c0-0.4,0-0.8,0-1.2
27
+ C-88.7,297.3-124.9,303.5-128,287.9z M-87.1,254.8c-1.9-1.9-7-0.4-9.7,1.9c-2.7,2.3-1.6,3.9-1.6,3.9c2.3-3.1,5.8-4.7,8.2-5.1
28
+ C-88.3,255.6-87.1,254.8-87.1,254.8z M-28.7,260.2c0,0-3.9,0.8-7.4-3.1c0,0,1.6,0,1.9-1.9C-34.5,255.2-32.2,259.5-28.7,260.2z
29
+ M-29.9,278.2c0,0-0.8,4.3-5.8,4.3c-5.5,0-10.1-7.4-11.7-24.9c1.2,1.9,2.7,3.9,3.5,6.2c0-2.3,1.2-6.6-4.3-10.5
30
+ c0,0,14.4,3.1,14.8,17.9c0,2.7-1.6,5.1-2.3,6.2c0,0-0.8,0.4-0.4,1.6c0.4,0.8,1.6,0.8,2.7-0.4S-29.9,275.8-29.9,278.2z M-50.1,286.3
31
+ c0,0-3.9,7.4-9.4,7.4c-2.3,0-3.9-1.6-3.9-3.5s2.7-9.7,14-9.7c2.7,0,4.7,0.4,4.7,2.3C-44.3,284-47,286.7-50.1,286.3z M-50.1,249.3
32
+ c0,0-1.6-0.4-1.2-2.7c0.4-0.8,0.4-0.8,0.4-1.9c0-1.2,0.4-2.3,1.2-2.7c0,0,1.6,0,1.6,4.3C-48.2,248.2-50.1,249.3-50.1,249.3z
33
+ M-55.2,249.3c-2.3-0.4-0.8-3.9-2.3-3.9c-0.8,0-0.8,3.9-1.6,3.5c-1.6-1.2-0.4-3.9-0.4-5.1c0-1.2,0.8-2.7-0.4-1.9
34
+ c-1.2,0.8-1.9,2.7-2.3,4.3c-0.4,1.6,0.4,2.3,1.2,4.3c0.8,1.9-0.4,1.2-0.4,1.2c-5.8-3.5-1.6-11.3-1.6-11.3c2.7-4.3,6.6-3.1,6.6-3.1
35
+ c0.8,0.4,0.8,0.8,0.8,1.6c0,0.8,0.4,1.2,1.2,1.6c1.2,0.4,1.6,1.6,1.6,3.9C-52.8,247-54.4,249.7-55.2,249.3z M-46.2,248.9l9.7,3.1
36
+ c0,0,1.9,3.5-1.9,2.3c-3.5-1.2-7-1.9-7-1.9S-47,251.3-46.2,248.9z M-94.1,263.7c-7,1.2-9.4-0.8-9.4-2.7c0-5.5,16-9.7,20.3-9.7
37
+ c0.8,1.2-0.8,1.2-1.2,3.1c-0.8,1.9-2.3,2.7-4.7,3.1c-2.3,0.4-4.7,1.6-6.2,2.7c-1.6,1.2,0,1.9,2.7,1.2c2.7-0.8,3.1,0.4,4.3,0
38
+ c1.2-0.4,0-1.9,0.8-1.9c0.8-0.4,2.3,0.8,2.3,0.8l3.9,2.7l-4.7,2.3c-0.4,0-0.8,0.4-0.8,1.2c0,0.8,2.3,0.8,3.5,0.8
39
+ c0.8,0,1.2,1.6,1.2,1.6c-1.2,1.6-4.7,1.6-4.7,1.6c0.8,1.6,5.1,1.2,5.8,1.6c0.8,0,0.8,1.2,1.6,3.1c0.4,1.9,3.1,2.7,3.1,2.7
40
+ c0.8,3.1-2.7,3.9-5.1,3.9C-95.7,280.9-94.1,263.7-94.1,263.7z M-92.6,241.9C-93,241.9-93,241.9-92.6,241.9
41
+ c-1.2-5.8,1.2-12.5,1.2-12.5s-5.8,4.7-5.5,12.5c-5.1,0-12.5,0.4-16.8,4.3c-6.2,5.1-7,14.8-7.4,17.5c0,0,0,0.4-0.4,0.4h-0.4
42
+ c-1.6-0.8-4.7-3.9-2.3-10.5c3.1-8.6,12.5-25.7,30.8-26.9c0,0,5.8-0.4,9.4,1.9c0,0,0.4,0,0.4,0.4v0.4c-1.6,1.2-6.6,5.8-8.2,12.9
43
+ c0,0.4-0.4,0.4-0.4,0.4C-92.2,241.9-92.6,241.9-92.6,241.9z M-78.9,264.9c0,0,1.6,7.4,5.1,10.1c0,0,0,0.8-1.2,0.8
44
+ c-1.2,0-3.1-1.6-3.9-5.8C-79.7,266.1-78.9,264.9-78.9,264.9z M-73.9,246.2c-4.3,0-6.2-3.9-6.2-3.9s5.1,4.7,12.1,0.8
45
+ C-68,243.5-69.6,246.2-73.9,246.2z M-20.1,252.5c-1.2-2.3-2.7-3.9-6.2-4.7c-3.1-0.8-5.8-0.4-7.8,1.2l-6.2-2.3
46
+ c-0.8-1.6-1.2-3.1-1.9-4.3c-0.4-1.2-1.2-1.9-1.9-2.3c-4.3-2.3-15.6-8.2-19.9-2.3c0,0-0.4,0.4-0.4,0.8l-1.6-0.4
47
+ c-0.8-2.3-3.1-4.3-6.2-5.1c-1.9-0.4-3.9-0.4-5.5,0.4c-1.2,0.4-1.2,1.9,0,2.3l5.1,1.9c1.2,0.4,1.9,1.6,1.2,2.7
48
+ c-0.4,1.2-1.6,1.9-2.7,1.2c0,0-3.1-0.8-5.5-1.6c-1.2-0.4-2.3,0.8-1.9,1.9c0.8,2.3,3.1,4.7,6.2,5.5c2.7,0.8,5.5,0,7.4-1.2l1.2,0.4
49
+ c0,2.7,2.3,6.2,4.3,7.4c3.1,1.9,5.8,0.4,9-1.6c0,0,8.2,6.2,4.3,24.5c-10.1,1.2-14,4.7-17.5,8.6c-2.3-0.8-9.4-1.6-13.2-0.4
50
+ c1.2-1.2,2.7-2.3,5.1-3.1c1.2-0.4,1.6-1.2,1.6-2.3c0,0,4.7-1.6,4.7-3.1c0-1.6-5.8-1.6-8.2-14c-0.4-1.6-0.8-2.7-3.1-3.5
51
+ c0,0-0.8-1.2,0.4-2.7c1.2-1.6,1.6-1.9,1.2-3.1c-0.4-1.2-1.9-1.9-1.9-1.9v-1.9c0,0-4.7-3.5-4.7-8.6c0-5.1,6.6-10.9,8.2-12.5v-0.4
52
+ c0,0,0-0.4-0.4-0.4c-2.3-1.6-6.6-3.9-13.6-3.9c-27.3,0-33.5,24.5-39.4,36.2c0,0.4,0,0.4,0,0.8c5.8,11.7,15.2,10.5,16,20.6
53
+ c0.4,7.8-1.6,7.8-3.1,7.8c-1.2,0-3.1-1.2-3.9-1.9h-0.4c0,0-0.4,0.4,0,0.4c0.8,1.9,2.7,6.6,9,6.6c3.1,0,3.9-2.7,3.9-5.5v-9
54
+ c0-0.8-0.4-1.6-0.8-2.3c-1.9-1.9-6.6-6.2-6.6-9.7c0-1.6,1.2-2.3,2.3-2.3c2.3,0,4.7,3.5,6.6,8.2c0.4,1.2,1.2,1.9,1.6,2.3h0.4
55
+ c0,0,0,0,0-0.4c-0.8-2.7-0.8-5.5-1.2-6.6c0-0.4-0.4-1.2-0.8-1.6c-1.9-1.6-6.6-6.2-6.6-13.2c0-9.4,15.2-8.6,18.3-8.6
56
+ c0.8,3.1,2.7,4.7,3.9,5.1c-4.7,1.6-14.8,5.1-14.8,10.9c0,6.2,10.5,5.8,10.5,5.8s-1.6,12.5,10.1,16.4c0,0-1.9,1.9-8.2,1.9
57
+ c-5.8,0-8.6-2.7-10.1-5.8c0,0,0-0.4-0.4-0.4c0,0-0.4,0-0.4,0.4c-0.4,0.8-0.4,2.3,0.4,3.9c1.6,2.7,3.9,4.3,3.9,7.8
58
+ c0,2.7-2.3,4.3-3.5,4.7c2.3,0.4,12.9,3.1,19.9,6.6c0.8-0.4,7-4.3,7.4-9.7c0,0,0.4-3.5-4.7-3.9c0,0,2.3-0.8,6.2-0.8
59
+ c3.9,0,8.2,3.1,0.4,19.5c0,0,0,0,0,0.4c1.2,0.8,10.1,6.6,16,12.1h0.4c0,0,0.4,0,0.4-0.4c0.8-0.8,2.3-3.5,3.1-10.1
60
+ c0-0.8,0.4-1.2,1.2-1.2c3.5-1.2,15.2-4.7,20.3-11.7c5.8-8.2,8.2-9.4,8.2-9.4s3.9-2.3,2.3-8.2c-0.8-3.1-5.5-10.9-10.1-19.1
61
+ c1.2,0.8,2.3,1.2,3.5,1.6c2.7,0.8,5.5,0.4,7.4-0.8c0.4-0.4,0.8-0.8,0.8-1.2c0-0.4-0.4-0.8-0.8-1.2l-5.1-1.9
62
+ c-1.2-0.4-1.9-1.9-1.6-3.1c0.4-1.2,1.9-1.9,3.1-1.6l4.7,1.6c0.4,0,0.8,0,1.2-0.4C-19.7,252.8-20.1,253.2-20.1,252.5z M-89.1,267.2
63
+ c0,0-1.9,0.4-1.6,3.5c0.4,2.7,2.7,2.7,2.7,2.7C-89.9,271.1-89.1,267.2-89.1,267.2z M-123,305.8c7,3.9,22.6,8.6,25.7,16.8
64
+ c0,0,1.2,1.9-5.1,1.9c-4.7,0-5.8-1.2-9-5.1c-4.7-5.5-14.4-9-14.4-13.2C-126.1,306.6-125.7,304.3-123,305.8z"/>
4
65
  </svg>
@@ -1,4 +1,65 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" fill="none">
2
- <rect width="64" height="64" rx="12" fill="#0F62FE"/>
3
- <text x="32" y="42" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif" font-weight="700" font-size="28" fill="#FFFFFF" text-anchor="middle">ST</text>
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- ServiceTitan wordmark official mark, sourced from Wikimedia Commons (public domain: consists only of simple geometric shapes and text). -->
3
+ <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="-131 222 585 106">
4
+ <path fill="#000000" d="M41.8,264.1c-2.7-2.3-6.2-3.9-10.1-3.9c-2.7,0-6.2,1.6-6.2,4.7c0,3.5,3.9,4.7,6.6,5.5l3.9,1.2c8.2,2.3,14.4,6.2,14.4,15.6
5
+ c0,5.8-1.6,11.7-5.8,15.6c-4.7,3.9-10.5,5.5-16.4,5.5c-7.4,0-14.4-2.3-20.3-6.6l6.2-12.1c3.9,3.1,8.2,5.8,13.2,5.8
6
+ c3.5,0,7.4-1.9,7.4-5.8c0-4.3-5.8-5.8-9-6.6c-9.4-2.7-15.6-5.1-15.6-16.4c0-11.7,8.2-19.5,19.9-19.5c5.8,0,12.9,1.9,17.9,4.7
7
+ L41.8,264.1z M83.5,281.7c-0.8-3.9-4.3-6.2-7.8-6.2c-3.9,0-7.4,2.3-8.2,6.2H83.5z M96.4,289.5H67.1c0,5.8,3.1,9,8.6,9
8
+ c3.1,0,5.1-0.8,6.6-3.5h13.2c-2.3,9.7-10.9,13.6-19.9,13.6c-12.9,0-22.6-7.4-22.6-21c0-13.2,9-21,21.8-21c13.6,0,21.4,8.6,21.4,21.8
9
+ V289.5z M113.5,275.8c3.1-5.8,7.4-7.4,12.9-7.4h2.7v13.2c-2.3-1.2-3.9-1.2-5.8-1.2c-7.8,0-9.7,5.1-9.7,11.7v16H99.9v-39.4h13.6
10
+ V275.8z M151.7,288.7l9.4-19.9h15.6l-20.7,39h-9l-20.3-39h15.6L151.7,288.7z M192.2,307.8h-14.4v-39.4h14.4V307.8z M193,255.2
11
+ c0,4.3-3.5,7.8-7.8,7.8c-4.3,0-7.8-3.5-7.8-7.8c0-4.3,3.5-7.8,7.8-7.8C189.5,247.4,193,250.9,193,255.2z M227.7,280.5
12
+ c-2.3-1.6-4.7-2.7-7.4-2.7c-5.5,0-9.7,4.3-9.7,9.7c0,5.8,4.3,9.7,9.7,9.7c2.3,0,4.7-1.2,7-2.7v11.7c-3.5,1.6-6.6,2.3-9.7,2.3
13
+ c-11.7,0-21.4-8.6-21.4-20.6c0-12.5,9.7-21.4,22.2-21.4c3.1,0,6.6,0.8,9.4,2.3V280.5z M261.6,281.7c-0.8-3.9-4.3-6.2-7.8-6.2
14
+ c-3.9,0-7.4,2.3-8.2,6.2H261.6z M274.4,289.5h-29.2c0,5.8,3.1,9,8.6,9c3.1,0,5.1-0.8,6.6-3.5h13.2c-2.3,9.7-10.9,13.6-19.9,13.6
15
+ c-12.9,0-22.6-7.4-22.6-21c0-13.2,9-21,21.8-21c13.6,0,21.4,8.6,21.4,21.8V289.5z M302.1,307.8h-14.4V263h-12.9v-12.5h39.7V263
16
+ h-12.5V307.8z M331.7,307.8h-14v-39.4h14V307.8z M332.5,255.2c0,4.3-3.5,7.8-7.8,7.8s-7.8-3.5-7.8-7.8c0-4.3,3.5-7.8,7.8-7.8
17
+ S332.5,250.9,332.5,255.2z M354.3,307.8h-14v-27.7H336v-11.3h4.3v-12.1h14v12.1h7.8v11.3h-7.8V307.8z M376.1,288.3c0,5.1,3.1,9,9,9
18
+ c5.8,0,9-3.9,9-9c0-4.7-3.1-9-9-9C379.6,279.3,376.1,283.6,376.1,288.3z M407.7,307.8h-14v-4.3c-2.3,3.9-7.4,5.8-11.7,5.8
19
+ c-11.7,0-19.9-9.7-19.9-21s8.2-21,19.5-21c4.7,0,9,1.6,12.1,5.1v-3.9h14V307.8z M426,273.9c3.5-4.7,7.4-6.2,12.9-6.2
20
+ c10.5,0,14.8,6.6,14.8,16.4v24.2h-14v-19.1c0-3.9,0.8-10.1-6.2-10.1c-5.8,0-7.4,4.3-7.4,9v20.3h-14v-39.4h14V273.9z M460.7,266.5
21
+ v1.6h1.2c0.8,0,2.3,0,2.3-0.8c0-0.8-0.8-0.8-1.6-0.8H460.7z M464.9,272.3h-1.2l-1.6-3.1h-1.6v3.1h-0.8v-7h2.7c1.2,0,2.3,0.4,2.3,1.9
22
+ c0,1.2-0.8,1.9-1.9,1.9L464.9,272.3z M457.2,268.8c0,2.7,2.3,5.1,5.1,5.1c2.7,0,5.1-2.3,5.1-5.1c0-2.7-2.3-5.1-5.1-5.1
23
+ C459.5,263.8,457.2,265.7,457.2,268.8z M456,268.8c0-3.5,2.7-6.2,6.2-6.2c3.5,0,6.2,2.7,6.2,6.2c0,3.5-2.7,6.2-6.2,6.2
24
+ C458.7,274.7,456,271.9,456,268.8z M-121.8,317.1c0,0,5.8,2.3,5.8,4.7c0,0-0.4,1.9-2.3,1.9c-1.9,0-4.7-3.1-4.7-4.7
25
+ S-121.8,317.1-121.8,317.1z M-128,287.9C-128.4,287.9-128.4,287.5-128,287.9c-0.4-0.4-0.4-0.4-0.4-0.4c-1.2,0.4-3.1,1.9-3.1,4.7
26
+ c0,10.5,28.4,11.7,44,30.8c1.6,1.9,4.3,3.1,7,3.1c3.5,0,7-0.4,10.1-0.8c0.4,0,0.8-0.4,0.8-0.8c0-0.4,0-0.8,0-1.2
27
+ C-88.7,297.3-124.9,303.5-128,287.9z M-87.1,254.8c-1.9-1.9-7-0.4-9.7,1.9c-2.7,2.3-1.6,3.9-1.6,3.9c2.3-3.1,5.8-4.7,8.2-5.1
28
+ C-88.3,255.6-87.1,254.8-87.1,254.8z M-28.7,260.2c0,0-3.9,0.8-7.4-3.1c0,0,1.6,0,1.9-1.9C-34.5,255.2-32.2,259.5-28.7,260.2z
29
+ M-29.9,278.2c0,0-0.8,4.3-5.8,4.3c-5.5,0-10.1-7.4-11.7-24.9c1.2,1.9,2.7,3.9,3.5,6.2c0-2.3,1.2-6.6-4.3-10.5
30
+ c0,0,14.4,3.1,14.8,17.9c0,2.7-1.6,5.1-2.3,6.2c0,0-0.8,0.4-0.4,1.6c0.4,0.8,1.6,0.8,2.7-0.4S-29.9,275.8-29.9,278.2z M-50.1,286.3
31
+ c0,0-3.9,7.4-9.4,7.4c-2.3,0-3.9-1.6-3.9-3.5s2.7-9.7,14-9.7c2.7,0,4.7,0.4,4.7,2.3C-44.3,284-47,286.7-50.1,286.3z M-50.1,249.3
32
+ c0,0-1.6-0.4-1.2-2.7c0.4-0.8,0.4-0.8,0.4-1.9c0-1.2,0.4-2.3,1.2-2.7c0,0,1.6,0,1.6,4.3C-48.2,248.2-50.1,249.3-50.1,249.3z
33
+ M-55.2,249.3c-2.3-0.4-0.8-3.9-2.3-3.9c-0.8,0-0.8,3.9-1.6,3.5c-1.6-1.2-0.4-3.9-0.4-5.1c0-1.2,0.8-2.7-0.4-1.9
34
+ c-1.2,0.8-1.9,2.7-2.3,4.3c-0.4,1.6,0.4,2.3,1.2,4.3c0.8,1.9-0.4,1.2-0.4,1.2c-5.8-3.5-1.6-11.3-1.6-11.3c2.7-4.3,6.6-3.1,6.6-3.1
35
+ c0.8,0.4,0.8,0.8,0.8,1.6c0,0.8,0.4,1.2,1.2,1.6c1.2,0.4,1.6,1.6,1.6,3.9C-52.8,247-54.4,249.7-55.2,249.3z M-46.2,248.9l9.7,3.1
36
+ c0,0,1.9,3.5-1.9,2.3c-3.5-1.2-7-1.9-7-1.9S-47,251.3-46.2,248.9z M-94.1,263.7c-7,1.2-9.4-0.8-9.4-2.7c0-5.5,16-9.7,20.3-9.7
37
+ c0.8,1.2-0.8,1.2-1.2,3.1c-0.8,1.9-2.3,2.7-4.7,3.1c-2.3,0.4-4.7,1.6-6.2,2.7c-1.6,1.2,0,1.9,2.7,1.2c2.7-0.8,3.1,0.4,4.3,0
38
+ c1.2-0.4,0-1.9,0.8-1.9c0.8-0.4,2.3,0.8,2.3,0.8l3.9,2.7l-4.7,2.3c-0.4,0-0.8,0.4-0.8,1.2c0,0.8,2.3,0.8,3.5,0.8
39
+ c0.8,0,1.2,1.6,1.2,1.6c-1.2,1.6-4.7,1.6-4.7,1.6c0.8,1.6,5.1,1.2,5.8,1.6c0.8,0,0.8,1.2,1.6,3.1c0.4,1.9,3.1,2.7,3.1,2.7
40
+ c0.8,3.1-2.7,3.9-5.1,3.9C-95.7,280.9-94.1,263.7-94.1,263.7z M-92.6,241.9C-93,241.9-93,241.9-92.6,241.9
41
+ c-1.2-5.8,1.2-12.5,1.2-12.5s-5.8,4.7-5.5,12.5c-5.1,0-12.5,0.4-16.8,4.3c-6.2,5.1-7,14.8-7.4,17.5c0,0,0,0.4-0.4,0.4h-0.4
42
+ c-1.6-0.8-4.7-3.9-2.3-10.5c3.1-8.6,12.5-25.7,30.8-26.9c0,0,5.8-0.4,9.4,1.9c0,0,0.4,0,0.4,0.4v0.4c-1.6,1.2-6.6,5.8-8.2,12.9
43
+ c0,0.4-0.4,0.4-0.4,0.4C-92.2,241.9-92.6,241.9-92.6,241.9z M-78.9,264.9c0,0,1.6,7.4,5.1,10.1c0,0,0,0.8-1.2,0.8
44
+ c-1.2,0-3.1-1.6-3.9-5.8C-79.7,266.1-78.9,264.9-78.9,264.9z M-73.9,246.2c-4.3,0-6.2-3.9-6.2-3.9s5.1,4.7,12.1,0.8
45
+ C-68,243.5-69.6,246.2-73.9,246.2z M-20.1,252.5c-1.2-2.3-2.7-3.9-6.2-4.7c-3.1-0.8-5.8-0.4-7.8,1.2l-6.2-2.3
46
+ c-0.8-1.6-1.2-3.1-1.9-4.3c-0.4-1.2-1.2-1.9-1.9-2.3c-4.3-2.3-15.6-8.2-19.9-2.3c0,0-0.4,0.4-0.4,0.8l-1.6-0.4
47
+ c-0.8-2.3-3.1-4.3-6.2-5.1c-1.9-0.4-3.9-0.4-5.5,0.4c-1.2,0.4-1.2,1.9,0,2.3l5.1,1.9c1.2,0.4,1.9,1.6,1.2,2.7
48
+ c-0.4,1.2-1.6,1.9-2.7,1.2c0,0-3.1-0.8-5.5-1.6c-1.2-0.4-2.3,0.8-1.9,1.9c0.8,2.3,3.1,4.7,6.2,5.5c2.7,0.8,5.5,0,7.4-1.2l1.2,0.4
49
+ c0,2.7,2.3,6.2,4.3,7.4c3.1,1.9,5.8,0.4,9-1.6c0,0,8.2,6.2,4.3,24.5c-10.1,1.2-14,4.7-17.5,8.6c-2.3-0.8-9.4-1.6-13.2-0.4
50
+ c1.2-1.2,2.7-2.3,5.1-3.1c1.2-0.4,1.6-1.2,1.6-2.3c0,0,4.7-1.6,4.7-3.1c0-1.6-5.8-1.6-8.2-14c-0.4-1.6-0.8-2.7-3.1-3.5
51
+ c0,0-0.8-1.2,0.4-2.7c1.2-1.6,1.6-1.9,1.2-3.1c-0.4-1.2-1.9-1.9-1.9-1.9v-1.9c0,0-4.7-3.5-4.7-8.6c0-5.1,6.6-10.9,8.2-12.5v-0.4
52
+ c0,0,0-0.4-0.4-0.4c-2.3-1.6-6.6-3.9-13.6-3.9c-27.3,0-33.5,24.5-39.4,36.2c0,0.4,0,0.4,0,0.8c5.8,11.7,15.2,10.5,16,20.6
53
+ c0.4,7.8-1.6,7.8-3.1,7.8c-1.2,0-3.1-1.2-3.9-1.9h-0.4c0,0-0.4,0.4,0,0.4c0.8,1.9,2.7,6.6,9,6.6c3.1,0,3.9-2.7,3.9-5.5v-9
54
+ c0-0.8-0.4-1.6-0.8-2.3c-1.9-1.9-6.6-6.2-6.6-9.7c0-1.6,1.2-2.3,2.3-2.3c2.3,0,4.7,3.5,6.6,8.2c0.4,1.2,1.2,1.9,1.6,2.3h0.4
55
+ c0,0,0,0,0-0.4c-0.8-2.7-0.8-5.5-1.2-6.6c0-0.4-0.4-1.2-0.8-1.6c-1.9-1.6-6.6-6.2-6.6-13.2c0-9.4,15.2-8.6,18.3-8.6
56
+ c0.8,3.1,2.7,4.7,3.9,5.1c-4.7,1.6-14.8,5.1-14.8,10.9c0,6.2,10.5,5.8,10.5,5.8s-1.6,12.5,10.1,16.4c0,0-1.9,1.9-8.2,1.9
57
+ c-5.8,0-8.6-2.7-10.1-5.8c0,0,0-0.4-0.4-0.4c0,0-0.4,0-0.4,0.4c-0.4,0.8-0.4,2.3,0.4,3.9c1.6,2.7,3.9,4.3,3.9,7.8
58
+ c0,2.7-2.3,4.3-3.5,4.7c2.3,0.4,12.9,3.1,19.9,6.6c0.8-0.4,7-4.3,7.4-9.7c0,0,0.4-3.5-4.7-3.9c0,0,2.3-0.8,6.2-0.8
59
+ c3.9,0,8.2,3.1,0.4,19.5c0,0,0,0,0,0.4c1.2,0.8,10.1,6.6,16,12.1h0.4c0,0,0.4,0,0.4-0.4c0.8-0.8,2.3-3.5,3.1-10.1
60
+ c0-0.8,0.4-1.2,1.2-1.2c3.5-1.2,15.2-4.7,20.3-11.7c5.8-8.2,8.2-9.4,8.2-9.4s3.9-2.3,2.3-8.2c-0.8-3.1-5.5-10.9-10.1-19.1
61
+ c1.2,0.8,2.3,1.2,3.5,1.6c2.7,0.8,5.5,0.4,7.4-0.8c0.4-0.4,0.8-0.8,0.8-1.2c0-0.4-0.4-0.8-0.8-1.2l-5.1-1.9
62
+ c-1.2-0.4-1.9-1.9-1.6-3.1c0.4-1.2,1.9-1.9,3.1-1.6l4.7,1.6c0.4,0,0.8,0,1.2-0.4C-19.7,252.8-20.1,253.2-20.1,252.5z M-89.1,267.2
63
+ c0,0-1.9,0.4-1.6,3.5c0.4,2.7,2.7,2.7,2.7,2.7C-89.9,271.1-89.1,267.2-89.1,267.2z M-123,305.8c7,3.9,22.6,8.6,25.7,16.8
64
+ c0,0,1.2,1.9-5.1,1.9c-4.7,0-5.8-1.2-9-5.1c-4.7-5.5-14.4-9-14.4-13.2C-126.1,306.6-125.7,304.3-123,305.8z"/>
4
65
  </svg>
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Shared, low-churn ServiceTitan API shapes.
3
+ *
4
+ * These are first-pass and intentionally narrow — only fields we actively read
5
+ * across the codebase. Extend as new resources are ported.
6
+ */
7
+ /**
8
+ * Standard ServiceTitan list-envelope. Almost every ST list endpoint returns
9
+ * the same wrapper: page metadata + a `data` array of the resource.
10
+ */
11
+ export interface STListEnvelope<T> {
12
+ page?: number;
13
+ pageSize?: number;
14
+ hasMore?: boolean;
15
+ totalCount?: number;
16
+ data?: T[];
17
+ }
18
+ /**
19
+ * Raw shape ServiceTitan returns inside `customFields[]` on most resources.
20
+ * `value` is `unknown` because ST encodes typed values per `typeId` and we
21
+ * don't normalize them yet.
22
+ */
23
+ export interface STCustomField {
24
+ name: string;
25
+ typeId: number;
26
+ value: unknown;
27
+ }
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ /**
3
+ * Shared, low-churn ServiceTitan API shapes.
4
+ *
5
+ * These are first-pass and intentionally narrow — only fields we actively read
6
+ * across the codebase. Extend as new resources are ported.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ //# sourceMappingURL=common.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"common.js","sourceRoot":"","sources":["../../../../nodes/ServiceTitan/types/common.ts"],"names":[],"mappings":";AAAA;;;;;GAKG"}
@@ -0,0 +1,15 @@
1
+ import type { STCustomField } from './common';
2
+ /**
3
+ * Minimal ServiceTitan `Customer` shape. First-pass — extend as the Customer
4
+ * resource grows beyond Customer.Get.
5
+ */
6
+ export interface Customer {
7
+ id: number;
8
+ name: string;
9
+ type?: string;
10
+ address?: unknown;
11
+ customFields?: STCustomField[];
12
+ modifiedOn?: string;
13
+ createdOn?: string;
14
+ active: boolean;
15
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=customer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"customer.js","sourceRoot":"","sources":["../../../../nodes/ServiceTitan/types/customer.ts"],"names":[],"mappings":""}
@@ -0,0 +1,3 @@
1
+ export type { STListEnvelope, STCustomField } from './common';
2
+ export type { Job } from './job';
3
+ export type { Customer } from './customer';
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../nodes/ServiceTitan/types/index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,21 @@
1
+ import type { STCustomField } from './common';
2
+ /**
3
+ * Subset of the ServiceTitan `Job` shape we read in the trigger and downstream
4
+ * helpers. First-pass — extend as new fields are needed.
5
+ */
6
+ export interface Job {
7
+ id: number;
8
+ jobNumber?: string;
9
+ customerId?: number;
10
+ locationId?: number;
11
+ jobStatus?: string;
12
+ businessUnitId?: number;
13
+ jobTypeId?: number;
14
+ campaignId?: number;
15
+ modifiedOn?: string;
16
+ createdOn?: string;
17
+ completedOn?: string;
18
+ summary?: string;
19
+ customFields?: STCustomField[];
20
+ jobAssignments?: unknown[];
21
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=job.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"job.js","sourceRoot":"","sources":["../../../../nodes/ServiceTitan/types/job.ts"],"names":[],"mappings":""}
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thegraphitelab/n8n-nodes-servicetitan",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "n8n custom node for ServiceTitan — TGL internal use.",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thegraphitelab/n8n-nodes-servicetitan",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "n8n custom node for ServiceTitan — TGL internal use.",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",