n8n-nodes-veridion 0.1.1
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/LICENSE +21 -0
- package/README.md +148 -0
- package/dist/credentials/VeridionApi.credentials.js +45 -0
- package/dist/nodes/Veridion/Veridion.node.js +846 -0
- package/dist/nodes/Veridion/veridion.svg +13 -0
- package/package.json +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Veridion
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# n8n-nodes-veridion
|
|
2
|
+
|
|
3
|
+
An [n8n](https://n8n.io) community node for the Veridion company data APIs. It currently includes three operations inside a single `Veridion` node:
|
|
4
|
+
|
|
5
|
+
- `Enrich Company`
|
|
6
|
+
- `Company Search`
|
|
7
|
+
- `Supplier Search`
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## What it does
|
|
12
|
+
|
|
13
|
+
- **Enrich Company** matches a known company and returns the enriched Veridion company profile.
|
|
14
|
+
- **Company Search** discovers companies using location, industry, NAICS, keyword, and company-size filters.
|
|
15
|
+
- **Supplier Search** discovers suppliers using grouped product keyword logic plus optional supplier and company-level filters.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
### Community Nodes UI (n8n ≥ 0.187)
|
|
22
|
+
|
|
23
|
+
1. Open your n8n instance → **Settings → Community Nodes**.
|
|
24
|
+
2. Click **Install a community node**.
|
|
25
|
+
3. Enter `n8n-nodes-veridion` and confirm.
|
|
26
|
+
|
|
27
|
+
### Manual (self-hosted)
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
cd ~/.n8n/custom # or your n8n custom-extensions path
|
|
31
|
+
npm install n8n-nodes-veridion
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Restart n8n after installation.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Credentials
|
|
39
|
+
|
|
40
|
+
Create a **Veridion API** credential in n8n and paste your Veridion API key into the **API Key** field. The key is sent via the `x-api-key` request header.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Enrich Company
|
|
45
|
+
|
|
46
|
+
### Required
|
|
47
|
+
|
|
48
|
+
| Parameter | Description | API field |
|
|
49
|
+
|---|---|---|
|
|
50
|
+
| Legal Name | Official registered name | `legal_names` (array) |
|
|
51
|
+
| Commercial Name | Trading / brand name | `commercial_names` (array) |
|
|
52
|
+
|
|
53
|
+
### Optional
|
|
54
|
+
|
|
55
|
+
| Parameter | Description | API field |
|
|
56
|
+
|---|---|---|
|
|
57
|
+
| Website | Company website domain or URL | `website` |
|
|
58
|
+
| Address | Free-text address | `address_txt` |
|
|
59
|
+
| Phone Number | Company phone number | `phone_number` |
|
|
60
|
+
| Registry ID | Company registry or tax ID | `registry_id` |
|
|
61
|
+
| Minimum Confidence Score | Match threshold 0–1 (default `0.6`) | `min_confidence_score` query param |
|
|
62
|
+
|
|
63
|
+
Empty optional fields are stripped from the request body before sending.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Search Operations
|
|
68
|
+
|
|
69
|
+
### Company Search
|
|
70
|
+
|
|
71
|
+
- Shared filters: country, region, city, postcode, industries, NAICS, employee count, revenue, year founded, page size, pagination token
|
|
72
|
+
- Company-only filters: keywords, exclude keywords, keyword strictness
|
|
73
|
+
- Industry values are loaded live from `GET /industries/v0`
|
|
74
|
+
|
|
75
|
+
### Supplier Search
|
|
76
|
+
|
|
77
|
+
- Shared filters: country, region, city, postcode, industries, NAICS, employee count, revenue, year founded, page size, pagination token
|
|
78
|
+
- Supplier-only filters:
|
|
79
|
+
- `Product Keywords Group 1`
|
|
80
|
+
- `Product Keywords Group 2`
|
|
81
|
+
- `Product Keywords Group 3`
|
|
82
|
+
- `Product Exclude Keywords`
|
|
83
|
+
- `Supplier Types`
|
|
84
|
+
- `Proximity Address`
|
|
85
|
+
- `Proximity Radius`
|
|
86
|
+
- `Proximity Unit`
|
|
87
|
+
- Supplier keyword groups follow the Make-style logic:
|
|
88
|
+
- OR within a group
|
|
89
|
+
- AND across groups
|
|
90
|
+
|
|
91
|
+
## Output structure
|
|
92
|
+
|
|
93
|
+
### Successful match (`HTTP 200`)
|
|
94
|
+
|
|
95
|
+
The full Veridion company JSON object is passed through as the node output. Example top-level keys:
|
|
96
|
+
|
|
97
|
+
```json
|
|
98
|
+
{
|
|
99
|
+
"legal_name": "Acme Corporation",
|
|
100
|
+
"website_domain": "acme.com",
|
|
101
|
+
"main_country_code": "US",
|
|
102
|
+
"employee_count": 520,
|
|
103
|
+
"revenue_range": "10M-50M",
|
|
104
|
+
"industries": ["Manufacturing", "Industrial Machinery"],
|
|
105
|
+
...
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### No match (`HTTP 202`)
|
|
110
|
+
|
|
111
|
+
```json
|
|
112
|
+
{
|
|
113
|
+
"matched": false,
|
|
114
|
+
"reason": "No match found above the requested confidence threshold"
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Errors
|
|
119
|
+
|
|
120
|
+
`400` responses surface the API's error message as a descriptive `NodeApiError`. All other non-2xx statuses also throw a `NodeApiError` with the HTTP status code attached.
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Edge cases
|
|
125
|
+
|
|
126
|
+
- If neither **Legal Name** nor **Commercial Name** is filled in, the node throws a descriptive error **before** making any API call.
|
|
127
|
+
- `legal_names` and `commercial_names` are always sent as arrays (Veridion API requirement), even though the node accepts a single string.
|
|
128
|
+
- Optional fields with empty string values are stripped from the request body.
|
|
129
|
+
- Search responses can be returned either as one item per company or as a single full-response item, depending on `Output Mode`.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Development
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
git clone https://github.com/veridion/n8n-nodes-veridion.git
|
|
137
|
+
cd n8n-nodes-veridion
|
|
138
|
+
npm install
|
|
139
|
+
npm run build # compile TypeScript → dist/
|
|
140
|
+
npm run dev # watch mode
|
|
141
|
+
npm run lint # ESLint check
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## License
|
|
147
|
+
|
|
148
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VeridionApi = void 0;
|
|
4
|
+
class VeridionApi {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.name = 'veridionApi';
|
|
7
|
+
this.displayName = 'Veridion API';
|
|
8
|
+
this.documentationUrl = 'https://developer.veridion.com/docs/getting-started';
|
|
9
|
+
this.icon = 'file:../nodes/Veridion/veridion.svg';
|
|
10
|
+
this.properties = [
|
|
11
|
+
{
|
|
12
|
+
displayName: 'API Key',
|
|
13
|
+
name: 'apiKey',
|
|
14
|
+
type: 'string',
|
|
15
|
+
typeOptions: {
|
|
16
|
+
password: true,
|
|
17
|
+
},
|
|
18
|
+
default: '',
|
|
19
|
+
required: true,
|
|
20
|
+
description: 'The API key from your Veridion account (x-api-key header)',
|
|
21
|
+
},
|
|
22
|
+
];
|
|
23
|
+
this.authenticate = {
|
|
24
|
+
type: 'generic',
|
|
25
|
+
properties: {
|
|
26
|
+
headers: {
|
|
27
|
+
'x-api-key': '={{$credentials.apiKey}}',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
this.test = {
|
|
32
|
+
request: {
|
|
33
|
+
baseURL: 'https://data.veridion.com',
|
|
34
|
+
url: '/match/v5/companies',
|
|
35
|
+
method: 'POST',
|
|
36
|
+
headers: {
|
|
37
|
+
'Content-Type': 'application/json',
|
|
38
|
+
},
|
|
39
|
+
// Minimal valid body to validate the key — expect 200 or 202, not 401/403
|
|
40
|
+
body: JSON.stringify({ legal_names: ['Acme Corporation'] }),
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
exports.VeridionApi = VeridionApi;
|
|
@@ -0,0 +1,846 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Veridion = void 0;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
// ─── Node definition ────────────────────────────────────────────────────────
|
|
6
|
+
class Veridion {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.description = {
|
|
9
|
+
displayName: 'Veridion',
|
|
10
|
+
name: 'veridion',
|
|
11
|
+
icon: 'file:veridion.svg',
|
|
12
|
+
group: ['transform'],
|
|
13
|
+
version: 1,
|
|
14
|
+
subtitle: '={{$parameter["operation"]}}',
|
|
15
|
+
description: 'Match and enrich company data using the Veridion API',
|
|
16
|
+
defaults: {
|
|
17
|
+
name: 'Veridion',
|
|
18
|
+
},
|
|
19
|
+
inputs: ['main'],
|
|
20
|
+
outputs: ['main'],
|
|
21
|
+
credentials: [
|
|
22
|
+
{
|
|
23
|
+
name: 'veridionApi',
|
|
24
|
+
required: true,
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
properties: [
|
|
28
|
+
// ── Operation selector ──────────────────────────────────────────
|
|
29
|
+
{
|
|
30
|
+
displayName: 'Operation',
|
|
31
|
+
name: 'operation',
|
|
32
|
+
type: 'options',
|
|
33
|
+
noDataExpression: true,
|
|
34
|
+
options: [
|
|
35
|
+
{
|
|
36
|
+
name: 'Enrich Company',
|
|
37
|
+
value: 'enrichCompany',
|
|
38
|
+
description: 'Match a company and return its enriched Veridion profile',
|
|
39
|
+
action: 'Enrich a company',
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: 'Company Search',
|
|
43
|
+
value: 'searchCompanies',
|
|
44
|
+
description: 'Discover companies by applying company-oriented search filters',
|
|
45
|
+
action: 'Search for companies',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: 'Supplier Search',
|
|
49
|
+
value: 'supplierSearch',
|
|
50
|
+
description: 'Discover suppliers by product, service, and supplier-specific filters',
|
|
51
|
+
action: 'Search for suppliers',
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
default: 'enrichCompany',
|
|
55
|
+
},
|
|
56
|
+
// ── Company name (at least one required) ────────────────────────
|
|
57
|
+
{
|
|
58
|
+
displayName: 'Legal Name',
|
|
59
|
+
name: 'legalName',
|
|
60
|
+
type: 'string',
|
|
61
|
+
default: '',
|
|
62
|
+
description: 'Official registered name of the company.',
|
|
63
|
+
displayOptions: { show: { operation: ['enrichCompany'] } },
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
displayName: 'Commercial Name',
|
|
67
|
+
name: 'commercialName',
|
|
68
|
+
type: 'string',
|
|
69
|
+
default: '',
|
|
70
|
+
description: 'Trading or brand name of the company.',
|
|
71
|
+
displayOptions: { show: { operation: ['enrichCompany'] } },
|
|
72
|
+
},
|
|
73
|
+
// ── Locating signals ────────────────────────────────────────────
|
|
74
|
+
{
|
|
75
|
+
displayName: 'Website',
|
|
76
|
+
name: 'website',
|
|
77
|
+
type: 'string',
|
|
78
|
+
default: '',
|
|
79
|
+
placeholder: 'acme.com',
|
|
80
|
+
description: 'Company website domain or URL.',
|
|
81
|
+
displayOptions: { show: { operation: ['enrichCompany'] } },
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
displayName: 'Address',
|
|
85
|
+
name: 'addressTxt',
|
|
86
|
+
type: 'string',
|
|
87
|
+
default: '',
|
|
88
|
+
placeholder: '123 Main St, Cluj-Napoca, Romania',
|
|
89
|
+
description: 'Free-text address used as a locating signal.',
|
|
90
|
+
displayOptions: { show: { operation: ['enrichCompany'] } },
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
displayName: 'Phone Number',
|
|
94
|
+
name: 'phoneNumber',
|
|
95
|
+
type: 'string',
|
|
96
|
+
default: '',
|
|
97
|
+
placeholder: '+40 123 456 789',
|
|
98
|
+
description: 'Company phone number including country code.',
|
|
99
|
+
displayOptions: { show: { operation: ['enrichCompany'] } },
|
|
100
|
+
},
|
|
101
|
+
// ── Registry ID path (combination B: registry_id + address_txt) ─
|
|
102
|
+
{
|
|
103
|
+
displayName: 'Registry ID',
|
|
104
|
+
name: 'registryId',
|
|
105
|
+
type: 'string',
|
|
106
|
+
default: '',
|
|
107
|
+
placeholder: 'RO12345678',
|
|
108
|
+
description: 'Company registry or tax ID.',
|
|
109
|
+
displayOptions: { show: { operation: ['enrichCompany'] } },
|
|
110
|
+
},
|
|
111
|
+
// ── Matching tuning ────────────────────────────────────────────
|
|
112
|
+
{
|
|
113
|
+
displayName: 'Minimum Confidence Score',
|
|
114
|
+
name: 'minConfidenceScore',
|
|
115
|
+
type: 'number',
|
|
116
|
+
typeOptions: {
|
|
117
|
+
minValue: 0,
|
|
118
|
+
maxValue: 1,
|
|
119
|
+
numberStepSize: 0.05,
|
|
120
|
+
numberPrecision: 2,
|
|
121
|
+
},
|
|
122
|
+
default: 0.6,
|
|
123
|
+
description: 'Only return a match if its confidence score meets this threshold (0-1).',
|
|
124
|
+
displayOptions: { show: { operation: ['enrichCompany'] } },
|
|
125
|
+
},
|
|
126
|
+
// ── Search Operations ──────────────────────────────────────────
|
|
127
|
+
{
|
|
128
|
+
displayName: 'Keywords',
|
|
129
|
+
name: 'searchKeywords',
|
|
130
|
+
type: 'string',
|
|
131
|
+
default: '',
|
|
132
|
+
placeholder: 'fleet management, telematics, logistics',
|
|
133
|
+
description: 'Comma-separated keywords to match on company content.',
|
|
134
|
+
displayOptions: { show: { operation: ['searchCompanies'] } },
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
displayName: 'Exclude Keywords',
|
|
138
|
+
name: 'searchKeywordsExclude',
|
|
139
|
+
type: 'string',
|
|
140
|
+
default: '',
|
|
141
|
+
placeholder: 'consulting, agency',
|
|
142
|
+
description: 'Comma-separated keywords to exclude.',
|
|
143
|
+
displayOptions: { show: { operation: ['searchCompanies'] } },
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
displayName: 'Keywords Strictness',
|
|
147
|
+
name: 'searchKeywordsStrictness',
|
|
148
|
+
type: 'options',
|
|
149
|
+
options: [
|
|
150
|
+
{ name: '1 - Business Tags Only', value: 1 },
|
|
151
|
+
{ name: '2 - Tags + Meta', value: 2 },
|
|
152
|
+
{ name: '3 - Broad', value: 3 },
|
|
153
|
+
],
|
|
154
|
+
default: 3,
|
|
155
|
+
description: 'How broadly to search keyword matches.',
|
|
156
|
+
displayOptions: { show: { operation: ['searchCompanies'] } },
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
displayName: 'Product Keywords Group 1',
|
|
160
|
+
name: 'searchProductKeywordGroup1',
|
|
161
|
+
type: 'string',
|
|
162
|
+
default: '',
|
|
163
|
+
placeholder: 'lcd screens, lcd display, lcd panel',
|
|
164
|
+
description: 'First product keyword group. Terms within a group are matched with OR.',
|
|
165
|
+
displayOptions: { show: { operation: ['supplierSearch'] } },
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
displayName: 'Product Keywords Group 2',
|
|
169
|
+
name: 'searchProductKeywordGroup2',
|
|
170
|
+
type: 'string',
|
|
171
|
+
default: '',
|
|
172
|
+
placeholder: 'CE certified, CE certification',
|
|
173
|
+
description: 'Optional second keyword group. Separate groups are matched with AND.',
|
|
174
|
+
displayOptions: { show: { operation: ['supplierSearch'] } },
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
displayName: 'Product Keywords Group 3',
|
|
178
|
+
name: 'searchProductKeywordGroup3',
|
|
179
|
+
type: 'string',
|
|
180
|
+
default: '',
|
|
181
|
+
placeholder: 'OEM, private label',
|
|
182
|
+
description: 'Optional third keyword group. Separate groups are matched with AND.',
|
|
183
|
+
displayOptions: { show: { operation: ['supplierSearch'] } },
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
displayName: 'Product Exclude Keywords',
|
|
187
|
+
name: 'searchProductExcludeKeywords',
|
|
188
|
+
type: 'string',
|
|
189
|
+
default: '',
|
|
190
|
+
placeholder: 'retail',
|
|
191
|
+
description: 'Comma-separated keywords to exclude from product matches.',
|
|
192
|
+
displayOptions: { show: { operation: ['supplierSearch'] } },
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
displayName: 'Supplier Types',
|
|
196
|
+
name: 'searchSupplierTypes',
|
|
197
|
+
type: 'multiOptions',
|
|
198
|
+
options: [
|
|
199
|
+
{ name: 'Manufacturer', value: 'manufacturer' },
|
|
200
|
+
{ name: 'Distributor', value: 'distributor' },
|
|
201
|
+
{ name: 'Service Provider', value: 'service_provider' },
|
|
202
|
+
{ name: 'Software Provider', value: 'software_provider' },
|
|
203
|
+
],
|
|
204
|
+
default: [],
|
|
205
|
+
description: 'Optional supplier types for product search.',
|
|
206
|
+
displayOptions: { show: { operation: ['supplierSearch'] } },
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
displayName: 'Country Code',
|
|
210
|
+
name: 'searchCountry',
|
|
211
|
+
type: 'string',
|
|
212
|
+
default: '',
|
|
213
|
+
placeholder: 'US',
|
|
214
|
+
description: 'ISO 3166-1 alpha-2 country code.',
|
|
215
|
+
displayOptions: { show: { operation: ['searchCompanies', 'supplierSearch'] } },
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
displayName: 'Region / State',
|
|
219
|
+
name: 'searchRegion',
|
|
220
|
+
type: 'string',
|
|
221
|
+
default: '',
|
|
222
|
+
description: 'Optional region or state for the company location filter.',
|
|
223
|
+
displayOptions: { show: { operation: ['searchCompanies', 'supplierSearch'] } },
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
displayName: 'City',
|
|
227
|
+
name: 'searchCity',
|
|
228
|
+
type: 'string',
|
|
229
|
+
default: '',
|
|
230
|
+
description: 'Optional city for the company location filter.',
|
|
231
|
+
displayOptions: { show: { operation: ['searchCompanies', 'supplierSearch'] } },
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
displayName: 'Location Strictness',
|
|
235
|
+
name: 'searchLocationStrictness',
|
|
236
|
+
type: 'options',
|
|
237
|
+
options: [
|
|
238
|
+
{ name: '1 - Main Location', value: 1 },
|
|
239
|
+
{ name: '2 - Secondary Locations', value: 2 },
|
|
240
|
+
{ name: '3 - Main + Secondary', value: 3 },
|
|
241
|
+
],
|
|
242
|
+
default: 3,
|
|
243
|
+
description: 'Whether to search main locations, secondary locations, or both.',
|
|
244
|
+
displayOptions: { show: { operation: ['searchCompanies', 'supplierSearch'] } },
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
displayName: 'Postcode',
|
|
248
|
+
name: 'searchPostcodes',
|
|
249
|
+
type: 'string',
|
|
250
|
+
default: '',
|
|
251
|
+
placeholder: '94123, 10001',
|
|
252
|
+
description: 'Comma-separated list of postcodes.',
|
|
253
|
+
displayOptions: { show: { operation: ['searchCompanies', 'supplierSearch'] } },
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
displayName: 'Postcode Strictness',
|
|
257
|
+
name: 'searchPostcodeStrictness',
|
|
258
|
+
type: 'options',
|
|
259
|
+
options: [
|
|
260
|
+
{ name: '1 - Main Location', value: 1 },
|
|
261
|
+
{ name: '2 - Secondary Locations', value: 2 },
|
|
262
|
+
{ name: '3 - Main + Secondary', value: 3 },
|
|
263
|
+
],
|
|
264
|
+
default: 3,
|
|
265
|
+
description: 'Whether postcode matching should target main locations, secondary locations, or both.',
|
|
266
|
+
displayOptions: { show: { operation: ['searchCompanies', 'supplierSearch'] } },
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
displayName: 'Industries',
|
|
270
|
+
name: 'searchIndustry',
|
|
271
|
+
type: 'multiOptions',
|
|
272
|
+
typeOptions: {
|
|
273
|
+
loadOptionsMethod: 'getIndustries',
|
|
274
|
+
},
|
|
275
|
+
default: [],
|
|
276
|
+
description: 'Select one or more Veridion industries.',
|
|
277
|
+
displayOptions: { show: { operation: ['searchCompanies', 'supplierSearch'] } },
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
displayName: 'NAICS Codes',
|
|
281
|
+
name: 'searchNaicsCodes',
|
|
282
|
+
type: 'string',
|
|
283
|
+
default: '',
|
|
284
|
+
placeholder: '541512, 511210',
|
|
285
|
+
description: 'Comma-separated NAICS codes. Prefix values are supported.',
|
|
286
|
+
displayOptions: { show: { operation: ['searchCompanies', 'supplierSearch'] } },
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
displayName: 'NAICS Strictness',
|
|
290
|
+
name: 'searchNaicsStrictness',
|
|
291
|
+
type: 'options',
|
|
292
|
+
options: [
|
|
293
|
+
{ name: '1 - Primary Only', value: 1 },
|
|
294
|
+
{ name: '2 - Secondary Only', value: 2 },
|
|
295
|
+
{ name: '3 - Primary + Secondary', value: 3 },
|
|
296
|
+
],
|
|
297
|
+
default: 3,
|
|
298
|
+
description: 'Whether to search primary NAICS, secondary NAICS, or both.',
|
|
299
|
+
displayOptions: { show: { operation: ['searchCompanies', 'supplierSearch'] } },
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
displayName: 'Employee Count (Min)',
|
|
303
|
+
name: 'searchEmployeeCountMin',
|
|
304
|
+
type: 'string',
|
|
305
|
+
default: '',
|
|
306
|
+
description: 'Minimum employee count.',
|
|
307
|
+
displayOptions: { show: { operation: ['searchCompanies', 'supplierSearch'] } },
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
displayName: 'Employee Count (Max)',
|
|
311
|
+
name: 'searchEmployeeCountMax',
|
|
312
|
+
type: 'string',
|
|
313
|
+
default: '',
|
|
314
|
+
description: 'Maximum employee count.',
|
|
315
|
+
displayOptions: { show: { operation: ['searchCompanies', 'supplierSearch'] } },
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
displayName: 'Estimated Revenue Min USD',
|
|
319
|
+
name: 'searchRevenueMin',
|
|
320
|
+
type: 'string',
|
|
321
|
+
default: '',
|
|
322
|
+
description: 'Minimum estimated annual revenue in USD.',
|
|
323
|
+
displayOptions: { show: { operation: ['searchCompanies', 'supplierSearch'] } },
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
displayName: 'Estimated Revenue Max USD',
|
|
327
|
+
name: 'searchRevenueMax',
|
|
328
|
+
type: 'string',
|
|
329
|
+
default: '',
|
|
330
|
+
description: 'Maximum estimated annual revenue in USD.',
|
|
331
|
+
displayOptions: { show: { operation: ['searchCompanies', 'supplierSearch'] } },
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
displayName: 'Year Founded Min',
|
|
335
|
+
name: 'searchYearFoundedMin',
|
|
336
|
+
type: 'string',
|
|
337
|
+
default: '',
|
|
338
|
+
description: 'Minimum founding year.',
|
|
339
|
+
displayOptions: { show: { operation: ['searchCompanies', 'supplierSearch'] } },
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
displayName: 'Year Founded Max',
|
|
343
|
+
name: 'searchYearFoundedMax',
|
|
344
|
+
type: 'string',
|
|
345
|
+
default: '',
|
|
346
|
+
description: 'Maximum founding year.',
|
|
347
|
+
displayOptions: { show: { operation: ['searchCompanies', 'supplierSearch'] } },
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
displayName: 'Proximity Address',
|
|
351
|
+
name: 'searchProximityAddress',
|
|
352
|
+
type: 'string',
|
|
353
|
+
default: '',
|
|
354
|
+
placeholder: 'Toronto, M5B 2L7, Ontario',
|
|
355
|
+
description: 'Reference address for proximity search.',
|
|
356
|
+
displayOptions: { show: { operation: ['supplierSearch'] } },
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
displayName: 'Proximity Radius',
|
|
360
|
+
name: 'searchProximityRadius',
|
|
361
|
+
type: 'string',
|
|
362
|
+
default: '',
|
|
363
|
+
description: 'Radius for proximity search. Maximum 500 km or 300 mi.',
|
|
364
|
+
displayOptions: { show: { operation: ['supplierSearch'] } },
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
displayName: 'Proximity Unit',
|
|
368
|
+
name: 'searchProximityUnit',
|
|
369
|
+
type: 'options',
|
|
370
|
+
options: [
|
|
371
|
+
{ name: 'Kilometers', value: 'km' },
|
|
372
|
+
{ name: 'Miles', value: 'mi' },
|
|
373
|
+
],
|
|
374
|
+
default: 'km',
|
|
375
|
+
displayOptions: { show: { operation: ['supplierSearch'] } },
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
displayName: 'Page Size',
|
|
379
|
+
name: 'pageSize',
|
|
380
|
+
type: 'number',
|
|
381
|
+
typeOptions: {
|
|
382
|
+
minValue: 1,
|
|
383
|
+
maxValue: 200,
|
|
384
|
+
numberPrecision: 0,
|
|
385
|
+
},
|
|
386
|
+
default: 10,
|
|
387
|
+
description: 'Number of results per page. Maximum is 200.',
|
|
388
|
+
displayOptions: { show: { operation: ['searchCompanies', 'supplierSearch'] } },
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
displayName: 'Pagination Token',
|
|
392
|
+
name: 'paginationToken',
|
|
393
|
+
type: 'string',
|
|
394
|
+
default: '',
|
|
395
|
+
description: 'Opaque token from a previous response to fetch the next page.',
|
|
396
|
+
displayOptions: { show: { operation: ['searchCompanies', 'supplierSearch'] } },
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
displayName: 'Output Mode',
|
|
400
|
+
name: 'searchOutputMode',
|
|
401
|
+
type: 'options',
|
|
402
|
+
options: [
|
|
403
|
+
{
|
|
404
|
+
name: 'Company Items',
|
|
405
|
+
value: 'companyItems',
|
|
406
|
+
description: 'Return one n8n item per company and include search metadata on each item',
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
name: 'Full Response',
|
|
410
|
+
value: 'fullResponse',
|
|
411
|
+
description: 'Return the raw Veridion response as a single n8n item',
|
|
412
|
+
},
|
|
413
|
+
],
|
|
414
|
+
default: 'companyItems',
|
|
415
|
+
displayOptions: { show: { operation: ['searchCompanies', 'supplierSearch'] } },
|
|
416
|
+
},
|
|
417
|
+
],
|
|
418
|
+
};
|
|
419
|
+
this.methods = {
|
|
420
|
+
loadOptions: {
|
|
421
|
+
async getIndustries() {
|
|
422
|
+
const response = await Veridion.loadIndustries(this);
|
|
423
|
+
return response
|
|
424
|
+
.filter((entry) => (entry === null || entry === void 0 ? void 0 : entry.type) === 'industry' && typeof entry.name === 'string')
|
|
425
|
+
.map((entry) => ({
|
|
426
|
+
name: entry.name,
|
|
427
|
+
value: entry.name,
|
|
428
|
+
}))
|
|
429
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
430
|
+
},
|
|
431
|
+
},
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
// ── Execution ──────────────────────────────────────────────────────────
|
|
435
|
+
async execute() {
|
|
436
|
+
const items = this.getInputData();
|
|
437
|
+
const returnData = [];
|
|
438
|
+
for (let i = 0; i < items.length; i++) {
|
|
439
|
+
const operation = this.getNodeParameter('operation', i);
|
|
440
|
+
if (operation === 'enrichCompany') {
|
|
441
|
+
const result = await Veridion.enrichCompany(this, i);
|
|
442
|
+
returnData.push({ json: result });
|
|
443
|
+
continue;
|
|
444
|
+
}
|
|
445
|
+
if (operation === 'searchCompanies') {
|
|
446
|
+
const results = await Veridion.searchCompanies(this, i, operation);
|
|
447
|
+
returnData.push(...results);
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
if (operation === 'supplierSearch') {
|
|
451
|
+
const results = await Veridion.searchCompanies(this, i, operation);
|
|
452
|
+
returnData.push(...results);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
return [returnData];
|
|
456
|
+
}
|
|
457
|
+
// ── enrichCompany helper ───────────────────────────────────────────────
|
|
458
|
+
static async enrichCompany(ctx, itemIndex) {
|
|
459
|
+
// ── 1. Collect and trim all parameters ────────────────────────────
|
|
460
|
+
const legalName = ctx.getNodeParameter('legalName', itemIndex, '').trim();
|
|
461
|
+
const commercialName = ctx.getNodeParameter('commercialName', itemIndex, '').trim();
|
|
462
|
+
const website = ctx.getNodeParameter('website', itemIndex, '').trim();
|
|
463
|
+
const addressTxt = ctx.getNodeParameter('addressTxt', itemIndex, '').trim();
|
|
464
|
+
const phoneNumber = ctx.getNodeParameter('phoneNumber', itemIndex, '').trim();
|
|
465
|
+
const registryId = ctx.getNodeParameter('registryId', itemIndex, '').trim();
|
|
466
|
+
const minConfidenceScore = ctx.getNodeParameter('minConfidenceScore', itemIndex, 0.6);
|
|
467
|
+
const hasName = !!(legalName || commercialName);
|
|
468
|
+
const hasSignal = !!(website || addressTxt || phoneNumber || registryId);
|
|
469
|
+
// ── 2. Validate input combinations before calling the API ──────────
|
|
470
|
+
if (!hasName) {
|
|
471
|
+
throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), 'Provide at least one of Legal Name or Commercial Name.', { itemIndex });
|
|
472
|
+
}
|
|
473
|
+
if (!hasSignal) {
|
|
474
|
+
throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), 'A company name alone is not enough. Please also provide at least one of: Address, Website, Registry ID, or Phone Number.', { itemIndex });
|
|
475
|
+
}
|
|
476
|
+
// ── 3. Build request body — omit empty fields ──────────────────────
|
|
477
|
+
const identifiers = {};
|
|
478
|
+
if (legalName)
|
|
479
|
+
identifiers.legal_names = [legalName];
|
|
480
|
+
if (commercialName)
|
|
481
|
+
identifiers.commercial_names = [commercialName];
|
|
482
|
+
if (website)
|
|
483
|
+
identifiers.website = website;
|
|
484
|
+
if (addressTxt)
|
|
485
|
+
identifiers.address_txt = addressTxt;
|
|
486
|
+
if (phoneNumber)
|
|
487
|
+
identifiers.phone_number = phoneNumber;
|
|
488
|
+
if (registryId)
|
|
489
|
+
identifiers.registry_id = registryId;
|
|
490
|
+
const body = { identifiers };
|
|
491
|
+
// ── 4. Call the API and handle response ────────────────────────────
|
|
492
|
+
return Veridion.callWithFullResponse(ctx, body, minConfidenceScore, itemIndex);
|
|
493
|
+
}
|
|
494
|
+
static async searchCompanies(ctx, itemIndex, operation) {
|
|
495
|
+
var _a, _b, _c, _d, _e, _f;
|
|
496
|
+
const pageSize = ctx.getNodeParameter('pageSize', itemIndex, 10);
|
|
497
|
+
const paginationToken = ctx.getNodeParameter('paginationToken', itemIndex, '').trim();
|
|
498
|
+
const outputMode = ctx.getNodeParameter('searchOutputMode', itemIndex, 'companyItems');
|
|
499
|
+
const country = ctx.getNodeParameter('searchCountry', itemIndex, '').trim();
|
|
500
|
+
const region = ctx.getNodeParameter('searchRegion', itemIndex, '').trim();
|
|
501
|
+
const city = ctx.getNodeParameter('searchCity', itemIndex, '').trim();
|
|
502
|
+
const locationStrictness = ctx.getNodeParameter('searchLocationStrictness', itemIndex, 3);
|
|
503
|
+
const postcodes = Veridion.parseCommaSeparated(ctx.getNodeParameter('searchPostcodes', itemIndex, ''));
|
|
504
|
+
const postcodeStrictness = ctx.getNodeParameter('searchPostcodeStrictness', itemIndex, 3);
|
|
505
|
+
const naicsCodes = Veridion.parseCommaSeparated(ctx.getNodeParameter('searchNaicsCodes', itemIndex, ''));
|
|
506
|
+
const naicsStrictness = ctx.getNodeParameter('searchNaicsStrictness', itemIndex, 3);
|
|
507
|
+
const industries = Veridion.getStringListParameter(ctx.getNodeParameter('searchIndustry', itemIndex, []));
|
|
508
|
+
const keywords = Veridion.parseCommaSeparated(ctx.getNodeParameter('searchKeywords', itemIndex, ''));
|
|
509
|
+
const excludeKeywords = Veridion.parseCommaSeparated(ctx.getNodeParameter('searchKeywordsExclude', itemIndex, ''));
|
|
510
|
+
const keywordsStrictness = ctx.getNodeParameter('searchKeywordsStrictness', itemIndex, 3);
|
|
511
|
+
const employeeCountMin = Veridion.getOptionalNumberParameter(ctx, 'searchEmployeeCountMin', itemIndex);
|
|
512
|
+
const employeeCountMax = Veridion.getOptionalNumberParameter(ctx, 'searchEmployeeCountMax', itemIndex);
|
|
513
|
+
const revenueMin = Veridion.getOptionalNumberParameter(ctx, 'searchRevenueMin', itemIndex);
|
|
514
|
+
const revenueMax = Veridion.getOptionalNumberParameter(ctx, 'searchRevenueMax', itemIndex);
|
|
515
|
+
const yearFoundedMin = Veridion.getOptionalNumberParameter(ctx, 'searchYearFoundedMin', itemIndex);
|
|
516
|
+
const yearFoundedMax = Veridion.getOptionalNumberParameter(ctx, 'searchYearFoundedMax', itemIndex);
|
|
517
|
+
const proximityAddress = ctx.getNodeParameter('searchProximityAddress', itemIndex, '').trim();
|
|
518
|
+
const proximityRadius = Veridion.getOptionalNumberParameter(ctx, 'searchProximityRadius', itemIndex);
|
|
519
|
+
const proximityUnit = ctx.getNodeParameter('searchProximityUnit', itemIndex, 'km');
|
|
520
|
+
const productKeywordGroup1 = Veridion.parseCommaSeparated(ctx.getNodeParameter('searchProductKeywordGroup1', itemIndex, ''));
|
|
521
|
+
const productKeywordGroup2 = Veridion.parseCommaSeparated(ctx.getNodeParameter('searchProductKeywordGroup2', itemIndex, ''));
|
|
522
|
+
const productKeywordGroup3 = Veridion.parseCommaSeparated(ctx.getNodeParameter('searchProductKeywordGroup3', itemIndex, ''));
|
|
523
|
+
const productExcludeKeywords = Veridion.parseCommaSeparated(ctx.getNodeParameter('searchProductExcludeKeywords', itemIndex, ''));
|
|
524
|
+
const supplierTypes = ctx.getNodeParameter('searchSupplierTypes', itemIndex, []);
|
|
525
|
+
if ((region || city) && !country) {
|
|
526
|
+
throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), 'Country Code is required when Region / State or City is provided.', { itemIndex });
|
|
527
|
+
}
|
|
528
|
+
if ((proximityAddress && !proximityRadius) || (!proximityAddress && proximityRadius)) {
|
|
529
|
+
throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), 'Proximity Address and Proximity Radius must be provided together.', { itemIndex });
|
|
530
|
+
}
|
|
531
|
+
const productKeywordGroups = [
|
|
532
|
+
productKeywordGroup1,
|
|
533
|
+
productKeywordGroup2,
|
|
534
|
+
productKeywordGroup3,
|
|
535
|
+
].filter((group) => group.length > 0);
|
|
536
|
+
if (operation === 'supplierSearch' && productKeywordGroups.length === 0) {
|
|
537
|
+
throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), 'Supplier Search requires at least Product Keywords Group 1, 2, or 3.', { itemIndex });
|
|
538
|
+
}
|
|
539
|
+
const filters = [];
|
|
540
|
+
if (country) {
|
|
541
|
+
const locationValue = { country };
|
|
542
|
+
if (region)
|
|
543
|
+
locationValue.region = region;
|
|
544
|
+
if (city)
|
|
545
|
+
locationValue.city = city;
|
|
546
|
+
filters.push({
|
|
547
|
+
attribute: 'company_location',
|
|
548
|
+
relation: 'in',
|
|
549
|
+
value: [locationValue],
|
|
550
|
+
strictness: locationStrictness,
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
if (postcodes.length > 0) {
|
|
554
|
+
filters.push({
|
|
555
|
+
attribute: 'company_postcode',
|
|
556
|
+
relation: postcodes.length === 1 ? 'equals' : 'in',
|
|
557
|
+
value: postcodes.length === 1 ? postcodes[0] : postcodes,
|
|
558
|
+
strictness: postcodeStrictness,
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
if (naicsCodes.length > 0) {
|
|
562
|
+
filters.push({
|
|
563
|
+
attribute: 'company_naics_code',
|
|
564
|
+
relation: naicsCodes.length === 1 ? 'equals' : 'in',
|
|
565
|
+
value: naicsCodes.length === 1 ? naicsCodes[0] : naicsCodes,
|
|
566
|
+
strictness: naicsStrictness,
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
if (industries.length > 0) {
|
|
570
|
+
filters.push({
|
|
571
|
+
attribute: 'company_industry',
|
|
572
|
+
relation: industries.length === 1 ? 'equals' : 'in',
|
|
573
|
+
value: industries.length === 1 ? industries[0] : industries,
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
if (operation === 'searchCompanies' && keywords.length > 0) {
|
|
577
|
+
const keywordValue = {
|
|
578
|
+
match: {
|
|
579
|
+
operator: 'or',
|
|
580
|
+
operands: keywords,
|
|
581
|
+
},
|
|
582
|
+
};
|
|
583
|
+
if (excludeKeywords.length > 0) {
|
|
584
|
+
keywordValue.exclude = {
|
|
585
|
+
operator: 'or',
|
|
586
|
+
operands: excludeKeywords,
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
filters.push({
|
|
590
|
+
attribute: 'company_keywords',
|
|
591
|
+
relation: 'match_expression',
|
|
592
|
+
value: keywordValue,
|
|
593
|
+
strictness: keywordsStrictness,
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
if (employeeCountMin !== null || employeeCountMax !== null) {
|
|
597
|
+
filters.push(Veridion.buildRangeFilter('company_employee_count', employeeCountMin, employeeCountMax));
|
|
598
|
+
}
|
|
599
|
+
if (revenueMin !== null || revenueMax !== null) {
|
|
600
|
+
filters.push(Veridion.buildRangeFilter('company_estimated_revenue', revenueMin, revenueMax));
|
|
601
|
+
}
|
|
602
|
+
if (yearFoundedMin !== null || yearFoundedMax !== null) {
|
|
603
|
+
filters.push(Veridion.buildRangeFilter('company_year_founded', yearFoundedMin, yearFoundedMax));
|
|
604
|
+
}
|
|
605
|
+
if (proximityAddress && proximityRadius !== null) {
|
|
606
|
+
filters.push({
|
|
607
|
+
attribute: 'company_location',
|
|
608
|
+
relation: 'within',
|
|
609
|
+
value: {
|
|
610
|
+
address: proximityAddress,
|
|
611
|
+
radius: proximityRadius,
|
|
612
|
+
unit: proximityUnit,
|
|
613
|
+
},
|
|
614
|
+
strictness: locationStrictness,
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
if (productKeywordGroups.length > 0) {
|
|
618
|
+
const match = productKeywordGroups.length === 1
|
|
619
|
+
? {
|
|
620
|
+
operator: 'or',
|
|
621
|
+
operands: productKeywordGroups[0],
|
|
622
|
+
}
|
|
623
|
+
: {
|
|
624
|
+
operator: 'and',
|
|
625
|
+
operands: productKeywordGroups.map((group) => ({
|
|
626
|
+
operator: 'or',
|
|
627
|
+
operands: group,
|
|
628
|
+
})),
|
|
629
|
+
};
|
|
630
|
+
const productValue = {
|
|
631
|
+
match,
|
|
632
|
+
};
|
|
633
|
+
if (productExcludeKeywords.length > 0) {
|
|
634
|
+
productValue.exclude = {
|
|
635
|
+
operator: 'or',
|
|
636
|
+
operands: productExcludeKeywords,
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
const productFilter = {
|
|
640
|
+
attribute: 'company_products',
|
|
641
|
+
relation: 'match_expression',
|
|
642
|
+
value: productValue,
|
|
643
|
+
};
|
|
644
|
+
if (supplierTypes.length > 0) {
|
|
645
|
+
productFilter.supplier_types = supplierTypes;
|
|
646
|
+
}
|
|
647
|
+
filters.push(productFilter);
|
|
648
|
+
}
|
|
649
|
+
if (filters.length === 0) {
|
|
650
|
+
throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), 'Provide at least one search filter before executing Search Companies.', { itemIndex });
|
|
651
|
+
}
|
|
652
|
+
const body = {
|
|
653
|
+
filters: {
|
|
654
|
+
and: filters,
|
|
655
|
+
},
|
|
656
|
+
};
|
|
657
|
+
const requestDebug = {
|
|
658
|
+
url: 'https://data.veridion.com/search/v4/companies',
|
|
659
|
+
qs: {
|
|
660
|
+
page_size: pageSize,
|
|
661
|
+
...(paginationToken ? { pagination_token: paginationToken } : {}),
|
|
662
|
+
},
|
|
663
|
+
body,
|
|
664
|
+
};
|
|
665
|
+
const fullResponse = (await Veridion.callApi(ctx, {
|
|
666
|
+
method: 'POST',
|
|
667
|
+
url: requestDebug.url,
|
|
668
|
+
qs: requestDebug.qs,
|
|
669
|
+
body: JSON.stringify(body),
|
|
670
|
+
}, itemIndex, true));
|
|
671
|
+
if (fullResponse.statusCode === 400) {
|
|
672
|
+
const errBody = fullResponse.body;
|
|
673
|
+
const message = (_b = (_a = errBody === null || errBody === void 0 ? void 0 : errBody.message) !== null && _a !== void 0 ? _a : errBody === null || errBody === void 0 ? void 0 : errBody.error) !== null && _b !== void 0 ? _b : 'Bad request: check your search filters';
|
|
674
|
+
throw new n8n_workflow_1.NodeApiError(ctx.getNode(), { message, statusCode: fullResponse.statusCode }, { message, itemIndex, httpCode: String(fullResponse.statusCode) });
|
|
675
|
+
}
|
|
676
|
+
if (fullResponse.statusCode !== 200) {
|
|
677
|
+
const errBody = fullResponse.body;
|
|
678
|
+
const message = (_d = (_c = errBody === null || errBody === void 0 ? void 0 : errBody.message) !== null && _c !== void 0 ? _c : errBody === null || errBody === void 0 ? void 0 : errBody.error) !== null && _d !== void 0 ? _d : `Unexpected HTTP ${fullResponse.statusCode} from Veridion Search API`;
|
|
679
|
+
throw new n8n_workflow_1.NodeApiError(ctx.getNode(), { message, statusCode: fullResponse.statusCode }, { message, itemIndex, httpCode: String(fullResponse.statusCode) });
|
|
680
|
+
}
|
|
681
|
+
const response = fullResponse.body;
|
|
682
|
+
if (outputMode === 'fullResponse') {
|
|
683
|
+
return [{ json: { ...response, _veridion_request: requestDebug } }];
|
|
684
|
+
}
|
|
685
|
+
const results = Array.isArray(response.result) ? response.result : [];
|
|
686
|
+
if (results.length === 0) {
|
|
687
|
+
return [
|
|
688
|
+
{
|
|
689
|
+
json: {
|
|
690
|
+
count: (_e = response.count) !== null && _e !== void 0 ? _e : 0,
|
|
691
|
+
pagination: (_f = response.pagination) !== null && _f !== void 0 ? _f : {},
|
|
692
|
+
result: [],
|
|
693
|
+
_veridion_request: requestDebug,
|
|
694
|
+
},
|
|
695
|
+
},
|
|
696
|
+
];
|
|
697
|
+
}
|
|
698
|
+
return results.map((company) => {
|
|
699
|
+
var _a, _b;
|
|
700
|
+
return ({
|
|
701
|
+
json: {
|
|
702
|
+
...company,
|
|
703
|
+
_veridion_search: {
|
|
704
|
+
count: (_a = response.count) !== null && _a !== void 0 ? _a : results.length,
|
|
705
|
+
pagination: (_b = response.pagination) !== null && _b !== void 0 ? _b : {},
|
|
706
|
+
request: requestDebug,
|
|
707
|
+
},
|
|
708
|
+
},
|
|
709
|
+
});
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
static parseCommaSeparated(input) {
|
|
713
|
+
return input
|
|
714
|
+
.split(',')
|
|
715
|
+
.map((value) => value.trim())
|
|
716
|
+
.filter((value) => value.length > 0);
|
|
717
|
+
}
|
|
718
|
+
static async loadIndustries(ctx) {
|
|
719
|
+
const credentials = await ctx.getCredentials('veridionApi');
|
|
720
|
+
return (await ctx.helpers.httpRequest({
|
|
721
|
+
method: 'GET',
|
|
722
|
+
url: 'https://data.veridion.com/industries/v0',
|
|
723
|
+
headers: {
|
|
724
|
+
'Content-Type': 'application/json',
|
|
725
|
+
'x-api-key': credentials.apiKey,
|
|
726
|
+
},
|
|
727
|
+
}));
|
|
728
|
+
}
|
|
729
|
+
static getStringListParameter(input) {
|
|
730
|
+
if (Array.isArray(input)) {
|
|
731
|
+
return input
|
|
732
|
+
.map((value) => value.trim())
|
|
733
|
+
.filter((value) => value.length > 0);
|
|
734
|
+
}
|
|
735
|
+
return Veridion.parseCommaSeparated(input);
|
|
736
|
+
}
|
|
737
|
+
static getOptionalNumberParameter(ctx, name, itemIndex) {
|
|
738
|
+
const rawValue = ctx.getNodeParameter(name, itemIndex, '')
|
|
739
|
+
.toString()
|
|
740
|
+
.trim();
|
|
741
|
+
if (rawValue === '') {
|
|
742
|
+
return null;
|
|
743
|
+
}
|
|
744
|
+
const value = Number(rawValue);
|
|
745
|
+
if (!Number.isFinite(value)) {
|
|
746
|
+
throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `"${name}" must be a valid number.`, { itemIndex });
|
|
747
|
+
}
|
|
748
|
+
return value;
|
|
749
|
+
}
|
|
750
|
+
static buildRangeFilter(attribute, min, max) {
|
|
751
|
+
if (min !== null && max !== null) {
|
|
752
|
+
if (min === max) {
|
|
753
|
+
return {
|
|
754
|
+
attribute,
|
|
755
|
+
relation: 'equals',
|
|
756
|
+
value: min,
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
return {
|
|
760
|
+
attribute,
|
|
761
|
+
relation: 'between',
|
|
762
|
+
value: [min, max],
|
|
763
|
+
};
|
|
764
|
+
}
|
|
765
|
+
if (min !== null) {
|
|
766
|
+
return {
|
|
767
|
+
attribute,
|
|
768
|
+
relation: 'greater_than_or_equal',
|
|
769
|
+
value: min,
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
return {
|
|
773
|
+
attribute,
|
|
774
|
+
relation: 'less_than_or_equal',
|
|
775
|
+
value: max,
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
/**
|
|
779
|
+
* Makes the API call with returnFullResponse: true so we can inspect the
|
|
780
|
+
* HTTP status code alongside the parsed body.
|
|
781
|
+
*/
|
|
782
|
+
static async callWithFullResponse(ctx, body, minConfidenceScore, itemIndex) {
|
|
783
|
+
var _a, _b, _c;
|
|
784
|
+
const fullResponse = await Veridion.callApi(ctx, {
|
|
785
|
+
method: 'POST',
|
|
786
|
+
url: 'https://data.veridion.com/match/v5/companies',
|
|
787
|
+
qs: { min_confidence_score: minConfidenceScore },
|
|
788
|
+
body: JSON.stringify(body),
|
|
789
|
+
}, itemIndex, true);
|
|
790
|
+
const { statusCode, body: responseBody } = fullResponse;
|
|
791
|
+
// 200 — match found, return enriched profile
|
|
792
|
+
if (statusCode === 200) {
|
|
793
|
+
return responseBody;
|
|
794
|
+
}
|
|
795
|
+
// 202 — accepted but no match meets the confidence threshold
|
|
796
|
+
if (statusCode === 202) {
|
|
797
|
+
return {
|
|
798
|
+
matched: false,
|
|
799
|
+
reason: 'No match found above the requested confidence threshold',
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
// 400 — bad request; surface the API's error message
|
|
803
|
+
if (statusCode === 400) {
|
|
804
|
+
const errBody = responseBody;
|
|
805
|
+
const message = (_b = (_a = errBody === null || errBody === void 0 ? void 0 : errBody.message) !== null && _a !== void 0 ? _a : errBody === null || errBody === void 0 ? void 0 : errBody.error) !== null && _b !== void 0 ? _b : 'Bad request: check your input fields';
|
|
806
|
+
throw new n8n_workflow_1.NodeApiError(ctx.getNode(), { message, statusCode }, { message, itemIndex, httpCode: String(statusCode) });
|
|
807
|
+
}
|
|
808
|
+
// Any other error status
|
|
809
|
+
const genericBody = responseBody;
|
|
810
|
+
const message = (_c = genericBody === null || genericBody === void 0 ? void 0 : genericBody.message) !== null && _c !== void 0 ? _c : `Unexpected HTTP ${statusCode} from Veridion API`;
|
|
811
|
+
throw new n8n_workflow_1.NodeApiError(ctx.getNode(), { message, statusCode }, { message, itemIndex, httpCode: String(statusCode) });
|
|
812
|
+
}
|
|
813
|
+
static async callApi(ctx, options, itemIndex, returnFullResponse = false) {
|
|
814
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
815
|
+
const credentials = await ctx.getCredentials('veridionApi');
|
|
816
|
+
try {
|
|
817
|
+
return (await ctx.helpers.httpRequest({
|
|
818
|
+
method: options.method,
|
|
819
|
+
url: options.url,
|
|
820
|
+
qs: options.qs,
|
|
821
|
+
headers: {
|
|
822
|
+
'Content-Type': 'application/json',
|
|
823
|
+
'x-api-key': credentials.apiKey,
|
|
824
|
+
},
|
|
825
|
+
body: options.body,
|
|
826
|
+
returnFullResponse,
|
|
827
|
+
ignoreHttpStatusErrors: returnFullResponse,
|
|
828
|
+
}));
|
|
829
|
+
}
|
|
830
|
+
catch (error) {
|
|
831
|
+
const err = error;
|
|
832
|
+
const apiMessage = (_f = (_c = (_b = (_a = err.response) === null || _a === void 0 ? void 0 : _a.body) === null || _b === void 0 ? void 0 : _b.message) !== null && _c !== void 0 ? _c : (_e = (_d = err.response) === null || _d === void 0 ? void 0 : _d.body) === null || _e === void 0 ? void 0 : _e.error) !== null && _f !== void 0 ? _f : err.message;
|
|
833
|
+
const statusCode = (_j = (_h = (_g = err.response) === null || _g === void 0 ? void 0 : _g.statusCode) !== null && _h !== void 0 ? _h : err.statusCode) !== null && _j !== void 0 ? _j : (typeof err.httpCode === 'string' ? Number(err.httpCode) : err.httpCode);
|
|
834
|
+
throw new n8n_workflow_1.NodeApiError(ctx.getNode(), {
|
|
835
|
+
message: apiMessage,
|
|
836
|
+
name: err.name,
|
|
837
|
+
...(statusCode ? { statusCode } : {}),
|
|
838
|
+
}, {
|
|
839
|
+
itemIndex,
|
|
840
|
+
message: apiMessage,
|
|
841
|
+
...(statusCode ? { httpCode: String(statusCode) } : {}),
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
exports.Veridion = Veridion;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60" width="60" height="60">
|
|
2
|
+
<!-- Background square with Veridion brand purple -->
|
|
3
|
+
<rect width="60" height="60" rx="10" ry="10" fill="#4F46E5"/>
|
|
4
|
+
<!-- Stylised "V" letterform -->
|
|
5
|
+
<polyline
|
|
6
|
+
points="10,16 30,46 50,16"
|
|
7
|
+
fill="none"
|
|
8
|
+
stroke="#FFFFFF"
|
|
9
|
+
stroke-width="7"
|
|
10
|
+
stroke-linecap="round"
|
|
11
|
+
stroke-linejoin="round"
|
|
12
|
+
/>
|
|
13
|
+
</svg>
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "n8n-nodes-veridion",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "n8n community node for Veridion company enrich, company search, and supplier search",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"n8n-community-node-package",
|
|
7
|
+
"n8n",
|
|
8
|
+
"veridion",
|
|
9
|
+
"company-data",
|
|
10
|
+
"enrichment",
|
|
11
|
+
"b2b"
|
|
12
|
+
],
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"homepage": "https://github.com/cosmin-veridion/n8n-nodes-veridion",
|
|
15
|
+
"author": {
|
|
16
|
+
"name": "Veridion",
|
|
17
|
+
"email": "support@veridion.com"
|
|
18
|
+
},
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "https://github.com/cosmin-veridion/n8n-nodes-veridion.git"
|
|
22
|
+
},
|
|
23
|
+
"main": "index.js",
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsc -p tsconfig.build.json && node scripts/copy-icons.js",
|
|
26
|
+
"dev": "tsc -p tsconfig.build.json --watch",
|
|
27
|
+
"lint": "eslint",
|
|
28
|
+
"lintfix": "eslint --fix",
|
|
29
|
+
"prepublishOnly": "npm run build && npm run lint"
|
|
30
|
+
},
|
|
31
|
+
"n8n": {
|
|
32
|
+
"n8nNodesApiVersion": 1,
|
|
33
|
+
"credentials": [
|
|
34
|
+
"dist/credentials/VeridionApi.credentials.js"
|
|
35
|
+
],
|
|
36
|
+
"nodes": [
|
|
37
|
+
"dist/nodes/Veridion/Veridion.node.js"
|
|
38
|
+
]
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@n8n/node-cli": "^0.23.0",
|
|
42
|
+
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
43
|
+
"@typescript-eslint/parser": "^8.0.0",
|
|
44
|
+
"eslint": "^9.0.0",
|
|
45
|
+
"n8n-core": "2.16.1",
|
|
46
|
+
"n8n-workflow": "2.16.0",
|
|
47
|
+
"typescript": "^5.4.5"
|
|
48
|
+
},
|
|
49
|
+
"peerDependencies": {
|
|
50
|
+
"n8n-workflow": "2.16.0"
|
|
51
|
+
},
|
|
52
|
+
"files": [
|
|
53
|
+
"dist"
|
|
54
|
+
]
|
|
55
|
+
}
|