@dench.com/cli 3.5.6-staging.344 → 3.6.0-staging.356

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.
@@ -29,6 +29,7 @@ export type CustomOperation = {
29
29
  | "commandsList"
30
30
  | "chatSpawn"
31
31
  | "chatFollow"
32
+ | "campaignBuilder"
32
33
  | "cronDelete"
33
34
  | "cronRunNow"
34
35
  | "connectionsList"
@@ -133,6 +134,219 @@ function local(reason: string): LocalOnlyOperation {
133
134
  }
134
135
 
135
136
  const rawApiOperations = [
137
+ op({
138
+ id: "campaigns.drafts.create",
139
+ group: "campaigns",
140
+ summary:
141
+ "Create an idempotent empty campaign draft with the caller's available sender pools. Does not launch or send.",
142
+ cli: "dench campaign draft",
143
+ method: "POST",
144
+ path: "/campaigns/drafts",
145
+ requestSchema: anyObject,
146
+ responseSchema: anyResponse,
147
+ backend: convex(
148
+ "mutation",
149
+ "functions/outreachSignalCampaigns:createPlainCampaign",
150
+ ),
151
+ }),
152
+ op({
153
+ id: "campaigns.audience.search",
154
+ group: "campaigns",
155
+ summary:
156
+ "Search people by filters without adding CRM records or campaign recipients. Search credits apply; this is not a free provider preview.",
157
+ cli: "dench campaign search",
158
+ method: "POST",
159
+ path: "/campaigns/audience/search",
160
+ requestSchema: anyObject,
161
+ responseSchema: anyResponse,
162
+ backend: convex("action", "functions/outreachAudienceNode:search"),
163
+ }),
164
+ op({
165
+ id: "campaigns.people.addFromSearch",
166
+ group: "campaigns",
167
+ summary:
168
+ "Search and add up to 50 people to an existing campaign. New identities become Leads. Search credits apply; use nextPageToken for another page.",
169
+ cli: "dench campaign add --search",
170
+ method: "POST",
171
+ path: "/campaigns/{sequenceId}/people/search",
172
+ requestSchema: anyObject,
173
+ responseSchema: anyResponse,
174
+ backend: convex("action", "functions/outreachAudienceNode:addFromSearch"),
175
+ }),
176
+ op({
177
+ id: "campaigns.people.addBatch",
178
+ group: "campaigns",
179
+ summary:
180
+ "Add up to 50 selected recipients without another search. Deduplicates, applies suppression, and preserves the campaign's launch state.",
181
+ cli: "dench campaign add --people-file",
182
+ method: "POST",
183
+ path: "/campaigns/{sequenceId}/people/batch",
184
+ requestSchema: anyObject,
185
+ responseSchema: anyResponse,
186
+ backend: convex("mutation", "functions/outreachSequences:addEnrollments"),
187
+ }),
188
+ op({
189
+ id: "campaigns.sources.get",
190
+ group: "campaigns",
191
+ summary: "Read a campaign's saved signal source, targeting and schedule.",
192
+ cli: "dench campaign sources get",
193
+ method: "GET",
194
+ path: "/campaigns/{sequenceId}/sources",
195
+ requestSchema: anyObject,
196
+ responseSchema: anyResponse,
197
+ backend: convex("query", "functions/outreachLeadSources:getForSequence"),
198
+ }),
199
+ op({
200
+ id: "campaigns.sources.signals",
201
+ group: "campaigns",
202
+ summary:
203
+ "Read signal choices, suggested terms and current source configuration.",
204
+ cli: "dench campaign sources signals",
205
+ method: "GET",
206
+ path: "/campaigns/{sequenceId}/sources/signals",
207
+ requestSchema: anyObject,
208
+ responseSchema: anyResponse,
209
+ backend: convex("query", "functions/outreachLeadSources:signalsOverview"),
210
+ }),
211
+ op({
212
+ id: "campaigns.sources.update",
213
+ group: "campaigns",
214
+ summary:
215
+ "Update source cadence, pause/resume, targeting, signals or terms. Active sources may perform billable future work.",
216
+ cli: "dench campaign sources set",
217
+ method: "PATCH",
218
+ path: "/campaigns/{sequenceId}/sources",
219
+ requestSchema: anyObject,
220
+ responseSchema: anyResponse,
221
+ backend: convex("mutation", "functions/outreachLeadSources:update"),
222
+ }),
223
+ op({
224
+ id: "campaigns.sources.preview",
225
+ group: "campaigns",
226
+ summary:
227
+ "Preview a signal source using its current audience. Provider credits may apply; does not enroll people.",
228
+ cli: "dench campaign sources preview",
229
+ method: "POST",
230
+ path: "/campaigns/{sequenceId}/sources/preview",
231
+ requestSchema: anyObject,
232
+ responseSchema: anyResponse,
233
+ backend: convex(
234
+ "action",
235
+ "functions/outreachLeadSourcesNode:previewSignal",
236
+ ),
237
+ }),
238
+ op({
239
+ id: "campaigns.builder.profile",
240
+ group: "campaigns",
241
+ summary: "Read the company profile and suggested campaign audience.",
242
+ cli: "dench campaign builder profile",
243
+ method: "GET",
244
+ path: "/campaigns/builder/profile",
245
+ requestSchema: anyObject,
246
+ responseSchema: anyResponse,
247
+ backend: convex("query", "functions/outreachSignalCampaigns:getProfile"),
248
+ }),
249
+ op({
250
+ id: "campaigns.builder.save",
251
+ group: "campaigns",
252
+ summary:
253
+ "Save a signal campaign's audience, signals and terms idempotently. Does not start a build.",
254
+ cli: "dench campaign builder save",
255
+ method: "POST",
256
+ path: "/campaigns/builder/drafts",
257
+ requestSchema: anyObject,
258
+ responseSchema: anyResponse,
259
+ backend: convex(
260
+ "mutation",
261
+ "functions/outreachSignalCampaigns:upsertDraft",
262
+ ),
263
+ }),
264
+ op({
265
+ id: "campaigns.builder.get",
266
+ group: "campaigns",
267
+ summary: "Read signal build progress, preview people, messages and errors.",
268
+ cli: "dench campaign builder get",
269
+ method: "GET",
270
+ path: "/campaigns/builder/drafts/{draftId}",
271
+ requestSchema: anyObject,
272
+ responseSchema: anyResponse,
273
+ backend: convex("query", "functions/outreachSignalCampaigns:getDraft"),
274
+ }),
275
+ op({
276
+ id: "campaigns.builder.build",
277
+ group: "campaigns",
278
+ summary:
279
+ "Start the same billable signal/audience/message build used by the UI; poll the draft for progress.",
280
+ cli: "dench campaign builder build",
281
+ method: "POST",
282
+ path: "/campaigns/builder/drafts/{draftId}/build",
283
+ requestSchema: anyObject,
284
+ responseSchema: anyResponse,
285
+ backend: custom("campaignBuilder", "bearer"),
286
+ }),
287
+ op({
288
+ id: "campaigns.builder.create",
289
+ group: "campaigns",
290
+ summary:
291
+ "Create a campaign from a reviewed signal draft. Does not launch the campaign.",
292
+ cli: "dench campaign builder create",
293
+ method: "POST",
294
+ path: "/campaigns/builder/drafts/{draftId}/create",
295
+ requestSchema: anyObject,
296
+ responseSchema: anyResponse,
297
+ backend: convex("mutation", "functions/outreachSignalCampaigns:create"),
298
+ }),
299
+ op({
300
+ id: "campaigns.builder.opener",
301
+ group: "campaigns",
302
+ summary:
303
+ "Edit or reset one preview person's opening line before creating the campaign.",
304
+ cli: "dench campaign builder opener",
305
+ method: "PUT",
306
+ path: "/campaigns/builder/drafts/{draftId}/people/{leadId}/opener",
307
+ requestSchema: anyObject,
308
+ responseSchema: anyResponse,
309
+ backend: convex(
310
+ "mutation",
311
+ "functions/outreachSignalCampaigns:updateLeadOpener",
312
+ ),
313
+ }),
314
+ op({
315
+ id: "campaigns.builder.autofill",
316
+ group: "campaigns",
317
+ summary:
318
+ "Turn an audience description into targeting filters. AI credits apply.",
319
+ cli: "dench campaign builder autofill",
320
+ method: "POST",
321
+ path: "/campaigns/builder/autofill",
322
+ requestSchema: anyObject,
323
+ responseSchema: anyResponse,
324
+ backend: custom("campaignBuilder", "bearer"),
325
+ }),
326
+ op({
327
+ id: "campaigns.builder.terms",
328
+ group: "campaigns",
329
+ summary:
330
+ "Suggest signal terms for the selected audience. AI credits apply.",
331
+ cli: "dench campaign builder terms",
332
+ method: "POST",
333
+ path: "/campaigns/builder/terms",
334
+ requestSchema: anyObject,
335
+ responseSchema: anyResponse,
336
+ backend: custom("campaignBuilder", "bearer"),
337
+ }),
338
+ op({
339
+ id: "campaigns.builder.estimate",
340
+ group: "campaigns",
341
+ summary:
342
+ "Estimate audience or weekly signal volume using the UI's cached estimator. Provider credits may apply.",
343
+ cli: "dench campaign builder estimate",
344
+ method: "POST",
345
+ path: "/campaigns/builder/estimate",
346
+ requestSchema: anyObject,
347
+ responseSchema: anyResponse,
348
+ backend: custom("campaignBuilder", "bearer"),
349
+ }),
136
350
  op({
137
351
  id: "commands.list",
138
352
  group: "meta",
@@ -3516,6 +3730,18 @@ const rawApiOperations = [
3516
3730
  responseSchema: anyResponse,
3517
3731
  backend: convex("query", "functions/linkedinAccounts:listSenderAccounts"),
3518
3732
  }),
3733
+ op({
3734
+ id: "campaigns.accounts.email",
3735
+ group: "campaigns",
3736
+ summary:
3737
+ "Discover the mailboxes the acting workspace member can send campaign email from: ids for emailMailboxAccountIds, status, warm-up state and when it is done, the daily cold-email cap and what is left today. Never returns provider credentials.",
3738
+ cli: "dench campaign accounts",
3739
+ method: "GET",
3740
+ path: "/campaigns/accounts/email",
3741
+ requestSchema: anyObject,
3742
+ responseSchema: anyResponse,
3743
+ backend: convex("query", "functions/emailMailboxAccounts:listAccounts"),
3744
+ }),
3519
3745
  op({
3520
3746
  id: "campaigns.list",
3521
3747
  group: "campaigns",
@@ -3539,6 +3765,21 @@ const rawApiOperations = [
3539
3765
  responseSchema: anyResponse,
3540
3766
  backend: convex("mutation", "functions/outreachSequences:createSequence"),
3541
3767
  }),
3768
+ op({
3769
+ id: "campaigns.duplicate",
3770
+ group: "campaigns",
3771
+ summary:
3772
+ 'Copy a campaign into a new draft named "<name> copy": the same steps, schedule, sender pools and personalized lines, its lead source paused, and no people. To reach more than 300 people, duplicate and add the next page of the search to the copy.',
3773
+ cli: "dench campaign duplicate",
3774
+ method: "POST",
3775
+ path: "/campaigns/{sequenceId}/duplicate",
3776
+ requestSchema: anyObject,
3777
+ responseSchema: anyResponse,
3778
+ backend: convex(
3779
+ "mutation",
3780
+ "functions/outreachSequences:duplicateSequence",
3781
+ ),
3782
+ }),
3542
3783
  op({
3543
3784
  id: "campaigns.get",
3544
3785
  group: "campaigns",
@@ -3578,6 +3819,21 @@ const rawApiOperations = [
3578
3819
  responseSchema: anyResponse,
3579
3820
  backend: convex("query", "functions/outreachSequences:getSequenceLive"),
3580
3821
  }),
3822
+ op({
3823
+ id: "campaigns.pipeline",
3824
+ group: "campaigns",
3825
+ summary:
3826
+ "Where a campaign's people sit in the workspace's lead pipeline right now: each lead Status in order with how many enrolled leads are at it, and how many have no lead or a status outside the pipeline.",
3827
+ cli: "dench campaign pipeline",
3828
+ method: "GET",
3829
+ path: "/campaigns/{sequenceId}/pipeline",
3830
+ requestSchema: anyObject,
3831
+ responseSchema: anyResponse,
3832
+ backend: convex(
3833
+ "query",
3834
+ "functions/outreachSequences:getSequenceLeadStatusCounts",
3835
+ ),
3836
+ }),
3581
3837
  op({
3582
3838
  id: "campaigns.update",
3583
3839
  group: "campaigns",
@@ -3594,7 +3850,7 @@ const rawApiOperations = [
3594
3850
  id: "campaigns.steps.replace",
3595
3851
  group: "campaigns",
3596
3852
  summary:
3597
- "Replace every step of a draft campaign (email, wait, LinkedIn visit, connection request, message). Configure sender pools on the campaign; step sender ids are legacy-only.",
3853
+ "Replace every step of a draft campaign (email, wait, LinkedIn visit, connection request, message, post reaction, InMail). Steps are checked first: a field the step type would drop, missing text or {TOKEN} single braces are rejected, not saved. Configure sender pools on the campaign; step sender ids are legacy-only.",
3598
3854
  cli: "dench campaign steps set",
3599
3855
  method: "PUT",
3600
3856
  path: "/campaigns/{sequenceId}/steps",
@@ -3602,6 +3858,102 @@ const rawApiOperations = [
3602
3858
  responseSchema: anyResponse,
3603
3859
  backend: convex("mutation", "functions/outreachSequences:replaceSteps"),
3604
3860
  }),
3861
+ op({
3862
+ id: "campaigns.steps.update",
3863
+ group: "campaigns",
3864
+ summary:
3865
+ "Edit the text, waits and conditions of a launched (or paused) campaign's steps in place. Each step keeps its id, type and place; pass the stepId and the updatedAt you read, and only the fields you change. Messages already sent or being prepared keep their text; drafts use campaigns.steps.replace.",
3866
+ cli: "dench campaign steps edit",
3867
+ method: "PATCH",
3868
+ path: "/campaigns/{sequenceId}/steps",
3869
+ requestSchema: anyObject,
3870
+ responseSchema: anyResponse,
3871
+ backend: convex("mutation", "functions/outreachSequences:updateSteps"),
3872
+ }),
3873
+ op({
3874
+ id: "campaigns.personalization.get",
3875
+ group: "campaigns",
3876
+ summary:
3877
+ "Read a campaign's personalized lines (written for each person): their {{keys}} for steps, instructions and fallbacks, how many people have them, and the cost per person.",
3878
+ cli: "dench campaign personalize get",
3879
+ method: "GET",
3880
+ path: "/campaigns/{sequenceId}/personalization",
3881
+ requestSchema: anyObject,
3882
+ responseSchema: anyResponse,
3883
+ backend: convex("query", "functions/outreachPersonalization:getStatus"),
3884
+ }),
3885
+ op({
3886
+ id: "campaigns.personalization.set",
3887
+ group: "campaigns",
3888
+ summary:
3889
+ "Define up to 5 personalized lines (each used as {{key}} in steps) and what you sell. Saving starts writing missing lines for up to 100 people, about 3 cents a person; for more it returns needsConfirm and writes nothing until campaigns.personalization.write is confirmed.",
3890
+ cli: "dench campaign personalize set",
3891
+ method: "PUT",
3892
+ path: "/campaigns/{sequenceId}/personalization",
3893
+ requestSchema: anyObject,
3894
+ responseSchema: anyResponse,
3895
+ backend: convex(
3896
+ "mutation",
3897
+ "functions/outreachPersonalization:setTemplate",
3898
+ ),
3899
+ }),
3900
+ op({
3901
+ id: "campaigns.personalization.clear",
3902
+ group: "campaigns",
3903
+ summary:
3904
+ "Turn off a campaign's personalized lines and cancel the ones not yet written. Refused while a step still uses one of the {{keys}}.",
3905
+ cli: "dench campaign personalize clear",
3906
+ method: "DELETE",
3907
+ path: "/campaigns/{sequenceId}/personalization",
3908
+ requestSchema: anyObject,
3909
+ responseSchema: anyResponse,
3910
+ backend: convex(
3911
+ "mutation",
3912
+ "functions/outreachPersonalization:setTemplate",
3913
+ ),
3914
+ }),
3915
+ op({
3916
+ id: "campaigns.personalization.write",
3917
+ group: "campaigns",
3918
+ summary:
3919
+ "Write personalized lines for the people missing them (mode missing) or rewrite everyone's (mode all). Rewriting everyone or more than 100 people returns needsConfirm and the estimate until called with confirmed: true.",
3920
+ cli: "dench campaign personalize write",
3921
+ method: "POST",
3922
+ path: "/campaigns/{sequenceId}/personalization/write",
3923
+ requestSchema: anyObject,
3924
+ responseSchema: anyResponse,
3925
+ backend: convex("mutation", "functions/outreachPersonalization:backfill"),
3926
+ }),
3927
+ op({
3928
+ id: "campaigns.personalization.edit",
3929
+ group: "campaigns",
3930
+ summary:
3931
+ "Set one person's personalized line by hand; an empty value undoes the edit. Braces are removed and text past the line's length is cut.",
3932
+ cli: "dench campaign personalize edit",
3933
+ method: "PUT",
3934
+ path: "/campaigns/people/{enrollmentId}/personalization",
3935
+ requestSchema: anyObject,
3936
+ responseSchema: anyResponse,
3937
+ backend: convex(
3938
+ "mutation",
3939
+ "functions/outreachPersonalization:updateAiVariable",
3940
+ ),
3941
+ }),
3942
+ op({
3943
+ id: "campaigns.personalization.rewrite",
3944
+ group: "campaigns",
3945
+ summary:
3946
+ "Write a fresh personalized line for one person: one key, or all of their lines when key is left out. About 3 cents.",
3947
+ cli: "dench campaign personalize rewrite",
3948
+ method: "POST",
3949
+ path: "/campaigns/people/{enrollmentId}/personalization/rewrite",
3950
+ requestSchema: anyObject,
3951
+ responseSchema: anyResponse,
3952
+ backend: convex(
3953
+ "mutation",
3954
+ "functions/outreachPersonalization:rewriteForEnrollment",
3955
+ ),
3956
+ }),
3605
3957
  op({
3606
3958
  id: "campaigns.people.addFromCrm",
3607
3959
  group: "campaigns",
@@ -3625,12 +3977,24 @@ const rawApiOperations = [
3625
3977
  responseSchema: anyResponse,
3626
3978
  backend: convex("mutation", "functions/outreachSequences:addEnrollment"),
3627
3979
  }),
3980
+ op({
3981
+ id: "campaigns.preview",
3982
+ group: "campaigns",
3983
+ summary:
3984
+ "Show the message one person gets at one email or LinkedIn step, with their fields and personalized lines filled in: subject, body, sender, recipient, merge fields still missing, warnings, and why the step would be skipped. Nothing is sent, prepared or charged.",
3985
+ cli: "dench campaign preview",
3986
+ method: "GET",
3987
+ path: "/campaigns/{sequenceId}/preview",
3988
+ requestSchema: anyObject,
3989
+ responseSchema: anyResponse,
3990
+ backend: convex("query", "functions/outreachMessagePreview:preview"),
3991
+ }),
3628
3992
  op({
3629
3993
  id: "campaigns.launch.preview",
3630
3994
  group: "campaigns",
3631
3995
  summary:
3632
- "Launch readiness: blockers grouped by problem, warnings, and the fingerprint `launch` requires.",
3633
- cli: "dench campaign launch",
3996
+ "Launch readiness without launching: blockers grouped by problem, warnings, and the fingerprint `launch` requires.",
3997
+ cli: "dench campaign launch --check",
3634
3998
  method: "GET",
3635
3999
  path: "/campaigns/{sequenceId}/launch",
3636
4000
  requestSchema: anyObject,
@@ -3674,8 +4038,10 @@ const rawApiOperations = [
3674
4038
  op({
3675
4039
  id: "campaigns.archive",
3676
4040
  group: "campaigns",
3677
- summary: "Archive a campaign and stop everyone still in flight.",
4041
+ summary:
4042
+ "Archive a campaign and stop everyone still in flight. This is the app's Delete: campaigns are never erased, and their people, leads and activity stay.",
3678
4043
  cli: "dench campaign archive",
4044
+ aliases: ["dench campaign delete"],
3679
4045
  method: "POST",
3680
4046
  path: "/campaigns/{sequenceId}/archive",
3681
4047
  requestSchema: anyObject,
@@ -3717,6 +4083,123 @@ const rawApiOperations = [
3717
4083
  responseSchema: anyResponse,
3718
4084
  backend: convex("mutation", "functions/outreachSequences:resumeEnrollment"),
3719
4085
  }),
4086
+ op({
4087
+ id: "campaigns.people.setEmail",
4088
+ group: "campaigns",
4089
+ summary:
4090
+ "Give a person in a draft campaign a new or corrected email address; their linked CRM record gets it too. Only before launch; another person in the campaign can't share it.",
4091
+ cli: "dench campaign person set --email",
4092
+ method: "PUT",
4093
+ path: "/campaigns/people/{enrollmentId}/email",
4094
+ requestSchema: anyObject,
4095
+ responseSchema: anyResponse,
4096
+ backend: convex(
4097
+ "mutation",
4098
+ "functions/outreachSequences:updateEnrollmentEmail",
4099
+ ),
4100
+ }),
4101
+ op({
4102
+ id: "campaigns.people.setLinkedin",
4103
+ group: "campaigns",
4104
+ summary:
4105
+ "Give a person in a draft campaign a new or corrected LinkedIn profile URL; their linked CRM record gets it too. Only before launch.",
4106
+ cli: "dench campaign person set --linkedin-url",
4107
+ method: "PUT",
4108
+ path: "/campaigns/people/{enrollmentId}/linkedin",
4109
+ requestSchema: anyObject,
4110
+ responseSchema: anyResponse,
4111
+ backend: convex(
4112
+ "mutation",
4113
+ "functions/outreachSequences:updateEnrollmentLinkedin",
4114
+ ),
4115
+ }),
4116
+ op({
4117
+ id: "campaigns.people.setField",
4118
+ group: "campaigns",
4119
+ summary:
4120
+ "Fill or fix one {{merge field}} for a person in a draft campaign, e.g. a missing Company that blocks launch; a matching column on their CRM record is updated too. Only before launch; written personalized lines are not merge fields and can't be set here.",
4121
+ cli: "dench campaign person set --field",
4122
+ method: "PUT",
4123
+ path: "/campaigns/people/{enrollmentId}/fields",
4124
+ requestSchema: anyObject,
4125
+ responseSchema: anyResponse,
4126
+ backend: convex(
4127
+ "mutation",
4128
+ "functions/outreachSequences:updateEnrollmentMergeField",
4129
+ ),
4130
+ }),
4131
+ op({
4132
+ id: "campaigns.people.resumeAll",
4133
+ group: "campaigns",
4134
+ summary:
4135
+ "Resume every paused person in an active campaign at once, e.g. after a sending account is reconnected. Returns how many were resumed.",
4136
+ cli: "dench campaign resume-person --all",
4137
+ method: "POST",
4138
+ path: "/campaigns/{sequenceId}/people/resume",
4139
+ requestSchema: anyObject,
4140
+ responseSchema: anyResponse,
4141
+ backend: convex(
4142
+ "mutation",
4143
+ "functions/outreachSequences:resumePausedEnrollments",
4144
+ ),
4145
+ }),
4146
+ op({
4147
+ id: "campaigns.people.sync",
4148
+ group: "campaigns",
4149
+ summary:
4150
+ "Fill each unfinished person's empty email, LinkedIn URL, name and merge fields from their linked CRM record, e.g. after enriching the records. Never overwrites a value the campaign already has; returns how many people changed.",
4151
+ cli: "dench campaign sync",
4152
+ method: "POST",
4153
+ path: "/campaigns/{sequenceId}/people/sync",
4154
+ requestSchema: anyObject,
4155
+ responseSchema: anyResponse,
4156
+ backend: convex(
4157
+ "mutation",
4158
+ "functions/outreachSequences:syncEnrollmentsFromRecords",
4159
+ ),
4160
+ }),
4161
+ op({
4162
+ id: "campaigns.people.forRecord",
4163
+ group: "campaigns",
4164
+ summary:
4165
+ "The campaigns a CRM record (a person or a lead) is in, newest first, with the person's status in each and the accounts that send to them.",
4166
+ cli: "dench campaign where",
4167
+ method: "GET",
4168
+ path: "/campaigns/by-record/{entryId}",
4169
+ requestSchema: anyObject,
4170
+ responseSchema: anyResponse,
4171
+ backend: convex(
4172
+ "query",
4173
+ "functions/outreachSequences:listEnrollmentsForEntry",
4174
+ ),
4175
+ }),
4176
+ op({
4177
+ id: "campaigns.people.timeline",
4178
+ group: "campaigns",
4179
+ summary:
4180
+ "Everything a campaign did for one person, step by step: what was sent (subject and text), whether it was opened or clicked, what was skipped and why, waits, failures and retries.",
4181
+ cli: "dench campaign person show",
4182
+ method: "GET",
4183
+ path: "/campaigns/people/{enrollmentId}/timeline",
4184
+ requestSchema: anyObject,
4185
+ responseSchema: anyResponse,
4186
+ backend: convex("query", "functions/outreachSequences:timeline"),
4187
+ }),
4188
+ op({
4189
+ id: "campaigns.people.remove",
4190
+ group: "campaigns",
4191
+ summary:
4192
+ "Remove a person who was never contacted from a draft campaign. Frees their place in the 300-person cap and keeps the draft launchable; stop anyone already contacted instead.",
4193
+ cli: "dench campaign remove",
4194
+ method: "DELETE",
4195
+ path: "/campaigns/people/{enrollmentId}",
4196
+ requestSchema: anyObject,
4197
+ responseSchema: anyResponse,
4198
+ backend: convex(
4199
+ "mutation",
4200
+ "functions/outreachSequences:removeDraftEnrollment",
4201
+ ),
4202
+ }),
3720
4203
 
3721
4204
  // Routines.
3722
4205
  op({
@@ -4142,8 +4625,8 @@ const rawApiOperations = [
4142
4625
  id: "integrations.list",
4143
4626
  group: "gateway",
4144
4627
  summary:
4145
- "List every connected integration — Composio apps plus the Gmail, " +
4146
- "Outlook, Calendar, LinkedIn and Slack connections Dench brokers " +
4628
+ "List every connected integration — connected apps plus the Gmail, " +
4629
+ "Outlook, Calendar, LinkedIn and Slack connections Dench provides " +
4147
4630
  "itself (Slack rows report source: dench).",
4148
4631
  cli: "dench integrations",
4149
4632
  method: "GET",
@@ -4315,7 +4798,7 @@ const rawApiOperations = [
4315
4798
  group: "gateway",
4316
4799
  summary:
4317
4800
  "Create a tool OAuth connection link. Slack is in-house, never " +
4318
- "Composio: for toolkit slack the CLI prints the first-party install " +
4801
+ "a third-party connection: for toolkit slack the CLI prints the first-party install " +
4319
4802
  "URL (/api/slack/install?slug=<workspace>, org admin) instead of " +
4320
4803
  "calling this endpoint.",
4321
4804
  cli: "dench tool connect",
@@ -4344,7 +4827,7 @@ const rawApiOperations = [
4344
4827
  "with Block Kit blocks/buttons, SLACK_FETCH_HISTORY, " +
4345
4828
  "SLACK_LIST_CHANNELS, …) run through Dench's in-house Slack app " +
4346
4829
  "install — any channel in the connected workspace — and are always " +
4347
- "preferred over Composio Slack.",
4830
+ "preferred over any third-party Slack connection.",
4348
4831
  cli: "dench tool run",
4349
4832
  method: "POST",
4350
4833
  path: "/tools/run",
@@ -0,0 +1,10 @@
1
+ export type ConnectionSource = "app" | "dench";
2
+
3
+ export function connectionSource(
4
+ value: unknown,
5
+ fallback: ConnectionSource,
6
+ ): ConnectionSource {
7
+ if (value === "app" || value === "composio") return "app";
8
+ if (value === "dench" || value === "unipile") return "dench";
9
+ return fallback;
10
+ }
@@ -53,6 +53,15 @@ export type SearchFilterItem =
53
53
 
54
54
  export type SearchRangeFilter = { min?: number; max?: number };
55
55
 
56
+ export type SearchBilling = {
57
+ estimatedCents: number;
58
+ chargedCents: number;
59
+ };
60
+
61
+ export type SearchResponse = Record<string, unknown> & {
62
+ billing?: SearchBilling;
63
+ };
64
+
56
65
  export type SearchPeopleBody = {
57
66
  /** Canonical gateway fields. locations is the PERSON; companyHeadquarters is the employer. */
58
67
  linkedinUrl?: string;
@@ -159,6 +168,7 @@ export type SearchSignalsBody = {
159
168
  headcountRanges?: string[];
160
169
  excludeDomains?: string[];
161
170
  excludeCompanies?: string[];
171
+ includeCompanies?: string[];
162
172
  postedAfter: string;
163
173
  limit?: number;
164
174
  terms?: string[];
@@ -196,7 +206,11 @@ export type SearchSignalsResponse = {
196
206
  signals: SignalRow[];
197
207
  creditsCharged: number;
198
208
  volume?: { term: string; count: number; saturated?: true }[];
199
- metadata?: { warnings?: string[] };
209
+ metadata?: {
210
+ warnings?: string[];
211
+ checked?: number;
212
+ misses?: { title: number; location: number; company: number };
213
+ };
200
214
  };
201
215
 
202
216
  /**
@@ -1541,7 +1555,7 @@ export function buildPeopleSearchRequest(
1541
1555
  export async function searchPeople(
1542
1556
  body: SearchPeopleBody,
1543
1557
  options: EnrichmentGatewayOptions = {},
1544
- ): Promise<Record<string, unknown>> {
1558
+ ): Promise<SearchResponse> {
1545
1559
  return callGatewayJson("/v1/enrichment/people/search", {
1546
1560
  method: "POST",
1547
1561
  body: buildPeopleSearchRequest(body),
@@ -1552,7 +1566,7 @@ export async function searchPeople(
1552
1566
  export async function searchCompanies(
1553
1567
  body: SearchCompaniesBody,
1554
1568
  options: EnrichmentGatewayOptions = {},
1555
- ): Promise<Record<string, unknown>> {
1569
+ ): Promise<SearchResponse> {
1556
1570
  const payload: Record<string, unknown> = {
1557
1571
  preferCache: body.preferCache ?? true,
1558
1572
  };
@@ -1616,6 +1630,8 @@ export async function searchSignals(
1616
1630
  if (body.excludeDomains?.length) payload.excludeDomains = body.excludeDomains;
1617
1631
  if (body.excludeCompanies?.length)
1618
1632
  payload.excludeCompanies = body.excludeCompanies;
1633
+ if (body.includeCompanies?.length)
1634
+ payload.includeCompanies = body.includeCompanies;
1619
1635
  if (body.limit !== undefined) payload.limit = body.limit;
1620
1636
  if (body.terms?.length) payload.terms = body.terms;
1621
1637
  if (body.recency) payload.recency = body.recency;
@@ -1653,6 +1669,7 @@ export type SignalBuyerBody = {
1653
1669
  companyDomain: string;
1654
1670
  titles?: string[];
1655
1671
  withEmail?: boolean;
1672
+ limit?: number;
1656
1673
  };
1657
1674
 
1658
1675
  export type SignalBuyer = {
@@ -1665,6 +1682,7 @@ export type SignalBuyer = {
1665
1682
 
1666
1683
  export type SignalBuyerResponse = {
1667
1684
  buyer: SignalBuyer | null;
1685
+ buyers?: SignalBuyer[];
1668
1686
  reveal?: "found" | "not_found" | "failed";
1669
1687
  };
1670
1688
 
@@ -1687,6 +1705,7 @@ export async function findSignalBuyer(
1687
1705
  };
1688
1706
  if (body.titles?.length) payload.titles = body.titles;
1689
1707
  if (body.withEmail !== undefined) payload.withEmail = body.withEmail;
1708
+ if (body.limit !== undefined) payload.limit = body.limit;
1690
1709
  return (await callGatewayJson("/v1/enrichment/signals/buyer", {
1691
1710
  method: "POST",
1692
1711
  body: payload,