@elevasis/core 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/dist/index.d.ts +1718 -19
  2. package/dist/index.js +369 -25
  3. package/dist/organization-model/index.d.ts +1718 -19
  4. package/dist/organization-model/index.js +369 -25
  5. package/dist/test-utils/index.d.ts +1108 -371
  6. package/dist/test-utils/index.js +357 -17
  7. package/package.json +5 -1
  8. package/src/__tests__/publish.test.ts +14 -13
  9. package/src/__tests__/template-core-compatibility.test.ts +4 -4
  10. package/src/_gen/__tests__/__snapshots__/contracts.md.snap +1109 -882
  11. package/src/auth/multi-tenancy/index.ts +3 -0
  12. package/src/auth/multi-tenancy/theme-presets.ts +45 -0
  13. package/src/auth/multi-tenancy/types.ts +57 -83
  14. package/src/auth/multi-tenancy/users/api-schemas.ts +165 -194
  15. package/src/business/acquisition/activity-events.ts +13 -4
  16. package/src/business/acquisition/api-schemas.test.ts +315 -4
  17. package/src/business/acquisition/api-schemas.ts +122 -8
  18. package/src/business/acquisition/build-templates.ts +44 -0
  19. package/src/business/acquisition/crm-next-action.test.ts +262 -0
  20. package/src/business/acquisition/crm-next-action.ts +220 -0
  21. package/src/business/acquisition/crm-priority.test.ts +216 -0
  22. package/src/business/acquisition/crm-priority.ts +349 -0
  23. package/src/business/acquisition/crm-state-actions.test.ts +151 -160
  24. package/src/business/acquisition/deal-ownership.test.ts +351 -0
  25. package/src/business/acquisition/deal-ownership.ts +120 -0
  26. package/src/business/acquisition/derive-actions.test.ts +101 -37
  27. package/src/business/acquisition/derive-actions.ts +102 -87
  28. package/src/business/acquisition/index.ts +10 -0
  29. package/src/business/acquisition/types.ts +400 -366
  30. package/src/business/crm/api-schemas.ts +40 -0
  31. package/src/business/crm/index.ts +1 -0
  32. package/src/business/deals/api-schemas.ts +79 -0
  33. package/src/business/deals/index.ts +1 -0
  34. package/src/business/projects/types.ts +124 -88
  35. package/src/execution/core/runner-types.ts +61 -80
  36. package/src/execution/engine/index.ts +4 -3
  37. package/src/execution/engine/tools/integration/server/adapters/gmail/gmail-tools.ts +105 -104
  38. package/src/execution/engine/tools/integration/server/adapters/instantly/instantly-tools.ts +1474 -1473
  39. package/src/execution/engine/tools/integration/server/adapters/millionverifier/millionverifier-tools.ts +103 -102
  40. package/src/execution/engine/tools/integration/server/adapters/signature-api/signature-api-tools.ts +182 -179
  41. package/src/execution/engine/tools/integration/server/adapters/stripe/stripe-tools.ts +310 -309
  42. package/src/execution/engine/tools/integration/tool.ts +255 -253
  43. package/src/execution/engine/tools/lead-service-types.ts +939 -924
  44. package/src/execution/engine/tools/messages.ts +43 -0
  45. package/src/execution/engine/tools/platform/acquisition/list-tools.ts +6 -5
  46. package/src/execution/engine/tools/platform/acquisition/types.ts +5 -2
  47. package/src/execution/engine/tools/platform/email/types.ts +97 -96
  48. package/src/execution/engine/tools/registry.ts +4 -3
  49. package/src/execution/engine/tools/tool-maps.ts +3 -1
  50. package/src/execution/engine/tools/types.ts +234 -233
  51. package/src/execution/engine/workflow/types.ts +195 -193
  52. package/src/execution/external/api-schemas.ts +40 -0
  53. package/src/execution/external/index.ts +1 -0
  54. package/src/knowledge/README.md +32 -0
  55. package/src/knowledge/__tests__/queries.test.ts +504 -0
  56. package/src/knowledge/format.ts +99 -0
  57. package/src/knowledge/index.ts +5 -0
  58. package/src/knowledge/queries.ts +256 -0
  59. package/src/organization-model/__tests__/defaults.test.ts +172 -172
  60. package/src/organization-model/__tests__/foundation.test.ts +7 -7
  61. package/src/organization-model/__tests__/icons.test.ts +27 -0
  62. package/src/organization-model/__tests__/knowledge.test.ts +214 -0
  63. package/src/organization-model/contracts.ts +17 -15
  64. package/src/organization-model/defaults.ts +74 -19
  65. package/src/organization-model/domains/knowledge.ts +53 -0
  66. package/src/organization-model/domains/navigation.ts +416 -399
  67. package/src/organization-model/domains/prospecting.ts +204 -1
  68. package/src/organization-model/domains/sales.test.ts +29 -0
  69. package/src/organization-model/domains/sales.ts +102 -0
  70. package/src/organization-model/domains/shared.ts +6 -5
  71. package/src/organization-model/foundation.ts +10 -6
  72. package/src/organization-model/graph/build.ts +209 -182
  73. package/src/organization-model/graph/schema.ts +37 -34
  74. package/src/organization-model/graph/types.ts +47 -31
  75. package/src/organization-model/icons.ts +81 -0
  76. package/src/organization-model/index.ts +8 -3
  77. package/src/organization-model/organization-model.mdx +1 -1
  78. package/src/organization-model/published.ts +103 -86
  79. package/src/organization-model/schema.ts +90 -85
  80. package/src/organization-model/types.ts +42 -35
  81. package/src/platform/constants/versions.ts +1 -1
  82. package/src/platform/index.ts +23 -27
  83. package/src/platform/registry/index.ts +0 -4
  84. package/src/platform/registry/resource-registry.ts +0 -77
  85. package/src/platform/registry/serialized-types.ts +148 -219
  86. package/src/platform/registry/stats-types.ts +60 -60
  87. package/src/reference/_generated/contracts.md +829 -595
  88. package/src/supabase/database.types.ts +2978 -2958
