clawdbot-pipedrive 1.2.0 → 2.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/README.md +9 -7
- package/index.ts +278 -67
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,15 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
Pipedrive CRM integration plugin for [Clawdbot](https://clawd.bot).
|
|
4
4
|
|
|
5
|
+
**Now using Pipedrive API v2** - 50% lower token costs, better filtering, cursor-based pagination.
|
|
6
|
+
|
|
5
7
|
## Features
|
|
6
8
|
|
|
7
|
-
- **Deals**: Search, list, create, update, delete deals
|
|
8
|
-
- **Persons**: Search,
|
|
9
|
-
- **Organizations**: Search,
|
|
10
|
-
- **Activities**: List, create, update, delete tasks/calls/meetings
|
|
11
|
-
- **Pipelines & Stages**: List pipelines and stages
|
|
12
|
-
- **Notes**: List
|
|
13
|
-
- **Users**: List users, get current user
|
|
9
|
+
- **Deals**: Search, list, create, update, delete deals (v2)
|
|
10
|
+
- **Persons**: Search, list, create, update, delete contacts (v2)
|
|
11
|
+
- **Organizations**: Search, list, create, update, delete companies (v2)
|
|
12
|
+
- **Activities**: List, create, update, delete tasks/calls/meetings (v2)
|
|
13
|
+
- **Pipelines & Stages**: List pipelines and stages (v2)
|
|
14
|
+
- **Notes**: List, create, update, delete notes (v1 - no v2 yet)
|
|
15
|
+
- **Users**: List users, get current user (v1 - no v2 yet)
|
|
14
16
|
|
|
15
17
|
## Installation
|
|
16
18
|
|
package/index.ts
CHANGED
|
@@ -80,15 +80,12 @@ function setupSkillTemplate(): void {
|
|
|
80
80
|
mkdirSync(skillDir, { recursive: true });
|
|
81
81
|
|
|
82
82
|
if (!existsSync(skillFile)) {
|
|
83
|
-
// First install - create the skill file
|
|
84
83
|
writeFileSync(skillFile, SKILL_TEMPLATE);
|
|
85
84
|
console.log(`[pipedrive] Created skill template: ${skillFile}`);
|
|
86
85
|
console.log("[pipedrive] Customize this file with your organization's workflows.");
|
|
87
86
|
} else {
|
|
88
|
-
// Skill exists - check if template has changed
|
|
89
87
|
const existing = readFileSync(skillFile, "utf-8");
|
|
90
88
|
if (existing !== SKILL_TEMPLATE) {
|
|
91
|
-
// Save latest template for comparison
|
|
92
89
|
writeFileSync(latestFile, SKILL_TEMPLATE);
|
|
93
90
|
console.log(`[pipedrive] Skill file exists: ${skillFile} (not modified)`);
|
|
94
91
|
console.log(`[pipedrive] New template available: ${latestFile}`);
|
|
@@ -130,8 +127,8 @@ type ClawdbotPluginDefinition = {
|
|
|
130
127
|
const plugin: ClawdbotPluginDefinition = {
|
|
131
128
|
id: "pipedrive",
|
|
132
129
|
name: "Pipedrive CRM",
|
|
133
|
-
description: "Interact with Pipedrive deals, persons, organizations, and activities",
|
|
134
|
-
version: "
|
|
130
|
+
description: "Interact with Pipedrive deals, persons, organizations, and activities (API v2)",
|
|
131
|
+
version: "2.0.0",
|
|
135
132
|
|
|
136
133
|
configSchema: {
|
|
137
134
|
parse: (v) => v as PipedriveConfig,
|
|
@@ -150,7 +147,6 @@ const plugin: ClawdbotPluginDefinition = {
|
|
|
150
147
|
},
|
|
151
148
|
|
|
152
149
|
register(api) {
|
|
153
|
-
// Set up skill template on first run
|
|
154
150
|
setupSkillTemplate();
|
|
155
151
|
|
|
156
152
|
const cfg = api.pluginConfig as PipedriveConfig;
|
|
@@ -160,16 +156,20 @@ const plugin: ClawdbotPluginDefinition = {
|
|
|
160
156
|
return;
|
|
161
157
|
}
|
|
162
158
|
|
|
163
|
-
const
|
|
159
|
+
const baseUrlV2 = `https://${cfg.domain}.pipedrive.com/api/v2`;
|
|
160
|
+
const baseUrlV1 = `https://${cfg.domain}.pipedrive.com/api/v1`; // For endpoints not yet in v2
|
|
164
161
|
|
|
165
|
-
async function pipedriveRequest(endpoint: string, options?: RequestInit) {
|
|
162
|
+
async function pipedriveRequest(endpoint: string, options?: RequestInit & { useV1?: boolean }) {
|
|
163
|
+
const baseUrl = options?.useV1 ? baseUrlV1 : baseUrlV2;
|
|
166
164
|
const url = new URL(`${baseUrl}${endpoint}`);
|
|
167
165
|
url.searchParams.set("api_token", cfg.apiKey!);
|
|
166
|
+
|
|
167
|
+
const { useV1, ...fetchOptions } = options || {};
|
|
168
168
|
const res = await fetch(url.toString(), {
|
|
169
|
-
...
|
|
169
|
+
...fetchOptions,
|
|
170
170
|
headers: {
|
|
171
171
|
"Content-Type": "application/json",
|
|
172
|
-
...
|
|
172
|
+
...fetchOptions?.headers,
|
|
173
173
|
},
|
|
174
174
|
});
|
|
175
175
|
if (!res.ok) {
|
|
@@ -179,7 +179,7 @@ const plugin: ClawdbotPluginDefinition = {
|
|
|
179
179
|
return res.json();
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
-
// ============ DEALS ============
|
|
182
|
+
// ============ DEALS (v2) ============
|
|
183
183
|
|
|
184
184
|
api.registerTool({
|
|
185
185
|
name: "pipedrive_search_deals",
|
|
@@ -187,14 +187,16 @@ const plugin: ClawdbotPluginDefinition = {
|
|
|
187
187
|
parameters: Type.Object({
|
|
188
188
|
term: Type.String({ description: "Search term" }),
|
|
189
189
|
status: Type.Optional(
|
|
190
|
-
Type.String({ description: "Filter by status: open, won, lost, deleted
|
|
190
|
+
Type.String({ description: "Filter by status: open, won, lost, deleted" })
|
|
191
191
|
),
|
|
192
|
+
limit: Type.Optional(Type.Number({ description: "Number of results (default 100)" })),
|
|
192
193
|
}),
|
|
193
194
|
async execute(_id, params) {
|
|
194
|
-
const { term, status } = params as { term: string; status?: string };
|
|
195
|
-
|
|
196
|
-
if (status)
|
|
197
|
-
|
|
195
|
+
const { term, status, limit } = params as { term: string; status?: string; limit?: number };
|
|
196
|
+
const query = new URLSearchParams({ term });
|
|
197
|
+
if (status) query.set("status", status);
|
|
198
|
+
if (limit) query.set("limit", String(limit));
|
|
199
|
+
const data = await pipedriveRequest(`/deals/search?${query}`);
|
|
198
200
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
199
201
|
},
|
|
200
202
|
});
|
|
@@ -216,26 +218,22 @@ const plugin: ClawdbotPluginDefinition = {
|
|
|
216
218
|
name: "pipedrive_list_deals",
|
|
217
219
|
description: "List deals with optional filters",
|
|
218
220
|
parameters: Type.Object({
|
|
219
|
-
status: Type.Optional(Type.String({ description: "Filter by status: open, won, lost, deleted
|
|
221
|
+
status: Type.Optional(Type.String({ description: "Filter by status: open, won, lost, deleted" })),
|
|
220
222
|
stage_id: Type.Optional(Type.Number({ description: "Filter by pipeline stage ID" })),
|
|
221
|
-
|
|
223
|
+
owner_id: Type.Optional(Type.Number({ description: "Filter by owner user ID" })),
|
|
224
|
+
person_id: Type.Optional(Type.Number({ description: "Filter by person ID" })),
|
|
225
|
+
org_id: Type.Optional(Type.Number({ description: "Filter by organization ID" })),
|
|
226
|
+
pipeline_id: Type.Optional(Type.Number({ description: "Filter by pipeline ID" })),
|
|
222
227
|
limit: Type.Optional(Type.Number({ description: "Number of results (default 100, max 500)" })),
|
|
223
|
-
|
|
228
|
+
cursor: Type.Optional(Type.String({ description: "Pagination cursor from previous response" })),
|
|
229
|
+
sort_by: Type.Optional(Type.String({ description: "Sort by: id, add_time, update_time" })),
|
|
230
|
+
sort_direction: Type.Optional(Type.String({ description: "Sort direction: asc, desc" })),
|
|
224
231
|
}),
|
|
225
232
|
async execute(_id, params) {
|
|
226
|
-
const { status, stage_id, user_id, limit, start } = params as {
|
|
227
|
-
status?: string;
|
|
228
|
-
stage_id?: number;
|
|
229
|
-
user_id?: number;
|
|
230
|
-
limit?: number;
|
|
231
|
-
start?: number;
|
|
232
|
-
};
|
|
233
233
|
const query = new URLSearchParams();
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
if (limit) query.set("limit", String(limit));
|
|
238
|
-
if (start) query.set("start", String(start));
|
|
234
|
+
for (const [key, value] of Object.entries(params)) {
|
|
235
|
+
if (value !== undefined) query.set(key, String(value));
|
|
236
|
+
}
|
|
239
237
|
const data = await pipedriveRequest(`/deals?${query}`);
|
|
240
238
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
241
239
|
},
|
|
@@ -251,7 +249,8 @@ const plugin: ClawdbotPluginDefinition = {
|
|
|
251
249
|
person_id: Type.Optional(Type.Number({ description: "Associated person/contact ID" })),
|
|
252
250
|
org_id: Type.Optional(Type.Number({ description: "Associated organization ID" })),
|
|
253
251
|
stage_id: Type.Optional(Type.Number({ description: "Pipeline stage ID" })),
|
|
254
|
-
|
|
252
|
+
owner_id: Type.Optional(Type.Number({ description: "Owner user ID" })),
|
|
253
|
+
pipeline_id: Type.Optional(Type.Number({ description: "Pipeline ID" })),
|
|
255
254
|
expected_close_date: Type.Optional(Type.String({ description: "Expected close date (YYYY-MM-DD)" })),
|
|
256
255
|
}),
|
|
257
256
|
async execute(_id, params) {
|
|
@@ -273,14 +272,15 @@ const plugin: ClawdbotPluginDefinition = {
|
|
|
273
272
|
currency: Type.Optional(Type.String({ description: "Currency code" })),
|
|
274
273
|
status: Type.Optional(Type.String({ description: "Status: open, won, lost, deleted" })),
|
|
275
274
|
stage_id: Type.Optional(Type.Number({ description: "Move to stage ID" })),
|
|
276
|
-
|
|
275
|
+
owner_id: Type.Optional(Type.Number({ description: "New owner user ID" })),
|
|
276
|
+
pipeline_id: Type.Optional(Type.Number({ description: "Move to pipeline ID" })),
|
|
277
277
|
expected_close_date: Type.Optional(Type.String({ description: "Expected close date (YYYY-MM-DD)" })),
|
|
278
278
|
lost_reason: Type.Optional(Type.String({ description: "Reason for losing (when status=lost)" })),
|
|
279
279
|
}),
|
|
280
280
|
async execute(_id, params) {
|
|
281
281
|
const { id, ...updateParams } = params as { id: number } & Record<string, unknown>;
|
|
282
282
|
const data = await pipedriveRequest(`/deals/${id}`, {
|
|
283
|
-
method: "
|
|
283
|
+
method: "PATCH", // v2 uses PATCH instead of PUT
|
|
284
284
|
body: JSON.stringify(updateParams),
|
|
285
285
|
});
|
|
286
286
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
@@ -289,7 +289,7 @@ const plugin: ClawdbotPluginDefinition = {
|
|
|
289
289
|
|
|
290
290
|
api.registerTool({
|
|
291
291
|
name: "pipedrive_delete_deal",
|
|
292
|
-
description: "Delete a deal",
|
|
292
|
+
description: "Delete a deal (marks as deleted, 30-day retention)",
|
|
293
293
|
parameters: Type.Object({
|
|
294
294
|
id: Type.Number({ description: "Deal ID to delete" }),
|
|
295
295
|
}),
|
|
@@ -300,17 +300,20 @@ const plugin: ClawdbotPluginDefinition = {
|
|
|
300
300
|
},
|
|
301
301
|
});
|
|
302
302
|
|
|
303
|
-
// ============ PERSONS (
|
|
303
|
+
// ============ PERSONS (v2) ============
|
|
304
304
|
|
|
305
305
|
api.registerTool({
|
|
306
306
|
name: "pipedrive_search_persons",
|
|
307
|
-
description: "Search for persons/contacts
|
|
307
|
+
description: "Search for persons/contacts by name, email, phone, or notes",
|
|
308
308
|
parameters: Type.Object({
|
|
309
309
|
term: Type.String({ description: "Search term (name, email, phone)" }),
|
|
310
|
+
limit: Type.Optional(Type.Number({ description: "Number of results" })),
|
|
310
311
|
}),
|
|
311
312
|
async execute(_id, params) {
|
|
312
|
-
const { term } = params as { term: string };
|
|
313
|
-
const
|
|
313
|
+
const { term, limit } = params as { term: string; limit?: number };
|
|
314
|
+
const query = new URLSearchParams({ term });
|
|
315
|
+
if (limit) query.set("limit", String(limit));
|
|
316
|
+
const data = await pipedriveRequest(`/persons/search?${query}`);
|
|
314
317
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
315
318
|
},
|
|
316
319
|
});
|
|
@@ -328,6 +331,27 @@ const plugin: ClawdbotPluginDefinition = {
|
|
|
328
331
|
},
|
|
329
332
|
});
|
|
330
333
|
|
|
334
|
+
api.registerTool({
|
|
335
|
+
name: "pipedrive_list_persons",
|
|
336
|
+
description: "List all persons with optional filters",
|
|
337
|
+
parameters: Type.Object({
|
|
338
|
+
owner_id: Type.Optional(Type.Number({ description: "Filter by owner user ID" })),
|
|
339
|
+
org_id: Type.Optional(Type.Number({ description: "Filter by organization ID" })),
|
|
340
|
+
limit: Type.Optional(Type.Number({ description: "Number of results (default 100)" })),
|
|
341
|
+
cursor: Type.Optional(Type.String({ description: "Pagination cursor" })),
|
|
342
|
+
sort_by: Type.Optional(Type.String({ description: "Sort by: id, add_time, update_time, name" })),
|
|
343
|
+
sort_direction: Type.Optional(Type.String({ description: "Sort direction: asc, desc" })),
|
|
344
|
+
}),
|
|
345
|
+
async execute(_id, params) {
|
|
346
|
+
const query = new URLSearchParams();
|
|
347
|
+
for (const [key, value] of Object.entries(params)) {
|
|
348
|
+
if (value !== undefined) query.set(key, String(value));
|
|
349
|
+
}
|
|
350
|
+
const data = await pipedriveRequest(`/persons?${query}`);
|
|
351
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
352
|
+
},
|
|
353
|
+
});
|
|
354
|
+
|
|
331
355
|
api.registerTool({
|
|
332
356
|
name: "pipedrive_create_person",
|
|
333
357
|
description: "Create a new person/contact",
|
|
@@ -336,11 +360,18 @@ const plugin: ClawdbotPluginDefinition = {
|
|
|
336
360
|
email: Type.Optional(Type.String({ description: "Email address" })),
|
|
337
361
|
phone: Type.Optional(Type.String({ description: "Phone number" })),
|
|
338
362
|
org_id: Type.Optional(Type.Number({ description: "Associated organization ID" })),
|
|
363
|
+
owner_id: Type.Optional(Type.Number({ description: "Owner user ID" })),
|
|
339
364
|
}),
|
|
340
365
|
async execute(_id, params) {
|
|
366
|
+
// v2 expects email/phone as arrays of objects
|
|
367
|
+
const { email, phone, ...rest } = params as { email?: string; phone?: string } & Record<string, unknown>;
|
|
368
|
+
const body: Record<string, unknown> = { ...rest };
|
|
369
|
+
if (email) body.emails = [{ value: email, primary: true, label: "work" }];
|
|
370
|
+
if (phone) body.phones = [{ value: phone, primary: true, label: "work" }];
|
|
371
|
+
|
|
341
372
|
const data = await pipedriveRequest("/persons", {
|
|
342
373
|
method: "POST",
|
|
343
|
-
body: JSON.stringify(
|
|
374
|
+
body: JSON.stringify(body),
|
|
344
375
|
});
|
|
345
376
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
346
377
|
},
|
|
@@ -355,28 +386,49 @@ const plugin: ClawdbotPluginDefinition = {
|
|
|
355
386
|
email: Type.Optional(Type.String({ description: "New email" })),
|
|
356
387
|
phone: Type.Optional(Type.String({ description: "New phone" })),
|
|
357
388
|
org_id: Type.Optional(Type.Number({ description: "New organization ID" })),
|
|
389
|
+
owner_id: Type.Optional(Type.Number({ description: "New owner user ID" })),
|
|
358
390
|
}),
|
|
359
391
|
async execute(_id, params) {
|
|
360
|
-
const { id, ...
|
|
392
|
+
const { id, email, phone, ...rest } = params as { id: number; email?: string; phone?: string } & Record<string, unknown>;
|
|
393
|
+
const body: Record<string, unknown> = { ...rest };
|
|
394
|
+
if (email) body.emails = [{ value: email, primary: true, label: "work" }];
|
|
395
|
+
if (phone) body.phones = [{ value: phone, primary: true, label: "work" }];
|
|
396
|
+
|
|
361
397
|
const data = await pipedriveRequest(`/persons/${id}`, {
|
|
362
|
-
method: "
|
|
363
|
-
body: JSON.stringify(
|
|
398
|
+
method: "PATCH",
|
|
399
|
+
body: JSON.stringify(body),
|
|
364
400
|
});
|
|
365
401
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
366
402
|
},
|
|
367
403
|
});
|
|
368
404
|
|
|
369
|
-
|
|
405
|
+
api.registerTool({
|
|
406
|
+
name: "pipedrive_delete_person",
|
|
407
|
+
description: "Delete a person (marks as deleted, 30-day retention)",
|
|
408
|
+
parameters: Type.Object({
|
|
409
|
+
id: Type.Number({ description: "Person ID to delete" }),
|
|
410
|
+
}),
|
|
411
|
+
async execute(_id, params) {
|
|
412
|
+
const { id } = params as { id: number };
|
|
413
|
+
const data = await pipedriveRequest(`/persons/${id}`, { method: "DELETE" });
|
|
414
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
415
|
+
},
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
// ============ ORGANIZATIONS (v2) ============
|
|
370
419
|
|
|
371
420
|
api.registerTool({
|
|
372
421
|
name: "pipedrive_search_organizations",
|
|
373
|
-
description: "Search for organizations
|
|
422
|
+
description: "Search for organizations by name, address, or notes",
|
|
374
423
|
parameters: Type.Object({
|
|
375
424
|
term: Type.String({ description: "Search term (organization name)" }),
|
|
425
|
+
limit: Type.Optional(Type.Number({ description: "Number of results" })),
|
|
376
426
|
}),
|
|
377
427
|
async execute(_id, params) {
|
|
378
|
-
const { term } = params as { term: string };
|
|
379
|
-
const
|
|
428
|
+
const { term, limit } = params as { term: string; limit?: number };
|
|
429
|
+
const query = new URLSearchParams({ term });
|
|
430
|
+
if (limit) query.set("limit", String(limit));
|
|
431
|
+
const data = await pipedriveRequest(`/organizations/search?${query}`);
|
|
380
432
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
381
433
|
},
|
|
382
434
|
});
|
|
@@ -394,12 +446,33 @@ const plugin: ClawdbotPluginDefinition = {
|
|
|
394
446
|
},
|
|
395
447
|
});
|
|
396
448
|
|
|
449
|
+
api.registerTool({
|
|
450
|
+
name: "pipedrive_list_organizations",
|
|
451
|
+
description: "List all organizations with optional filters",
|
|
452
|
+
parameters: Type.Object({
|
|
453
|
+
owner_id: Type.Optional(Type.Number({ description: "Filter by owner user ID" })),
|
|
454
|
+
limit: Type.Optional(Type.Number({ description: "Number of results (default 100)" })),
|
|
455
|
+
cursor: Type.Optional(Type.String({ description: "Pagination cursor" })),
|
|
456
|
+
sort_by: Type.Optional(Type.String({ description: "Sort by: id, add_time, update_time, name" })),
|
|
457
|
+
sort_direction: Type.Optional(Type.String({ description: "Sort direction: asc, desc" })),
|
|
458
|
+
}),
|
|
459
|
+
async execute(_id, params) {
|
|
460
|
+
const query = new URLSearchParams();
|
|
461
|
+
for (const [key, value] of Object.entries(params)) {
|
|
462
|
+
if (value !== undefined) query.set(key, String(value));
|
|
463
|
+
}
|
|
464
|
+
const data = await pipedriveRequest(`/organizations?${query}`);
|
|
465
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
466
|
+
},
|
|
467
|
+
});
|
|
468
|
+
|
|
397
469
|
api.registerTool({
|
|
398
470
|
name: "pipedrive_create_organization",
|
|
399
471
|
description: "Create a new organization",
|
|
400
472
|
parameters: Type.Object({
|
|
401
473
|
name: Type.String({ description: "Organization name (required)" }),
|
|
402
474
|
address: Type.Optional(Type.String({ description: "Address" })),
|
|
475
|
+
owner_id: Type.Optional(Type.Number({ description: "Owner user ID" })),
|
|
403
476
|
}),
|
|
404
477
|
async execute(_id, params) {
|
|
405
478
|
const data = await pipedriveRequest("/organizations", {
|
|
@@ -410,7 +483,39 @@ const plugin: ClawdbotPluginDefinition = {
|
|
|
410
483
|
},
|
|
411
484
|
});
|
|
412
485
|
|
|
413
|
-
|
|
486
|
+
api.registerTool({
|
|
487
|
+
name: "pipedrive_update_organization",
|
|
488
|
+
description: "Update an existing organization",
|
|
489
|
+
parameters: Type.Object({
|
|
490
|
+
id: Type.Number({ description: "Organization ID to update (required)" }),
|
|
491
|
+
name: Type.Optional(Type.String({ description: "New name" })),
|
|
492
|
+
address: Type.Optional(Type.String({ description: "New address" })),
|
|
493
|
+
owner_id: Type.Optional(Type.Number({ description: "New owner user ID" })),
|
|
494
|
+
}),
|
|
495
|
+
async execute(_id, params) {
|
|
496
|
+
const { id, ...updateParams } = params as { id: number } & Record<string, unknown>;
|
|
497
|
+
const data = await pipedriveRequest(`/organizations/${id}`, {
|
|
498
|
+
method: "PATCH",
|
|
499
|
+
body: JSON.stringify(updateParams),
|
|
500
|
+
});
|
|
501
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
502
|
+
},
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
api.registerTool({
|
|
506
|
+
name: "pipedrive_delete_organization",
|
|
507
|
+
description: "Delete an organization (marks as deleted, 30-day retention)",
|
|
508
|
+
parameters: Type.Object({
|
|
509
|
+
id: Type.Number({ description: "Organization ID to delete" }),
|
|
510
|
+
}),
|
|
511
|
+
async execute(_id, params) {
|
|
512
|
+
const { id } = params as { id: number };
|
|
513
|
+
const data = await pipedriveRequest(`/organizations/${id}`, { method: "DELETE" });
|
|
514
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
515
|
+
},
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
// ============ ACTIVITIES (v2) ============
|
|
414
519
|
|
|
415
520
|
api.registerTool({
|
|
416
521
|
name: "pipedrive_list_activities",
|
|
@@ -419,10 +524,13 @@ const plugin: ClawdbotPluginDefinition = {
|
|
|
419
524
|
deal_id: Type.Optional(Type.Number({ description: "Filter by deal ID" })),
|
|
420
525
|
person_id: Type.Optional(Type.Number({ description: "Filter by person ID" })),
|
|
421
526
|
org_id: Type.Optional(Type.Number({ description: "Filter by organization ID" })),
|
|
422
|
-
|
|
527
|
+
owner_id: Type.Optional(Type.Number({ description: "Filter by owner user ID" })),
|
|
528
|
+
done: Type.Optional(Type.Boolean({ description: "Filter by completion: true = done, false = not done" })),
|
|
423
529
|
type: Type.Optional(Type.String({ description: "Filter by type: call, meeting, task, deadline, email, lunch" })),
|
|
424
530
|
limit: Type.Optional(Type.Number({ description: "Number of results (default 100)" })),
|
|
425
|
-
|
|
531
|
+
cursor: Type.Optional(Type.String({ description: "Pagination cursor" })),
|
|
532
|
+
sort_by: Type.Optional(Type.String({ description: "Sort by: id, add_time, update_time, due_date" })),
|
|
533
|
+
sort_direction: Type.Optional(Type.String({ description: "Sort direction: asc, desc" })),
|
|
426
534
|
}),
|
|
427
535
|
async execute(_id, params) {
|
|
428
536
|
const query = new URLSearchParams();
|
|
@@ -460,7 +568,8 @@ const plugin: ClawdbotPluginDefinition = {
|
|
|
460
568
|
person_id: Type.Optional(Type.Number({ description: "Associated person ID" })),
|
|
461
569
|
org_id: Type.Optional(Type.Number({ description: "Associated organization ID" })),
|
|
462
570
|
note: Type.Optional(Type.String({ description: "Activity notes/description" })),
|
|
463
|
-
done: Type.Optional(Type.
|
|
571
|
+
done: Type.Optional(Type.Boolean({ description: "Mark as done: true = done, false = not done" })),
|
|
572
|
+
owner_id: Type.Optional(Type.Number({ description: "Owner user ID" })),
|
|
464
573
|
}),
|
|
465
574
|
async execute(_id, params) {
|
|
466
575
|
const data = await pipedriveRequest("/activities", {
|
|
@@ -480,13 +589,14 @@ const plugin: ClawdbotPluginDefinition = {
|
|
|
480
589
|
type: Type.Optional(Type.String({ description: "New type" })),
|
|
481
590
|
due_date: Type.Optional(Type.String({ description: "New due date (YYYY-MM-DD)" })),
|
|
482
591
|
due_time: Type.Optional(Type.String({ description: "New due time (HH:MM)" })),
|
|
483
|
-
done: Type.Optional(Type.
|
|
592
|
+
done: Type.Optional(Type.Boolean({ description: "Mark as done: true = done, false = not done" })),
|
|
484
593
|
note: Type.Optional(Type.String({ description: "New notes" })),
|
|
594
|
+
owner_id: Type.Optional(Type.Number({ description: "New owner user ID" })),
|
|
485
595
|
}),
|
|
486
596
|
async execute(_id, params) {
|
|
487
597
|
const { id, ...updateParams } = params as { id: number } & Record<string, unknown>;
|
|
488
598
|
const data = await pipedriveRequest(`/activities/${id}`, {
|
|
489
|
-
method: "
|
|
599
|
+
method: "PATCH",
|
|
490
600
|
body: JSON.stringify(updateParams),
|
|
491
601
|
});
|
|
492
602
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
@@ -495,7 +605,7 @@ const plugin: ClawdbotPluginDefinition = {
|
|
|
495
605
|
|
|
496
606
|
api.registerTool({
|
|
497
607
|
name: "pipedrive_delete_activity",
|
|
498
|
-
description: "Delete an activity",
|
|
608
|
+
description: "Delete an activity (marks as deleted, 30-day retention)",
|
|
499
609
|
parameters: Type.Object({
|
|
500
610
|
id: Type.Number({ description: "Activity ID to delete" }),
|
|
501
611
|
}),
|
|
@@ -506,34 +616,74 @@ const plugin: ClawdbotPluginDefinition = {
|
|
|
506
616
|
},
|
|
507
617
|
});
|
|
508
618
|
|
|
509
|
-
// ============ PIPELINES
|
|
619
|
+
// ============ PIPELINES (v2) ============
|
|
510
620
|
|
|
511
621
|
api.registerTool({
|
|
512
622
|
name: "pipedrive_list_pipelines",
|
|
513
623
|
description: "List all pipelines",
|
|
514
|
-
parameters: Type.Object({
|
|
515
|
-
|
|
516
|
-
|
|
624
|
+
parameters: Type.Object({
|
|
625
|
+
limit: Type.Optional(Type.Number({ description: "Number of results" })),
|
|
626
|
+
cursor: Type.Optional(Type.String({ description: "Pagination cursor" })),
|
|
627
|
+
}),
|
|
628
|
+
async execute(_id, params) {
|
|
629
|
+
const query = new URLSearchParams();
|
|
630
|
+
for (const [key, value] of Object.entries(params)) {
|
|
631
|
+
if (value !== undefined) query.set(key, String(value));
|
|
632
|
+
}
|
|
633
|
+
const endpoint = query.toString() ? `/pipelines?${query}` : "/pipelines";
|
|
634
|
+
const data = await pipedriveRequest(endpoint);
|
|
635
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
636
|
+
},
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
api.registerTool({
|
|
640
|
+
name: "pipedrive_get_pipeline",
|
|
641
|
+
description: "Get details of a specific pipeline by ID",
|
|
642
|
+
parameters: Type.Object({
|
|
643
|
+
id: Type.Number({ description: "Pipeline ID" }),
|
|
644
|
+
}),
|
|
645
|
+
async execute(_id, params) {
|
|
646
|
+
const { id } = params as { id: number };
|
|
647
|
+
const data = await pipedriveRequest(`/pipelines/${id}`);
|
|
517
648
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
518
649
|
},
|
|
519
650
|
});
|
|
520
651
|
|
|
652
|
+
// ============ STAGES (v2) ============
|
|
653
|
+
|
|
521
654
|
api.registerTool({
|
|
522
655
|
name: "pipedrive_list_stages",
|
|
523
656
|
description: "List all stages, optionally filtered by pipeline",
|
|
524
657
|
parameters: Type.Object({
|
|
525
658
|
pipeline_id: Type.Optional(Type.Number({ description: "Filter by pipeline ID" })),
|
|
659
|
+
limit: Type.Optional(Type.Number({ description: "Number of results" })),
|
|
660
|
+
cursor: Type.Optional(Type.String({ description: "Pagination cursor" })),
|
|
526
661
|
}),
|
|
527
662
|
async execute(_id, params) {
|
|
528
|
-
const
|
|
529
|
-
|
|
530
|
-
|
|
663
|
+
const query = new URLSearchParams();
|
|
664
|
+
for (const [key, value] of Object.entries(params)) {
|
|
665
|
+
if (value !== undefined) query.set(key, String(value));
|
|
666
|
+
}
|
|
667
|
+
const endpoint = query.toString() ? `/stages?${query}` : "/stages";
|
|
531
668
|
const data = await pipedriveRequest(endpoint);
|
|
532
669
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
533
670
|
},
|
|
534
671
|
});
|
|
535
672
|
|
|
536
|
-
|
|
673
|
+
api.registerTool({
|
|
674
|
+
name: "pipedrive_get_stage",
|
|
675
|
+
description: "Get details of a specific stage by ID",
|
|
676
|
+
parameters: Type.Object({
|
|
677
|
+
id: Type.Number({ description: "Stage ID" }),
|
|
678
|
+
}),
|
|
679
|
+
async execute(_id, params) {
|
|
680
|
+
const { id } = params as { id: number };
|
|
681
|
+
const data = await pipedriveRequest(`/stages/${id}`);
|
|
682
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
683
|
+
},
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
// ============ NOTES (v1 - no v2 available yet) ============
|
|
537
687
|
|
|
538
688
|
api.registerTool({
|
|
539
689
|
name: "pipedrive_list_notes",
|
|
@@ -543,13 +693,27 @@ const plugin: ClawdbotPluginDefinition = {
|
|
|
543
693
|
person_id: Type.Optional(Type.Number({ description: "Filter by person ID" })),
|
|
544
694
|
org_id: Type.Optional(Type.Number({ description: "Filter by organization ID" })),
|
|
545
695
|
limit: Type.Optional(Type.Number({ description: "Number of results" })),
|
|
696
|
+
start: Type.Optional(Type.Number({ description: "Pagination offset" })),
|
|
546
697
|
}),
|
|
547
698
|
async execute(_id, params) {
|
|
548
699
|
const query = new URLSearchParams();
|
|
549
700
|
for (const [key, value] of Object.entries(params)) {
|
|
550
701
|
if (value !== undefined) query.set(key, String(value));
|
|
551
702
|
}
|
|
552
|
-
const data = await pipedriveRequest(`/notes?${query}
|
|
703
|
+
const data = await pipedriveRequest(`/notes?${query}`, { useV1: true });
|
|
704
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
705
|
+
},
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
api.registerTool({
|
|
709
|
+
name: "pipedrive_get_note",
|
|
710
|
+
description: "Get details of a specific note by ID",
|
|
711
|
+
parameters: Type.Object({
|
|
712
|
+
id: Type.Number({ description: "Note ID" }),
|
|
713
|
+
}),
|
|
714
|
+
async execute(_id, params) {
|
|
715
|
+
const { id } = params as { id: number };
|
|
716
|
+
const data = await pipedriveRequest(`/notes/${id}`, { useV1: true });
|
|
553
717
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
554
718
|
},
|
|
555
719
|
});
|
|
@@ -567,19 +731,51 @@ const plugin: ClawdbotPluginDefinition = {
|
|
|
567
731
|
const data = await pipedriveRequest("/notes", {
|
|
568
732
|
method: "POST",
|
|
569
733
|
body: JSON.stringify(params),
|
|
734
|
+
useV1: true,
|
|
735
|
+
});
|
|
736
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
737
|
+
},
|
|
738
|
+
});
|
|
739
|
+
|
|
740
|
+
api.registerTool({
|
|
741
|
+
name: "pipedrive_update_note",
|
|
742
|
+
description: "Update an existing note",
|
|
743
|
+
parameters: Type.Object({
|
|
744
|
+
id: Type.Number({ description: "Note ID to update (required)" }),
|
|
745
|
+
content: Type.String({ description: "New content" }),
|
|
746
|
+
}),
|
|
747
|
+
async execute(_id, params) {
|
|
748
|
+
const { id, ...updateParams } = params as { id: number } & Record<string, unknown>;
|
|
749
|
+
const data = await pipedriveRequest(`/notes/${id}`, {
|
|
750
|
+
method: "PUT", // v1 uses PUT
|
|
751
|
+
body: JSON.stringify(updateParams),
|
|
752
|
+
useV1: true,
|
|
570
753
|
});
|
|
571
754
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
572
755
|
},
|
|
573
756
|
});
|
|
574
757
|
|
|
575
|
-
|
|
758
|
+
api.registerTool({
|
|
759
|
+
name: "pipedrive_delete_note",
|
|
760
|
+
description: "Delete a note",
|
|
761
|
+
parameters: Type.Object({
|
|
762
|
+
id: Type.Number({ description: "Note ID to delete" }),
|
|
763
|
+
}),
|
|
764
|
+
async execute(_id, params) {
|
|
765
|
+
const { id } = params as { id: number };
|
|
766
|
+
const data = await pipedriveRequest(`/notes/${id}`, { method: "DELETE", useV1: true });
|
|
767
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
768
|
+
},
|
|
769
|
+
});
|
|
770
|
+
|
|
771
|
+
// ============ USERS (v1 - mostly no v2 available) ============
|
|
576
772
|
|
|
577
773
|
api.registerTool({
|
|
578
774
|
name: "pipedrive_list_users",
|
|
579
775
|
description: "List all users in the Pipedrive account",
|
|
580
776
|
parameters: Type.Object({}),
|
|
581
777
|
async execute() {
|
|
582
|
-
const data = await pipedriveRequest("/users");
|
|
778
|
+
const data = await pipedriveRequest("/users", { useV1: true });
|
|
583
779
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
584
780
|
},
|
|
585
781
|
});
|
|
@@ -589,12 +785,27 @@ const plugin: ClawdbotPluginDefinition = {
|
|
|
589
785
|
description: "Get the current authenticated user's details",
|
|
590
786
|
parameters: Type.Object({}),
|
|
591
787
|
async execute() {
|
|
592
|
-
const data = await pipedriveRequest("/users/me");
|
|
788
|
+
const data = await pipedriveRequest("/users/me", { useV1: true });
|
|
789
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
790
|
+
},
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
api.registerTool({
|
|
794
|
+
name: "pipedrive_get_user",
|
|
795
|
+
description: "Get details of a specific user by ID",
|
|
796
|
+
parameters: Type.Object({
|
|
797
|
+
id: Type.Number({ description: "User ID" }),
|
|
798
|
+
}),
|
|
799
|
+
async execute(_id, params) {
|
|
800
|
+
const { id } = params as { id: number };
|
|
801
|
+
const data = await pipedriveRequest(`/users/${id}`, { useV1: true });
|
|
593
802
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
594
803
|
},
|
|
595
804
|
});
|
|
596
805
|
|
|
597
|
-
|
|
806
|
+
const v2Tools = 22;
|
|
807
|
+
const v1Tools = 6;
|
|
808
|
+
console.log(`[pipedrive] Registered ${v2Tools + v1Tools} tools (${v2Tools} v2, ${v1Tools} v1) for ${cfg.domain}.pipedrive.com`);
|
|
598
809
|
},
|
|
599
810
|
};
|
|
600
811
|
|