@dench.com/cli 3.8.0-staging.389 → 3.8.2-staging.391
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ads.ts +6 -2
- package/app-sdk.d.ts +132 -27
- package/apps.ts +7 -3
- package/browser.ts +8 -3
- package/campaign.ts +336 -55
- package/chat.ts +8 -6
- package/cron.ts +7 -3
- package/dench.ts +52 -94
- package/image.ts +6 -2
- package/leads.ts +6 -2
- package/lib/api-schemas.ts +186 -20
- package/lib/app-sdk-catalog.generated.ts +3 -3
- package/lib/campaign-steps.ts +474 -12
- package/lib/campaign-timeline.ts +69 -4
- package/lib/command-args.ts +26 -9
- package/lib/command-help.ts +192 -39
- package/lib/command-registry.ts +3 -3
- package/lib/file-command-contract.ts +106 -0
- package/lib/references.generated.json +14 -14
- package/meetings.ts +6 -2
- package/members.ts +6 -2
- package/package.json +1 -1
- package/search.ts +6 -2
- package/tools.ts +9 -5
- package/web.ts +20 -6
package/lib/api-schemas.ts
CHANGED
|
@@ -20,15 +20,23 @@
|
|
|
20
20
|
*/
|
|
21
21
|
import { type ZodTypeAny, z } from "zod";
|
|
22
22
|
import {
|
|
23
|
+
CONDITION_CHECKS,
|
|
24
|
+
CONDITION_OPERATORS,
|
|
23
25
|
checkCampaignSteps,
|
|
24
26
|
checkStepEdits,
|
|
27
|
+
hasWorkflowShape,
|
|
25
28
|
LINKEDIN_REACTIONS,
|
|
29
|
+
MAX_ACCEPTANCE_WAIT_HOURS,
|
|
26
30
|
MAX_CAMPAIGN_STEPS,
|
|
27
31
|
MAX_INMAIL_BODY_CHARS,
|
|
28
32
|
MAX_INMAIL_SUBJECT_CHARS,
|
|
29
33
|
MAX_LIKE_POST_AGE_DAYS,
|
|
34
|
+
MAX_LIKE_POST_COUNT,
|
|
30
35
|
MAX_WAIT_HOURS,
|
|
36
|
+
MIN_ACCEPTANCE_WAIT_HOURS,
|
|
31
37
|
MIN_WITHDRAW_AFTER_HOURS,
|
|
38
|
+
STEP_TYPES,
|
|
39
|
+
WORKFLOW_END,
|
|
32
40
|
} from "./campaign-steps";
|
|
33
41
|
|
|
34
42
|
const nativeEmailRecipient = z.object({
|
|
@@ -667,16 +675,25 @@ const campaignSendSchedule = z
|
|
|
667
675
|
);
|
|
668
676
|
|
|
669
677
|
const campaignStepType = z
|
|
670
|
-
.enum(
|
|
671
|
-
|
|
672
|
-
"
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
678
|
+
.enum(STEP_TYPES)
|
|
679
|
+
.describe(
|
|
680
|
+
"Step type. condition (If / else) and wait_for_connection (Wait for them to accept) send nothing; they continue on yesNext or noNext and make the campaign a workflow.",
|
|
681
|
+
);
|
|
682
|
+
|
|
683
|
+
const workflowTarget = (exit: string) =>
|
|
684
|
+
z
|
|
685
|
+
.string()
|
|
686
|
+
.min(1)
|
|
687
|
+
.optional()
|
|
688
|
+
.describe(
|
|
689
|
+
`Workflow steps: ${exit}: another step's key, or "${WORKFLOW_END}" to stop there. Fixed once the campaign launches.`,
|
|
690
|
+
);
|
|
691
|
+
|
|
692
|
+
const campaignStepModel = z
|
|
693
|
+
.enum(["linear", "workflow"])
|
|
694
|
+
.describe(
|
|
695
|
+
"linear: steps run in order. workflow: steps have keys and paths (next, yesNext, noNext) and may include condition and wait_for_connection. Left out, a workflow stays one and steps with keys, paths or those types make one; nothing turns a workflow back into a list.",
|
|
696
|
+
);
|
|
680
697
|
|
|
681
698
|
const campaignStepInput = z
|
|
682
699
|
.object({
|
|
@@ -723,7 +740,7 @@ const campaignStepInput = z
|
|
|
723
740
|
.string()
|
|
724
741
|
.optional()
|
|
725
742
|
.describe(
|
|
726
|
-
"Legacy LinkedIn step sender. Pool campaigns use the campaign's linkedinAccountIds instead.",
|
|
743
|
+
"Legacy LinkedIn step sender (also on wait_for_connection, and on a condition that checks linkedin_connected). Pool campaigns use the campaign's linkedinAccountIds instead.",
|
|
727
744
|
),
|
|
728
745
|
linkedinMessageBody: z
|
|
729
746
|
.string()
|
|
@@ -743,7 +760,7 @@ const campaignStepInput = z
|
|
|
743
760
|
.max(MAX_WAIT_HOURS)
|
|
744
761
|
.optional()
|
|
745
762
|
.describe(
|
|
746
|
-
`linkedin_message with linkedinMessageMode=wait_until_connected: wait 1-${MAX_WAIT_HOURS} hours for acceptance (default 720)
|
|
763
|
+
`linkedin_message with linkedinMessageMode=wait_until_connected: wait 1-${MAX_WAIT_HOURS} hours for acceptance (default 720); after a connection step this mode is the default in a linear campaign. wait_for_connection: wait ${MIN_ACCEPTANCE_WAIT_HOURS}-${MAX_ACCEPTANCE_WAIT_HOURS} hours (1-90 days, default 720) before taking the Not accepted path.`,
|
|
747
764
|
),
|
|
748
765
|
likeReaction: z
|
|
749
766
|
.enum(LINKEDIN_REACTIONS)
|
|
@@ -760,12 +777,27 @@ const campaignStepInput = z
|
|
|
760
777
|
.describe(
|
|
761
778
|
`linkedin_like_post only: select the newest eligible original post from the last 1-${MAX_LIKE_POST_AGE_DAYS} days (default 30).`,
|
|
762
779
|
),
|
|
780
|
+
likeSkipPreviousWait: z
|
|
781
|
+
.boolean()
|
|
782
|
+
.optional()
|
|
783
|
+
.describe(
|
|
784
|
+
"linkedin_like_post only: before the immediately preceding wait starts, check for eligible posts. If none exist, skip that wait and the like step, keeping following waits. Defaults to false when omitted.",
|
|
785
|
+
),
|
|
763
786
|
likeSkipNextWait: z
|
|
764
787
|
.boolean()
|
|
765
788
|
.optional()
|
|
766
789
|
.describe(
|
|
767
790
|
"linkedin_like_post only: skip consecutive following wait steps whenever the reaction step is skipped (default true). False keeps those waits.",
|
|
768
791
|
),
|
|
792
|
+
likeCount: z
|
|
793
|
+
.number()
|
|
794
|
+
.int()
|
|
795
|
+
.min(1)
|
|
796
|
+
.max(MAX_LIKE_POST_COUNT)
|
|
797
|
+
.optional()
|
|
798
|
+
.describe(
|
|
799
|
+
`linkedin_like_post only: how many recent posts to react to, 1-${MAX_LIKE_POST_COUNT} (default 1).`,
|
|
800
|
+
),
|
|
769
801
|
inmailSubject: z
|
|
770
802
|
.string()
|
|
771
803
|
.optional()
|
|
@@ -781,6 +813,46 @@ const campaignStepInput = z
|
|
|
781
813
|
.describe(
|
|
782
814
|
`linkedin_connection only: withdraw a still-pending invitation sent by this campaign after ${MIN_WITHDRAW_AFTER_HOURS}-${MAX_WAIT_HOURS} hours (7-90 days). Omitted means off on creation; null turns it off on edit.`,
|
|
783
815
|
),
|
|
816
|
+
key: z
|
|
817
|
+
.string()
|
|
818
|
+
.min(1)
|
|
819
|
+
.optional()
|
|
820
|
+
.describe(
|
|
821
|
+
`Workflow steps: this step's id, unique in the campaign, which next, yesNext and noNext point at (not "${WORKFLOW_END}"). Left out, one is made.`,
|
|
822
|
+
),
|
|
823
|
+
next: workflowTarget(
|
|
824
|
+
"where a step that isn't a condition or wait_for_connection goes; left out, the following step",
|
|
825
|
+
),
|
|
826
|
+
yesNext: workflowTarget(
|
|
827
|
+
"condition and wait_for_connection only, required: where the Yes (Accepted) path goes",
|
|
828
|
+
),
|
|
829
|
+
noNext: workflowTarget(
|
|
830
|
+
"condition and wait_for_connection only, required: where the No (Not accepted) path goes",
|
|
831
|
+
),
|
|
832
|
+
check: z
|
|
833
|
+
.enum(CONDITION_CHECKS)
|
|
834
|
+
.optional()
|
|
835
|
+
.describe(
|
|
836
|
+
"condition only, required: linkedin_connected, has_email, opened_last_email (needs an email before it on every path) or field_value. Fixed once the campaign launches.",
|
|
837
|
+
),
|
|
838
|
+
conditionField: z
|
|
839
|
+
.string()
|
|
840
|
+
.optional()
|
|
841
|
+
.describe(
|
|
842
|
+
'condition with check=field_value: the lead field to check, like "Job Title" or "Source" (the campaign itself changes Status, Last touch and LinkedIn connection). Fixed once the campaign launches.',
|
|
843
|
+
),
|
|
844
|
+
conditionOperator: z
|
|
845
|
+
.enum(CONDITION_OPERATORS)
|
|
846
|
+
.optional()
|
|
847
|
+
.describe(
|
|
848
|
+
"condition with check=field_value: is, is_not, contains, is_empty or is_not_empty (all case-insensitive). Fixed once the campaign launches.",
|
|
849
|
+
),
|
|
850
|
+
conditionValue: z
|
|
851
|
+
.string()
|
|
852
|
+
.optional()
|
|
853
|
+
.describe(
|
|
854
|
+
"condition with check=field_value and is, is_not or contains: the text to compare with. Editable after launch.",
|
|
855
|
+
),
|
|
784
856
|
})
|
|
785
857
|
.describe("One campaign step, in order.");
|
|
786
858
|
|
|
@@ -814,10 +886,16 @@ const campaignTimelineRun = z
|
|
|
814
886
|
.optional()
|
|
815
887
|
.describe("The email this run sent, in emailMessages[]."),
|
|
816
888
|
lastError: z.string().optional(),
|
|
889
|
+
timeoutAt: z
|
|
890
|
+
.number()
|
|
891
|
+
.optional()
|
|
892
|
+
.describe("wait_for_connection: when it stops waiting for the accept."),
|
|
817
893
|
result: z
|
|
818
894
|
.unknown()
|
|
819
895
|
.optional()
|
|
820
|
-
.describe(
|
|
896
|
+
.describe(
|
|
897
|
+
'For a skipped step, { reason } says why. A finished condition or wait_for_connection has { branch: "yes" | "no" } (Yes / Accepted or No / Not accepted), and { reason } for some No paths.',
|
|
898
|
+
),
|
|
821
899
|
})
|
|
822
900
|
.passthrough();
|
|
823
901
|
|
|
@@ -863,6 +941,7 @@ const campaignRow = z
|
|
|
863
941
|
sendSchedules: z.array(campaignSendSchedule).optional(),
|
|
864
942
|
dailySendCap: z.number().optional(),
|
|
865
943
|
stepCount: z.number(),
|
|
944
|
+
stepModel: campaignStepModel.optional(),
|
|
866
945
|
totalEnrolled: z.number(),
|
|
867
946
|
activeCount: z.number(),
|
|
868
947
|
pausedCount: z.number(),
|
|
@@ -3110,7 +3189,12 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
|
|
|
3110
3189
|
.min(1)
|
|
3111
3190
|
.max(MAX_CAMPAIGN_STEPS)
|
|
3112
3191
|
.describe(
|
|
3113
|
-
"Every step, in send order. Rejected, not trimmed: a field the step type would drop (onlyIf off email/post-like steps, a body on a visit, reply-in-thread on the first email), a missing subject or required body, an overlength note/message/InMail subject, out-of-range wait/withdrawal/post-age values,
|
|
3192
|
+
"Every step, in send order (a workflow may list them in any order; they are stored in path order). Rejected, not trimmed: a field the step type would drop (onlyIf off email/post-like steps, a body on a visit, reply-in-thread on the first email, condition fields its check ignores), a missing subject or required body, an overlength note/message/InMail subject, out-of-range wait/withdrawal/post-age/acceptance values, {TOKEN} single braces, which would be sent as typed, and workflow paths that don't hold together (duplicate keys, a target that isn't a key or end, a condition or wait_for_connection without both yesNext and noNext, or any other step with them).",
|
|
3193
|
+
),
|
|
3194
|
+
stepModel: campaignStepModel
|
|
3195
|
+
.optional()
|
|
3196
|
+
.describe(
|
|
3197
|
+
"linear: steps run in order. workflow: steps have keys and paths (next, yesNext, noNext) and may include condition and wait_for_connection. Left out, a workflow stays one and steps with keys, paths or those types make one; nothing turns a workflow back into a list. When replacing a workflow's steps, send workflow, so steps without keys or paths are checked here the way they will be saved.",
|
|
3114
3198
|
),
|
|
3115
3199
|
allowSingleBraces: z
|
|
3116
3200
|
.boolean()
|
|
@@ -3121,8 +3205,20 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
|
|
|
3121
3205
|
})
|
|
3122
3206
|
.strict()
|
|
3123
3207
|
.superRefine((input, ctx) => {
|
|
3208
|
+
if (input.stepModel === "linear") {
|
|
3209
|
+
const shaped = input.steps.findIndex(hasWorkflowShape);
|
|
3210
|
+
if (shaped >= 0)
|
|
3211
|
+
ctx.addIssue({
|
|
3212
|
+
code: "custom",
|
|
3213
|
+
message: `Step ${shaped + 1}: condition, wait_for_connection, key, next, yesNext and noNext only work in a workflow. Send stepModel "workflow", or leave it out.`,
|
|
3214
|
+
path: ["steps", shaped],
|
|
3215
|
+
});
|
|
3216
|
+
}
|
|
3124
3217
|
for (const issue of checkCampaignSteps(input.steps, {
|
|
3125
3218
|
allowSingleBraces: input.allowSingleBraces,
|
|
3219
|
+
...(input.stepModel
|
|
3220
|
+
? { workflow: input.stepModel === "workflow" }
|
|
3221
|
+
: {}),
|
|
3126
3222
|
}).errors)
|
|
3127
3223
|
ctx.addIssue({
|
|
3128
3224
|
code: "custom",
|
|
@@ -3154,13 +3250,13 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
|
|
|
3154
3250
|
})
|
|
3155
3251
|
.strict()
|
|
3156
3252
|
.describe(
|
|
3157
|
-
"One step edit: only the fields given change. Clear text with an empty string, onlyIf with always, emailReplyToPrevious with false and withdrawAfterHours with null. Like settings are optional; omitted values stay unchanged.",
|
|
3253
|
+
"One step edit: only the fields given change. Clear text with an empty string, onlyIf with always, emailReplyToPrevious with false and withdrawAfterHours with null. Like settings are optional; omitted values stay unchanged. A workflow step's key, next, yesNext, noNext, check, conditionField and conditionOperator can't change after launch (send them unchanged or leave them out); conditionValue and a wait_for_connection's connectionTimeoutHours can.",
|
|
3158
3254
|
),
|
|
3159
3255
|
)
|
|
3160
3256
|
.min(1)
|
|
3161
3257
|
.max(MAX_CAMPAIGN_STEPS)
|
|
3162
3258
|
.describe(
|
|
3163
|
-
"The steps to change. Rejected, not trimmed: a field the step type would drop, an overlength note/message/InMail subject, out-of-range wait/withdrawal/post-age values,
|
|
3259
|
+
"The steps to change. Rejected, not trimmed: a field the step type would drop, an overlength note/message/InMail subject, out-of-range wait/withdrawal/post-age/acceptance values, {TOKEN} single braces, and a changed path or If / else check.",
|
|
3164
3260
|
),
|
|
3165
3261
|
allowSingleBraces: z
|
|
3166
3262
|
.boolean()
|
|
@@ -5039,7 +5135,11 @@ export const responseSchemas: Record<string, ZodTypeAny> = {
|
|
|
5039
5135
|
"campaigns.get": z
|
|
5040
5136
|
.object({
|
|
5041
5137
|
sequence: campaignRow,
|
|
5042
|
-
steps: z
|
|
5138
|
+
steps: z
|
|
5139
|
+
.array(campaignStepInput.passthrough())
|
|
5140
|
+
.describe(
|
|
5141
|
+
"The steps in order, each with its _id. condition and wait_for_connection steps also carry a readable label.",
|
|
5142
|
+
),
|
|
5043
5143
|
enrollments: z.array(z.record(z.string(), z.unknown())),
|
|
5044
5144
|
currentRuns: z
|
|
5045
5145
|
.record(z.string(), z.unknown())
|
|
@@ -5079,6 +5179,22 @@ export const responseSchemas: Record<string, ZodTypeAny> = {
|
|
|
5079
5179
|
replyRate: z.number(),
|
|
5080
5180
|
openRate: z.number(),
|
|
5081
5181
|
clickRate: z.number(),
|
|
5182
|
+
branchStats: z
|
|
5183
|
+
.array(
|
|
5184
|
+
z.object({
|
|
5185
|
+
stepId: z.string(),
|
|
5186
|
+
yes: z
|
|
5187
|
+
.number()
|
|
5188
|
+
.describe("People who took the Yes (Accepted) path."),
|
|
5189
|
+
no: z
|
|
5190
|
+
.number()
|
|
5191
|
+
.describe("People who took the No (Not accepted) path."),
|
|
5192
|
+
}),
|
|
5193
|
+
)
|
|
5194
|
+
.optional()
|
|
5195
|
+
.describe(
|
|
5196
|
+
"Workflows only: each condition and wait_for_connection step, in step order, with how many people took each path.",
|
|
5197
|
+
),
|
|
5082
5198
|
truncated: z.boolean(),
|
|
5083
5199
|
})
|
|
5084
5200
|
.passthrough(),
|
|
@@ -5091,7 +5207,28 @@ export const responseSchemas: Record<string, ZodTypeAny> = {
|
|
|
5091
5207
|
dailySendCap: z.number().nullable(),
|
|
5092
5208
|
nextEmailAt: z.number().nullable(),
|
|
5093
5209
|
now: z.record(z.string(), z.unknown()).nullable(),
|
|
5094
|
-
next: z
|
|
5210
|
+
next: z
|
|
5211
|
+
.object({
|
|
5212
|
+
kind: z
|
|
5213
|
+
.string()
|
|
5214
|
+
.describe(
|
|
5215
|
+
"profile, visit, invite, message, like, inmail, withdraw, email, wait or check (an If / else).",
|
|
5216
|
+
),
|
|
5217
|
+
acceptanceDeadline: z
|
|
5218
|
+
.number()
|
|
5219
|
+
.optional()
|
|
5220
|
+
.describe(
|
|
5221
|
+
"Waiting for them to accept: when the wait ends either way.",
|
|
5222
|
+
),
|
|
5223
|
+
label: z
|
|
5224
|
+
.string()
|
|
5225
|
+
.optional()
|
|
5226
|
+
.describe(
|
|
5227
|
+
'kind check: what the If / else checks, e.g. "Has an email address".',
|
|
5228
|
+
),
|
|
5229
|
+
})
|
|
5230
|
+
.passthrough()
|
|
5231
|
+
.nullable(),
|
|
5095
5232
|
paused: z.record(z.string(), z.unknown()).nullable(),
|
|
5096
5233
|
senders: z.array(z.record(z.string(), z.unknown())),
|
|
5097
5234
|
})
|
|
@@ -6387,18 +6524,41 @@ export const requestExamples: Record<string, unknown> = {
|
|
|
6387
6524
|
},
|
|
6388
6525
|
"campaigns.steps.replace": {
|
|
6389
6526
|
sequenceId: "<sequenceId>",
|
|
6527
|
+
stepModel: "workflow",
|
|
6390
6528
|
steps: [
|
|
6391
|
-
{ type: "linkedin_visit" },
|
|
6392
6529
|
{
|
|
6530
|
+
key: "connected",
|
|
6531
|
+
type: "condition",
|
|
6532
|
+
check: "linkedin_connected",
|
|
6533
|
+
yesNext: "message",
|
|
6534
|
+
noNext: "invite",
|
|
6535
|
+
},
|
|
6536
|
+
{
|
|
6537
|
+
key: "invite",
|
|
6393
6538
|
type: "linkedin_connection",
|
|
6394
6539
|
linkedinMessageBody:
|
|
6395
6540
|
"Hi {{First name}}, enjoyed your post on {{Topic}}.",
|
|
6541
|
+
next: "accept",
|
|
6542
|
+
},
|
|
6543
|
+
{
|
|
6544
|
+
key: "accept",
|
|
6545
|
+
type: "wait_for_connection",
|
|
6546
|
+
connectionTimeoutHours: 240,
|
|
6547
|
+
yesNext: "message",
|
|
6548
|
+
noNext: "email",
|
|
6396
6549
|
},
|
|
6397
|
-
{ type: "wait", waitHours: 48 },
|
|
6398
6550
|
{
|
|
6551
|
+
key: "email",
|
|
6399
6552
|
type: "email",
|
|
6400
6553
|
emailSubject: "Quick question, {{First name}}",
|
|
6401
6554
|
emailTextBody: "Hi {{First name}}, ...",
|
|
6555
|
+
next: "end",
|
|
6556
|
+
},
|
|
6557
|
+
{
|
|
6558
|
+
key: "message",
|
|
6559
|
+
type: "linkedin_message",
|
|
6560
|
+
linkedinMessageBody: "Thanks for connecting, {{First name}}. ...",
|
|
6561
|
+
next: "end",
|
|
6402
6562
|
},
|
|
6403
6563
|
],
|
|
6404
6564
|
},
|
|
@@ -6417,6 +6577,12 @@ export const requestExamples: Record<string, unknown> = {
|
|
|
6417
6577
|
type: "wait",
|
|
6418
6578
|
waitHours: 96,
|
|
6419
6579
|
},
|
|
6580
|
+
{
|
|
6581
|
+
stepId: "<waitForConnectionStepId>",
|
|
6582
|
+
expectedUpdatedAt: 1765526400000,
|
|
6583
|
+
type: "wait_for_connection",
|
|
6584
|
+
connectionTimeoutHours: 336,
|
|
6585
|
+
},
|
|
6420
6586
|
],
|
|
6421
6587
|
},
|
|
6422
6588
|
"campaigns.personalization.set": {
|
|
@@ -381,12 +381,12 @@ export const APP_SDK_OPERATIONS: readonly AppSdkOperationInfo[] = [
|
|
|
381
381
|
{"id":"campaigns.create","group":"campaigns","summary":"Create a draft outreach campaign with email and LinkedIn sender pools and a business-hours send window in the given timezone.","cli":"dench campaign create","method":"POST","path":"/campaigns","permission":"campaigns:write","pathParams":[]},
|
|
382
382
|
{"id":"campaigns.duplicate","group":"campaigns","summary":"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.","cli":"dench campaign duplicate","method":"POST","path":"/campaigns/{sequenceId}/duplicate","permission":"campaigns:write","pathParams":["sequenceId"]},
|
|
383
383
|
{"id":"campaigns.get","group":"campaigns","summary":"Read a campaign: settings, steps, people, their current step and hold, last touches, linked leads.","cli":"dench campaign get","method":"GET","path":"/campaigns/{sequenceId}","permission":"campaigns:read","pathParams":["sequenceId"]},
|
|
384
|
-
{"id":"campaigns.analytics","group":"campaigns","summary":"Campaign funnel: enrolled, contacted, invited, accepted, messaged, replied, opens and clicks.","cli":"dench campaign get","method":"GET","path":"/campaigns/{sequenceId}/analytics","permission":"campaigns:read","pathParams":["sequenceId"]},
|
|
384
|
+
{"id":"campaigns.analytics","group":"campaigns","summary":"Campaign funnel: enrolled, contacted, invited, accepted, messaged, replied, opens and clicks, and for a workflow how many people took each If / else and Wait for them to accept path (branchStats).","cli":"dench campaign get","method":"GET","path":"/campaigns/{sequenceId}/analytics","permission":"campaigns:read","pathParams":["sequenceId"]},
|
|
385
385
|
{"id":"campaigns.live","group":"campaigns","summary":"What the campaign is doing now, what comes next and when, and why it is holding.","cli":"dench campaign get","method":"GET","path":"/campaigns/{sequenceId}/live","permission":"campaigns:read","pathParams":["sequenceId"]},
|
|
386
386
|
{"id":"campaigns.pipeline","group":"campaigns","summary":"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.","cli":"dench campaign pipeline","method":"GET","path":"/campaigns/{sequenceId}/pipeline","permission":"campaigns:read","pathParams":["sequenceId"]},
|
|
387
387
|
{"id":"campaigns.update","group":"campaigns","summary":"Change a campaign's name, sender pools, send windows, daily email cap, stop-on-reply, start or end. Works until archived; existing conversations keep their assigned sender.","cli":"dench campaign set","method":"PATCH","path":"/campaigns/{sequenceId}","permission":"campaigns:write","pathParams":["sequenceId"]},
|
|
388
|
-
{"id":"campaigns.steps.replace","group":"campaigns","summary":"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
|
|
389
|
-
{"id":"campaigns.steps.update","group":"campaigns","summary":"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.","cli":"dench campaign steps edit","method":"PATCH","path":"/campaigns/{sequenceId}/steps","permission":"campaigns:write","pathParams":["sequenceId"]},
|
|
388
|
+
{"id":"campaigns.steps.replace","group":"campaigns","summary":"Replace every step of a draft campaign (email, wait, LinkedIn visit, connection request, message, post reaction, InMail, If / else, Wait for them to accept). A workflow (stepModel workflow, or steps with key, next, yesNext or noNext) branches: condition and wait_for_connection steps continue on yesNext or noNext, other steps on next; each points at another step's key or end. Steps are checked first: a field the step type would drop, missing text, {TOKEN} single braces or paths that don't hold together are rejected, not saved. Configure sender pools on the campaign; step sender ids are legacy-only.","cli":"dench campaign steps set","method":"PUT","path":"/campaigns/{sequenceId}/steps","permission":"campaigns:write","pathParams":["sequenceId"]},
|
|
389
|
+
{"id":"campaigns.steps.update","group":"campaigns","summary":"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. A workflow keeps its paths and what each If / else checks (key, next, yesNext, noNext, check, conditionField, conditionOperator); conditionValue and acceptance waits stay editable. Messages already sent or being prepared keep their text; drafts use campaigns.steps.replace.","cli":"dench campaign steps edit","method":"PATCH","path":"/campaigns/{sequenceId}/steps","permission":"campaigns:write","pathParams":["sequenceId"]},
|
|
390
390
|
{"id":"campaigns.personalization.get","group":"campaigns","summary":"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.","cli":"dench campaign personalize get","method":"GET","path":"/campaigns/{sequenceId}/personalization","permission":"campaigns:read","pathParams":["sequenceId"]},
|
|
391
391
|
{"id":"campaigns.personalization.set","group":"campaigns","summary":"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.","cli":"dench campaign personalize set","method":"PUT","path":"/campaigns/{sequenceId}/personalization","permission":"campaigns:write","pathParams":["sequenceId"]},
|
|
392
392
|
{"id":"campaigns.personalization.clear","group":"campaigns","summary":"Turn off a campaign's personalized lines and cancel the ones not yet written. Refused while a step still uses one of the {{keys}}.","cli":"dench campaign personalize clear","method":"DELETE","path":"/campaigns/{sequenceId}/personalization","permission":"campaigns:write","pathParams":["sequenceId"]},
|