@pvass24/n8n-nodes-twenty-crm 1.0.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/LICENSE.md +21 -0
- package/README.md +297 -0
- package/dist/credentials/TwentyCrmApi.credentials.d.ts +10 -0
- package/dist/credentials/TwentyCrmApi.credentials.js +47 -0
- package/dist/credentials/TwentyCrmApi.credentials.js.map +1 -0
- package/dist/nodes/TwentyCrm/GenericFunctions.d.ts +7 -0
- package/dist/nodes/TwentyCrm/GenericFunctions.js +96 -0
- package/dist/nodes/TwentyCrm/GenericFunctions.js.map +1 -0
- package/dist/nodes/TwentyCrm/TwentyCrm.node.d.ts +5 -0
- package/dist/nodes/TwentyCrm/TwentyCrm.node.js +113 -0
- package/dist/nodes/TwentyCrm/TwentyCrm.node.js.map +1 -0
- package/dist/nodes/TwentyCrm/TwentyCrm.node.json +17 -0
- package/dist/nodes/TwentyCrm/resources/company/company.description.d.ts +3 -0
- package/dist/nodes/TwentyCrm/resources/company/company.description.js +296 -0
- package/dist/nodes/TwentyCrm/resources/company/company.description.js.map +1 -0
- package/dist/nodes/TwentyCrm/resources/company/company.handler.d.ts +2 -0
- package/dist/nodes/TwentyCrm/resources/company/company.handler.js +147 -0
- package/dist/nodes/TwentyCrm/resources/company/company.handler.js.map +1 -0
- package/dist/nodes/TwentyCrm/resources/note/note.description.d.ts +3 -0
- package/dist/nodes/TwentyCrm/resources/note/note.description.js +78 -0
- package/dist/nodes/TwentyCrm/resources/note/note.description.js.map +1 -0
- package/dist/nodes/TwentyCrm/resources/note/note.handler.d.ts +2 -0
- package/dist/nodes/TwentyCrm/resources/note/note.handler.js +57 -0
- package/dist/nodes/TwentyCrm/resources/note/note.handler.js.map +1 -0
- package/dist/nodes/TwentyCrm/resources/opportunity/opportunity.description.d.ts +3 -0
- package/dist/nodes/TwentyCrm/resources/opportunity/opportunity.description.js +102 -0
- package/dist/nodes/TwentyCrm/resources/opportunity/opportunity.description.js.map +1 -0
- package/dist/nodes/TwentyCrm/resources/opportunity/opportunity.handler.d.ts +2 -0
- package/dist/nodes/TwentyCrm/resources/opportunity/opportunity.handler.js +83 -0
- package/dist/nodes/TwentyCrm/resources/opportunity/opportunity.handler.js.map +1 -0
- package/dist/nodes/TwentyCrm/resources/person/person.description.d.ts +3 -0
- package/dist/nodes/TwentyCrm/resources/person/person.description.js +127 -0
- package/dist/nodes/TwentyCrm/resources/person/person.description.js.map +1 -0
- package/dist/nodes/TwentyCrm/resources/person/person.handler.d.ts +2 -0
- package/dist/nodes/TwentyCrm/resources/person/person.handler.js +106 -0
- package/dist/nodes/TwentyCrm/resources/person/person.handler.js.map +1 -0
- package/dist/nodes/TwentyCrm/resources/task/task.description.d.ts +3 -0
- package/dist/nodes/TwentyCrm/resources/task/task.description.js +96 -0
- package/dist/nodes/TwentyCrm/resources/task/task.description.js.map +1 -0
- package/dist/nodes/TwentyCrm/resources/task/task.handler.d.ts +2 -0
- package/dist/nodes/TwentyCrm/resources/task/task.handler.js +79 -0
- package/dist/nodes/TwentyCrm/resources/task/task.handler.js.map +1 -0
- package/dist/nodes/TwentyCrm/types.d.ts +29 -0
- package/dist/nodes/TwentyCrm/types.js +3 -0
- package/dist/nodes/TwentyCrm/types.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +62 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Patrick Vassell
|
|
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,297 @@
|
|
|
1
|
+
# n8n-nodes-twenty-crm
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/n8n-nodes-twenty-crm)
|
|
4
|
+
[](https://www.npmjs.com/package/n8n-nodes-twenty-crm)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
[](https://docs.n8n.io/integrations/community-nodes/)
|
|
7
|
+
|
|
8
|
+
> The production-grade n8n community node for [Twenty CRM](https://twenty.com) — the open-source alternative to Salesforce.
|
|
9
|
+
|
|
10
|
+
Full CRUD operations with **cursor pagination**, **advanced filtering**, **composite field handling**, and **zero runtime dependencies**.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Why This Node?
|
|
15
|
+
|
|
16
|
+
| Feature | n8n-nodes-twenty-crm | Other Twenty nodes |
|
|
17
|
+
|---------|---------------------|-------------------|
|
|
18
|
+
| Pagination (Return All) | **Full cursor-based** | None (max 60 results) |
|
|
19
|
+
| Filtering | **Built-in filter UI** | Manual query strings |
|
|
20
|
+
| Create/Update bodies | **Properly structured** | Broken (bugs #5, #6) |
|
|
21
|
+
| Error messages | **Actionable & clear** | Raw JSON dumps |
|
|
22
|
+
| Runtime dependencies | **Zero** | Has runtime deps |
|
|
23
|
+
| n8n verification eligible | **Yes** | No |
|
|
24
|
+
| AI Agent compatible | **Yes** (`usableAsTool`) | No |
|
|
25
|
+
| Custom field support | **Via Additional Fields** | Not supported |
|
|
26
|
+
| Test coverage | **24 tests (unit + integration)** | None |
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
### In n8n (Recommended)
|
|
33
|
+
|
|
34
|
+
1. Go to **Settings** → **Community Nodes**
|
|
35
|
+
2. Select **Install a community node**
|
|
36
|
+
3. Enter: `n8n-nodes-twenty-crm`
|
|
37
|
+
4. Click **Install**
|
|
38
|
+
|
|
39
|
+
### Via npm (Self-hosted)
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
cd ~/.n8n/custom
|
|
43
|
+
npm install n8n-nodes-twenty-crm
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Then restart n8n.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Setup
|
|
51
|
+
|
|
52
|
+
1. In your Twenty CRM instance, go to **Settings** → **Playground**
|
|
53
|
+
2. Generate an API key
|
|
54
|
+
3. In n8n, create new credentials:
|
|
55
|
+
- **Credential Type**: Twenty CRM API
|
|
56
|
+
- **Server URL**: Your Twenty instance URL (e.g. `https://crm.yourdomain.com`)
|
|
57
|
+
- **API Key**: Paste the key from step 2
|
|
58
|
+
|
|
59
|
+
The credential test will automatically verify connectivity.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Resources & Operations
|
|
64
|
+
|
|
65
|
+
### Company
|
|
66
|
+
|
|
67
|
+
| Operation | Description |
|
|
68
|
+
|-----------|-------------|
|
|
69
|
+
| **Create** | Create a company with name, domain, address, LinkedIn, revenue |
|
|
70
|
+
| **Get** | Retrieve a company by ID |
|
|
71
|
+
| **Get Many** | List companies with filters, ordering, and pagination |
|
|
72
|
+
| **Update** | Update any company field |
|
|
73
|
+
| **Delete** | Soft-delete a company |
|
|
74
|
+
|
|
75
|
+
### Person (Contact)
|
|
76
|
+
|
|
77
|
+
| Operation | Description |
|
|
78
|
+
|-----------|-------------|
|
|
79
|
+
| **Create** | Create a contact with name, email, phone, job title, company link |
|
|
80
|
+
| **Get** | Retrieve a person by ID |
|
|
81
|
+
| **Get Many** | List people with filters on name, email, company |
|
|
82
|
+
| **Update** | Update any person field |
|
|
83
|
+
| **Delete** | Soft-delete a person |
|
|
84
|
+
|
|
85
|
+
### Opportunity (Deal)
|
|
86
|
+
|
|
87
|
+
| Operation | Description |
|
|
88
|
+
|-----------|-------------|
|
|
89
|
+
| **Create** | Create a deal with stage, amount, close date, company/contact links |
|
|
90
|
+
| **Get** | Retrieve an opportunity by ID |
|
|
91
|
+
| **Get Many** | List opportunities filtered by stage, company, date |
|
|
92
|
+
| **Update** | Update stage, amount, or any field |
|
|
93
|
+
| **Delete** | Soft-delete an opportunity |
|
|
94
|
+
|
|
95
|
+
### Note
|
|
96
|
+
|
|
97
|
+
| Operation | Description |
|
|
98
|
+
|-----------|-------------|
|
|
99
|
+
| **Create** | Create a note with title and body |
|
|
100
|
+
| **Get** | Retrieve a note by ID |
|
|
101
|
+
| **Get Many** | List all notes with pagination |
|
|
102
|
+
| **Update** | Update title or body |
|
|
103
|
+
| **Delete** | Soft-delete a note |
|
|
104
|
+
|
|
105
|
+
### Task
|
|
106
|
+
|
|
107
|
+
| Operation | Description |
|
|
108
|
+
|-----------|-------------|
|
|
109
|
+
| **Create** | Create a task with title, body, due date, status, assignee |
|
|
110
|
+
| **Get** | Retrieve a task by ID |
|
|
111
|
+
| **Get Many** | List tasks filtered by status |
|
|
112
|
+
| **Update** | Update status, assignee, due date, or any field |
|
|
113
|
+
| **Delete** | Soft-delete a task |
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Usage Examples
|
|
118
|
+
|
|
119
|
+
### Create a Company
|
|
120
|
+
|
|
121
|
+
```json
|
|
122
|
+
{
|
|
123
|
+
"resource": "company",
|
|
124
|
+
"operation": "create",
|
|
125
|
+
"name": "Acme Corp",
|
|
126
|
+
"additionalFields": {
|
|
127
|
+
"domainUrl": "https://acme.com",
|
|
128
|
+
"addressCity": "San Francisco",
|
|
129
|
+
"addressState": "CA",
|
|
130
|
+
"addressCountry": "US",
|
|
131
|
+
"annualRevenueMicros": 5000000000,
|
|
132
|
+
"currencyCode": "USD"
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### List People by Company
|
|
138
|
+
|
|
139
|
+
```json
|
|
140
|
+
{
|
|
141
|
+
"resource": "person",
|
|
142
|
+
"operation": "getAll",
|
|
143
|
+
"returnAll": false,
|
|
144
|
+
"limit": 20,
|
|
145
|
+
"filters": {
|
|
146
|
+
"companyId": "your-company-uuid"
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Update Opportunity Stage
|
|
152
|
+
|
|
153
|
+
```json
|
|
154
|
+
{
|
|
155
|
+
"resource": "opportunity",
|
|
156
|
+
"operation": "update",
|
|
157
|
+
"opportunityId": "your-opportunity-uuid",
|
|
158
|
+
"updateFields": {
|
|
159
|
+
"stage": "CLOSED_WON",
|
|
160
|
+
"amountMicros": 25000000000,
|
|
161
|
+
"currencyCode": "USD"
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Search Companies by Name
|
|
167
|
+
|
|
168
|
+
```json
|
|
169
|
+
{
|
|
170
|
+
"resource": "company",
|
|
171
|
+
"operation": "getAll",
|
|
172
|
+
"returnAll": false,
|
|
173
|
+
"limit": 10,
|
|
174
|
+
"filters": {
|
|
175
|
+
"nameContains": "tech"
|
|
176
|
+
},
|
|
177
|
+
"orderBy": "createdAt[DescNullsLast]"
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Filtering
|
|
184
|
+
|
|
185
|
+
The node provides a user-friendly filter UI that translates to Twenty's powerful filter syntax:
|
|
186
|
+
|
|
187
|
+
| Filter Type | Example | Twenty Syntax |
|
|
188
|
+
|-------------|---------|---------------|
|
|
189
|
+
| Substring match | Name Contains: "tech" | `name[ilike]:"%tech%"` |
|
|
190
|
+
| Date range | Created After: 2024-01-01 | `createdAt[gte]:"2024-01-01"` |
|
|
191
|
+
| Exact match | Company ID: uuid | `companyId[eq]:"uuid"` |
|
|
192
|
+
| Stage filter | Stage: CLOSED_WON | `stage[eq]:"CLOSED_WON"` |
|
|
193
|
+
|
|
194
|
+
All filters are combined with AND logic. For advanced queries, use the Twenty API directly.
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Pagination
|
|
199
|
+
|
|
200
|
+
- **Return All = false**: Returns up to `limit` results (max 60 per page)
|
|
201
|
+
- **Return All = true**: Automatically paginates through all results using cursor-based pagination
|
|
202
|
+
|
|
203
|
+
The node handles cursor tokens internally — you never need to manage `starting_after` or `endCursor` manually.
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## AI Agent Support
|
|
208
|
+
|
|
209
|
+
This node has `usableAsTool: true`, making it available as a tool in n8n's AI Agent workflows. An LLM can:
|
|
210
|
+
|
|
211
|
+
- Search for companies or contacts
|
|
212
|
+
- Create tasks and notes
|
|
213
|
+
- Update opportunity stages
|
|
214
|
+
- Query pipeline data
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## Compatibility
|
|
219
|
+
|
|
220
|
+
| Requirement | Version |
|
|
221
|
+
|-------------|---------|
|
|
222
|
+
| n8n | >= 1.0.0 |
|
|
223
|
+
| Node.js | >= 18 |
|
|
224
|
+
| Twenty CRM | >= 0.30 (REST API v1) |
|
|
225
|
+
|
|
226
|
+
Works with both [Twenty Cloud](https://twenty.com) and self-hosted instances.
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## Development
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
git clone https://github.com/pvass24/n8n-nodes-twenty-crm.git
|
|
234
|
+
cd n8n-nodes-twenty-crm
|
|
235
|
+
npm install
|
|
236
|
+
npm run build
|
|
237
|
+
|
|
238
|
+
# Run unit tests
|
|
239
|
+
npm test
|
|
240
|
+
|
|
241
|
+
# Run integration tests (requires .env.test with API credentials)
|
|
242
|
+
npm run test:integration
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Testing Setup
|
|
246
|
+
|
|
247
|
+
Create `.env.test` in the project root:
|
|
248
|
+
|
|
249
|
+
```env
|
|
250
|
+
TWENTY_API_URL=https://your-twenty-instance.com/rest
|
|
251
|
+
TWENTY_API_KEY=your-api-key-here
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Contributing
|
|
257
|
+
|
|
258
|
+
Contributions are welcome! Please:
|
|
259
|
+
|
|
260
|
+
1. Fork this repository
|
|
261
|
+
2. Create a feature branch (`git checkout -b feat/new-resource`)
|
|
262
|
+
3. Write tests for your changes
|
|
263
|
+
4. Ensure `npm test` and `npm run build` pass
|
|
264
|
+
5. Submit a Pull Request
|
|
265
|
+
|
|
266
|
+
### Adding a New Resource
|
|
267
|
+
|
|
268
|
+
1. Create `nodes/TwentyCrm/resources/{resource}/{resource}.description.ts`
|
|
269
|
+
2. Create `nodes/TwentyCrm/resources/{resource}/{resource}.handler.ts`
|
|
270
|
+
3. Import and register in `TwentyCrm.node.ts`
|
|
271
|
+
4. Add integration tests
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## Roadmap
|
|
276
|
+
|
|
277
|
+
- [ ] Polling Trigger node (new companies, updated deals, etc.)
|
|
278
|
+
- [ ] Webhook Trigger (when Twenty adds webhook support)
|
|
279
|
+
- [ ] Custom Objects support
|
|
280
|
+
- [ ] Custom Fields dynamic loading
|
|
281
|
+
- [ ] Batch operations (create/update many)
|
|
282
|
+
- [ ] File attachments
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## License
|
|
287
|
+
|
|
288
|
+
[MIT](LICENSE.md) — built by [Patrick Vassell](https://iampjv.co)
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## Links
|
|
293
|
+
|
|
294
|
+
- [Twenty CRM](https://twenty.com) — The open-source CRM
|
|
295
|
+
- [Twenty REST API Docs](https://twenty.com/developers/rest-api)
|
|
296
|
+
- [n8n Community Nodes](https://docs.n8n.io/integrations/community-nodes/)
|
|
297
|
+
- [Report Issues](https://github.com/pvass24/n8n-nodes-twenty-crm/issues)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties, Icon } from 'n8n-workflow';
|
|
2
|
+
export declare class TwentyCrmApi implements ICredentialType {
|
|
3
|
+
name: string;
|
|
4
|
+
displayName: string;
|
|
5
|
+
icon: Icon;
|
|
6
|
+
documentationUrl: string;
|
|
7
|
+
properties: INodeProperties[];
|
|
8
|
+
authenticate: IAuthenticateGeneric;
|
|
9
|
+
test: ICredentialTestRequest;
|
|
10
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TwentyCrmApi = void 0;
|
|
4
|
+
class TwentyCrmApi {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.name = 'twentyCrmApi';
|
|
7
|
+
this.displayName = 'Twenty CRM API';
|
|
8
|
+
this.icon = 'file:../icons/twenty.svg';
|
|
9
|
+
this.documentationUrl = 'https://twenty.com/developers/rest-api';
|
|
10
|
+
this.properties = [
|
|
11
|
+
{
|
|
12
|
+
displayName: 'Server URL',
|
|
13
|
+
name: 'serverUrl',
|
|
14
|
+
type: 'string',
|
|
15
|
+
default: 'https://api.twenty.com',
|
|
16
|
+
placeholder: 'https://crm.yourdomain.com',
|
|
17
|
+
description: 'The base URL of your Twenty CRM instance (no trailing slash)',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
displayName: 'API Key',
|
|
21
|
+
name: 'apiKey',
|
|
22
|
+
type: 'string',
|
|
23
|
+
typeOptions: { password: true },
|
|
24
|
+
default: '',
|
|
25
|
+
description: 'Generate in Settings → Playground in your Twenty workspace',
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
this.authenticate = {
|
|
29
|
+
type: 'generic',
|
|
30
|
+
properties: {
|
|
31
|
+
headers: {
|
|
32
|
+
Authorization: '=Bearer {{$credentials.apiKey}}',
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
this.test = {
|
|
37
|
+
request: {
|
|
38
|
+
baseURL: '={{$credentials.serverUrl}}',
|
|
39
|
+
url: '/rest/companies',
|
|
40
|
+
method: 'GET',
|
|
41
|
+
qs: { limit: '1' },
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
exports.TwentyCrmApi = TwentyCrmApi;
|
|
47
|
+
//# sourceMappingURL=TwentyCrmApi.credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TwentyCrmApi.credentials.js","sourceRoot":"","sources":["../../credentials/TwentyCrmApi.credentials.ts"],"names":[],"mappings":";;;AAQA,MAAa,YAAY;IAAzB;QACC,SAAI,GAAG,cAAc,CAAC;QAEtB,gBAAW,GAAG,gBAAgB,CAAC;QAE/B,SAAI,GAAS,0BAA0B,CAAC;QAExC,qBAAgB,GAAG,wCAAwC,CAAC;QAE5D,eAAU,GAAsB;YAC/B;gBACC,WAAW,EAAE,YAAY;gBACzB,IAAI,EAAE,WAAW;gBACjB,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,wBAAwB;gBACjC,WAAW,EAAE,4BAA4B;gBACzC,WAAW,EAAE,8DAA8D;aAC3E;YACD;gBACC,WAAW,EAAE,SAAS;gBACtB,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAC/B,OAAO,EAAE,EAAE;gBACX,WAAW,EAAE,4DAA4D;aACzE;SACD,CAAC;QAEF,iBAAY,GAAyB;YACpC,IAAI,EAAE,SAAS;YACf,UAAU,EAAE;gBACX,OAAO,EAAE;oBACR,aAAa,EAAE,iCAAiC;iBAChD;aACD;SACD,CAAC;QAEF,SAAI,GAA2B;YAC9B,OAAO,EAAE;gBACR,OAAO,EAAE,6BAA6B;gBACtC,GAAG,EAAE,iBAAiB;gBACtB,MAAM,EAAE,KAAK;gBACb,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE;aAClB;SACD,CAAC;IACH,CAAC;CAAA;AA7CD,oCA6CC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { IDataObject, IExecuteFunctions, IHttpRequestMethods, ILoadOptionsFunctions } from 'n8n-workflow';
|
|
2
|
+
import type { ITwentyFilter } from './types';
|
|
3
|
+
export declare function buildFilterString(filters: ITwentyFilter[]): string;
|
|
4
|
+
export declare function buildRequestUrl(serverUrl: string, resource: string, id?: string): string;
|
|
5
|
+
export declare function extractDataFromResponse(response: IDataObject, key: string): unknown;
|
|
6
|
+
export declare function twentyApiRequest(this: IExecuteFunctions | ILoadOptionsFunctions, method: IHttpRequestMethods, resource: string, body?: IDataObject, query?: IDataObject, id?: string): Promise<IDataObject>;
|
|
7
|
+
export declare function twentyApiRequestAllItems(this: IExecuteFunctions, resource: string, query?: IDataObject, limit?: number): Promise<IDataObject[]>;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildFilterString = buildFilterString;
|
|
4
|
+
exports.buildRequestUrl = buildRequestUrl;
|
|
5
|
+
exports.extractDataFromResponse = extractDataFromResponse;
|
|
6
|
+
exports.twentyApiRequest = twentyApiRequest;
|
|
7
|
+
exports.twentyApiRequestAllItems = twentyApiRequestAllItems;
|
|
8
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
9
|
+
const NUMERIC_RE = /^\d+(\.\d+)?$/;
|
|
10
|
+
const BOOLEAN_RE = /^(true|false)$/;
|
|
11
|
+
const NULL_RE = /^NULL$/;
|
|
12
|
+
const ARRAY_RE = /^\[.*\]$/;
|
|
13
|
+
function buildFilterString(filters) {
|
|
14
|
+
if (filters.length === 0)
|
|
15
|
+
return '';
|
|
16
|
+
return filters
|
|
17
|
+
.map((f) => {
|
|
18
|
+
const value = formatFilterValue(f.value, f.comparator);
|
|
19
|
+
return `${f.field}[${f.comparator}]:${value}`;
|
|
20
|
+
})
|
|
21
|
+
.join(',');
|
|
22
|
+
}
|
|
23
|
+
function formatFilterValue(value, comparator) {
|
|
24
|
+
if (NULL_RE.test(value))
|
|
25
|
+
return value;
|
|
26
|
+
if (BOOLEAN_RE.test(value))
|
|
27
|
+
return value;
|
|
28
|
+
if (NUMERIC_RE.test(value))
|
|
29
|
+
return value;
|
|
30
|
+
if (ARRAY_RE.test(value))
|
|
31
|
+
return value;
|
|
32
|
+
if (comparator === 'is')
|
|
33
|
+
return value;
|
|
34
|
+
return `"${value}"`;
|
|
35
|
+
}
|
|
36
|
+
function buildRequestUrl(serverUrl, resource, id) {
|
|
37
|
+
const base = serverUrl.replace(/\/$/, '');
|
|
38
|
+
const path = id ? `/rest/${resource}/${id}` : `/rest/${resource}`;
|
|
39
|
+
return `${base}${path}`;
|
|
40
|
+
}
|
|
41
|
+
function extractDataFromResponse(response, key) {
|
|
42
|
+
if (response.error || response.statusCode) {
|
|
43
|
+
const messages = response.messages || [response.error];
|
|
44
|
+
throw new Error(messages.join('; '));
|
|
45
|
+
}
|
|
46
|
+
const data = response.data;
|
|
47
|
+
if (!data) {
|
|
48
|
+
throw new Error('Response missing data field');
|
|
49
|
+
}
|
|
50
|
+
return data[key];
|
|
51
|
+
}
|
|
52
|
+
async function twentyApiRequest(method, resource, body, query, id) {
|
|
53
|
+
const credentials = await this.getCredentials('twentyCrmApi');
|
|
54
|
+
const serverUrl = credentials.serverUrl.replace(/\/$/, '');
|
|
55
|
+
const options = {
|
|
56
|
+
method,
|
|
57
|
+
url: buildRequestUrl(serverUrl, resource, id),
|
|
58
|
+
json: true,
|
|
59
|
+
body: body && Object.keys(body).length > 0 ? body : undefined,
|
|
60
|
+
qs: query && Object.keys(query).length > 0 ? query : undefined,
|
|
61
|
+
};
|
|
62
|
+
try {
|
|
63
|
+
return (await this.helpers.httpRequestWithAuthentication.call(this, 'twentyCrmApi', options));
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
throw new n8n_workflow_1.NodeApiError(this.getNode(), error);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async function twentyApiRequestAllItems(resource, query = {}, limit) {
|
|
70
|
+
const results = [];
|
|
71
|
+
let hasNextPage = true;
|
|
72
|
+
query.limit = limit ? Math.min(limit, 60) : 60;
|
|
73
|
+
while (hasNextPage) {
|
|
74
|
+
const response = await twentyApiRequest.call(this, 'GET', resource, undefined, query);
|
|
75
|
+
const pageInfo = response.pageInfo;
|
|
76
|
+
const data = response.data;
|
|
77
|
+
if (data) {
|
|
78
|
+
const items = data[resource];
|
|
79
|
+
if (items) {
|
|
80
|
+
results.push(...items);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (limit && results.length >= limit) {
|
|
84
|
+
return results.slice(0, limit);
|
|
85
|
+
}
|
|
86
|
+
if ((pageInfo === null || pageInfo === void 0 ? void 0 : pageInfo.hasNextPage) && pageInfo.endCursor) {
|
|
87
|
+
query.starting_after = pageInfo.endCursor;
|
|
88
|
+
hasNextPage = true;
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
hasNextPage = false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return results;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=GenericFunctions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GenericFunctions.js","sourceRoot":"","sources":["../../../nodes/TwentyCrm/GenericFunctions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
|
|
2
|
+
export declare class TwentyCrm implements INodeType {
|
|
3
|
+
description: INodeTypeDescription;
|
|
4
|
+
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
5
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TwentyCrm = void 0;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
const company_description_1 = require("./resources/company/company.description");
|
|
6
|
+
const company_handler_1 = require("./resources/company/company.handler");
|
|
7
|
+
const person_description_1 = require("./resources/person/person.description");
|
|
8
|
+
const person_handler_1 = require("./resources/person/person.handler");
|
|
9
|
+
const opportunity_description_1 = require("./resources/opportunity/opportunity.description");
|
|
10
|
+
const opportunity_handler_1 = require("./resources/opportunity/opportunity.handler");
|
|
11
|
+
const note_description_1 = require("./resources/note/note.description");
|
|
12
|
+
const note_handler_1 = require("./resources/note/note.handler");
|
|
13
|
+
const task_description_1 = require("./resources/task/task.description");
|
|
14
|
+
const task_handler_1 = require("./resources/task/task.handler");
|
|
15
|
+
class TwentyCrm {
|
|
16
|
+
constructor() {
|
|
17
|
+
this.description = {
|
|
18
|
+
displayName: 'Twenty CRM',
|
|
19
|
+
name: 'twentyCrm',
|
|
20
|
+
icon: 'file:../../icons/twenty.svg',
|
|
21
|
+
group: ['transform'],
|
|
22
|
+
version: 1,
|
|
23
|
+
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
|
24
|
+
description: 'Interact with Twenty CRM API',
|
|
25
|
+
defaults: {
|
|
26
|
+
name: 'Twenty CRM',
|
|
27
|
+
},
|
|
28
|
+
usableAsTool: true,
|
|
29
|
+
inputs: [n8n_workflow_1.NodeConnectionTypes.Main],
|
|
30
|
+
outputs: [n8n_workflow_1.NodeConnectionTypes.Main],
|
|
31
|
+
credentials: [
|
|
32
|
+
{
|
|
33
|
+
name: 'twentyCrmApi',
|
|
34
|
+
required: true,
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
properties: [
|
|
38
|
+
{
|
|
39
|
+
displayName: 'Resource',
|
|
40
|
+
name: 'resource',
|
|
41
|
+
type: 'options',
|
|
42
|
+
noDataExpression: true,
|
|
43
|
+
options: [
|
|
44
|
+
{ name: 'Company', value: 'company' },
|
|
45
|
+
{ name: 'Note', value: 'note' },
|
|
46
|
+
{ name: 'Opportunity', value: 'opportunity' },
|
|
47
|
+
{ name: 'Person', value: 'person' },
|
|
48
|
+
{ name: 'Task', value: 'task' },
|
|
49
|
+
],
|
|
50
|
+
default: 'company',
|
|
51
|
+
},
|
|
52
|
+
...company_description_1.companyOperations,
|
|
53
|
+
...company_description_1.companyFields,
|
|
54
|
+
...person_description_1.personOperations,
|
|
55
|
+
...person_description_1.personFields,
|
|
56
|
+
...note_description_1.noteOperations,
|
|
57
|
+
...note_description_1.noteFields,
|
|
58
|
+
...opportunity_description_1.opportunityOperations,
|
|
59
|
+
...opportunity_description_1.opportunityFields,
|
|
60
|
+
...task_description_1.taskOperations,
|
|
61
|
+
...task_description_1.taskFields,
|
|
62
|
+
],
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
async execute() {
|
|
66
|
+
const items = this.getInputData();
|
|
67
|
+
const returnData = [];
|
|
68
|
+
const resource = this.getNodeParameter('resource', 0);
|
|
69
|
+
const operation = this.getNodeParameter('operation', 0);
|
|
70
|
+
for (let i = 0; i < items.length; i++) {
|
|
71
|
+
try {
|
|
72
|
+
let result;
|
|
73
|
+
switch (resource) {
|
|
74
|
+
case 'company':
|
|
75
|
+
result = await company_handler_1.handleCompanyOperation.call(this, operation, i);
|
|
76
|
+
break;
|
|
77
|
+
case 'person':
|
|
78
|
+
result = await person_handler_1.handlePersonOperation.call(this, operation, i);
|
|
79
|
+
break;
|
|
80
|
+
case 'opportunity':
|
|
81
|
+
result = await opportunity_handler_1.handleOpportunityOperation.call(this, operation, i);
|
|
82
|
+
break;
|
|
83
|
+
case 'note':
|
|
84
|
+
result = await note_handler_1.handleNoteOperation.call(this, operation, i);
|
|
85
|
+
break;
|
|
86
|
+
case 'task':
|
|
87
|
+
result = await task_handler_1.handleTaskOperation.call(this, operation, i);
|
|
88
|
+
break;
|
|
89
|
+
default:
|
|
90
|
+
throw new Error(`Unknown resource: ${resource}`);
|
|
91
|
+
}
|
|
92
|
+
if (Array.isArray(result)) {
|
|
93
|
+
for (const item of result) {
|
|
94
|
+
returnData.push({ json: item });
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
returnData.push({ json: result });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
if (this.continueOnFail()) {
|
|
103
|
+
returnData.push({ json: { error: error.message }, pairedItem: { item: i } });
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
throw error;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return [returnData];
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
exports.TwentyCrm = TwentyCrm;
|
|
113
|
+
//# sourceMappingURL=TwentyCrm.node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TwentyCrm.node.js","sourceRoot":"","sources":["../../../nodes/TwentyCrm/TwentyCrm.node.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"node": "n8n-nodes-base.twentyCrm",
|
|
3
|
+
"nodeVersion": "1.0",
|
|
4
|
+
"codexVersion": "1.0",
|
|
5
|
+
"categories": ["Sales"],
|
|
6
|
+
"resources": {
|
|
7
|
+
"primaryDocumentation": [
|
|
8
|
+
{
|
|
9
|
+
"url": "https://twenty.com/developers/rest-api"
|
|
10
|
+
}
|
|
11
|
+
]
|
|
12
|
+
},
|
|
13
|
+
"alias": ["CRM", "Twenty", "Sales", "Contacts", "Deals", "Pipeline"],
|
|
14
|
+
"subcategories": {
|
|
15
|
+
"Sales": ["Contact Management", "Deal Tracking"]
|
|
16
|
+
}
|
|
17
|
+
}
|