@@ -1,1473 +1,1474 @@
1
- import { z } from 'zod'
2
- import { createIntegrationTool } from '../../../tool'
3
- import type { Tool } from '../../../../types'
4
-
5
- /**
6
- * Create Instantly send reply tool
7
- *
8
- * Sends a reply to an email thread maintaining proper threading.
9
- *
10
- * @param credentialName - Name of credential in credentials table
11
- * @returns Tool that sends email replies via Instantly
12
- *
13
- * @example
14
- * const sendReply = createInstantlySendReplyTool('instantly-elevasis')
15
- */
16
- export function createInstantlySendReplyTool(credentialName: string): Tool {
17
- return createIntegrationTool({
18
- name: 'instantly_send_reply',
19
- description: `Send a reply to an email thread via Instantly.ai
20
-
21
- USE CASES:
22
- - Send a personalized reply to a prospect who responded to your outreach campaign
23
- - Maintain email threading for organized conversations
24
- - Schedule replies for future delivery
25
-
26
- REQUIRED PARAMETERS:
27
- - eaccount: Email account that will send the reply (must be connected to workspace)
28
- - reply_to_uuid: UUID of the email you are replying to (from webhook payload)
29
- - subject: Subject line of the reply (typically "Re: Original Subject")
30
- - body: Email body (must provide either text or html)
31
-
32
- OPTIONAL PARAMETERS:
33
- - timestamp: ISO timestamp to schedule reply for future
34
-
35
- EXAMPLE:
36
- {
37
- "eaccount": "alex@elevasis.com",
38
- "reply_to_uuid": "123e4567-e89b-12d3-a456-426614174000",
39
- "subject": "Re: Automate your client acquisition",
40
- "body": {
41
- "text": "Thanks for your interest! Here's my calendar: https://calendly.com/..."
42
- }
43
- }
44
-
45
- OUTPUT:
46
- {
47
- "success": true,
48
- "email_id": "email-abc123",
49
- "thread_id": "thread-xyz789",
50
- "sent_at": "2025-12-22T14:30:00Z"
51
- }`,
52
-
53
- inputSchema: z.object({
54
- eaccount: z.string().describe('Email account that will send the reply (must be connected to workspace)'),
55
- reply_to_uuid: z.string().describe('UUID of the email you are replying to (from webhook payload)'),
56
- subject: z.string().describe('Subject line of the reply (typically "Re: Original Subject")'),
57
- body: z
58
- .object({
59
- text: z.string().optional().describe('Plain text email body'),
60
- html: z.string().optional().describe('HTML email body')
61
- })
62
- .refine((data) => data.text || data.html, {
63
- message: 'Must provide either text or html body'
64
- }),
65
- timestamp: z.string().optional().describe('ISO timestamp to schedule reply for future (optional)')
66
- }),
67
-
68
- outputSchema: z.object({
69
- success: z.boolean().describe('Whether reply was sent successfully'),
70
- email_id: z.string().describe('ID of the sent email'),
71
- thread_id: z.string().describe('Thread ID for conversation tracking'),
72
- sent_at: z.string().describe('Timestamp when email was sent')
73
- }),
74
-
75
- integration: 'instantly' as const,
76
- method: 'sendReply' as const,
77
- credentialName
78
- })
79
- }
80
-
81
- /**
82
- * Create Instantly remove from subsequence tool
83
- *
84
- * Removes a lead from a campaign sequence to stop further outreach.
85
- *
86
- * @param credentialName - Name of credential in credentials table
87
- * @returns Tool that removes leads from campaigns
88
- *
89
- * @example
90
- * const removeFromCampaign = createInstantlyRemoveFromSubsequenceTool('instantly-elevasis')
91
- */
92
- export function createInstantlyRemoveFromSubsequenceTool(credentialName: string): Tool {
93
- return createIntegrationTool({
94
- name: 'instantly_remove_from_subsequence',
95
- description: `Remove a lead from an Instantly campaign subsequence
96
-
97
- USE CASES:
98
- - Stop a lead from receiving further emails in a campaign
99
- - Use after positive reply, hard-no, or disqualification
100
- - Prevent over-emailing engaged prospects
101
-
102
- REQUIRED PARAMETERS:
103
- - lead_email: Email address of the lead to remove
104
- - campaign_id: Campaign ID to remove lead from
105
-
106
- EXAMPLE:
107
- {
108
- "lead_email": "prospect@agency.com",
109
- "campaign_id": "campaign-123"
110
- }
111
-
112
- OUTPUT:
113
- {
114
- "success": true,
115
- "lead_email": "prospect@agency.com",
116
- "campaign_id": "campaign-123"
117
- }`,
118
-
119
- inputSchema: z.object({
120
- lead_email: z.string().email().describe('Email address of the lead to remove'),
121
- campaign_id: z.string().describe('Campaign ID to remove lead from')
122
- }),
123
-
124
- outputSchema: z.object({
125
- success: z.boolean().describe('Whether removal was successful'),
126
- lead_email: z.string().describe('Email of the removed lead'),
127
- campaign_id: z.string().describe('Campaign ID lead was removed from')
128
- }),
129
-
130
- integration: 'instantly' as const,
131
- method: 'removeFromSubsequence' as const,
132
- credentialName
133
- })
134
- }
135
-
136
- /**
137
- * Create Instantly get emails tool
138
- *
139
- * Fetches email data from Instantly workspace with optional filtering and pagination.
140
- *
141
- * @param credentialName - Name of credential in credentials table
142
- * @returns Tool that retrieves email data
143
- */
144
- export function createInstantlyGetEmailsTool(credentialName: string): Tool {
145
- return createIntegrationTool({
146
- name: 'instantly_get_emails',
147
- description: `Get emails from Instantly workspace
148
-
149
- USE CASES:
150
- - Fetch a specific email by ID for reply processing
151
- - Retrieve full email threads by thread ID
152
- - Filter emails by campaign, lead, or type
153
- - List recent emails with cursor pagination
154
-
155
- OPTIONAL PARAMETERS:
156
- - email_id: Specific email ID to fetch (returns single email)
157
- - search: Email address OR "thread:{thread_id}" for thread retrieval
158
- - campaign_id: Filter by campaign UUID
159
- - lead: Filter by lead email address
160
- - email_type: Filter by type (received, sent, manual)
161
- - eaccount: Filter by sending account (comma-separated)
162
- - sort_order: Sort by creation date (asc, desc; default: desc)
163
- - preview_only: Return preview only (lighter response)
164
- - is_unread: Filter by unread status
165
- - limit: Items per page (1-100)
166
- - starting_after: Cursor for pagination (from next_starting_after in previous response)
167
-
168
- EXAMPLE (Single Email):
169
- {
170
- "email_id": "email-abc123"
171
- }
172
-
173
- EXAMPLE (Thread Retrieval):
174
- {
175
- "search": "thread:019a449c-f57a-73b2-91b5-75537ca85008",
176
- "sort_order": "asc",
177
- "limit": 50
178
- }
179
-
180
- EXAMPLE (Lead Emails in Campaign):
181
- {
182
- "lead": "jondoe@example.com",
183
- "campaign_id": "019a449c-f57a-73b2-91b5-754f1fca9f44",
184
- "sort_order": "asc"
185
- }
186
-
187
- OUTPUT:
188
- {
189
- "emails": [...email data...],
190
- "total_count": 3,
191
- "next_starting_after": "cursor-token-or-undefined"
192
- }`,
193
-
194
- inputSchema: z.object({
195
- email_id: z.string().optional().describe('Specific email ID to fetch'),
196
- search: z
197
- .string()
198
- .optional()
199
- .describe('Search query: email address or "thread:{thread_id}" for thread retrieval'),
200
- campaign_id: z.string().optional().describe('Filter by campaign UUID'),
201
- lead: z.string().optional().describe('Filter by lead email address'),
202
- email_type: z.enum(['received', 'sent', 'manual']).optional().describe('Filter by email type'),
203
- eaccount: z.string().optional().describe('Filter by sending account (comma-separated)'),
204
- sort_order: z.enum(['asc', 'desc']).optional().describe('Sort order by creation date (default: desc)'),
205
- preview_only: z.boolean().optional().describe('Return preview only (lighter response)'),
206
- is_unread: z.boolean().optional().describe('Filter by unread status'),
207
- limit: z.number().min(1).max(100).optional().describe('Items per page (1-100)'),
208
- starting_after: z.string().optional().describe('Cursor for pagination (from next_starting_after)')
209
- }),
210
-
211
- outputSchema: z.object({
212
- emails: z.array(z.unknown()).describe('Array of email data'),
213
- total_count: z.number().describe('Count of emails returned'),
214
- next_starting_after: z.string().optional().describe('Cursor for next page (undefined = no more)')
215
- }),
216
-
217
- integration: 'instantly' as const,
218
- method: 'getEmails' as const,
219
- credentialName
220
- })
221
- }
222
-
223
- /**
224
- * Create Instantly update interest status tool
225
- *
226
- * Updates the interest status of a lead, which can stop email sequences,
227
- * mark leads as interested, or reset to default status.
228
- *
229
- * @param credentialName - Name of credential in credentials table
230
- * @returns Tool that updates lead interest status
231
- *
232
- * @example
233
- * const updateStatus = createInstantlyUpdateInterestStatusTool('instantly-elevasis')
234
- */
235
- export function createInstantlyUpdateInterestStatusTool(credentialName: string): Tool {
236
- return createIntegrationTool({
237
- name: 'instantly_update_interest_status',
238
- description: `Update interest status of a lead in Instantly
239
-
240
- USE CASES:
241
- - Stop email sequence for leads who reply with "unsubscribe" or "not interested" (set interest_value: -1)
242
- - Mark engaged leads as interested for prioritization (set interest_value: 1)
243
- - Reset lead status back to default (set interest_value: 0)
244
- - Comply with opt-out requests to protect sender reputation
245
-
246
- INTEREST VALUES:
247
- - 1: Interested - Marks lead as interested
248
- - 0: Lead (default) - Resets to default status
249
- - -1: Not Interested - STOPS sequence (no more emails sent)
250
-
251
- REQUIRED PARAMETERS:
252
- - lead_email: Email address of the lead
253
- - interest_value: Status value (1, 0, or -1)
254
-
255
- OPTIONAL PARAMETERS:
256
- - campaign_id: Limit status update to specific campaign (UUID)
257
- - disable_auto_interest: Disable AI auto-tagging for this lead
258
-
259
- EXAMPLE (Stop Sequence):
260
- {
261
- "lead_email": "prospect@agency.com",
262
- "interest_value": -1
263
- }
264
-
265
- EXAMPLE (Mark Interested in Specific Campaign):
266
- {
267
- "lead_email": "prospect@agency.com",
268
- "interest_value": 1,
269
- "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43"
270
- }
271
-
272
- OUTPUT:
273
- {
274
- "success": true,
275
- "lead_email": "prospect@agency.com"
276
- }`,
277
-
278
- inputSchema: z.object({
279
- lead_email: z.string().email().describe('Email address of the lead'),
280
- interest_value: z
281
- .number()
282
- .int()
283
- .min(-1)
284
- .max(1)
285
- .describe('Interest status: 1 (interested), 0 (default), -1 (not interested - stops sequence)'),
286
- campaign_id: z.string().optional().describe('Campaign ID to limit status update to (UUID)'),
287
- disable_auto_interest: z.boolean().optional().describe('Disable AI auto-tagging for this lead')
288
- }),
289
-
290
- outputSchema: z.object({
291
- success: z.boolean().describe('Whether status update was successful'),
292
- lead_email: z.string().describe('Email of the updated lead')
293
- }),
294
-
295
- integration: 'instantly' as const,
296
- method: 'updateInterestStatus' as const,
297
- credentialName
298
- })
299
- }
300
-
301
- /**
302
- * Create Instantly add to campaign tool
303
- *
304
- * Adds leads to an Instantly campaign to begin the email sequence.
305
- *
306
- * @param credentialName - Name of credential in credentials table
307
- * @returns Tool that adds leads to a campaign
308
- *
309
- * @example
310
- * const addToCampaign = createInstantlyAddToCampaignTool('instantly-elevasis')
311
- */
312
- export function createInstantlyAddToCampaignTool(credentialName: string): Tool {
313
- return createIntegrationTool({
314
- name: 'instantly_add_to_campaign',
315
- description: `Add leads to an Instantly campaign
316
-
317
- USE CASES:
318
- - Add interested leads to a follow-up campaign after initial outreach
319
- - Start a drip sequence for leads who requested more information
320
- - Enroll new leads into automated nurture campaigns
321
-
322
- REQUIRED PARAMETERS:
323
- - campaign_id: UUID of the Instantly campaign to add leads to
324
- - leads: Array of lead objects (at least one required)
325
-
326
- LEAD OBJECT FIELDS:
327
- - email: (required) Email address of the lead
328
- - first_name: (optional) First name for personalization
329
- - last_name: (optional) Last name for personalization
330
- - company_name: (optional) Company name for personalization
331
-
332
- IMPORTANT: When leads are added, they begin receiving the campaign sequence immediately.
333
- "Day 0" starts at the moment of addition.
334
-
335
- EXAMPLE:
336
- {
337
- "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43",
338
- "leads": [
339
- {
340
- "email": "prospect@agency.com",
341
- "first_name": "John",
342
- "last_name": "Smith",
343
- "company_name": "Acme Agency"
344
- }
345
- ]
346
- }
347
-
348
- OUTPUT:
349
- {
350
- "success": true,
351
- "added_count": 1,
352
- "errors": null
353
- }`,
354
-
355
- inputSchema: z.object({
356
- campaign_id: z.string().describe('UUID of the Instantly campaign to add leads to'),
357
- leads: z
358
- .array(
359
- z.object({
360
- email: z.string().email().describe('Email address of the lead'),
361
- first_name: z.string().optional().describe('First name for personalization'),
362
- last_name: z.string().optional().describe('Last name for personalization'),
363
- company_name: z.string().optional().describe('Company name for personalization')
364
- })
365
- )
366
- .min(1)
367
- .describe('Array of leads to add (at least one required)')
368
- }),
369
-
370
- outputSchema: z.object({
371
- success: z.boolean().describe('Whether leads were added successfully'),
372
- added_count: z.number().describe('Number of leads successfully added'),
373
- errors: z.array(z.string()).optional().describe('Array of error messages if any failures occurred')
374
- }),
375
-
376
- integration: 'instantly' as const,
377
- method: 'addToCampaign' as const,
378
- credentialName
379
- })
380
- }
381
-
382
- /**
383
- * Create Instantly list campaigns tool
384
- *
385
- * Lists all campaigns in the workspace with optional filtering and pagination.
386
- *
387
- * @param credentialName - Name of credential in credentials table
388
- * @returns Tool that lists Instantly campaigns
389
- *
390
- * @example
391
- * const listCampaigns = createInstantlyListCampaignsTool('instantly-elevasis')
392
- */
393
- export function createInstantlyListCampaignsTool(credentialName: string): Tool {
394
- return createIntegrationTool({
395
- name: 'instantly_list_campaigns',
396
- description: `List all campaigns in the Instantly workspace
397
-
398
- USE CASES:
399
- - Discover available campaigns before adding leads or fetching analytics
400
- - Filter campaigns by status to find active or paused campaigns
401
- - Search campaigns by name to locate a specific campaign ID
402
- - Paginate through large campaign lists using cursor-based pagination
403
-
404
- OPTIONAL PARAMETERS:
405
- - limit: Number of campaigns to return per page (default: 10)
406
- - starting_after: Cursor from previous response for pagination
407
- - status: Filter by campaign status (e.g. "active", "paused", "completed")
408
- - search: Search term to filter campaigns by name
409
-
410
- EXAMPLE (List Active Campaigns):
411
- {
412
- "status": "active",
413
- "limit": 20
414
- }
415
-
416
- EXAMPLE (Search by Name):
417
- {
418
- "search": "cold outreach Q1"
419
- }
420
-
421
- OUTPUT:
422
- {
423
- "campaigns": [
424
- {
425
- "id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43",
426
- "name": "Cold Outreach Q1",
427
- "status": "active",
428
- "created_at": "2025-01-15T09:00:00Z"
429
- }
430
- ],
431
- "next_starting_after": "cursor-token"
432
- }`,
433
-
434
- inputSchema: z.object({
435
- limit: z.number().min(1).max(100).optional().describe('Number of campaigns per page (default: 10)'),
436
- starting_after: z
437
- .string()
438
- .optional()
439
- .describe('Cursor for pagination (from next_starting_after in previous response)'),
440
- status: z.string().optional().describe('Filter by campaign status (e.g. "active", "paused", "completed")'),
441
- search: z.string().optional().describe('Search term to filter campaigns by name')
442
- }),
443
-
444
- outputSchema: z.object({
445
- campaigns: z
446
- .array(
447
- z.object({
448
- id: z.string().describe('Campaign UUID'),
449
- name: z.string().describe('Campaign name'),
450
- status: z.string().describe('Campaign status'),
451
- created_at: z.string().describe('Campaign creation timestamp')
452
- })
453
- )
454
- .describe('Array of campaigns'),
455
- next_starting_after: z.string().optional().describe('Cursor for next page (undefined = no more pages)')
456
- }),
457
-
458
- integration: 'instantly' as const,
459
- method: 'listCampaigns' as const,
460
- credentialName
461
- })
462
- }
463
-
464
- /**
465
- * Create Instantly get campaign tool
466
- *
467
- * Fetches full details for a single campaign including sequences and configuration.
468
- *
469
- * @param credentialName - Name of credential in credentials table
470
- * @returns Tool that retrieves a single Instantly campaign
471
- *
472
- * @example
473
- * const getCampaign = createInstantlyGetCampaignTool('instantly-elevasis')
474
- */
475
- export function createInstantlyGetCampaignTool(credentialName: string): Tool {
476
- return createIntegrationTool({
477
- name: 'instantly_get_campaign',
478
- description: `Get full details of a specific Instantly campaign
479
-
480
- USE CASES:
481
- - Inspect campaign sequences and step configurations before modifying
482
- - Verify campaign status before activating or pausing
483
- - Retrieve sending settings such as daily limits and reply handling
484
- - Confirm the campaign name and metadata before referencing in reports
485
-
486
- REQUIRED PARAMETERS:
487
- - campaign_id: UUID of the campaign to retrieve
488
-
489
- EXAMPLE:
490
- {
491
- "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43"
492
- }
493
-
494
- OUTPUT:
495
- {
496
- "id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43",
497
- "name": "Cold Outreach Q1",
498
- "status": "active",
499
- "sequences": [...],
500
- "created_at": "2025-01-15T09:00:00Z"
501
- }`,
502
-
503
- inputSchema: z.object({
504
- campaign_id: z.string().describe('UUID of the campaign to retrieve')
505
- }),
506
-
507
- outputSchema: z.object({
508
- id: z.string().describe('Campaign UUID'),
509
- name: z.string().describe('Campaign name'),
510
- status: z.string().describe('Campaign status'),
511
- sequences: z.array(z.unknown()).describe('Campaign sequence steps'),
512
- created_at: z.string().describe('Campaign creation timestamp')
513
- }),
514
-
515
- integration: 'instantly' as const,
516
- method: 'getCampaign' as const,
517
- credentialName
518
- })
519
- }
520
-
521
- /**
522
- * Create Instantly update campaign tool
523
- *
524
- * Partially updates a campaign's fields such as name, sequences, or sending limits.
525
- *
526
- * @param credentialName - Name of credential in credentials table
527
- * @returns Tool that updates an Instantly campaign
528
- *
529
- * @example
530
- * const updateCampaign = createInstantlyUpdateCampaignTool('instantly-elevasis')
531
- */
532
- export function createInstantlyUpdateCampaignTool(credentialName: string): Tool {
533
- return createIntegrationTool({
534
- name: 'instantly_update_campaign',
535
- description: `Update fields on an existing Instantly campaign
536
-
537
- USE CASES:
538
- - Rename a campaign to reflect updated targeting or messaging
539
- - Adjust the daily sending limit to control volume
540
- - Replace or edit campaign sequences and email steps
541
- - Toggle stop-on-reply to pause the sequence when a lead responds
542
-
543
- REQUIRED PARAMETERS:
544
- - campaign_id: UUID of the campaign to update
545
-
546
- OPTIONAL PARAMETERS (at least one must be provided):
547
- - name: New campaign name
548
- - sequences: Full replacement of campaign sequences (array of step objects)
549
- - daily_limit: Maximum emails to send per day
550
- - stop_on_reply: Whether to stop sending when a lead replies (true/false)
551
-
552
- EXAMPLE (Rename and Adjust Limit):
553
- {
554
- "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43",
555
- "name": "Cold Outreach Q2",
556
- "daily_limit": 50
557
- }
558
-
559
- OUTPUT:
560
- {
561
- "success": true,
562
- "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43"
563
- }`,
564
-
565
- inputSchema: z.object({
566
- campaign_id: z.string().describe('UUID of the campaign to update'),
567
- name: z.string().optional().describe('New campaign name'),
568
- sequences: z.array(z.unknown()).optional().describe('Replacement campaign sequences (array of step objects)'),
569
- daily_limit: z.number().int().min(1).optional().describe('Maximum emails to send per day'),
570
- stop_on_reply: z.boolean().optional().describe('Whether to stop the sequence when a lead replies')
571
- }),
572
-
573
- outputSchema: z.object({
574
- success: z.boolean().describe('Whether the update was successful'),
575
- campaign_id: z.string().describe('UUID of the updated campaign')
576
- }),
577
-
578
- integration: 'instantly' as const,
579
- method: 'updateCampaign' as const,
580
- credentialName
581
- })
582
- }
583
-
584
- /**
585
- * Create Instantly pause campaign tool
586
- *
587
- * Pauses an active campaign to stop email sends until reactivated.
588
- *
589
- * @param credentialName - Name of credential in credentials table
590
- * @returns Tool that pauses an Instantly campaign
591
- *
592
- * @example
593
- * const pauseCampaign = createInstantlyPauseCampaignTool('instantly-elevasis')
594
- */
595
- export function createInstantlyPauseCampaignTool(credentialName: string): Tool {
596
- return createIntegrationTool({
597
- name: 'instantly_pause_campaign',
598
- description: `Pause an active Instantly campaign
599
-
600
- USE CASES:
601
- - Temporarily halt outreach while reviewing campaign performance
602
- - Pause before making sequence edits to avoid sending stale content
603
- - Stop sends during public holidays or blackout periods
604
- - Hold sends when a domain reputation issue is detected
605
-
606
- REQUIRED PARAMETERS:
607
- - campaign_id: UUID of the campaign to pause
608
-
609
- EXAMPLE:
610
- {
611
- "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43"
612
- }
613
-
614
- OUTPUT:
615
- {
616
- "success": true,
617
- "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43"
618
- }`,
619
-
620
- inputSchema: z.object({
621
- campaign_id: z.string().describe('UUID of the campaign to pause')
622
- }),
623
-
624
- outputSchema: z.object({
625
- success: z.boolean().describe('Whether the campaign was paused successfully'),
626
- campaign_id: z.string().describe('UUID of the paused campaign')
627
- }),
628
-
629
- integration: 'instantly' as const,
630
- method: 'pauseCampaign' as const,
631
- credentialName
632
- })
633
- }
634
-
635
- /**
636
- * Create Instantly delete campaign tool
637
- *
638
- * Permanently deletes a campaign and all associated data.
639
- *
640
- * @param credentialName - Name of credential in credentials table
641
- * @returns Tool that deletes an Instantly campaign
642
- *
643
- * @example
644
- * const deleteCampaign = createInstantlyDeleteCampaignTool('instantly-elevasis')
645
- */
646
- export function createInstantlyDeleteCampaignTool(credentialName: string): Tool {
647
- return createIntegrationTool({
648
- name: 'instantly_delete_campaign',
649
- description: `Permanently delete an Instantly campaign
650
-
651
- USE CASES:
652
- - Clean up test or draft campaigns after diagnostic runs
653
- - Remove campaigns that are no longer needed
654
- - Delete accidentally created campaigns
655
-
656
- REQUIRED PARAMETERS:
657
- - campaign_id: UUID of the campaign to delete
658
-
659
- EXAMPLE:
660
- {
661
- "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43"
662
- }
663
-
664
- OUTPUT:
665
- {
666
- "success": true,
667
- "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43"
668
- }`,
669
-
670
- inputSchema: z.object({
671
- campaign_id: z.string().describe('UUID of the campaign to delete')
672
- }),
673
-
674
- outputSchema: z.object({
675
- success: z.boolean().describe('Whether the campaign was deleted successfully'),
676
- campaign_id: z.string().describe('UUID of the deleted campaign')
677
- }),
678
-
679
- integration: 'instantly' as const,
680
- method: 'deleteCampaign' as const,
681
- credentialName
682
- })
683
- }
684
-
685
- /**
686
- * Create Instantly activate campaign tool
687
- *
688
- * Activates or resumes a paused or draft campaign to begin sending.
689
- *
690
- * @param credentialName - Name of credential in credentials table
691
- * @returns Tool that activates an Instantly campaign
692
- *
693
- * @example
694
- * const activateCampaign = createInstantlyActivateCampaignTool('instantly-elevasis')
695
- */
696
- export function createInstantlyActivateCampaignTool(credentialName: string): Tool {
697
- return createIntegrationTool({
698
- name: 'instantly_activate_campaign',
699
- description: `Activate or resume an Instantly campaign
700
-
701
- USE CASES:
702
- - Launch a newly created campaign to begin sending to enrolled leads
703
- - Resume a paused campaign after completing edits or reviews
704
- - Re-activate a campaign after resolving domain reputation issues
705
- - Start scheduled outreach at the beginning of a new period
706
-
707
- REQUIRED PARAMETERS:
708
- - campaign_id: UUID of the campaign to activate
709
-
710
- EXAMPLE:
711
- {
712
- "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43"
713
- }
714
-
715
- OUTPUT:
716
- {
717
- "success": true,
718
- "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43"
719
- }`,
720
-
721
- inputSchema: z.object({
722
- campaign_id: z.string().describe('UUID of the campaign to activate')
723
- }),
724
-
725
- outputSchema: z.object({
726
- success: z.boolean().describe('Whether the campaign was activated successfully'),
727
- campaign_id: z.string().describe('UUID of the activated campaign')
728
- }),
729
-
730
- integration: 'instantly' as const,
731
- method: 'activateCampaign' as const,
732
- credentialName
733
- })
734
- }
735
-
736
- /**
737
- * Create Instantly get campaign analytics tool
738
- *
739
- * Retrieves aggregate engagement statistics for a campaign.
740
- *
741
- * @param credentialName - Name of credential in credentials table
742
- * @returns Tool that fetches campaign-level analytics
743
- *
744
- * @example
745
- * const getAnalytics = createInstantlyGetCampaignAnalyticsTool('instantly-elevasis')
746
- */
747
- export function createInstantlyGetCampaignAnalyticsTool(credentialName: string): Tool {
748
- return createIntegrationTool({
749
- name: 'instantly_get_campaign_analytics',
750
- description: `Get aggregate analytics for an Instantly campaign
751
-
752
- USE CASES:
753
- - Review overall campaign performance including open rate, reply rate, and bounce rate
754
- - Compare send volume against replies to evaluate outreach effectiveness
755
- - Monitor unsubscribes to maintain sender reputation
756
- - Scope analytics to a date range for period-specific reporting
757
-
758
- REQUIRED PARAMETERS:
759
- - campaign_id: UUID of the campaign
760
-
761
- OPTIONAL PARAMETERS:
762
- - start_date: ISO date string to filter analytics from (e.g. "2025-01-01")
763
- - end_date: ISO date string to filter analytics to (e.g. "2025-03-31")
764
-
765
- EXAMPLE:
766
- {
767
- "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43",
768
- "start_date": "2025-01-01",
769
- "end_date": "2025-03-31"
770
- }
771
-
772
- OUTPUT:
773
- {
774
- "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43",
775
- "sent": 450,
776
- "opened": 180,
777
- "unique_opened": 160,
778
- "replied": 35,
779
- "unique_replied": 30,
780
- "bounced": 5,
781
- "unsubscribed": 2
782
- }`,
783
-
784
- inputSchema: z.object({
785
- campaign_id: z.string().describe('UUID of the campaign to fetch analytics for'),
786
- start_date: z.string().optional().describe('Start date for analytics range (ISO date string, e.g. "2025-01-01")'),
787
- end_date: z.string().optional().describe('End date for analytics range (ISO date string, e.g. "2025-03-31")')
788
- }),
789
-
790
- outputSchema: z.object({
791
- campaign_id: z.string().describe('Campaign UUID'),
792
- sent: z.number().describe('Total emails sent'),
793
- opened: z.number().describe('Total open events'),
794
- unique_opened: z.number().describe('Unique leads who opened'),
795
- replied: z.number().describe('Total reply events'),
796
- unique_replied: z.number().describe('Unique leads who replied'),
797
- bounced: z.number().describe('Total bounced emails'),
798
- unsubscribed: z.number().describe('Total unsubscribes')
799
- }),
800
-
801
- integration: 'instantly' as const,
802
- method: 'getCampaignAnalytics' as const,
803
- credentialName
804
- })
805
- }
806
-
807
- /**
808
- * Create Instantly get step analytics tool
809
- *
810
- * Retrieves per-step and per-variant analytics for a campaign sequence.
811
- *
812
- * @param credentialName - Name of credential in credentials table
813
- * @returns Tool that fetches step-level analytics
814
- *
815
- * @example
816
- * const getStepAnalytics = createInstantlyGetStepAnalyticsTool('instantly-elevasis')
817
- */
818
- export function createInstantlyGetStepAnalyticsTool(credentialName: string): Tool {
819
- return createIntegrationTool({
820
- name: 'instantly_get_step_analytics',
821
- description: `Get per-step and per-variant analytics for an Instantly campaign
822
-
823
- USE CASES:
824
- - Identify which sequence step has the highest reply rate
825
- - Compare A/B subject line variants to determine the winning version
826
- - Diagnose a drop-off point where prospects stop engaging
827
- - Optimize multi-step sequences based on step-level performance data
828
-
829
- REQUIRED PARAMETERS:
830
- - campaign_id: UUID of the campaign
831
-
832
- EXAMPLE:
833
- {
834
- "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43"
835
- }
836
-
837
- OUTPUT:
838
- {
839
- "steps": [
840
- {
841
- "step": "1",
842
- "variant": "A",
843
- "sent": 200,
844
- "opened": 90,
845
- "unique_opened": 80,
846
- "replies": 20,
847
- "unique_replies": 18
848
- },
849
- {
850
- "step": "2",
851
- "variant": "A",
852
- "sent": 160,
853
- "opened": 55,
854
- "unique_opened": 50,
855
- "replies": 10,
856
- "unique_replies": 9
857
- }
858
- ]
859
- }`,
860
-
861
- inputSchema: z.object({
862
- campaign_id: z.string().describe('UUID of the campaign to fetch step analytics for')
863
- }),
864
-
865
- outputSchema: z.object({
866
- steps: z
867
- .array(
868
- z.object({
869
- step: z.string().describe('Step number in the sequence'),
870
- variant: z.string().describe('Variant label (e.g. "A", "B")'),
871
- sent: z.number().describe('Emails sent for this step/variant'),
872
- opened: z.number().describe('Total open events'),
873
- unique_opened: z.number().describe('Unique leads who opened'),
874
- replies: z.number().describe('Total reply events'),
875
- unique_replies: z.number().describe('Unique leads who replied')
876
- })
877
- )
878
- .describe('Per-step, per-variant analytics entries')
879
- }),
880
-
881
- integration: 'instantly' as const,
882
- method: 'getStepAnalytics' as const,
883
- credentialName
884
- })
885
- }
886
-
887
- /**
888
- * Create Instantly bulk add leads tool
889
- *
890
- * Adds up to 1000 leads to a campaign in a single request.
891
- *
892
- * @param credentialName - Name of credential in credentials table
893
- * @returns Tool that bulk-uploads leads to an Instantly campaign
894
- *
895
- * @example
896
- * const bulkAddLeads = createInstantlyBulkAddLeadsTool('instantly-elevasis')
897
- */
898
- export function createInstantlyBulkAddLeadsTool(credentialName: string): Tool {
899
- return createIntegrationTool({
900
- name: 'instantly_bulk_add_leads',
901
- description: `Bulk upload up to 1000 leads to an Instantly campaign
902
-
903
- USE CASES:
904
- - Import a large list of qualified prospects into a campaign at once
905
- - Enroll leads exported from a CRM or enrichment tool into outreach
906
- - Upload leads with custom variables for deep personalization
907
- - Add leads with website and company data for context-aware sequences
908
-
909
- REQUIRED PARAMETERS:
910
- - campaign_id: UUID of the campaign to add leads to
911
- - leads: Array of lead objects (1–1000 leads)
912
-
913
- LEAD OBJECT FIELDS:
914
- - email: (required) Email address of the lead
915
- - first_name: (optional) First name for personalization
916
- - last_name: (optional) Last name for personalization
917
- - company_name: (optional) Company name for personalization
918
- - website: (optional) Company website URL
919
- - custom_variables: (optional) Key-value map of custom merge fields
920
-
921
- EXAMPLE:
922
- {
923
- "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43",
924
- "leads": [
925
- {
926
- "email": "ceo@agency.com",
927
- "first_name": "Sarah",
928
- "last_name": "Chen",
929
- "company_name": "Growth Agency",
930
- "website": "https://growthagency.com",
931
- "custom_variables": { "pain_point": "manual lead gen" }
932
- }
933
- ]
934
- }
935
-
936
- OUTPUT:
937
- {
938
- "success": true,
939
- "added_count": 1,
940
- "failed_count": 0
941
- }`,
942
-
943
- inputSchema: z.object({
944
- campaign_id: z.string().describe('UUID of the campaign to add leads to'),
945
- leads: z
946
- .array(
947
- z.object({
948
- email: z.string().email().describe('Email address of the lead'),
949
- first_name: z.string().optional().describe('First name for personalization'),
950
- last_name: z.string().optional().describe('Last name for personalization'),
951
- company_name: z.string().optional().describe('Company name for personalization'),
952
- website: z.string().optional().describe('Company website URL'),
953
- custom_variables: z
954
- .record(z.string(), z.string())
955
- .optional()
956
- .describe('Key-value map of custom merge field values')
957
- })
958
- )
959
- .min(1)
960
- .max(1000)
961
- .describe('Array of leads to upload (1–1000)')
962
- }),
963
-
964
- outputSchema: z.object({
965
- success: z.boolean().describe('True if all leads were added without errors'),
966
- added_count: z.number().describe('Number of leads successfully added'),
967
- failed_count: z.number().describe('Number of leads that failed to add'),
968
- errors: z
969
- .array(
970
- z.object({
971
- email: z.string().describe('Email of the lead that failed'),
972
- error: z.string().describe('Error message for this lead')
973
- })
974
- )
975
- .optional()
976
- .describe('Per-lead error details when failures occurred')
977
- }),
978
-
979
- integration: 'instantly' as const,
980
- method: 'bulkAddLeads' as const,
981
- credentialName
982
- })
983
- }
984
-
985
- /**
986
- * Create Instantly get account health tool
987
- *
988
- * Lists email sending accounts with warmup status and health scores.
989
- *
990
- * @param credentialName - Name of credential in credentials table
991
- * @returns Tool that retrieves account health data
992
- *
993
- * @example
994
- * const getAccountHealth = createInstantlyGetAccountHealthTool('instantly-elevasis')
995
- */
996
- export function createInstantlyGetAccountHealthTool(credentialName: string): Tool {
997
- return createIntegrationTool({
998
- name: 'instantly_get_account_health',
999
- description: `List email accounts with health status and warmup information
1000
-
1001
- USE CASES:
1002
- - Audit all connected sending accounts before launching a campaign
1003
- - Identify accounts with low health scores that may cause deliverability issues
1004
- - Verify warmup status to ensure accounts are ready for full sending volume
1005
- - Monitor account portfolio health as part of regular sender hygiene checks
1006
-
1007
- OPTIONAL PARAMETERS:
1008
- - limit: Number of accounts to return per page
1009
- - starting_after: Cursor from previous response for pagination
1010
-
1011
- EXAMPLE:
1012
- {
1013
- "limit": 20
1014
- }
1015
-
1016
- OUTPUT:
1017
- {
1018
- "accounts": [
1019
- {
1020
- "id": "acct-abc123",
1021
- "email": "alex@elevasis.com",
1022
- "status": "active",
1023
- "warmup_enabled": true,
1024
- "health_score": 92
1025
- }
1026
- ],
1027
- "next_starting_after": "cursor-token"
1028
- }`,
1029
-
1030
- inputSchema: z.object({
1031
- limit: z.number().min(1).max(100).optional().describe('Number of accounts per page'),
1032
- starting_after: z
1033
- .string()
1034
- .optional()
1035
- .describe('Cursor for pagination (from next_starting_after in previous response)')
1036
- }),
1037
-
1038
- outputSchema: z.object({
1039
- accounts: z
1040
- .array(
1041
- z.object({
1042
- id: z.string().describe('Account ID'),
1043
- email: z.string().describe('Sending email address'),
1044
- status: z.string().describe('Account status'),
1045
- warmup_enabled: z.boolean().describe('Whether warmup is active for this account'),
1046
- health_score: z.number().optional().describe('Health score (0–100)')
1047
- })
1048
- )
1049
- .describe('Array of sending accounts with health data'),
1050
- next_starting_after: z.string().optional().describe('Cursor for next page (undefined = no more pages)')
1051
- }),
1052
-
1053
- integration: 'instantly' as const,
1054
- method: 'getAccountHealth' as const,
1055
- credentialName
1056
- })
1057
- }
1058
-
1059
- /**
1060
- * Create Instantly create campaign tool
1061
- *
1062
- * Creates a new campaign in Instantly with optional sequences and sending configuration.
1063
- *
1064
- * @param credentialName - Name of credential in credentials table
1065
- * @returns Tool that creates a new Instantly campaign
1066
- *
1067
- * @example
1068
- * const createCampaign = createInstantlyCreateCampaignTool('instantly-elevasis')
1069
- */
1070
- export function createInstantlyCreateCampaignTool(credentialName: string): Tool {
1071
- return createIntegrationTool({
1072
- name: 'instantly_create_campaign',
1073
- description: `Create a new Instantly campaign
1074
-
1075
- USE CASES:
1076
- - Set up a new cold outreach campaign with custom sequences
1077
- - Create a campaign with predefined email steps and A/B variants
1078
- - Configure sending limits and reply handling at creation time
1079
- - Prepare a campaign shell to be populated with leads and activated later
1080
-
1081
- REQUIRED PARAMETERS:
1082
- - name: Display name of the new campaign
1083
-
1084
- OPTIONAL PARAMETERS:
1085
- - sequences: Array of sequence objects with email steps and optional A/B variants
1086
- - email_list: Array of sending account emails to use for the campaign
1087
- - daily_limit: Maximum emails to send per day
1088
- - stop_on_reply: Whether to stop the sequence when a lead replies
1089
- - stop_on_auto_reply: Whether to stop on auto-replies
1090
- - track_opens: Whether to track email opens
1091
- - track_clicks: Whether to track link clicks
1092
- - text_only: Whether to send text-only emails (no HTML)
1093
-
1094
- EXAMPLE (Simple Campaign):
1095
- {
1096
- "name": "Cold Outreach Q2 2025",
1097
- "daily_limit": 50,
1098
- "stop_on_reply": true
1099
- }
1100
-
1101
- EXAMPLE (Campaign with Sequence):
1102
- {
1103
- "name": "Agency Outreach",
1104
- "sequences": [
1105
- {
1106
- "steps": [
1107
- {
1108
- "subject": "Quick question about {{company_name}}",
1109
- "body": "Hi {{first_name}}, I noticed..."
1110
- },
1111
- {
1112
- "subject": "Following up",
1113
- "body": "Hi {{first_name}}, just wanted to circle back..."
1114
- }
1115
- ]
1116
- }
1117
- ],
1118
- "daily_limit": 30,
1119
- "stop_on_reply": true
1120
- }
1121
-
1122
- OUTPUT:
1123
- {
1124
- "success": true,
1125
- "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43",
1126
- "name": "Cold Outreach Q2 2025"
1127
- }`,
1128
-
1129
- inputSchema: z.object({
1130
- name: z.string().describe('Display name of the new campaign'),
1131
- sequences: z
1132
- .array(
1133
- z.object({
1134
- steps: z.array(
1135
- z.object({
1136
- subject: z.string().describe('Email subject line for this step'),
1137
- body: z.string().describe('Email body for this step'),
1138
- variants: z
1139
- .array(
1140
- z.object({
1141
- subject: z.string().describe('A/B variant subject line'),
1142
- body: z.string().describe('A/B variant body')
1143
- })
1144
- )
1145
- .optional()
1146
- .describe('A/B test variants for this step')
1147
- })
1148
- )
1149
- })
1150
- )
1151
- .optional()
1152
- .describe('Campaign sequences with email steps'),
1153
- email_list: z.array(z.string()).optional().describe('Sending account emails to use for the campaign'),
1154
- daily_limit: z.number().int().min(1).optional().describe('Maximum emails to send per day'),
1155
- stop_on_reply: z.boolean().optional().describe('Whether to stop the sequence when a lead replies'),
1156
- stop_on_auto_reply: z.boolean().optional().describe('Whether to stop the sequence on auto-replies'),
1157
- track_opens: z.boolean().optional().describe('Whether to track email opens'),
1158
- track_clicks: z.boolean().optional().describe('Whether to track link clicks'),
1159
- text_only: z.boolean().optional().describe('Whether to send text-only emails (no HTML)')
1160
- }),
1161
-
1162
- outputSchema: z.object({
1163
- success: z.boolean().describe('Whether the campaign was created successfully'),
1164
- campaign_id: z.string().describe('UUID of the newly created campaign'),
1165
- name: z.string().describe('Name of the created campaign')
1166
- }),
1167
-
1168
- integration: 'instantly' as const,
1169
- method: 'createCampaign' as const,
1170
- credentialName
1171
- })
1172
- }
1173
-
1174
- /**
1175
- * Create Instantly create inbox test tool
1176
- *
1177
- * Initiates an inbox placement test to diagnose deliverability for a sending account.
1178
- *
1179
- * @param credentialName - Name of credential in credentials table
1180
- * @returns Tool that creates an Instantly inbox placement test
1181
- *
1182
- * @example
1183
- * const createInboxTest = createInstantlyCreateInboxTestTool('instantly-elevasis')
1184
- */
1185
- export function createInstantlyCreateInboxTestTool(credentialName: string): Tool {
1186
- return createIntegrationTool({
1187
- name: 'instantly_create_inbox_test',
1188
- description: `Create an inbox placement test for an Instantly sending account
1189
-
1190
- USE CASES:
1191
- - Verify deliverability before launching a high-volume campaign
1192
- - Diagnose spam filtering issues on a specific sending account
1193
- - Test whether custom subject lines land in inbox or spam
1194
- - Monitor placement across Gmail, Outlook, and other major providers
1195
-
1196
- REQUIRED PARAMETERS:
1197
- - email_account: The sending account to test (must be connected to workspace)
1198
-
1199
- OPTIONAL PARAMETERS:
1200
- - subject: Custom subject line to use in the test email
1201
- - body: Custom body text to use in the test email
1202
-
1203
- EXAMPLE:
1204
- {
1205
- "email_account": "alex@elevasis.com",
1206
- "subject": "Quick question about your agency",
1207
- "body": "Hi, I wanted to reach out about..."
1208
- }
1209
-
1210
- OUTPUT:
1211
- {
1212
- "success": true,
1213
- "test_id": "test-abc123",
1214
- "status": "pending"
1215
- }`,
1216
-
1217
- inputSchema: z.object({
1218
- email_account: z.string().describe('Sending account email address to test (must be connected to workspace)'),
1219
- subject: z.string().optional().describe('Custom subject line for the test email'),
1220
- body: z.string().optional().describe('Custom body text for the test email')
1221
- }),
1222
-
1223
- outputSchema: z.object({
1224
- success: z.boolean().describe('Whether the test was created successfully'),
1225
- test_id: z.string().describe('Unique ID of the inbox placement test'),
1226
- status: z.string().describe('Initial test status (e.g. "pending")')
1227
- }),
1228
-
1229
- integration: 'instantly' as const,
1230
- method: 'createInboxTest' as const,
1231
- credentialName
1232
- })
1233
- }
1234
-
1235
- /**
1236
- * Create Instantly get daily campaign analytics tool
1237
- *
1238
- * Retrieves day-by-day engagement breakdown for campaigns.
1239
- *
1240
- * @param credentialName - Name of credential in credentials table
1241
- * @returns Tool that fetches daily campaign analytics
1242
- *
1243
- * @example
1244
- * const getDailyAnalytics = createInstantlyGetDailyCampaignAnalyticsTool('instantly-elevasis')
1245
- */
1246
- export function createInstantlyGetDailyCampaignAnalyticsTool(credentialName: string): Tool {
1247
- return createIntegrationTool({
1248
- name: 'instantly_get_daily_campaign_analytics',
1249
- description: `Get day-by-day analytics breakdown for Instantly campaigns
1250
-
1251
- USE CASES:
1252
- - Analyze send and open trends over a date range to identify patterns
1253
- - Spot peak engagement days to optimize future send scheduling
1254
- - Compare daily click and reply volumes across a campaign period
1255
- - Get cross-campaign aggregate metrics when no campaign_id is specified
1256
-
1257
- OPTIONAL PARAMETERS:
1258
- - campaign_id: Filter analytics to a specific campaign UUID (omit for all campaigns)
1259
- - start_date: Start of date range (ISO date, e.g. "2025-01-01")
1260
- - end_date: End of date range (ISO date, e.g. "2025-03-31")
1261
-
1262
- EXAMPLE (Single Campaign):
1263
- {
1264
- "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43",
1265
- "start_date": "2025-01-01",
1266
- "end_date": "2025-01-31"
1267
- }
1268
-
1269
- EXAMPLE (All Campaigns):
1270
- {
1271
- "start_date": "2025-01-01",
1272
- "end_date": "2025-01-31"
1273
- }
1274
-
1275
- OUTPUT:
1276
- {
1277
- "data": [
1278
- {
1279
- "date": "2025-01-15",
1280
- "sent": 120,
1281
- "opened": 48,
1282
- "unique_opened": 42,
1283
- "replied": 8,
1284
- "unique_replied": 7,
1285
- "bounced": 1,
1286
- "clicks": 12,
1287
- "unique_clicks": 10
1288
- }
1289
- ]
1290
- }`,
1291
-
1292
- inputSchema: z.object({
1293
- campaign_id: z.string().optional().describe('Campaign UUID to filter analytics (omit to get all campaigns)'),
1294
- start_date: z.string().optional().describe('Start of date range (ISO date, e.g. "2025-01-01")'),
1295
- end_date: z.string().optional().describe('End of date range (ISO date, e.g. "2025-03-31")')
1296
- }),
1297
-
1298
- outputSchema: z.object({
1299
- data: z
1300
- .array(
1301
- z.object({
1302
- date: z.string().describe('Date (YYYY-MM-DD)'),
1303
- sent: z.number().describe('Emails sent on this date'),
1304
- opened: z.number().describe('Total open events'),
1305
- unique_opened: z.number().describe('Unique leads who opened'),
1306
- replied: z.number().describe('Total reply events'),
1307
- unique_replied: z.number().describe('Unique leads who replied'),
1308
- bounced: z.number().describe('Emails bounced'),
1309
- clicks: z.number().describe('Total link click events'),
1310
- unique_clicks: z.number().describe('Unique leads who clicked')
1311
- })
1312
- )
1313
- .describe('Array of daily analytics entries')
1314
- }),
1315
-
1316
- integration: 'instantly' as const,
1317
- method: 'getDailyCampaignAnalytics' as const,
1318
- credentialName
1319
- })
1320
- }
1321
-
1322
- /**
1323
- * Create Instantly list leads tool
1324
- *
1325
- * Lists leads with optional filtering by campaign or email and cursor pagination.
1326
- *
1327
- * @param credentialName - Name of credential in credentials table
1328
- * @returns Tool that lists Instantly leads
1329
- *
1330
- * @example
1331
- * const listLeads = createInstantlyListLeadsTool('instantly-elevasis')
1332
- */
1333
- export function createInstantlyListLeadsTool(credentialName: string): Tool {
1334
- return createIntegrationTool({
1335
- name: 'instantly_list_leads',
1336
- description: `List leads in Instantly with optional filtering and pagination
1337
-
1338
- USE CASES:
1339
- - Retrieve all leads enrolled in a specific campaign for auditing
1340
- - Look up a specific lead by email address to check their status
1341
- - Paginate through a large lead list using cursor-based pagination
1342
- - Review lead metadata including status, timestamps, and campaign assignment
1343
-
1344
- OPTIONAL PARAMETERS:
1345
- - campaign_id: Filter leads by campaign UUID
1346
- - email: Filter by specific lead email address
1347
- - limit: Number of leads per page
1348
- - starting_after: Cursor from previous response for pagination
1349
-
1350
- NOTE: This uses a POST endpoint per Instantly API design (complex filter arguments).
1351
-
1352
- EXAMPLE (Campaign Leads):
1353
- {
1354
- "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43",
1355
- "limit": 50
1356
- }
1357
-
1358
- EXAMPLE (Single Lead Lookup):
1359
- {
1360
- "email": "prospect@agency.com"
1361
- }
1362
-
1363
- OUTPUT:
1364
- {
1365
- "items": [
1366
- {
1367
- "id": "lead-abc123",
1368
- "email": "prospect@agency.com",
1369
- "first_name": "John",
1370
- "last_name": "Smith",
1371
- "company_name": "Acme Agency",
1372
- "status": "active",
1373
- "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43",
1374
- "timestamp_created": "2025-01-15T09:00:00Z"
1375
- }
1376
- ],
1377
- "next_starting_after": "cursor-token"
1378
- }`,
1379
-
1380
- inputSchema: z.object({
1381
- campaign_id: z.string().optional().describe('Filter leads by campaign UUID'),
1382
- email: z.string().optional().describe('Filter by specific lead email address'),
1383
- limit: z.number().min(1).max(100).optional().describe('Number of leads per page'),
1384
- starting_after: z
1385
- .string()
1386
- .optional()
1387
- .describe('Cursor for pagination (from next_starting_after in previous response)')
1388
- }),
1389
-
1390
- outputSchema: z.object({
1391
- items: z
1392
- .array(
1393
- z.object({
1394
- id: z.string().describe('Lead ID'),
1395
- email: z.string().describe('Lead email address'),
1396
- first_name: z.string().optional().describe('Lead first name'),
1397
- last_name: z.string().optional().describe('Lead last name'),
1398
- company_name: z.string().optional().describe('Lead company name'),
1399
- status: z.string().describe('Lead status in campaign'),
1400
- campaign_id: z.string().optional().describe('Campaign this lead belongs to'),
1401
- timestamp_created: z.string().optional().describe('Lead creation timestamp')
1402
- })
1403
- )
1404
- .describe('Array of leads'),
1405
- next_starting_after: z.string().optional().describe('Cursor for next page (undefined = no more pages)')
1406
- }),
1407
-
1408
- integration: 'instantly' as const,
1409
- method: 'listLeads' as const,
1410
- credentialName
1411
- })
1412
- }
1413
-
1414
- /**
1415
- * Create Instantly bulk delete leads tool
1416
- *
1417
- * Deletes leads in bulk by IDs, campaign, or list with an optional limit.
1418
- *
1419
- * @param credentialName - Name of credential in credentials table
1420
- * @returns Tool that bulk-deletes Instantly leads
1421
- *
1422
- * @example
1423
- * const bulkDeleteLeads = createInstantlyBulkDeleteLeadsTool('instantly-elevasis')
1424
- */
1425
- export function createInstantlyBulkDeleteLeadsTool(credentialName: string): Tool {
1426
- return createIntegrationTool({
1427
- name: 'instantly_bulk_delete_leads',
1428
- description: `Bulk delete leads from Instantly by IDs, campaign, or list
1429
-
1430
- USE CASES:
1431
- - Remove a batch of disqualified leads from a campaign at once
1432
- - Clean up leads from a completed campaign before archiving
1433
- - Delete leads from a specific list to maintain database hygiene
1434
- - Remove a set of specific leads by their IDs after a data audit
1435
-
1436
- OPTIONAL PARAMETERS (at least one filter recommended):
1437
- - ids: Array of specific lead IDs to delete
1438
- - campaign_id: Delete all leads from this campaign
1439
- - list_id: Delete all leads from this list
1440
- - limit: Maximum number of leads to delete in this operation (1-10000)
1441
-
1442
- EXAMPLE (Delete by IDs):
1443
- {
1444
- "ids": ["lead-abc123", "lead-def456"]
1445
- }
1446
-
1447
- EXAMPLE (Delete from Campaign):
1448
- {
1449
- "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43",
1450
- "limit": 100
1451
- }
1452
-
1453
- OUTPUT:
1454
- {
1455
- "deleted_count": 42
1456
- }`,
1457
-
1458
- inputSchema: z.object({
1459
- ids: z.array(z.string()).optional().describe('Specific lead IDs to delete'),
1460
- campaign_id: z.string().optional().describe('Delete leads from this campaign'),
1461
- list_id: z.string().optional().describe('Delete leads from this list'),
1462
- limit: z.number().int().min(1).optional().describe('Maximum number of leads to delete')
1463
- }),
1464
-
1465
- outputSchema: z.object({
1466
- deleted_count: z.number().describe('Number of leads deleted')
1467
- }),
1468
-
1469
- integration: 'instantly' as const,
1470
- method: 'bulkDeleteLeads' as const,
1471
- credentialName
1472
- })
1473
- }
1
+ import { z } from 'zod'
2
+ import { createIntegrationTool } from '../../../tool'
3
+ import type { Tool } from '../../../../types'
4
+ import { EmailSchema } from '../../../../../../../platform/utils/validation'
5
+
6
+ /**
7
+ * Create Instantly send reply tool
8
+ *
9
+ * Sends a reply to an email thread maintaining proper threading.
10
+ *
11
+ * @param credentialName - Name of credential in credentials table
12
+ * @returns Tool that sends email replies via Instantly
13
+ *
14
+ * @example
15
+ * const sendReply = createInstantlySendReplyTool('instantly-elevasis')
16
+ */
17
+ export function createInstantlySendReplyTool(credentialName: string): Tool {
18
+ return createIntegrationTool({
19
+ name: 'instantly_send_reply',
20
+ description: `Send a reply to an email thread via Instantly.ai
21
+
22
+ USE CASES:
23
+ - Send a personalized reply to a prospect who responded to your outreach campaign
24
+ - Maintain email threading for organized conversations
25
+ - Schedule replies for future delivery
26
+
27
+ REQUIRED PARAMETERS:
28
+ - eaccount: Email account that will send the reply (must be connected to workspace)
29
+ - reply_to_uuid: UUID of the email you are replying to (from webhook payload)
30
+ - subject: Subject line of the reply (typically "Re: Original Subject")
31
+ - body: Email body (must provide either text or html)
32
+
33
+ OPTIONAL PARAMETERS:
34
+ - timestamp: ISO timestamp to schedule reply for future
35
+
36
+ EXAMPLE:
37
+ {
38
+ "eaccount": "alex@elevasis.com",
39
+ "reply_to_uuid": "123e4567-e89b-12d3-a456-426614174000",
40
+ "subject": "Re: Automate your client acquisition",
41
+ "body": {
42
+ "text": "Thanks for your interest! Here's my calendar: https://calendly.com/..."
43
+ }
44
+ }
45
+
46
+ OUTPUT:
47
+ {
48
+ "success": true,
49
+ "email_id": "email-abc123",
50
+ "thread_id": "thread-xyz789",
51
+ "sent_at": "2025-12-22T14:30:00Z"
52
+ }`,
53
+
54
+ inputSchema: z.object({
55
+ eaccount: z.string().describe('Email account that will send the reply (must be connected to workspace)'),
56
+ reply_to_uuid: z.string().describe('UUID of the email you are replying to (from webhook payload)'),
57
+ subject: z.string().describe('Subject line of the reply (typically "Re: Original Subject")'),
58
+ body: z
59
+ .object({
60
+ text: z.string().optional().describe('Plain text email body'),
61
+ html: z.string().optional().describe('HTML email body')
62
+ })
63
+ .refine((data) => data.text || data.html, {
64
+ message: 'Must provide either text or html body'
65
+ }),
66
+ timestamp: z.string().optional().describe('ISO timestamp to schedule reply for future (optional)')
67
+ }),
68
+
69
+ outputSchema: z.object({
70
+ success: z.boolean().describe('Whether reply was sent successfully'),
71
+ email_id: z.string().describe('ID of the sent email'),
72
+ thread_id: z.string().describe('Thread ID for conversation tracking'),
73
+ sent_at: z.string().describe('Timestamp when email was sent')
74
+ }),
75
+
76
+ integration: 'instantly' as const,
77
+ method: 'sendReply' as const,
78
+ credentialName
79
+ })
80
+ }
81
+
82
+ /**
83
+ * Create Instantly remove from subsequence tool
84
+ *
85
+ * Removes a lead from a campaign sequence to stop further outreach.
86
+ *
87
+ * @param credentialName - Name of credential in credentials table
88
+ * @returns Tool that removes leads from campaigns
89
+ *
90
+ * @example
91
+ * const removeFromCampaign = createInstantlyRemoveFromSubsequenceTool('instantly-elevasis')
92
+ */
93
+ export function createInstantlyRemoveFromSubsequenceTool(credentialName: string): Tool {
94
+ return createIntegrationTool({
95
+ name: 'instantly_remove_from_subsequence',
96
+ description: `Remove a lead from an Instantly campaign subsequence
97
+
98
+ USE CASES:
99
+ - Stop a lead from receiving further emails in a campaign
100
+ - Use after positive reply, hard-no, or disqualification
101
+ - Prevent over-emailing engaged prospects
102
+
103
+ REQUIRED PARAMETERS:
104
+ - lead_email: Email address of the lead to remove
105
+ - campaign_id: Campaign ID to remove lead from
106
+
107
+ EXAMPLE:
108
+ {
109
+ "lead_email": "prospect@agency.com",
110
+ "campaign_id": "campaign-123"
111
+ }
112
+
113
+ OUTPUT:
114
+ {
115
+ "success": true,
116
+ "lead_email": "prospect@agency.com",
117
+ "campaign_id": "campaign-123"
118
+ }`,
119
+
120
+ inputSchema: z.object({
121
+ lead_email: EmailSchema.describe('Email address of the lead to remove'),
122
+ campaign_id: z.string().describe('Campaign ID to remove lead from')
123
+ }),
124
+
125
+ outputSchema: z.object({
126
+ success: z.boolean().describe('Whether removal was successful'),
127
+ lead_email: z.string().describe('Email of the removed lead'),
128
+ campaign_id: z.string().describe('Campaign ID lead was removed from')
129
+ }),
130
+
131
+ integration: 'instantly' as const,
132
+ method: 'removeFromSubsequence' as const,
133
+ credentialName
134
+ })
135
+ }
136
+
137
+ /**
138
+ * Create Instantly get emails tool
139
+ *
140
+ * Fetches email data from Instantly workspace with optional filtering and pagination.
141
+ *
142
+ * @param credentialName - Name of credential in credentials table
143
+ * @returns Tool that retrieves email data
144
+ */
145
+ export function createInstantlyGetEmailsTool(credentialName: string): Tool {
146
+ return createIntegrationTool({
147
+ name: 'instantly_get_emails',
148
+ description: `Get emails from Instantly workspace
149
+
150
+ USE CASES:
151
+ - Fetch a specific email by ID for reply processing
152
+ - Retrieve full email threads by thread ID
153
+ - Filter emails by campaign, lead, or type
154
+ - List recent emails with cursor pagination
155
+
156
+ OPTIONAL PARAMETERS:
157
+ - email_id: Specific email ID to fetch (returns single email)
158
+ - search: Email address OR "thread:{thread_id}" for thread retrieval
159
+ - campaign_id: Filter by campaign UUID
160
+ - lead: Filter by lead email address
161
+ - email_type: Filter by type (received, sent, manual)
162
+ - eaccount: Filter by sending account (comma-separated)
163
+ - sort_order: Sort by creation date (asc, desc; default: desc)
164
+ - preview_only: Return preview only (lighter response)
165
+ - is_unread: Filter by unread status
166
+ - limit: Items per page (1-100)
167
+ - starting_after: Cursor for pagination (from next_starting_after in previous response)
168
+
169
+ EXAMPLE (Single Email):
170
+ {
171
+ "email_id": "email-abc123"
172
+ }
173
+
174
+ EXAMPLE (Thread Retrieval):
175
+ {
176
+ "search": "thread:019a449c-f57a-73b2-91b5-75537ca85008",
177
+ "sort_order": "asc",
178
+ "limit": 50
179
+ }
180
+
181
+ EXAMPLE (Lead Emails in Campaign):
182
+ {
183
+ "lead": "jondoe@example.com",
184
+ "campaign_id": "019a449c-f57a-73b2-91b5-754f1fca9f44",
185
+ "sort_order": "asc"
186
+ }
187
+
188
+ OUTPUT:
189
+ {
190
+ "emails": [...email data...],
191
+ "total_count": 3,
192
+ "next_starting_after": "cursor-token-or-undefined"
193
+ }`,
194
+
195
+ inputSchema: z.object({
196
+ email_id: z.string().optional().describe('Specific email ID to fetch'),
197
+ search: z
198
+ .string()
199
+ .optional()
200
+ .describe('Search query: email address or "thread:{thread_id}" for thread retrieval'),
201
+ campaign_id: z.string().optional().describe('Filter by campaign UUID'),
202
+ lead: z.string().optional().describe('Filter by lead email address'),
203
+ email_type: z.enum(['received', 'sent', 'manual']).optional().describe('Filter by email type'),
204
+ eaccount: z.string().optional().describe('Filter by sending account (comma-separated)'),
205
+ sort_order: z.enum(['asc', 'desc']).optional().describe('Sort order by creation date (default: desc)'),
206
+ preview_only: z.boolean().optional().describe('Return preview only (lighter response)'),
207
+ is_unread: z.boolean().optional().describe('Filter by unread status'),
208
+ limit: z.number().min(1).max(100).optional().describe('Items per page (1-100)'),
209
+ starting_after: z.string().optional().describe('Cursor for pagination (from next_starting_after)')
210
+ }),
211
+
212
+ outputSchema: z.object({
213
+ emails: z.array(z.unknown()).describe('Array of email data'),
214
+ total_count: z.number().describe('Count of emails returned'),
215
+ next_starting_after: z.string().optional().describe('Cursor for next page (undefined = no more)')
216
+ }),
217
+
218
+ integration: 'instantly' as const,
219
+ method: 'getEmails' as const,
220
+ credentialName
221
+ })
222
+ }
223
+
224
+ /**
225
+ * Create Instantly update interest status tool
226
+ *
227
+ * Updates the interest status of a lead, which can stop email sequences,
228
+ * mark leads as interested, or reset to default status.
229
+ *
230
+ * @param credentialName - Name of credential in credentials table
231
+ * @returns Tool that updates lead interest status
232
+ *
233
+ * @example
234
+ * const updateStatus = createInstantlyUpdateInterestStatusTool('instantly-elevasis')
235
+ */
236
+ export function createInstantlyUpdateInterestStatusTool(credentialName: string): Tool {
237
+ return createIntegrationTool({
238
+ name: 'instantly_update_interest_status',
239
+ description: `Update interest status of a lead in Instantly
240
+
241
+ USE CASES:
242
+ - Stop email sequence for leads who reply with "unsubscribe" or "not interested" (set interest_value: -1)
243
+ - Mark engaged leads as interested for prioritization (set interest_value: 1)
244
+ - Reset lead status back to default (set interest_value: 0)
245
+ - Comply with opt-out requests to protect sender reputation
246
+
247
+ INTEREST VALUES:
248
+ - 1: Interested - Marks lead as interested
249
+ - 0: Lead (default) - Resets to default status
250
+ - -1: Not Interested - STOPS sequence (no more emails sent)
251
+
252
+ REQUIRED PARAMETERS:
253
+ - lead_email: Email address of the lead
254
+ - interest_value: Status value (1, 0, or -1)
255
+
256
+ OPTIONAL PARAMETERS:
257
+ - campaign_id: Limit status update to specific campaign (UUID)
258
+ - disable_auto_interest: Disable AI auto-tagging for this lead
259
+
260
+ EXAMPLE (Stop Sequence):
261
+ {
262
+ "lead_email": "prospect@agency.com",
263
+ "interest_value": -1
264
+ }
265
+
266
+ EXAMPLE (Mark Interested in Specific Campaign):
267
+ {
268
+ "lead_email": "prospect@agency.com",
269
+ "interest_value": 1,
270
+ "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43"
271
+ }
272
+
273
+ OUTPUT:
274
+ {
275
+ "success": true,
276
+ "lead_email": "prospect@agency.com"
277
+ }`,
278
+
279
+ inputSchema: z.object({
280
+ lead_email: EmailSchema.describe('Email address of the lead'),
281
+ interest_value: z
282
+ .number()
283
+ .int()
284
+ .min(-1)
285
+ .max(1)
286
+ .describe('Interest status: 1 (interested), 0 (default), -1 (not interested - stops sequence)'),
287
+ campaign_id: z.string().optional().describe('Campaign ID to limit status update to (UUID)'),
288
+ disable_auto_interest: z.boolean().optional().describe('Disable AI auto-tagging for this lead')
289
+ }),
290
+
291
+ outputSchema: z.object({
292
+ success: z.boolean().describe('Whether status update was successful'),
293
+ lead_email: z.string().describe('Email of the updated lead')
294
+ }),
295
+
296
+ integration: 'instantly' as const,
297
+ method: 'updateInterestStatus' as const,
298
+ credentialName
299
+ })
300
+ }
301
+
302
+ /**
303
+ * Create Instantly add to campaign tool
304
+ *
305
+ * Adds leads to an Instantly campaign to begin the email sequence.
306
+ *
307
+ * @param credentialName - Name of credential in credentials table
308
+ * @returns Tool that adds leads to a campaign
309
+ *
310
+ * @example
311
+ * const addToCampaign = createInstantlyAddToCampaignTool('instantly-elevasis')
312
+ */
313
+ export function createInstantlyAddToCampaignTool(credentialName: string): Tool {
314
+ return createIntegrationTool({
315
+ name: 'instantly_add_to_campaign',
316
+ description: `Add leads to an Instantly campaign
317
+
318
+ USE CASES:
319
+ - Add interested leads to a follow-up campaign after initial outreach
320
+ - Start a drip sequence for leads who requested more information
321
+ - Enroll new leads into automated nurture campaigns
322
+
323
+ REQUIRED PARAMETERS:
324
+ - campaign_id: UUID of the Instantly campaign to add leads to
325
+ - leads: Array of lead objects (at least one required)
326
+
327
+ LEAD OBJECT FIELDS:
328
+ - email: (required) Email address of the lead
329
+ - first_name: (optional) First name for personalization
330
+ - last_name: (optional) Last name for personalization
331
+ - company_name: (optional) Company name for personalization
332
+
333
+ IMPORTANT: When leads are added, they begin receiving the campaign sequence immediately.
334
+ "Day 0" starts at the moment of addition.
335
+
336
+ EXAMPLE:
337
+ {
338
+ "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43",
339
+ "leads": [
340
+ {
341
+ "email": "prospect@agency.com",
342
+ "first_name": "John",
343
+ "last_name": "Smith",
344
+ "company_name": "Acme Agency"
345
+ }
346
+ ]
347
+ }
348
+
349
+ OUTPUT:
350
+ {
351
+ "success": true,
352
+ "added_count": 1,
353
+ "errors": null
354
+ }`,
355
+
356
+ inputSchema: z.object({
357
+ campaign_id: z.string().describe('UUID of the Instantly campaign to add leads to'),
358
+ leads: z
359
+ .array(
360
+ z.object({
361
+ email: EmailSchema.describe('Email address of the lead'),
362
+ first_name: z.string().optional().describe('First name for personalization'),
363
+ last_name: z.string().optional().describe('Last name for personalization'),
364
+ company_name: z.string().optional().describe('Company name for personalization')
365
+ })
366
+ )
367
+ .min(1)
368
+ .describe('Array of leads to add (at least one required)')
369
+ }),
370
+
371
+ outputSchema: z.object({
372
+ success: z.boolean().describe('Whether leads were added successfully'),
373
+ added_count: z.number().describe('Number of leads successfully added'),
374
+ errors: z.array(z.string()).optional().describe('Array of error messages if any failures occurred')
375
+ }),
376
+
377
+ integration: 'instantly' as const,
378
+ method: 'addToCampaign' as const,
379
+ credentialName
380
+ })
381
+ }
382
+
383
+ /**
384
+ * Create Instantly list campaigns tool
385
+ *
386
+ * Lists all campaigns in the workspace with optional filtering and pagination.
387
+ *
388
+ * @param credentialName - Name of credential in credentials table
389
+ * @returns Tool that lists Instantly campaigns
390
+ *
391
+ * @example
392
+ * const listCampaigns = createInstantlyListCampaignsTool('instantly-elevasis')
393
+ */
394
+ export function createInstantlyListCampaignsTool(credentialName: string): Tool {
395
+ return createIntegrationTool({
396
+ name: 'instantly_list_campaigns',
397
+ description: `List all campaigns in the Instantly workspace
398
+
399
+ USE CASES:
400
+ - Discover available campaigns before adding leads or fetching analytics
401
+ - Filter campaigns by status to find active or paused campaigns
402
+ - Search campaigns by name to locate a specific campaign ID
403
+ - Paginate through large campaign lists using cursor-based pagination
404
+
405
+ OPTIONAL PARAMETERS:
406
+ - limit: Number of campaigns to return per page (default: 10)
407
+ - starting_after: Cursor from previous response for pagination
408
+ - status: Filter by campaign status (e.g. "active", "paused", "completed")
409
+ - search: Search term to filter campaigns by name
410
+
411
+ EXAMPLE (List Active Campaigns):
412
+ {
413
+ "status": "active",
414
+ "limit": 20
415
+ }
416
+
417
+ EXAMPLE (Search by Name):
418
+ {
419
+ "search": "cold outreach Q1"
420
+ }
421
+
422
+ OUTPUT:
423
+ {
424
+ "campaigns": [
425
+ {
426
+ "id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43",
427
+ "name": "Cold Outreach Q1",
428
+ "status": "active",
429
+ "created_at": "2025-01-15T09:00:00Z"
430
+ }
431
+ ],
432
+ "next_starting_after": "cursor-token"
433
+ }`,
434
+
435
+ inputSchema: z.object({
436
+ limit: z.number().min(1).max(100).optional().describe('Number of campaigns per page (default: 10)'),
437
+ starting_after: z
438
+ .string()
439
+ .optional()
440
+ .describe('Cursor for pagination (from next_starting_after in previous response)'),
441
+ status: z.string().optional().describe('Filter by campaign status (e.g. "active", "paused", "completed")'),
442
+ search: z.string().optional().describe('Search term to filter campaigns by name')
443
+ }),
444
+
445
+ outputSchema: z.object({
446
+ campaigns: z
447
+ .array(
448
+ z.object({
449
+ id: z.string().describe('Campaign UUID'),
450
+ name: z.string().describe('Campaign name'),
451
+ status: z.string().describe('Campaign status'),
452
+ created_at: z.string().describe('Campaign creation timestamp')
453
+ })
454
+ )
455
+ .describe('Array of campaigns'),
456
+ next_starting_after: z.string().optional().describe('Cursor for next page (undefined = no more pages)')
457
+ }),
458
+
459
+ integration: 'instantly' as const,
460
+ method: 'listCampaigns' as const,
461
+ credentialName
462
+ })
463
+ }
464
+
465
+ /**
466
+ * Create Instantly get campaign tool
467
+ *
468
+ * Fetches full details for a single campaign including sequences and configuration.
469
+ *
470
+ * @param credentialName - Name of credential in credentials table
471
+ * @returns Tool that retrieves a single Instantly campaign
472
+ *
473
+ * @example
474
+ * const getCampaign = createInstantlyGetCampaignTool('instantly-elevasis')
475
+ */
476
+ export function createInstantlyGetCampaignTool(credentialName: string): Tool {
477
+ return createIntegrationTool({
478
+ name: 'instantly_get_campaign',
479
+ description: `Get full details of a specific Instantly campaign
480
+
481
+ USE CASES:
482
+ - Inspect campaign sequences and step configurations before modifying
483
+ - Verify campaign status before activating or pausing
484
+ - Retrieve sending settings such as daily limits and reply handling
485
+ - Confirm the campaign name and metadata before referencing in reports
486
+
487
+ REQUIRED PARAMETERS:
488
+ - campaign_id: UUID of the campaign to retrieve
489
+
490
+ EXAMPLE:
491
+ {
492
+ "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43"
493
+ }
494
+
495
+ OUTPUT:
496
+ {
497
+ "id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43",
498
+ "name": "Cold Outreach Q1",
499
+ "status": "active",
500
+ "sequences": [...],
501
+ "created_at": "2025-01-15T09:00:00Z"
502
+ }`,
503
+
504
+ inputSchema: z.object({
505
+ campaign_id: z.string().describe('UUID of the campaign to retrieve')
506
+ }),
507
+
508
+ outputSchema: z.object({
509
+ id: z.string().describe('Campaign UUID'),
510
+ name: z.string().describe('Campaign name'),
511
+ status: z.string().describe('Campaign status'),
512
+ sequences: z.array(z.unknown()).describe('Campaign sequence steps'),
513
+ created_at: z.string().describe('Campaign creation timestamp')
514
+ }),
515
+
516
+ integration: 'instantly' as const,
517
+ method: 'getCampaign' as const,
518
+ credentialName
519
+ })
520
+ }
521
+
522
+ /**
523
+ * Create Instantly update campaign tool
524
+ *
525
+ * Partially updates a campaign's fields such as name, sequences, or sending limits.
526
+ *
527
+ * @param credentialName - Name of credential in credentials table
528
+ * @returns Tool that updates an Instantly campaign
529
+ *
530
+ * @example
531
+ * const updateCampaign = createInstantlyUpdateCampaignTool('instantly-elevasis')
532
+ */
533
+ export function createInstantlyUpdateCampaignTool(credentialName: string): Tool {
534
+ return createIntegrationTool({
535
+ name: 'instantly_update_campaign',
536
+ description: `Update fields on an existing Instantly campaign
537
+
538
+ USE CASES:
539
+ - Rename a campaign to reflect updated targeting or messaging
540
+ - Adjust the daily sending limit to control volume
541
+ - Replace or edit campaign sequences and email steps
542
+ - Toggle stop-on-reply to pause the sequence when a lead responds
543
+
544
+ REQUIRED PARAMETERS:
545
+ - campaign_id: UUID of the campaign to update
546
+
547
+ OPTIONAL PARAMETERS (at least one must be provided):
548
+ - name: New campaign name
549
+ - sequences: Full replacement of campaign sequences (array of step objects)
550
+ - daily_limit: Maximum emails to send per day
551
+ - stop_on_reply: Whether to stop sending when a lead replies (true/false)
552
+
553
+ EXAMPLE (Rename and Adjust Limit):
554
+ {
555
+ "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43",
556
+ "name": "Cold Outreach Q2",
557
+ "daily_limit": 50
558
+ }
559
+
560
+ OUTPUT:
561
+ {
562
+ "success": true,
563
+ "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43"
564
+ }`,
565
+
566
+ inputSchema: z.object({
567
+ campaign_id: z.string().describe('UUID of the campaign to update'),
568
+ name: z.string().optional().describe('New campaign name'),
569
+ sequences: z.array(z.unknown()).optional().describe('Replacement campaign sequences (array of step objects)'),
570
+ daily_limit: z.number().int().min(1).optional().describe('Maximum emails to send per day'),
571
+ stop_on_reply: z.boolean().optional().describe('Whether to stop the sequence when a lead replies')
572
+ }),
573
+
574
+ outputSchema: z.object({
575
+ success: z.boolean().describe('Whether the update was successful'),
576
+ campaign_id: z.string().describe('UUID of the updated campaign')
577
+ }),
578
+
579
+ integration: 'instantly' as const,
580
+ method: 'updateCampaign' as const,
581
+ credentialName
582
+ })
583
+ }
584
+
585
+ /**
586
+ * Create Instantly pause campaign tool
587
+ *
588
+ * Pauses an active campaign to stop email sends until reactivated.
589
+ *
590
+ * @param credentialName - Name of credential in credentials table
591
+ * @returns Tool that pauses an Instantly campaign
592
+ *
593
+ * @example
594
+ * const pauseCampaign = createInstantlyPauseCampaignTool('instantly-elevasis')
595
+ */
596
+ export function createInstantlyPauseCampaignTool(credentialName: string): Tool {
597
+ return createIntegrationTool({
598
+ name: 'instantly_pause_campaign',
599
+ description: `Pause an active Instantly campaign
600
+
601
+ USE CASES:
602
+ - Temporarily halt outreach while reviewing campaign performance
603
+ - Pause before making sequence edits to avoid sending stale content
604
+ - Stop sends during public holidays or blackout periods
605
+ - Hold sends when a domain reputation issue is detected
606
+
607
+ REQUIRED PARAMETERS:
608
+ - campaign_id: UUID of the campaign to pause
609
+
610
+ EXAMPLE:
611
+ {
612
+ "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43"
613
+ }
614
+
615
+ OUTPUT:
616
+ {
617
+ "success": true,
618
+ "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43"
619
+ }`,
620
+
621
+ inputSchema: z.object({
622
+ campaign_id: z.string().describe('UUID of the campaign to pause')
623
+ }),
624
+
625
+ outputSchema: z.object({
626
+ success: z.boolean().describe('Whether the campaign was paused successfully'),
627
+ campaign_id: z.string().describe('UUID of the paused campaign')
628
+ }),
629
+
630
+ integration: 'instantly' as const,
631
+ method: 'pauseCampaign' as const,
632
+ credentialName
633
+ })
634
+ }
635
+
636
+ /**
637
+ * Create Instantly delete campaign tool
638
+ *
639
+ * Permanently deletes a campaign and all associated data.
640
+ *
641
+ * @param credentialName - Name of credential in credentials table
642
+ * @returns Tool that deletes an Instantly campaign
643
+ *
644
+ * @example
645
+ * const deleteCampaign = createInstantlyDeleteCampaignTool('instantly-elevasis')
646
+ */
647
+ export function createInstantlyDeleteCampaignTool(credentialName: string): Tool {
648
+ return createIntegrationTool({
649
+ name: 'instantly_delete_campaign',
650
+ description: `Permanently delete an Instantly campaign
651
+
652
+ USE CASES:
653
+ - Clean up test or draft campaigns after diagnostic runs
654
+ - Remove campaigns that are no longer needed
655
+ - Delete accidentally created campaigns
656
+
657
+ REQUIRED PARAMETERS:
658
+ - campaign_id: UUID of the campaign to delete
659
+
660
+ EXAMPLE:
661
+ {
662
+ "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43"
663
+ }
664
+
665
+ OUTPUT:
666
+ {
667
+ "success": true,
668
+ "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43"
669
+ }`,
670
+
671
+ inputSchema: z.object({
672
+ campaign_id: z.string().describe('UUID of the campaign to delete')
673
+ }),
674
+
675
+ outputSchema: z.object({
676
+ success: z.boolean().describe('Whether the campaign was deleted successfully'),
677
+ campaign_id: z.string().describe('UUID of the deleted campaign')
678
+ }),
679
+
680
+ integration: 'instantly' as const,
681
+ method: 'deleteCampaign' as const,
682
+ credentialName
683
+ })
684
+ }
685
+
686
+ /**
687
+ * Create Instantly activate campaign tool
688
+ *
689
+ * Activates or resumes a paused or draft campaign to begin sending.
690
+ *
691
+ * @param credentialName - Name of credential in credentials table
692
+ * @returns Tool that activates an Instantly campaign
693
+ *
694
+ * @example
695
+ * const activateCampaign = createInstantlyActivateCampaignTool('instantly-elevasis')
696
+ */
697
+ export function createInstantlyActivateCampaignTool(credentialName: string): Tool {
698
+ return createIntegrationTool({
699
+ name: 'instantly_activate_campaign',
700
+ description: `Activate or resume an Instantly campaign
701
+
702
+ USE CASES:
703
+ - Launch a newly created campaign to begin sending to enrolled leads
704
+ - Resume a paused campaign after completing edits or reviews
705
+ - Re-activate a campaign after resolving domain reputation issues
706
+ - Start scheduled outreach at the beginning of a new period
707
+
708
+ REQUIRED PARAMETERS:
709
+ - campaign_id: UUID of the campaign to activate
710
+
711
+ EXAMPLE:
712
+ {
713
+ "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43"
714
+ }
715
+
716
+ OUTPUT:
717
+ {
718
+ "success": true,
719
+ "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43"
720
+ }`,
721
+
722
+ inputSchema: z.object({
723
+ campaign_id: z.string().describe('UUID of the campaign to activate')
724
+ }),
725
+
726
+ outputSchema: z.object({
727
+ success: z.boolean().describe('Whether the campaign was activated successfully'),
728
+ campaign_id: z.string().describe('UUID of the activated campaign')
729
+ }),
730
+
731
+ integration: 'instantly' as const,
732
+ method: 'activateCampaign' as const,
733
+ credentialName
734
+ })
735
+ }
736
+
737
+ /**
738
+ * Create Instantly get campaign analytics tool
739
+ *
740
+ * Retrieves aggregate engagement statistics for a campaign.
741
+ *
742
+ * @param credentialName - Name of credential in credentials table
743
+ * @returns Tool that fetches campaign-level analytics
744
+ *
745
+ * @example
746
+ * const getAnalytics = createInstantlyGetCampaignAnalyticsTool('instantly-elevasis')
747
+ */
748
+ export function createInstantlyGetCampaignAnalyticsTool(credentialName: string): Tool {
749
+ return createIntegrationTool({
750
+ name: 'instantly_get_campaign_analytics',
751
+ description: `Get aggregate analytics for an Instantly campaign
752
+
753
+ USE CASES:
754
+ - Review overall campaign performance including open rate, reply rate, and bounce rate
755
+ - Compare send volume against replies to evaluate outreach effectiveness
756
+ - Monitor unsubscribes to maintain sender reputation
757
+ - Scope analytics to a date range for period-specific reporting
758
+
759
+ REQUIRED PARAMETERS:
760
+ - campaign_id: UUID of the campaign
761
+
762
+ OPTIONAL PARAMETERS:
763
+ - start_date: ISO date string to filter analytics from (e.g. "2025-01-01")
764
+ - end_date: ISO date string to filter analytics to (e.g. "2025-03-31")
765
+
766
+ EXAMPLE:
767
+ {
768
+ "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43",
769
+ "start_date": "2025-01-01",
770
+ "end_date": "2025-03-31"
771
+ }
772
+
773
+ OUTPUT:
774
+ {
775
+ "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43",
776
+ "sent": 450,
777
+ "opened": 180,
778
+ "unique_opened": 160,
779
+ "replied": 35,
780
+ "unique_replied": 30,
781
+ "bounced": 5,
782
+ "unsubscribed": 2
783
+ }`,
784
+
785
+ inputSchema: z.object({
786
+ campaign_id: z.string().describe('UUID of the campaign to fetch analytics for'),
787
+ start_date: z.string().optional().describe('Start date for analytics range (ISO date string, e.g. "2025-01-01")'),
788
+ end_date: z.string().optional().describe('End date for analytics range (ISO date string, e.g. "2025-03-31")')
789
+ }),
790
+
791
+ outputSchema: z.object({
792
+ campaign_id: z.string().describe('Campaign UUID'),
793
+ sent: z.number().describe('Total emails sent'),
794
+ opened: z.number().describe('Total open events'),
795
+ unique_opened: z.number().describe('Unique leads who opened'),
796
+ replied: z.number().describe('Total reply events'),
797
+ unique_replied: z.number().describe('Unique leads who replied'),
798
+ bounced: z.number().describe('Total bounced emails'),
799
+ unsubscribed: z.number().describe('Total unsubscribes')
800
+ }),
801
+
802
+ integration: 'instantly' as const,
803
+ method: 'getCampaignAnalytics' as const,
804
+ credentialName
805
+ })
806
+ }
807
+
808
+ /**
809
+ * Create Instantly get step analytics tool
810
+ *
811
+ * Retrieves per-step and per-variant analytics for a campaign sequence.
812
+ *
813
+ * @param credentialName - Name of credential in credentials table
814
+ * @returns Tool that fetches step-level analytics
815
+ *
816
+ * @example
817
+ * const getStepAnalytics = createInstantlyGetStepAnalyticsTool('instantly-elevasis')
818
+ */
819
+ export function createInstantlyGetStepAnalyticsTool(credentialName: string): Tool {
820
+ return createIntegrationTool({
821
+ name: 'instantly_get_step_analytics',
822
+ description: `Get per-step and per-variant analytics for an Instantly campaign
823
+
824
+ USE CASES:
825
+ - Identify which sequence step has the highest reply rate
826
+ - Compare A/B subject line variants to determine the winning version
827
+ - Diagnose a drop-off point where prospects stop engaging
828
+ - Optimize multi-step sequences based on step-level performance data
829
+
830
+ REQUIRED PARAMETERS:
831
+ - campaign_id: UUID of the campaign
832
+
833
+ EXAMPLE:
834
+ {
835
+ "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43"
836
+ }
837
+
838
+ OUTPUT:
839
+ {
840
+ "steps": [
841
+ {
842
+ "step": "1",
843
+ "variant": "A",
844
+ "sent": 200,
845
+ "opened": 90,
846
+ "unique_opened": 80,
847
+ "replies": 20,
848
+ "unique_replies": 18
849
+ },
850
+ {
851
+ "step": "2",
852
+ "variant": "A",
853
+ "sent": 160,
854
+ "opened": 55,
855
+ "unique_opened": 50,
856
+ "replies": 10,
857
+ "unique_replies": 9
858
+ }
859
+ ]
860
+ }`,
861
+
862
+ inputSchema: z.object({
863
+ campaign_id: z.string().describe('UUID of the campaign to fetch step analytics for')
864
+ }),
865
+
866
+ outputSchema: z.object({
867
+ steps: z
868
+ .array(
869
+ z.object({
870
+ step: z.string().describe('Step number in the sequence'),
871
+ variant: z.string().describe('Variant label (e.g. "A", "B")'),
872
+ sent: z.number().describe('Emails sent for this step/variant'),
873
+ opened: z.number().describe('Total open events'),
874
+ unique_opened: z.number().describe('Unique leads who opened'),
875
+ replies: z.number().describe('Total reply events'),
876
+ unique_replies: z.number().describe('Unique leads who replied')
877
+ })
878
+ )
879
+ .describe('Per-step, per-variant analytics entries')
880
+ }),
881
+
882
+ integration: 'instantly' as const,
883
+ method: 'getStepAnalytics' as const,
884
+ credentialName
885
+ })
886
+ }
887
+
888
+ /**
889
+ * Create Instantly bulk add leads tool
890
+ *
891
+ * Adds up to 1000 leads to a campaign in a single request.
892
+ *
893
+ * @param credentialName - Name of credential in credentials table
894
+ * @returns Tool that bulk-uploads leads to an Instantly campaign
895
+ *
896
+ * @example
897
+ * const bulkAddLeads = createInstantlyBulkAddLeadsTool('instantly-elevasis')
898
+ */
899
+ export function createInstantlyBulkAddLeadsTool(credentialName: string): Tool {
900
+ return createIntegrationTool({
901
+ name: 'instantly_bulk_add_leads',
902
+ description: `Bulk upload up to 1000 leads to an Instantly campaign
903
+
904
+ USE CASES:
905
+ - Import a large list of qualified prospects into a campaign at once
906
+ - Enroll leads exported from a CRM or enrichment tool into outreach
907
+ - Upload leads with custom variables for deep personalization
908
+ - Add leads with website and company data for context-aware sequences
909
+
910
+ REQUIRED PARAMETERS:
911
+ - campaign_id: UUID of the campaign to add leads to
912
+ - leads: Array of lead objects (1–1000 leads)
913
+
914
+ LEAD OBJECT FIELDS:
915
+ - email: (required) Email address of the lead
916
+ - first_name: (optional) First name for personalization
917
+ - last_name: (optional) Last name for personalization
918
+ - company_name: (optional) Company name for personalization
919
+ - website: (optional) Company website URL
920
+ - custom_variables: (optional) Key-value map of custom merge fields
921
+
922
+ EXAMPLE:
923
+ {
924
+ "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43",
925
+ "leads": [
926
+ {
927
+ "email": "ceo@agency.com",
928
+ "first_name": "Sarah",
929
+ "last_name": "Chen",
930
+ "company_name": "Growth Agency",
931
+ "website": "https://growthagency.com",
932
+ "custom_variables": { "pain_point": "manual lead gen" }
933
+ }
934
+ ]
935
+ }
936
+
937
+ OUTPUT:
938
+ {
939
+ "success": true,
940
+ "added_count": 1,
941
+ "failed_count": 0
942
+ }`,
943
+
944
+ inputSchema: z.object({
945
+ campaign_id: z.string().describe('UUID of the campaign to add leads to'),
946
+ leads: z
947
+ .array(
948
+ z.object({
949
+ email: EmailSchema.describe('Email address of the lead'),
950
+ first_name: z.string().optional().describe('First name for personalization'),
951
+ last_name: z.string().optional().describe('Last name for personalization'),
952
+ company_name: z.string().optional().describe('Company name for personalization'),
953
+ website: z.string().optional().describe('Company website URL'),
954
+ custom_variables: z
955
+ .record(z.string(), z.string())
956
+ .optional()
957
+ .describe('Key-value map of custom merge field values')
958
+ })
959
+ )
960
+ .min(1)
961
+ .max(1000)
962
+ .describe('Array of leads to upload (1–1000)')
963
+ }),
964
+
965
+ outputSchema: z.object({
966
+ success: z.boolean().describe('True if all leads were added without errors'),
967
+ added_count: z.number().describe('Number of leads successfully added'),
968
+ failed_count: z.number().describe('Number of leads that failed to add'),
969
+ errors: z
970
+ .array(
971
+ z.object({
972
+ email: z.string().describe('Email of the lead that failed'),
973
+ error: z.string().describe('Error message for this lead')
974
+ })
975
+ )
976
+ .optional()
977
+ .describe('Per-lead error details when failures occurred')
978
+ }),
979
+
980
+ integration: 'instantly' as const,
981
+ method: 'bulkAddLeads' as const,
982
+ credentialName
983
+ })
984
+ }
985
+
986
+ /**
987
+ * Create Instantly get account health tool
988
+ *
989
+ * Lists email sending accounts with warmup status and health scores.
990
+ *
991
+ * @param credentialName - Name of credential in credentials table
992
+ * @returns Tool that retrieves account health data
993
+ *
994
+ * @example
995
+ * const getAccountHealth = createInstantlyGetAccountHealthTool('instantly-elevasis')
996
+ */
997
+ export function createInstantlyGetAccountHealthTool(credentialName: string): Tool {
998
+ return createIntegrationTool({
999
+ name: 'instantly_get_account_health',
1000
+ description: `List email accounts with health status and warmup information
1001
+
1002
+ USE CASES:
1003
+ - Audit all connected sending accounts before launching a campaign
1004
+ - Identify accounts with low health scores that may cause deliverability issues
1005
+ - Verify warmup status to ensure accounts are ready for full sending volume
1006
+ - Monitor account portfolio health as part of regular sender hygiene checks
1007
+
1008
+ OPTIONAL PARAMETERS:
1009
+ - limit: Number of accounts to return per page
1010
+ - starting_after: Cursor from previous response for pagination
1011
+
1012
+ EXAMPLE:
1013
+ {
1014
+ "limit": 20
1015
+ }
1016
+
1017
+ OUTPUT:
1018
+ {
1019
+ "accounts": [
1020
+ {
1021
+ "id": "acct-abc123",
1022
+ "email": "alex@elevasis.com",
1023
+ "status": "active",
1024
+ "warmup_enabled": true,
1025
+ "health_score": 92
1026
+ }
1027
+ ],
1028
+ "next_starting_after": "cursor-token"
1029
+ }`,
1030
+
1031
+ inputSchema: z.object({
1032
+ limit: z.number().min(1).max(100).optional().describe('Number of accounts per page'),
1033
+ starting_after: z
1034
+ .string()
1035
+ .optional()
1036
+ .describe('Cursor for pagination (from next_starting_after in previous response)')
1037
+ }),
1038
+
1039
+ outputSchema: z.object({
1040
+ accounts: z
1041
+ .array(
1042
+ z.object({
1043
+ id: z.string().describe('Account ID'),
1044
+ email: z.string().describe('Sending email address'),
1045
+ status: z.string().describe('Account status'),
1046
+ warmup_enabled: z.boolean().describe('Whether warmup is active for this account'),
1047
+ health_score: z.number().optional().describe('Health score (0–100)')
1048
+ })
1049
+ )
1050
+ .describe('Array of sending accounts with health data'),
1051
+ next_starting_after: z.string().optional().describe('Cursor for next page (undefined = no more pages)')
1052
+ }),
1053
+
1054
+ integration: 'instantly' as const,
1055
+ method: 'getAccountHealth' as const,
1056
+ credentialName
1057
+ })
1058
+ }
1059
+
1060
+ /**
1061
+ * Create Instantly create campaign tool
1062
+ *
1063
+ * Creates a new campaign in Instantly with optional sequences and sending configuration.
1064
+ *
1065
+ * @param credentialName - Name of credential in credentials table
1066
+ * @returns Tool that creates a new Instantly campaign
1067
+ *
1068
+ * @example
1069
+ * const createCampaign = createInstantlyCreateCampaignTool('instantly-elevasis')
1070
+ */
1071
+ export function createInstantlyCreateCampaignTool(credentialName: string): Tool {
1072
+ return createIntegrationTool({
1073
+ name: 'instantly_create_campaign',
1074
+ description: `Create a new Instantly campaign
1075
+
1076
+ USE CASES:
1077
+ - Set up a new cold outreach campaign with custom sequences
1078
+ - Create a campaign with predefined email steps and A/B variants
1079
+ - Configure sending limits and reply handling at creation time
1080
+ - Prepare a campaign shell to be populated with leads and activated later
1081
+
1082
+ REQUIRED PARAMETERS:
1083
+ - name: Display name of the new campaign
1084
+
1085
+ OPTIONAL PARAMETERS:
1086
+ - sequences: Array of sequence objects with email steps and optional A/B variants
1087
+ - email_list: Array of sending account emails to use for the campaign
1088
+ - daily_limit: Maximum emails to send per day
1089
+ - stop_on_reply: Whether to stop the sequence when a lead replies
1090
+ - stop_on_auto_reply: Whether to stop on auto-replies
1091
+ - track_opens: Whether to track email opens
1092
+ - track_clicks: Whether to track link clicks
1093
+ - text_only: Whether to send text-only emails (no HTML)
1094
+
1095
+ EXAMPLE (Simple Campaign):
1096
+ {
1097
+ "name": "Cold Outreach Q2 2025",
1098
+ "daily_limit": 50,
1099
+ "stop_on_reply": true
1100
+ }
1101
+
1102
+ EXAMPLE (Campaign with Sequence):
1103
+ {
1104
+ "name": "Agency Outreach",
1105
+ "sequences": [
1106
+ {
1107
+ "steps": [
1108
+ {
1109
+ "subject": "Quick question about {{company_name}}",
1110
+ "body": "Hi {{first_name}}, I noticed..."
1111
+ },
1112
+ {
1113
+ "subject": "Following up",
1114
+ "body": "Hi {{first_name}}, just wanted to circle back..."
1115
+ }
1116
+ ]
1117
+ }
1118
+ ],
1119
+ "daily_limit": 30,
1120
+ "stop_on_reply": true
1121
+ }
1122
+
1123
+ OUTPUT:
1124
+ {
1125
+ "success": true,
1126
+ "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43",
1127
+ "name": "Cold Outreach Q2 2025"
1128
+ }`,
1129
+
1130
+ inputSchema: z.object({
1131
+ name: z.string().describe('Display name of the new campaign'),
1132
+ sequences: z
1133
+ .array(
1134
+ z.object({
1135
+ steps: z.array(
1136
+ z.object({
1137
+ subject: z.string().describe('Email subject line for this step'),
1138
+ body: z.string().describe('Email body for this step'),
1139
+ variants: z
1140
+ .array(
1141
+ z.object({
1142
+ subject: z.string().describe('A/B variant subject line'),
1143
+ body: z.string().describe('A/B variant body')
1144
+ })
1145
+ )
1146
+ .optional()
1147
+ .describe('A/B test variants for this step')
1148
+ })
1149
+ )
1150
+ })
1151
+ )
1152
+ .optional()
1153
+ .describe('Campaign sequences with email steps'),
1154
+ email_list: z.array(z.string()).optional().describe('Sending account emails to use for the campaign'),
1155
+ daily_limit: z.number().int().min(1).optional().describe('Maximum emails to send per day'),
1156
+ stop_on_reply: z.boolean().optional().describe('Whether to stop the sequence when a lead replies'),
1157
+ stop_on_auto_reply: z.boolean().optional().describe('Whether to stop the sequence on auto-replies'),
1158
+ track_opens: z.boolean().optional().describe('Whether to track email opens'),
1159
+ track_clicks: z.boolean().optional().describe('Whether to track link clicks'),
1160
+ text_only: z.boolean().optional().describe('Whether to send text-only emails (no HTML)')
1161
+ }),
1162
+
1163
+ outputSchema: z.object({
1164
+ success: z.boolean().describe('Whether the campaign was created successfully'),
1165
+ campaign_id: z.string().describe('UUID of the newly created campaign'),
1166
+ name: z.string().describe('Name of the created campaign')
1167
+ }),
1168
+
1169
+ integration: 'instantly' as const,
1170
+ method: 'createCampaign' as const,
1171
+ credentialName
1172
+ })
1173
+ }
1174
+
1175
+ /**
1176
+ * Create Instantly create inbox test tool
1177
+ *
1178
+ * Initiates an inbox placement test to diagnose deliverability for a sending account.
1179
+ *
1180
+ * @param credentialName - Name of credential in credentials table
1181
+ * @returns Tool that creates an Instantly inbox placement test
1182
+ *
1183
+ * @example
1184
+ * const createInboxTest = createInstantlyCreateInboxTestTool('instantly-elevasis')
1185
+ */
1186
+ export function createInstantlyCreateInboxTestTool(credentialName: string): Tool {
1187
+ return createIntegrationTool({
1188
+ name: 'instantly_create_inbox_test',
1189
+ description: `Create an inbox placement test for an Instantly sending account
1190
+
1191
+ USE CASES:
1192
+ - Verify deliverability before launching a high-volume campaign
1193
+ - Diagnose spam filtering issues on a specific sending account
1194
+ - Test whether custom subject lines land in inbox or spam
1195
+ - Monitor placement across Gmail, Outlook, and other major providers
1196
+
1197
+ REQUIRED PARAMETERS:
1198
+ - email_account: The sending account to test (must be connected to workspace)
1199
+
1200
+ OPTIONAL PARAMETERS:
1201
+ - subject: Custom subject line to use in the test email
1202
+ - body: Custom body text to use in the test email
1203
+
1204
+ EXAMPLE:
1205
+ {
1206
+ "email_account": "alex@elevasis.com",
1207
+ "subject": "Quick question about your agency",
1208
+ "body": "Hi, I wanted to reach out about..."
1209
+ }
1210
+
1211
+ OUTPUT:
1212
+ {
1213
+ "success": true,
1214
+ "test_id": "test-abc123",
1215
+ "status": "pending"
1216
+ }`,
1217
+
1218
+ inputSchema: z.object({
1219
+ email_account: z.string().describe('Sending account email address to test (must be connected to workspace)'),
1220
+ subject: z.string().optional().describe('Custom subject line for the test email'),
1221
+ body: z.string().optional().describe('Custom body text for the test email')
1222
+ }),
1223
+
1224
+ outputSchema: z.object({
1225
+ success: z.boolean().describe('Whether the test was created successfully'),
1226
+ test_id: z.string().describe('Unique ID of the inbox placement test'),
1227
+ status: z.string().describe('Initial test status (e.g. "pending")')
1228
+ }),
1229
+
1230
+ integration: 'instantly' as const,
1231
+ method: 'createInboxTest' as const,
1232
+ credentialName
1233
+ })
1234
+ }
1235
+
1236
+ /**
1237
+ * Create Instantly get daily campaign analytics tool
1238
+ *
1239
+ * Retrieves day-by-day engagement breakdown for campaigns.
1240
+ *
1241
+ * @param credentialName - Name of credential in credentials table
1242
+ * @returns Tool that fetches daily campaign analytics
1243
+ *
1244
+ * @example
1245
+ * const getDailyAnalytics = createInstantlyGetDailyCampaignAnalyticsTool('instantly-elevasis')
1246
+ */
1247
+ export function createInstantlyGetDailyCampaignAnalyticsTool(credentialName: string): Tool {
1248
+ return createIntegrationTool({
1249
+ name: 'instantly_get_daily_campaign_analytics',
1250
+ description: `Get day-by-day analytics breakdown for Instantly campaigns
1251
+
1252
+ USE CASES:
1253
+ - Analyze send and open trends over a date range to identify patterns
1254
+ - Spot peak engagement days to optimize future send scheduling
1255
+ - Compare daily click and reply volumes across a campaign period
1256
+ - Get cross-campaign aggregate metrics when no campaign_id is specified
1257
+
1258
+ OPTIONAL PARAMETERS:
1259
+ - campaign_id: Filter analytics to a specific campaign UUID (omit for all campaigns)
1260
+ - start_date: Start of date range (ISO date, e.g. "2025-01-01")
1261
+ - end_date: End of date range (ISO date, e.g. "2025-03-31")
1262
+
1263
+ EXAMPLE (Single Campaign):
1264
+ {
1265
+ "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43",
1266
+ "start_date": "2025-01-01",
1267
+ "end_date": "2025-01-31"
1268
+ }
1269
+
1270
+ EXAMPLE (All Campaigns):
1271
+ {
1272
+ "start_date": "2025-01-01",
1273
+ "end_date": "2025-01-31"
1274
+ }
1275
+
1276
+ OUTPUT:
1277
+ {
1278
+ "data": [
1279
+ {
1280
+ "date": "2025-01-15",
1281
+ "sent": 120,
1282
+ "opened": 48,
1283
+ "unique_opened": 42,
1284
+ "replied": 8,
1285
+ "unique_replied": 7,
1286
+ "bounced": 1,
1287
+ "clicks": 12,
1288
+ "unique_clicks": 10
1289
+ }
1290
+ ]
1291
+ }`,
1292
+
1293
+ inputSchema: z.object({
1294
+ campaign_id: z.string().optional().describe('Campaign UUID to filter analytics (omit to get all campaigns)'),
1295
+ start_date: z.string().optional().describe('Start of date range (ISO date, e.g. "2025-01-01")'),
1296
+ end_date: z.string().optional().describe('End of date range (ISO date, e.g. "2025-03-31")')
1297
+ }),
1298
+
1299
+ outputSchema: z.object({
1300
+ data: z
1301
+ .array(
1302
+ z.object({
1303
+ date: z.string().describe('Date (YYYY-MM-DD)'),
1304
+ sent: z.number().describe('Emails sent on this date'),
1305
+ opened: z.number().describe('Total open events'),
1306
+ unique_opened: z.number().describe('Unique leads who opened'),
1307
+ replied: z.number().describe('Total reply events'),
1308
+ unique_replied: z.number().describe('Unique leads who replied'),
1309
+ bounced: z.number().describe('Emails bounced'),
1310
+ clicks: z.number().describe('Total link click events'),
1311
+ unique_clicks: z.number().describe('Unique leads who clicked')
1312
+ })
1313
+ )
1314
+ .describe('Array of daily analytics entries')
1315
+ }),
1316
+
1317
+ integration: 'instantly' as const,
1318
+ method: 'getDailyCampaignAnalytics' as const,
1319
+ credentialName
1320
+ })
1321
+ }
1322
+
1323
+ /**
1324
+ * Create Instantly list leads tool
1325
+ *
1326
+ * Lists leads with optional filtering by campaign or email and cursor pagination.
1327
+ *
1328
+ * @param credentialName - Name of credential in credentials table
1329
+ * @returns Tool that lists Instantly leads
1330
+ *
1331
+ * @example
1332
+ * const listLeads = createInstantlyListLeadsTool('instantly-elevasis')
1333
+ */
1334
+ export function createInstantlyListLeadsTool(credentialName: string): Tool {
1335
+ return createIntegrationTool({
1336
+ name: 'instantly_list_leads',
1337
+ description: `List leads in Instantly with optional filtering and pagination
1338
+
1339
+ USE CASES:
1340
+ - Retrieve all leads enrolled in a specific campaign for auditing
1341
+ - Look up a specific lead by email address to check their status
1342
+ - Paginate through a large lead list using cursor-based pagination
1343
+ - Review lead metadata including status, timestamps, and campaign assignment
1344
+
1345
+ OPTIONAL PARAMETERS:
1346
+ - campaign_id: Filter leads by campaign UUID
1347
+ - email: Filter by specific lead email address
1348
+ - limit: Number of leads per page
1349
+ - starting_after: Cursor from previous response for pagination
1350
+
1351
+ NOTE: This uses a POST endpoint per Instantly API design (complex filter arguments).
1352
+
1353
+ EXAMPLE (Campaign Leads):
1354
+ {
1355
+ "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43",
1356
+ "limit": 50
1357
+ }
1358
+
1359
+ EXAMPLE (Single Lead Lookup):
1360
+ {
1361
+ "email": "prospect@agency.com"
1362
+ }
1363
+
1364
+ OUTPUT:
1365
+ {
1366
+ "items": [
1367
+ {
1368
+ "id": "lead-abc123",
1369
+ "email": "prospect@agency.com",
1370
+ "first_name": "John",
1371
+ "last_name": "Smith",
1372
+ "company_name": "Acme Agency",
1373
+ "status": "active",
1374
+ "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43",
1375
+ "timestamp_created": "2025-01-15T09:00:00Z"
1376
+ }
1377
+ ],
1378
+ "next_starting_after": "cursor-token"
1379
+ }`,
1380
+
1381
+ inputSchema: z.object({
1382
+ campaign_id: z.string().optional().describe('Filter leads by campaign UUID'),
1383
+ email: z.string().optional().describe('Filter by specific lead email address'),
1384
+ limit: z.number().min(1).max(100).optional().describe('Number of leads per page'),
1385
+ starting_after: z
1386
+ .string()
1387
+ .optional()
1388
+ .describe('Cursor for pagination (from next_starting_after in previous response)')
1389
+ }),
1390
+
1391
+ outputSchema: z.object({
1392
+ items: z
1393
+ .array(
1394
+ z.object({
1395
+ id: z.string().describe('Lead ID'),
1396
+ email: z.string().describe('Lead email address'),
1397
+ first_name: z.string().optional().describe('Lead first name'),
1398
+ last_name: z.string().optional().describe('Lead last name'),
1399
+ company_name: z.string().optional().describe('Lead company name'),
1400
+ status: z.string().describe('Lead status in campaign'),
1401
+ campaign_id: z.string().optional().describe('Campaign this lead belongs to'),
1402
+ timestamp_created: z.string().optional().describe('Lead creation timestamp')
1403
+ })
1404
+ )
1405
+ .describe('Array of leads'),
1406
+ next_starting_after: z.string().optional().describe('Cursor for next page (undefined = no more pages)')
1407
+ }),
1408
+
1409
+ integration: 'instantly' as const,
1410
+ method: 'listLeads' as const,
1411
+ credentialName
1412
+ })
1413
+ }
1414
+
1415
+ /**
1416
+ * Create Instantly bulk delete leads tool
1417
+ *
1418
+ * Deletes leads in bulk by IDs, campaign, or list with an optional limit.
1419
+ *
1420
+ * @param credentialName - Name of credential in credentials table
1421
+ * @returns Tool that bulk-deletes Instantly leads
1422
+ *
1423
+ * @example
1424
+ * const bulkDeleteLeads = createInstantlyBulkDeleteLeadsTool('instantly-elevasis')
1425
+ */
1426
+ export function createInstantlyBulkDeleteLeadsTool(credentialName: string): Tool {
1427
+ return createIntegrationTool({
1428
+ name: 'instantly_bulk_delete_leads',
1429
+ description: `Bulk delete leads from Instantly by IDs, campaign, or list
1430
+
1431
+ USE CASES:
1432
+ - Remove a batch of disqualified leads from a campaign at once
1433
+ - Clean up leads from a completed campaign before archiving
1434
+ - Delete leads from a specific list to maintain database hygiene
1435
+ - Remove a set of specific leads by their IDs after a data audit
1436
+
1437
+ OPTIONAL PARAMETERS (at least one filter recommended):
1438
+ - ids: Array of specific lead IDs to delete
1439
+ - campaign_id: Delete all leads from this campaign
1440
+ - list_id: Delete all leads from this list
1441
+ - limit: Maximum number of leads to delete in this operation (1-10000)
1442
+
1443
+ EXAMPLE (Delete by IDs):
1444
+ {
1445
+ "ids": ["lead-abc123", "lead-def456"]
1446
+ }
1447
+
1448
+ EXAMPLE (Delete from Campaign):
1449
+ {
1450
+ "campaign_id": "019bb2e9-5d96-7f1d-9bc1-c0ef73001b43",
1451
+ "limit": 100
1452
+ }
1453
+
1454
+ OUTPUT:
1455
+ {
1456
+ "deleted_count": 42
1457
+ }`,
1458
+
1459
+ inputSchema: z.object({
1460
+ ids: z.array(z.string()).optional().describe('Specific lead IDs to delete'),
1461
+ campaign_id: z.string().optional().describe('Delete leads from this campaign'),
1462
+ list_id: z.string().optional().describe('Delete leads from this list'),
1463
+ limit: z.number().int().min(1).optional().describe('Maximum number of leads to delete')
1464
+ }),
1465
+
1466
+ outputSchema: z.object({
1467
+ deleted_count: z.number().describe('Number of leads deleted')
1468
+ }),
1469
+
1470
+ integration: 'instantly' as const,
1471
+ method: 'bulkDeleteLeads' as const,
1472
+ credentialName
1473
+ })
1474
+ }