@thegraphitelab/n8n-nodes-servicetitan 0.1.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.
package/README.md ADDED
@@ -0,0 +1,46 @@
1
+ # @thegraphitelab/n8n-nodes-servicetitan
2
+
3
+ n8n custom node for ServiceTitan. Internal use only — published to GitHub Packages under the `@thegraphitelab` scope.
4
+
5
+ ## Status
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
10
+
11
+ More resources (Job, Invoice, Estimate, Technician, Booking, etc.) get added per the `CONTRIBUTING.md` recipe in the repo root.
12
+
13
+ ## Credentials
14
+
15
+ Required fields:
16
+ - **Environment** — `Integration (Sandbox)` or `Production`
17
+ - **Tenant ID** — found in ServiceTitan → Settings → API Application Access
18
+ - **App Key** — `ST-App-Key`. TGL-owned application identifier. Same across all TGL clients (controlled by TGL's ServiceTitan developer portal app).
19
+ - **Client ID** — OAuth client ID, per-client
20
+ - **Client Secret** — OAuth client secret, per-client
21
+
22
+ The credential's "Test" button hits `/settings/v2/tenant/{tenantId}/user-roles?page=1&pageSize=1` to verify the full auth chain works.
23
+
24
+ ## Auth flow
25
+
26
+ 1. n8n calls `preAuthentication` which POSTs `grant_type=client_credentials` to the env-correct token URL
27
+ 2. The returned `access_token` (TTL 15min) is cached in the credential data
28
+ 3. Each request injects `Authorization: Bearer <token>` AND `ST-App-Key: <appKey>` headers
29
+ 4. The `requestDefaults.baseURL` on the node switches between integration and production hosts based on `$credentials.environment`
30
+
31
+ ## Local dev
32
+
33
+ ```bash
34
+ pnpm dev
35
+ # Opens http://localhost:5678 with an embedded n8n and this node pre-loaded
36
+ ```
37
+
38
+ ## Reference
39
+
40
+ Endpoints and operation catalog are ported from `The-Graphite-Lab/advanced_servicetitan_zapier_app`.
41
+
42
+ ## TODO
43
+
44
+ - [ ] Port additional resources (Job, Invoice, Estimate, Technician, Customer Note, etc.)
45
+ - [ ] Add polling trigger node (`ServiceTitanTrigger`) using `modifiedOnOrAfter` filter
46
+ - [ ] Wire usage metering to `api.thegraphitelab.com/usage` (matches the Zapier app's billing layer)
@@ -0,0 +1,14 @@
1
+ import type { IAuthenticateGeneric, ICredentialDataDecryptedObject, ICredentialTestRequest, ICredentialType, IHttpRequestHelper, INodeProperties } from 'n8n-workflow';
2
+ export declare function getApiHost(environment: string): string;
3
+ export declare class ServiceTitanApi implements ICredentialType {
4
+ name: string;
5
+ displayName: string;
6
+ icon: "file:../nodes/ServiceTitan/servicetitan.svg";
7
+ documentationUrl: string;
8
+ properties: INodeProperties[];
9
+ preAuthentication(this: IHttpRequestHelper, credentials: ICredentialDataDecryptedObject): Promise<{
10
+ accessToken: string;
11
+ }>;
12
+ authenticate: IAuthenticateGeneric;
13
+ test: ICredentialTestRequest;
14
+ }
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ServiceTitanApi = void 0;
4
+ exports.getApiHost = getApiHost;
5
+ /**
6
+ * ServiceTitan auth hosts by environment.
7
+ * Integration ("Sandbox") and Production are entirely separate ST environments —
8
+ * different app registrations, different tenant IDs, different credentials.
9
+ */
10
+ function getAuthHost(environment) {
11
+ return environment === 'production'
12
+ ? 'auth.servicetitan.io'
13
+ : 'auth-integration.servicetitan.io';
14
+ }
15
+ function getApiHost(environment) {
16
+ return environment === 'production'
17
+ ? 'api.servicetitan.io'
18
+ : 'api-integration.servicetitan.io';
19
+ }
20
+ class ServiceTitanApi {
21
+ name = 'serviceTitanApi';
22
+ displayName = 'ServiceTitan API';
23
+ icon = 'file:../nodes/ServiceTitan/servicetitan.svg';
24
+ documentationUrl = 'https://developer.servicetitan.io/';
25
+ properties = [
26
+ {
27
+ displayName: 'Environment',
28
+ name: 'environment',
29
+ type: 'options',
30
+ options: [
31
+ { name: 'Integration (Sandbox)', value: 'integration' },
32
+ { name: 'Production', value: 'production' },
33
+ ],
34
+ default: 'integration',
35
+ description: 'Which ServiceTitan environment to connect to. Integration and Production are entirely separate — they need different app registrations and credentials.',
36
+ },
37
+ {
38
+ displayName: 'Tenant ID',
39
+ name: 'tenantId',
40
+ type: 'string',
41
+ default: '',
42
+ required: true,
43
+ description: 'Found in ServiceTitan → Settings → API Application Access. Treated as a string in URL paths.',
44
+ },
45
+ {
46
+ displayName: 'App Key',
47
+ name: 'appKey',
48
+ type: 'string',
49
+ typeOptions: { password: true },
50
+ default: '',
51
+ required: true,
52
+ description: 'The ST-App-Key. TGL-owned application identifier — the same value is used across all TGL clients. Get this from the TGL ServiceTitan developer portal application.',
53
+ },
54
+ {
55
+ displayName: 'Client ID',
56
+ name: 'clientId',
57
+ type: 'string',
58
+ default: '',
59
+ required: true,
60
+ description: "Per-client OAuth client ID from ServiceTitan → Settings → API Application Access.",
61
+ },
62
+ {
63
+ displayName: 'Client Secret',
64
+ name: 'clientSecret',
65
+ type: 'string',
66
+ typeOptions: { password: true },
67
+ default: '',
68
+ required: true,
69
+ description: "Per-client OAuth client secret from ServiceTitan → Settings → API Application Access.",
70
+ },
71
+ // accessToken is populated by preAuthentication and consumed by authenticate
72
+ {
73
+ displayName: 'Access Token',
74
+ name: 'accessToken',
75
+ type: 'hidden',
76
+ typeOptions: { password: true },
77
+ default: '',
78
+ },
79
+ ];
80
+ async preAuthentication(credentials) {
81
+ const environment = credentials.environment ?? 'integration';
82
+ const clientId = credentials.clientId;
83
+ const clientSecret = credentials.clientSecret;
84
+ const body = new URLSearchParams({
85
+ grant_type: 'client_credentials',
86
+ client_id: clientId,
87
+ client_secret: clientSecret,
88
+ }).toString();
89
+ const response = (await this.helpers.httpRequest({
90
+ method: 'POST',
91
+ url: `https://${getAuthHost(environment)}/connect/token`,
92
+ headers: {
93
+ 'Content-Type': 'application/x-www-form-urlencoded',
94
+ },
95
+ body,
96
+ }));
97
+ return { accessToken: response.access_token };
98
+ }
99
+ authenticate = {
100
+ type: 'generic',
101
+ properties: {
102
+ headers: {
103
+ Authorization: '=Bearer {{$credentials.accessToken}}',
104
+ 'ST-App-Key': '={{$credentials.appKey}}',
105
+ },
106
+ },
107
+ };
108
+ test = {
109
+ request: {
110
+ baseURL: '=https://{{$credentials.environment === "production" ? "api.servicetitan.io" : "api-integration.servicetitan.io"}}',
111
+ url: '=/settings/v2/tenant/{{$credentials.tenantId}}/user-roles',
112
+ method: 'GET',
113
+ qs: {
114
+ page: 1,
115
+ pageSize: 1,
116
+ },
117
+ },
118
+ };
119
+ }
120
+ exports.ServiceTitanApi = ServiceTitanApi;
121
+ //# sourceMappingURL=ServiceTitanApi.credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ServiceTitanApi.credentials.js","sourceRoot":"","sources":["../../credentials/ServiceTitanApi.credentials.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { type INodeType, type INodeTypeDescription } from 'n8n-workflow';
2
+ export declare class TglServiceTitan implements INodeType {
3
+ description: INodeTypeDescription;
4
+ }
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TglServiceTitan = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
5
+ const customer_1 = require("./resources/customer");
6
+ class TglServiceTitan {
7
+ description = {
8
+ displayName: 'ServiceTitan',
9
+ name: 'tglServiceTitan',
10
+ icon: {
11
+ light: 'file:servicetitan.svg',
12
+ dark: 'file:servicetitan.dark.svg',
13
+ },
14
+ group: ['transform'],
15
+ version: 1,
16
+ subtitle: '={{$parameter.operation}}: {{$parameter.resource}}',
17
+ description: 'Interact with the ServiceTitan API',
18
+ defaults: {
19
+ name: 'ServiceTitan',
20
+ },
21
+ usableAsTool: true,
22
+ inputs: [n8n_workflow_1.NodeConnectionTypes.Main],
23
+ outputs: [n8n_workflow_1.NodeConnectionTypes.Main],
24
+ credentials: [
25
+ {
26
+ name: 'serviceTitanApi',
27
+ required: true,
28
+ },
29
+ ],
30
+ requestDefaults: {
31
+ baseURL: '=https://{{$credentials.environment === "production" ? "api.servicetitan.io" : "api-integration.servicetitan.io"}}',
32
+ headers: {
33
+ 'Content-Type': 'application/json',
34
+ Accept: 'application/json',
35
+ },
36
+ },
37
+ properties: [
38
+ {
39
+ displayName: 'Resource',
40
+ name: 'resource',
41
+ type: 'options',
42
+ noDataExpression: true,
43
+ options: [
44
+ {
45
+ name: 'Customer',
46
+ value: 'customer',
47
+ },
48
+ ],
49
+ default: 'customer',
50
+ },
51
+ ...customer_1.customerOperations,
52
+ ...customer_1.customerFields,
53
+ ],
54
+ };
55
+ }
56
+ exports.TglServiceTitan = TglServiceTitan;
57
+ //# sourceMappingURL=TglServiceTitan.node.js.map
@@ -0,0 +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"}
@@ -0,0 +1,18 @@
1
+ {
2
+ "node": "n8n-nodes-base.tglServiceTitan",
3
+ "nodeVersion": "1.0",
4
+ "codexVersion": "1.0",
5
+ "categories": ["Sales", "Productivity"],
6
+ "resources": {
7
+ "primaryDocumentation": [
8
+ {
9
+ "url": "https://github.com/thegraphitelab/tgl-custom-n8n-nodes/blob/main/packages/n8n-nodes-servicetitan/README.md"
10
+ }
11
+ ],
12
+ "credentialDocumentation": [
13
+ {
14
+ "url": "https://developer.servicetitan.io/"
15
+ }
16
+ ]
17
+ }
18
+ }
@@ -0,0 +1,2 @@
1
+ import type { INodeProperties } from 'n8n-workflow';
2
+ export declare const customerGetDescription: INodeProperties[];
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.customerGetDescription = void 0;
4
+ exports.customerGetDescription = [
5
+ {
6
+ displayName: 'Customer ID',
7
+ name: 'customerId',
8
+ type: 'string',
9
+ required: true,
10
+ default: '',
11
+ displayOptions: {
12
+ show: {
13
+ resource: ['customer'],
14
+ operation: ['get'],
15
+ },
16
+ },
17
+ description: 'The ID of the customer to retrieve',
18
+ routing: {
19
+ request: {
20
+ method: 'GET',
21
+ url: '=/crm/v2/tenant/{{$credentials.tenantId}}/customers/{{$value}}',
22
+ },
23
+ },
24
+ },
25
+ ];
26
+ //# sourceMappingURL=get.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get.js","sourceRoot":"","sources":["../../../../../nodes/ServiceTitan/resources/customer/get.ts"],"names":[],"mappings":";;;AAEa,QAAA,sBAAsB,GAAsB;IACxD;QACC,WAAW,EAAE,aAAa;QAC1B,IAAI,EAAE,YAAY;QAClB,IAAI,EAAE,QAAQ;QACd,QAAQ,EAAE,IAAI;QACd,OAAO,EAAE,EAAE;QACX,cAAc,EAAE;YACf,IAAI,EAAE;gBACL,QAAQ,EAAE,CAAC,UAAU,CAAC;gBACtB,SAAS,EAAE,CAAC,KAAK,CAAC;aAClB;SACD;QACD,WAAW,EAAE,oCAAoC;QACjD,OAAO,EAAE;YACR,OAAO,EAAE;gBACR,MAAM,EAAE,KAAK;gBACb,GAAG,EAAE,gEAAgE;aACrE;SACD;KACD;CACD,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { INodeProperties } from 'n8n-workflow';
2
+ export declare const customerOperations: INodeProperties[];
3
+ export declare const customerFields: INodeProperties[];
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.customerFields = exports.customerOperations = void 0;
4
+ const get_1 = require("./get");
5
+ exports.customerOperations = [
6
+ {
7
+ displayName: 'Operation',
8
+ name: 'operation',
9
+ type: 'options',
10
+ noDataExpression: true,
11
+ displayOptions: {
12
+ show: {
13
+ resource: ['customer'],
14
+ },
15
+ },
16
+ options: [
17
+ {
18
+ name: 'Get',
19
+ value: 'get',
20
+ description: 'Get a customer by ID',
21
+ action: 'Get a customer',
22
+ },
23
+ ],
24
+ default: 'get',
25
+ },
26
+ ];
27
+ exports.customerFields = [...get_1.customerGetDescription];
28
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../nodes/ServiceTitan/resources/customer/index.ts"],"names":[],"mappings":";;;AACA,+BAA+C;AAElC,QAAA,kBAAkB,GAAsB;IACpD;QACC,WAAW,EAAE,WAAW;QACxB,IAAI,EAAE,WAAW;QACjB,IAAI,EAAE,SAAS;QACf,gBAAgB,EAAE,IAAI;QACtB,cAAc,EAAE;YACf,IAAI,EAAE;gBACL,QAAQ,EAAE,CAAC,UAAU,CAAC;aACtB;SACD;QACD,OAAO,EAAE;YACR;gBACC,IAAI,EAAE,KAAK;gBACX,KAAK,EAAE,KAAK;gBACZ,WAAW,EAAE,sBAAsB;gBACnC,MAAM,EAAE,gBAAgB;aACxB;SACD;QACD,OAAO,EAAE,KAAK;KACd;CACD,CAAC;AAEW,QAAA,cAAc,GAAsB,CAAC,GAAG,4BAAsB,CAAC,CAAC"}
@@ -0,0 +1,4 @@
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>
4
+ </svg>
@@ -0,0 +1,4 @@
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>
4
+ </svg>
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@thegraphitelab/n8n-nodes-servicetitan",
3
+ "version": "0.1.0",
4
+ "description": "n8n custom node for ServiceTitan — TGL internal use.",
5
+ "keywords": [
6
+ "n8n-community-node-package",
7
+ "n8n",
8
+ "servicetitan",
9
+ "tgl"
10
+ ],
11
+ "license": "MIT",
12
+ "private": false,
13
+ "author": {
14
+ "name": "The Graphite Lab",
15
+ "email": "support@thegraphitelab.com"
16
+ },
17
+ "main": "index.js",
18
+ "files": [
19
+ "dist"
20
+ ],
21
+ "publishConfig": {
22
+ "registry": "https://registry.npmjs.org",
23
+ "access": "public"
24
+ },
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/thegraphitelab/tgl-custom-n8n-nodes.git",
28
+ "directory": "packages/n8n-nodes-servicetitan"
29
+ },
30
+ "engines": {
31
+ "node": ">=22"
32
+ },
33
+ "scripts": {
34
+ "build": "n8n-node build",
35
+ "dev": "n8n-node dev",
36
+ "lint": "n8n-node lint",
37
+ "lint:fix": "n8n-node lint --fix",
38
+ "test": "echo \"no tests yet\" && exit 0"
39
+ },
40
+ "n8n": {
41
+ "n8nNodesApiVersion": 1,
42
+ "strict": true,
43
+ "credentials": [
44
+ "dist/credentials/ServiceTitanApi.credentials.js"
45
+ ],
46
+ "nodes": [
47
+ "dist/nodes/ServiceTitan/TglServiceTitan.node.js"
48
+ ]
49
+ },
50
+ "devDependencies": {
51
+ "@eslint/js": "^9.29.0",
52
+ "@n8n/node-cli": "^0.23.0",
53
+ "eslint": "^9.29.0",
54
+ "n8n-workflow": "*"
55
+ },
56
+ "peerDependencies": {
57
+ "n8n-workflow": "*"
58
+ }
59
+ }
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@thegraphitelab/n8n-nodes-servicetitan",
3
+ "version": "0.1.0",
4
+ "description": "n8n custom node for ServiceTitan — TGL internal use.",
5
+ "keywords": [
6
+ "n8n-community-node-package",
7
+ "n8n",
8
+ "servicetitan",
9
+ "tgl"
10
+ ],
11
+ "license": "MIT",
12
+ "private": false,
13
+ "author": {
14
+ "name": "The Graphite Lab",
15
+ "email": "support@thegraphitelab.com"
16
+ },
17
+ "main": "index.js",
18
+ "files": [
19
+ "dist"
20
+ ],
21
+ "publishConfig": {
22
+ "registry": "https://registry.npmjs.org",
23
+ "access": "public"
24
+ },
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/thegraphitelab/tgl-custom-n8n-nodes.git",
28
+ "directory": "packages/n8n-nodes-servicetitan"
29
+ },
30
+ "engines": {
31
+ "node": ">=22"
32
+ },
33
+ "n8n": {
34
+ "n8nNodesApiVersion": 1,
35
+ "strict": true,
36
+ "credentials": [
37
+ "dist/credentials/ServiceTitanApi.credentials.js"
38
+ ],
39
+ "nodes": [
40
+ "dist/nodes/ServiceTitan/TglServiceTitan.node.js"
41
+ ]
42
+ },
43
+ "devDependencies": {
44
+ "@eslint/js": "^9.29.0",
45
+ "@n8n/node-cli": "^0.23.0",
46
+ "eslint": "^9.29.0",
47
+ "n8n-workflow": "*"
48
+ },
49
+ "peerDependencies": {
50
+ "n8n-workflow": "*"
51
+ },
52
+ "scripts": {
53
+ "build": "n8n-node build",
54
+ "dev": "n8n-node dev",
55
+ "lint": "n8n-node lint",
56
+ "lint:fix": "n8n-node lint --fix",
57
+ "test": "echo \"no tests yet\" && exit 0"
58
+ }
59
+ }