@devwithbobby/loops 0.1.8 → 0.1.10
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/dist/client/index.d.ts +7 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +6 -0
- package/dist/component/lib.d.ts +18 -4
- package/dist/component/lib.d.ts.map +1 -1
- package/dist/component/lib.js +158 -93
- package/package.json +1 -1
- package/src/client/index.ts +7 -0
- package/src/component/lib.ts +170 -102
package/dist/client/index.d.ts
CHANGED
|
@@ -148,11 +148,18 @@ export declare class Loops {
|
|
|
148
148
|
/**
|
|
149
149
|
* Trigger a loop for a contact
|
|
150
150
|
* Loops are automated email sequences that can be triggered by events
|
|
151
|
+
*
|
|
152
|
+
* Note: Loops.so doesn't have a direct loop trigger endpoint.
|
|
153
|
+
* Loops are triggered through events. Make sure your loop is configured
|
|
154
|
+
* in the Loops dashboard to listen for events.
|
|
155
|
+
*
|
|
156
|
+
* @param options.eventName - Optional event name. If not provided, uses `loop_{loopId}`
|
|
151
157
|
*/
|
|
152
158
|
triggerLoop(ctx: RunActionCtx, options: {
|
|
153
159
|
loopId: string;
|
|
154
160
|
email: string;
|
|
155
161
|
dataVariables?: Record<string, any>;
|
|
162
|
+
eventName?: string;
|
|
156
163
|
}): Promise<FunctionReturnType<Action>>;
|
|
157
164
|
/**
|
|
158
165
|
* For easy re-exporting.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gCAAgC,CAAC;AAC7D,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEpE,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;AAE5C,MAAM,WAAW,WAAW;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,yBAAyB;IACzC,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,YAAY;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CACtC;AAED,qBAAa,KAAK;IACjB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAiB;IAC3C,SAAgB,OAAO,CAAC,EAAE;QACzB,MAAM,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;gBAGD,SAAS,EAAE,cAAc,EACzB,OAAO,CAAC,EAAE;QACT,MAAM,CAAC,EAAE,MAAM,CAAC;KAChB;IAwCF,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAEhC;;OAEG;IACG,UAAU,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW;IAsBxD;;OAEG;IACG,aAAa,CAClB,GAAG,EAAE,YAAY,EACjB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG;QAC/B,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;KACpC;IASF;;OAEG;IACG,iBAAiB,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,yBAAyB;IAO7E;;OAEG;IACG,SAAS,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY;IAOxD;;;OAGG;IACG,WAAW,CAAC,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM;IAOlD;;;OAGG;IACG,mBAAmB,CAAC,GAAG,EAAE,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE;IAOpE;;;OAGG;IACG,kBAAkB,CAAC,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM;IAOzD;;;OAGG;IACG,kBAAkB,CAAC,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM;IAOzD;;;;OAIG;IACG,aAAa,CAClB,GAAG,EAAE,WAAW,EAChB,OAAO,CAAC,EAAE;QACT,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,OAAO,CAAC;KACrB;IAKF;;OAEG;IACG,mBAAmB,CACxB,GAAG,EAAE,WAAW,EAChB,OAAO,CAAC,EAAE;QACT,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,qBAAqB,CAAC,EAAE,MAAM,CAAC;KAC/B;IAQF;;OAEG;IACG,eAAe,CACpB,GAAG,EAAE,WAAW,EAChB,OAAO,CAAC,EAAE;QACT,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;KAC3B;IAQF;;OAEG;IACG,aAAa,CAClB,GAAG,EAAE,WAAW,EAChB,OAAO,CAAC,EAAE;QACT,YAAY,CAAC,EAAE,MAAM,CAAC;KACtB;IAOF;;OAEG;IACG,uBAAuB,CAC5B,GAAG,EAAE,WAAW,EAChB,OAAO,CAAC,EAAE;QACT,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;KAC3B;IAQF;;OAEG;IACG,uBAAuB,CAC5B,GAAG,EAAE,WAAW,EAChB,OAAO,EAAE;QACR,KAAK,EAAE,MAAM,CAAC;QACd,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,MAAM,CAAC;KAClB;IAKF;;OAEG;IACG,mBAAmB,CACxB,GAAG,EAAE,WAAW,EAChB,OAAO,EAAE;QACR,OAAO,EAAE,MAAM,CAAC;QAChB,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,MAAM,CAAC;KAClB;IAKF;;OAEG;IACG,oBAAoB,CACzB,GAAG,EAAE,WAAW,EAChB,OAAO,EAAE;QACR,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,MAAM,CAAC;KAClB;IAKF;;OAEG;IACG,aAAa,CAAC,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM;IAOpD;;;OAGG;IACG,YAAY,CACjB,GAAG,EAAE,YAAY,EACjB,OAAO,EAAE;QACR,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;QAClB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QACpC,eAAe,CAAC,EAAE;YACjB,SAAS,CAAC,EAAE,MAAM,CAAC;YACnB,MAAM,CAAC,EAAE,MAAM,CAAC;SAChB,CAAC;KACF;IAQF;;;;;;;;;OASG;IACG,WAAW,CAChB,GAAG,EAAE,YAAY,EACjB,OAAO,EAAE;QACR,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;QACd,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QACpC,SAAS,CAAC,EAAE,MAAM,CAAC;KACnB;IAQF;;;;;;OAMG;IACH,GAAG;;;;;;;;;;;;;;;;;;;;;CA6MH"}
|
package/dist/client/index.js
CHANGED
|
@@ -200,6 +200,12 @@ export class Loops {
|
|
|
200
200
|
/**
|
|
201
201
|
* Trigger a loop for a contact
|
|
202
202
|
* Loops are automated email sequences that can be triggered by events
|
|
203
|
+
*
|
|
204
|
+
* Note: Loops.so doesn't have a direct loop trigger endpoint.
|
|
205
|
+
* Loops are triggered through events. Make sure your loop is configured
|
|
206
|
+
* in the Loops dashboard to listen for events.
|
|
207
|
+
*
|
|
208
|
+
* @param options.eventName - Optional event name. If not provided, uses `loop_{loopId}`
|
|
203
209
|
*/
|
|
204
210
|
async triggerLoop(ctx, options) {
|
|
205
211
|
return ctx.runAction(this.component.lib.triggerLoop, {
|
package/dist/component/lib.d.ts
CHANGED
|
@@ -39,13 +39,26 @@ export declare const sendEvent: any;
|
|
|
39
39
|
export declare const deleteContact: any;
|
|
40
40
|
/**
|
|
41
41
|
* Send a campaign to contacts
|
|
42
|
-
* Campaigns
|
|
42
|
+
* Note: Campaigns in Loops.so are typically managed from the dashboard.
|
|
43
|
+
* This function sends transactional emails to multiple contacts as a workaround.
|
|
44
|
+
* If you need true campaign functionality, use the Loops.so dashboard or contact their support.
|
|
43
45
|
*/
|
|
44
46
|
export declare const sendCampaign: any;
|
|
45
47
|
/**
|
|
46
48
|
* Trigger a loop for a contact
|
|
47
|
-
* Loops are
|
|
48
|
-
* This
|
|
49
|
+
* Note: Loops in Loops.so are triggered through events, not a direct API endpoint.
|
|
50
|
+
* This function uses the events endpoint to trigger the loop.
|
|
51
|
+
* The loop must be configured in the Loops dashboard to listen for events.
|
|
52
|
+
*
|
|
53
|
+
* IMPORTANT: Loops.so doesn't have a direct /loops/trigger endpoint.
|
|
54
|
+
* Loops are triggered by sending events. Make sure your loop in the dashboard
|
|
55
|
+
* is configured to trigger on an event name (e.g., "loop_trigger").
|
|
56
|
+
*
|
|
57
|
+
* If you need to trigger a specific loop, you should:
|
|
58
|
+
* 1. Configure the loop in the dashboard to listen for a specific event name
|
|
59
|
+
* 2. Use sendEvent() with that event name instead
|
|
60
|
+
*
|
|
61
|
+
* This function is kept for backwards compatibility but works by sending an event.
|
|
49
62
|
*/
|
|
50
63
|
export declare const triggerLoop: any;
|
|
51
64
|
/**
|
|
@@ -56,7 +69,8 @@ export declare const triggerLoop: any;
|
|
|
56
69
|
export declare const findContact: any;
|
|
57
70
|
/**
|
|
58
71
|
* Batch create contacts
|
|
59
|
-
*
|
|
72
|
+
* Creates multiple contacts sequentially using the single contact create endpoint.
|
|
73
|
+
* Note: Loops.so doesn't have a batch endpoint, so we create contacts one by one.
|
|
60
74
|
*/
|
|
61
75
|
export declare const batchCreateContacts: any;
|
|
62
76
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../../src/component/lib.ts"],"names":[],"mappings":"AA0BA;;GAEG;AACH,eAAO,MAAM,YAAY,KA6CvB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,aAAa,KAexB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,iBAAiB,KAgC5B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,aAAa,KAwCxB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,UAAU,KAiHrB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,aAAa,KAoDxB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,iBAAiB,KAqD5B,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,SAAS,KAgCpB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,aAAa,KA8BxB,CAAC;AAEH
|
|
1
|
+
{"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../../src/component/lib.ts"],"names":[],"mappings":"AA0BA;;GAEG;AACH,eAAO,MAAM,YAAY,KA6CvB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,aAAa,KAexB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,iBAAiB,KAgC5B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,aAAa,KAwCxB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,UAAU,KAiHrB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,aAAa,KAoDxB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,iBAAiB,KAqD5B,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,SAAS,KAgCpB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,aAAa,KA8BxB,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,YAAY,KAiHvB,CAAC;AAEH;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,WAAW,KAyDtB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,WAAW,KA2DtB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,KAyD9B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,kBAAkB,KA+B7B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,kBAAkB,KA+B7B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,mBAAmB,KAwC9B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,eAAe,KAwC1B,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,aAAa,KAgDxB,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,uBAAuB,KAsGlC,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,uBAAuB,KA6ClC,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,mBAAmB,KA6C9B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,oBAAoB,KA8B/B,CAAC"}
|
package/dist/component/lib.js
CHANGED
|
@@ -450,7 +450,9 @@ export const deleteContact = za({
|
|
|
450
450
|
});
|
|
451
451
|
/**
|
|
452
452
|
* Send a campaign to contacts
|
|
453
|
-
* Campaigns
|
|
453
|
+
* Note: Campaigns in Loops.so are typically managed from the dashboard.
|
|
454
|
+
* This function sends transactional emails to multiple contacts as a workaround.
|
|
455
|
+
* If you need true campaign functionality, use the Loops.so dashboard or contact their support.
|
|
454
456
|
*/
|
|
455
457
|
export const sendCampaign = za({
|
|
456
458
|
args: z.object({
|
|
@@ -469,68 +471,102 @@ export const sendCampaign = za({
|
|
|
469
471
|
returns: z.object({
|
|
470
472
|
success: z.boolean(),
|
|
471
473
|
messageId: z.string().optional(),
|
|
474
|
+
sent: z.number().optional(),
|
|
475
|
+
errors: z.array(z.object({
|
|
476
|
+
email: z.string(),
|
|
477
|
+
error: z.string(),
|
|
478
|
+
})).optional(),
|
|
472
479
|
}),
|
|
473
480
|
handler: async (ctx, args) => {
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
payload.transactionalId = args.transactionalId;
|
|
482
|
-
}
|
|
483
|
-
if (args.dataVariables) {
|
|
484
|
-
payload.dataVariables = args.dataVariables;
|
|
481
|
+
// Loops.so doesn't have a campaigns API endpoint
|
|
482
|
+
// As a workaround, we'll send transactional emails to the specified contacts
|
|
483
|
+
if (!args.transactionalId) {
|
|
484
|
+
throw new Error("Campaigns require a transactionalId. " +
|
|
485
|
+
"Loops.so campaigns are managed from the dashboard. " +
|
|
486
|
+
"This function sends transactional emails to multiple contacts as a workaround. " +
|
|
487
|
+
"Please provide a transactionalId to send emails.");
|
|
485
488
|
}
|
|
486
|
-
if (args.
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
489
|
+
if (!args.emails || args.emails.length === 0) {
|
|
490
|
+
// If no emails provided but audienceFilters are, we need to query contacts
|
|
491
|
+
if (args.audienceFilters) {
|
|
492
|
+
// Query contacts from our database based on filters
|
|
493
|
+
const contacts = await ctx.runQuery((internal.lib).countContacts, {
|
|
494
|
+
userGroup: args.audienceFilters.userGroup,
|
|
495
|
+
source: args.audienceFilters.source,
|
|
496
|
+
});
|
|
497
|
+
if (contacts === 0) {
|
|
498
|
+
return {
|
|
499
|
+
success: false,
|
|
500
|
+
sent: 0,
|
|
501
|
+
errors: [{ email: "audience", error: "No contacts found matching filters" }],
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
// Note: We can't get email list from countContacts, so this is a limitation
|
|
505
|
+
throw new Error("Campaigns with audienceFilters require emails to be specified. " +
|
|
506
|
+
"Please provide the emails array with contacts to send to.");
|
|
507
|
+
}
|
|
508
|
+
throw new Error("Campaigns require either emails array or audienceFilters. " +
|
|
509
|
+
"Please provide at least one email address or use transactional emails for single contacts.");
|
|
501
510
|
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
511
|
+
// Send transactional emails to each contact as a workaround for campaigns
|
|
512
|
+
let sent = 0;
|
|
513
|
+
const errors = [];
|
|
514
|
+
for (const email of args.emails) {
|
|
515
|
+
try {
|
|
516
|
+
await ctx.runAction((internal.lib).sendTransactional, {
|
|
517
|
+
apiKey: args.apiKey,
|
|
518
|
+
transactionalId: args.transactionalId,
|
|
519
|
+
email,
|
|
520
|
+
dataVariables: args.dataVariables,
|
|
521
|
+
});
|
|
522
|
+
sent++;
|
|
523
|
+
// Log as campaign operation
|
|
505
524
|
await ctx.runMutation((internal.lib).logEmailOperation, {
|
|
506
525
|
operationType: "campaign",
|
|
507
526
|
email,
|
|
508
527
|
success: true,
|
|
509
528
|
campaignId: args.campaignId,
|
|
510
|
-
|
|
529
|
+
transactionalId: args.transactionalId,
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
catch (error) {
|
|
533
|
+
errors.push({
|
|
534
|
+
email,
|
|
535
|
+
error: error instanceof Error ? error.message : String(error),
|
|
536
|
+
});
|
|
537
|
+
// Log failed campaign operation
|
|
538
|
+
await ctx.runMutation((internal.lib).logEmailOperation, {
|
|
539
|
+
operationType: "campaign",
|
|
540
|
+
email,
|
|
541
|
+
success: false,
|
|
542
|
+
campaignId: args.campaignId,
|
|
543
|
+
transactionalId: args.transactionalId,
|
|
544
|
+
metadata: { error: error instanceof Error ? error.message : String(error) },
|
|
511
545
|
});
|
|
512
546
|
}
|
|
513
|
-
}
|
|
514
|
-
else {
|
|
515
|
-
await ctx.runMutation((internal.lib).logEmailOperation, {
|
|
516
|
-
operationType: "campaign",
|
|
517
|
-
email: "audience",
|
|
518
|
-
success: true,
|
|
519
|
-
campaignId: args.campaignId,
|
|
520
|
-
messageId: data.messageId,
|
|
521
|
-
metadata: { audienceFilters: args.audienceFilters },
|
|
522
|
-
});
|
|
523
547
|
}
|
|
524
548
|
return {
|
|
525
|
-
success:
|
|
526
|
-
|
|
549
|
+
success: sent > 0,
|
|
550
|
+
sent,
|
|
551
|
+
errors: errors.length > 0 ? errors : undefined,
|
|
527
552
|
};
|
|
528
553
|
},
|
|
529
554
|
});
|
|
530
555
|
/**
|
|
531
556
|
* Trigger a loop for a contact
|
|
532
|
-
* Loops are
|
|
533
|
-
* This
|
|
557
|
+
* Note: Loops in Loops.so are triggered through events, not a direct API endpoint.
|
|
558
|
+
* This function uses the events endpoint to trigger the loop.
|
|
559
|
+
* The loop must be configured in the Loops dashboard to listen for events.
|
|
560
|
+
*
|
|
561
|
+
* IMPORTANT: Loops.so doesn't have a direct /loops/trigger endpoint.
|
|
562
|
+
* Loops are triggered by sending events. Make sure your loop in the dashboard
|
|
563
|
+
* is configured to trigger on an event name (e.g., "loop_trigger").
|
|
564
|
+
*
|
|
565
|
+
* If you need to trigger a specific loop, you should:
|
|
566
|
+
* 1. Configure the loop in the dashboard to listen for a specific event name
|
|
567
|
+
* 2. Use sendEvent() with that event name instead
|
|
568
|
+
*
|
|
569
|
+
* This function is kept for backwards compatibility but works by sending an event.
|
|
534
570
|
*/
|
|
535
571
|
export const triggerLoop = za({
|
|
536
572
|
args: z.object({
|
|
@@ -538,41 +574,53 @@ export const triggerLoop = za({
|
|
|
538
574
|
loopId: z.string(),
|
|
539
575
|
email: z.string().email(),
|
|
540
576
|
dataVariables: z.record(z.string(), z.any()).optional(),
|
|
577
|
+
eventName: z.string().optional(), // Event name that triggers the loop
|
|
541
578
|
}),
|
|
542
579
|
returns: z.object({
|
|
543
580
|
success: z.boolean(),
|
|
581
|
+
warning: z.string().optional(),
|
|
544
582
|
}),
|
|
545
583
|
handler: async (ctx, args) => {
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
584
|
+
// Loops.so doesn't have a /loops/trigger endpoint
|
|
585
|
+
// Loops are triggered through events. We'll use the events endpoint.
|
|
586
|
+
// Default event name if not provided
|
|
587
|
+
const eventName = args.eventName || `loop_${args.loopId}`;
|
|
588
|
+
try {
|
|
589
|
+
// Send event to trigger the loop
|
|
590
|
+
await ctx.runAction((internal.lib).sendEvent, {
|
|
591
|
+
apiKey: args.apiKey,
|
|
554
592
|
email: args.email,
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
593
|
+
eventName,
|
|
594
|
+
eventProperties: {
|
|
595
|
+
...args.dataVariables,
|
|
596
|
+
loopId: args.loopId, // Include loopId in event properties
|
|
597
|
+
},
|
|
598
|
+
});
|
|
599
|
+
// Log as loop operation
|
|
600
|
+
await ctx.runMutation((internal.lib).logEmailOperation, {
|
|
601
|
+
operationType: "loop",
|
|
602
|
+
email: args.email,
|
|
603
|
+
success: true,
|
|
604
|
+
loopId: args.loopId,
|
|
605
|
+
eventName,
|
|
606
|
+
});
|
|
607
|
+
return {
|
|
608
|
+
success: true,
|
|
609
|
+
warning: "Loops are triggered via events. Ensure your loop is configured to listen for this event.",
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
catch (error) {
|
|
613
|
+
// Log failed loop operation
|
|
561
614
|
await ctx.runMutation((internal.lib).logEmailOperation, {
|
|
562
615
|
operationType: "loop",
|
|
563
616
|
email: args.email,
|
|
564
617
|
success: false,
|
|
565
618
|
loopId: args.loopId,
|
|
619
|
+
eventName,
|
|
620
|
+
metadata: { error: error instanceof Error ? error.message : String(error) },
|
|
566
621
|
});
|
|
567
|
-
throw
|
|
622
|
+
throw error;
|
|
568
623
|
}
|
|
569
|
-
await ctx.runMutation((internal.lib).logEmailOperation, {
|
|
570
|
-
operationType: "loop",
|
|
571
|
-
email: args.email,
|
|
572
|
-
success: true,
|
|
573
|
-
loopId: args.loopId,
|
|
574
|
-
});
|
|
575
|
-
return { success: true };
|
|
576
624
|
},
|
|
577
625
|
});
|
|
578
626
|
/**
|
|
@@ -632,7 +680,8 @@ export const findContact = za({
|
|
|
632
680
|
});
|
|
633
681
|
/**
|
|
634
682
|
* Batch create contacts
|
|
635
|
-
*
|
|
683
|
+
* Creates multiple contacts sequentially using the single contact create endpoint.
|
|
684
|
+
* Note: Loops.so doesn't have a batch endpoint, so we create contacts one by one.
|
|
636
685
|
*/
|
|
637
686
|
export const batchCreateContacts = za({
|
|
638
687
|
args: z.object({
|
|
@@ -642,36 +691,52 @@ export const batchCreateContacts = za({
|
|
|
642
691
|
returns: z.object({
|
|
643
692
|
success: z.boolean(),
|
|
644
693
|
created: z.number().optional(),
|
|
694
|
+
failed: z.number().optional(),
|
|
695
|
+
results: z.array(z.object({
|
|
696
|
+
email: z.string(),
|
|
697
|
+
success: z.boolean(),
|
|
698
|
+
error: z.string().optional(),
|
|
699
|
+
})).optional(),
|
|
645
700
|
}),
|
|
646
701
|
handler: async (ctx, args) => {
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
"Content-Type": "application/json",
|
|
652
|
-
},
|
|
653
|
-
body: JSON.stringify({ contacts: args.contacts }),
|
|
654
|
-
});
|
|
655
|
-
if (!response.ok) {
|
|
656
|
-
const errorText = await response.text();
|
|
657
|
-
console.error(`Loops API error [${response.status}]:`, errorText);
|
|
658
|
-
throw sanitizeError(response.status, errorText);
|
|
659
|
-
}
|
|
660
|
-
const data = (await response.json());
|
|
702
|
+
let created = 0;
|
|
703
|
+
let failed = 0;
|
|
704
|
+
const results = [];
|
|
705
|
+
// Create contacts one by one since Loops.so doesn't have a batch endpoint
|
|
661
706
|
for (const contact of args.contacts) {
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
707
|
+
try {
|
|
708
|
+
// Use the addContact function which handles create/update logic
|
|
709
|
+
const result = await ctx.runAction((internal.lib).addContact, {
|
|
710
|
+
apiKey: args.apiKey,
|
|
711
|
+
contact,
|
|
712
|
+
});
|
|
713
|
+
if (result.success) {
|
|
714
|
+
created++;
|
|
715
|
+
results.push({ email: contact.email, success: true });
|
|
716
|
+
}
|
|
717
|
+
else {
|
|
718
|
+
failed++;
|
|
719
|
+
results.push({
|
|
720
|
+
email: contact.email,
|
|
721
|
+
success: false,
|
|
722
|
+
error: "Unknown error"
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
catch (error) {
|
|
727
|
+
failed++;
|
|
728
|
+
results.push({
|
|
729
|
+
email: contact.email,
|
|
730
|
+
success: false,
|
|
731
|
+
error: error instanceof Error ? error.message : String(error),
|
|
732
|
+
});
|
|
733
|
+
}
|
|
671
734
|
}
|
|
672
735
|
return {
|
|
673
|
-
success:
|
|
674
|
-
created
|
|
736
|
+
success: created > 0,
|
|
737
|
+
created,
|
|
738
|
+
failed,
|
|
739
|
+
results,
|
|
675
740
|
};
|
|
676
741
|
},
|
|
677
742
|
});
|
package/package.json
CHANGED
package/src/client/index.ts
CHANGED
|
@@ -340,6 +340,12 @@ export class Loops {
|
|
|
340
340
|
/**
|
|
341
341
|
* Trigger a loop for a contact
|
|
342
342
|
* Loops are automated email sequences that can be triggered by events
|
|
343
|
+
*
|
|
344
|
+
* Note: Loops.so doesn't have a direct loop trigger endpoint.
|
|
345
|
+
* Loops are triggered through events. Make sure your loop is configured
|
|
346
|
+
* in the Loops dashboard to listen for events.
|
|
347
|
+
*
|
|
348
|
+
* @param options.eventName - Optional event name. If not provided, uses `loop_{loopId}`
|
|
343
349
|
*/
|
|
344
350
|
async triggerLoop(
|
|
345
351
|
ctx: RunActionCtx,
|
|
@@ -347,6 +353,7 @@ export class Loops {
|
|
|
347
353
|
loopId: string;
|
|
348
354
|
email: string;
|
|
349
355
|
dataVariables?: Record<string, any>;
|
|
356
|
+
eventName?: string; // Event name that triggers the loop
|
|
350
357
|
},
|
|
351
358
|
) {
|
|
352
359
|
return ctx.runAction((this.component.lib as any).triggerLoop, {
|
package/src/component/lib.ts
CHANGED
|
@@ -486,7 +486,9 @@ export const deleteContact = za({
|
|
|
486
486
|
|
|
487
487
|
/**
|
|
488
488
|
* Send a campaign to contacts
|
|
489
|
-
* Campaigns
|
|
489
|
+
* Note: Campaigns in Loops.so are typically managed from the dashboard.
|
|
490
|
+
* This function sends transactional emails to multiple contacts as a workaround.
|
|
491
|
+
* If you need true campaign functionality, use the Loops.so dashboard or contact their support.
|
|
490
492
|
*/
|
|
491
493
|
export const sendCampaign = za({
|
|
492
494
|
args: z.object({
|
|
@@ -505,77 +507,119 @@ export const sendCampaign = za({
|
|
|
505
507
|
returns: z.object({
|
|
506
508
|
success: z.boolean(),
|
|
507
509
|
messageId: z.string().optional(),
|
|
510
|
+
sent: z.number().optional(),
|
|
511
|
+
errors: z.array(z.object({
|
|
512
|
+
email: z.string(),
|
|
513
|
+
error: z.string(),
|
|
514
|
+
})).optional(),
|
|
508
515
|
}),
|
|
509
516
|
handler: async (ctx, args) => {
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
if (args.dataVariables) {
|
|
523
|
-
payload.dataVariables = args.dataVariables;
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
if (args.audienceFilters) {
|
|
527
|
-
payload.audienceFilters = args.audienceFilters;
|
|
517
|
+
// Loops.so doesn't have a campaigns API endpoint
|
|
518
|
+
// As a workaround, we'll send transactional emails to the specified contacts
|
|
519
|
+
|
|
520
|
+
if (!args.transactionalId) {
|
|
521
|
+
throw new Error(
|
|
522
|
+
"Campaigns require a transactionalId. " +
|
|
523
|
+
"Loops.so campaigns are managed from the dashboard. " +
|
|
524
|
+
"This function sends transactional emails to multiple contacts as a workaround. " +
|
|
525
|
+
"Please provide a transactionalId to send emails."
|
|
526
|
+
);
|
|
528
527
|
}
|
|
529
528
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
529
|
+
if (!args.emails || args.emails.length === 0) {
|
|
530
|
+
// If no emails provided but audienceFilters are, we need to query contacts
|
|
531
|
+
if (args.audienceFilters) {
|
|
532
|
+
// Query contacts from our database based on filters
|
|
533
|
+
const contacts = await ctx.runQuery(((internal as any).lib).countContacts as any, {
|
|
534
|
+
userGroup: args.audienceFilters.userGroup,
|
|
535
|
+
source: args.audienceFilters.source,
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
if (contacts === 0) {
|
|
539
|
+
return {
|
|
540
|
+
success: false,
|
|
541
|
+
sent: 0,
|
|
542
|
+
errors: [{ email: "audience", error: "No contacts found matching filters" }],
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// Note: We can't get email list from countContacts, so this is a limitation
|
|
547
|
+
throw new Error(
|
|
548
|
+
"Campaigns with audienceFilters require emails to be specified. " +
|
|
549
|
+
"Please provide the emails array with contacts to send to."
|
|
550
|
+
);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
throw new Error(
|
|
554
|
+
"Campaigns require either emails array or audienceFilters. " +
|
|
555
|
+
"Please provide at least one email address or use transactional emails for single contacts."
|
|
556
|
+
);
|
|
543
557
|
}
|
|
544
558
|
|
|
545
|
-
|
|
559
|
+
// Send transactional emails to each contact as a workaround for campaigns
|
|
560
|
+
let sent = 0;
|
|
561
|
+
const errors: Array<{ email: string; error: string }> = [];
|
|
546
562
|
|
|
547
|
-
|
|
548
|
-
|
|
563
|
+
for (const email of args.emails) {
|
|
564
|
+
try {
|
|
565
|
+
await ctx.runAction(((internal as any).lib).sendTransactional as any, {
|
|
566
|
+
apiKey: args.apiKey,
|
|
567
|
+
transactionalId: args.transactionalId!,
|
|
568
|
+
email,
|
|
569
|
+
dataVariables: args.dataVariables,
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
sent++;
|
|
573
|
+
|
|
574
|
+
// Log as campaign operation
|
|
549
575
|
await ctx.runMutation(((internal as any).lib).logEmailOperation as any, {
|
|
550
576
|
operationType: "campaign",
|
|
551
577
|
email,
|
|
552
578
|
success: true,
|
|
553
579
|
campaignId: args.campaignId,
|
|
554
|
-
|
|
580
|
+
transactionalId: args.transactionalId,
|
|
581
|
+
});
|
|
582
|
+
} catch (error) {
|
|
583
|
+
errors.push({
|
|
584
|
+
email,
|
|
585
|
+
error: error instanceof Error ? error.message : String(error),
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
// Log failed campaign operation
|
|
589
|
+
await ctx.runMutation(((internal as any).lib).logEmailOperation as any, {
|
|
590
|
+
operationType: "campaign",
|
|
591
|
+
email,
|
|
592
|
+
success: false,
|
|
593
|
+
campaignId: args.campaignId,
|
|
594
|
+
transactionalId: args.transactionalId,
|
|
595
|
+
metadata: { error: error instanceof Error ? error.message : String(error) },
|
|
555
596
|
});
|
|
556
597
|
}
|
|
557
|
-
} else {
|
|
558
|
-
await ctx.runMutation(((internal as any).lib).logEmailOperation as any, {
|
|
559
|
-
operationType: "campaign",
|
|
560
|
-
email: "audience",
|
|
561
|
-
success: true,
|
|
562
|
-
campaignId: args.campaignId,
|
|
563
|
-
messageId: data.messageId,
|
|
564
|
-
metadata: { audienceFilters: args.audienceFilters },
|
|
565
|
-
});
|
|
566
598
|
}
|
|
567
599
|
|
|
568
600
|
return {
|
|
569
|
-
success:
|
|
570
|
-
|
|
601
|
+
success: sent > 0,
|
|
602
|
+
sent,
|
|
603
|
+
errors: errors.length > 0 ? errors : undefined,
|
|
571
604
|
};
|
|
572
605
|
},
|
|
573
606
|
});
|
|
574
607
|
|
|
575
608
|
/**
|
|
576
609
|
* Trigger a loop for a contact
|
|
577
|
-
* Loops are
|
|
578
|
-
* This
|
|
610
|
+
* Note: Loops in Loops.so are triggered through events, not a direct API endpoint.
|
|
611
|
+
* This function uses the events endpoint to trigger the loop.
|
|
612
|
+
* The loop must be configured in the Loops dashboard to listen for events.
|
|
613
|
+
*
|
|
614
|
+
* IMPORTANT: Loops.so doesn't have a direct /loops/trigger endpoint.
|
|
615
|
+
* Loops are triggered by sending events. Make sure your loop in the dashboard
|
|
616
|
+
* is configured to trigger on an event name (e.g., "loop_trigger").
|
|
617
|
+
*
|
|
618
|
+
* If you need to trigger a specific loop, you should:
|
|
619
|
+
* 1. Configure the loop in the dashboard to listen for a specific event name
|
|
620
|
+
* 2. Use sendEvent() with that event name instead
|
|
621
|
+
*
|
|
622
|
+
* This function is kept for backwards compatibility but works by sending an event.
|
|
579
623
|
*/
|
|
580
624
|
export const triggerLoop = za({
|
|
581
625
|
args: z.object({
|
|
@@ -583,46 +627,56 @@ export const triggerLoop = za({
|
|
|
583
627
|
loopId: z.string(),
|
|
584
628
|
email: z.string().email(),
|
|
585
629
|
dataVariables: z.record(z.string(), z.any()).optional(),
|
|
630
|
+
eventName: z.string().optional(), // Event name that triggers the loop
|
|
586
631
|
}),
|
|
587
632
|
returns: z.object({
|
|
588
633
|
success: z.boolean(),
|
|
634
|
+
warning: z.string().optional(),
|
|
589
635
|
}),
|
|
590
636
|
handler: async (ctx, args) => {
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
637
|
+
// Loops.so doesn't have a /loops/trigger endpoint
|
|
638
|
+
// Loops are triggered through events. We'll use the events endpoint.
|
|
639
|
+
// Default event name if not provided
|
|
640
|
+
const eventName = args.eventName || `loop_${args.loopId}`;
|
|
641
|
+
|
|
642
|
+
try {
|
|
643
|
+
// Send event to trigger the loop
|
|
644
|
+
await ctx.runAction(((internal as any).lib).sendEvent as any, {
|
|
645
|
+
apiKey: args.apiKey,
|
|
599
646
|
email: args.email,
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
647
|
+
eventName,
|
|
648
|
+
eventProperties: {
|
|
649
|
+
...args.dataVariables,
|
|
650
|
+
loopId: args.loopId, // Include loopId in event properties
|
|
651
|
+
},
|
|
652
|
+
});
|
|
603
653
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
654
|
+
// Log as loop operation
|
|
655
|
+
await ctx.runMutation(((internal as any).lib).logEmailOperation as any, {
|
|
656
|
+
operationType: "loop",
|
|
657
|
+
email: args.email,
|
|
658
|
+
success: true,
|
|
659
|
+
loopId: args.loopId,
|
|
660
|
+
eventName,
|
|
661
|
+
});
|
|
662
|
+
|
|
663
|
+
return {
|
|
664
|
+
success: true,
|
|
665
|
+
warning: "Loops are triggered via events. Ensure your loop is configured to listen for this event.",
|
|
666
|
+
};
|
|
667
|
+
} catch (error) {
|
|
668
|
+
// Log failed loop operation
|
|
608
669
|
await ctx.runMutation(((internal as any).lib).logEmailOperation as any, {
|
|
609
670
|
operationType: "loop",
|
|
610
671
|
email: args.email,
|
|
611
672
|
success: false,
|
|
612
673
|
loopId: args.loopId,
|
|
674
|
+
eventName,
|
|
675
|
+
metadata: { error: error instanceof Error ? error.message : String(error) },
|
|
613
676
|
});
|
|
614
677
|
|
|
615
|
-
throw
|
|
678
|
+
throw error;
|
|
616
679
|
}
|
|
617
|
-
|
|
618
|
-
await ctx.runMutation(((internal as any).lib).logEmailOperation as any, {
|
|
619
|
-
operationType: "loop",
|
|
620
|
-
email: args.email,
|
|
621
|
-
success: true,
|
|
622
|
-
loopId: args.loopId,
|
|
623
|
-
});
|
|
624
|
-
|
|
625
|
-
return { success: true };
|
|
626
680
|
},
|
|
627
681
|
});
|
|
628
682
|
|
|
@@ -694,7 +748,8 @@ export const findContact = za({
|
|
|
694
748
|
|
|
695
749
|
/**
|
|
696
750
|
* Batch create contacts
|
|
697
|
-
*
|
|
751
|
+
* Creates multiple contacts sequentially using the single contact create endpoint.
|
|
752
|
+
* Note: Loops.so doesn't have a batch endpoint, so we create contacts one by one.
|
|
698
753
|
*/
|
|
699
754
|
export const batchCreateContacts = za({
|
|
700
755
|
args: z.object({
|
|
@@ -704,40 +759,53 @@ export const batchCreateContacts = za({
|
|
|
704
759
|
returns: z.object({
|
|
705
760
|
success: z.boolean(),
|
|
706
761
|
created: z.number().optional(),
|
|
762
|
+
failed: z.number().optional(),
|
|
763
|
+
results: z.array(z.object({
|
|
764
|
+
email: z.string(),
|
|
765
|
+
success: z.boolean(),
|
|
766
|
+
error: z.string().optional(),
|
|
767
|
+
})).optional(),
|
|
707
768
|
}),
|
|
708
769
|
handler: async (ctx, args) => {
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
Authorization: `Bearer ${args.apiKey}`,
|
|
713
|
-
"Content-Type": "application/json",
|
|
714
|
-
},
|
|
715
|
-
body: JSON.stringify({ contacts: args.contacts }),
|
|
716
|
-
});
|
|
717
|
-
|
|
718
|
-
if (!response.ok) {
|
|
719
|
-
const errorText = await response.text();
|
|
720
|
-
console.error(`Loops API error [${response.status}]:`, errorText);
|
|
721
|
-
throw sanitizeError(response.status, errorText);
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
const data = (await response.json()) as { created?: number };
|
|
770
|
+
let created = 0;
|
|
771
|
+
let failed = 0;
|
|
772
|
+
const results: Array<{ email: string; success: boolean; error?: string }> = [];
|
|
725
773
|
|
|
774
|
+
// Create contacts one by one since Loops.so doesn't have a batch endpoint
|
|
726
775
|
for (const contact of args.contacts) {
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
776
|
+
try {
|
|
777
|
+
// Use the addContact function which handles create/update logic
|
|
778
|
+
const result = await ctx.runAction(((internal as any).lib).addContact as any, {
|
|
779
|
+
apiKey: args.apiKey,
|
|
780
|
+
contact,
|
|
781
|
+
});
|
|
782
|
+
|
|
783
|
+
if (result.success) {
|
|
784
|
+
created++;
|
|
785
|
+
results.push({ email: contact.email, success: true });
|
|
786
|
+
} else {
|
|
787
|
+
failed++;
|
|
788
|
+
results.push({
|
|
789
|
+
email: contact.email,
|
|
790
|
+
success: false,
|
|
791
|
+
error: "Unknown error"
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
} catch (error) {
|
|
795
|
+
failed++;
|
|
796
|
+
results.push({
|
|
797
|
+
email: contact.email,
|
|
798
|
+
success: false,
|
|
799
|
+
error: error instanceof Error ? error.message : String(error),
|
|
800
|
+
});
|
|
801
|
+
}
|
|
736
802
|
}
|
|
737
803
|
|
|
738
804
|
return {
|
|
739
|
-
success:
|
|
740
|
-
created
|
|
805
|
+
success: created > 0,
|
|
806
|
+
created,
|
|
807
|
+
failed,
|
|
808
|
+
results,
|
|
741
809
|
};
|
|
742
810
|
},
|
|
743
811
|
});
|