@drawbridge/drawbridge-utils 0.0.132 → 0.0.134
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/connections/index.cjs +7 -13
- package/dist/connections/index.js +7 -13
- package/dist/plans.cjs +7 -13
- package/dist/plans.d.cts +10 -21
- package/dist/plans.d.ts +10 -21
- package/dist/plans.js +7 -13
- package/dist/pricing.cjs +7 -13
- package/dist/pricing.d.cts +0 -2
- package/dist/pricing.d.ts +0 -2
- package/dist/pricing.js +7 -13
- package/dist/providers.cjs +7 -13
- package/dist/providers.js +7 -13
- package/dist/twilio.cjs +94 -7
- package/dist/twilio.d.cts +121 -7
- package/dist/twilio.d.ts +121 -7
- package/dist/twilio.js +94 -7
- package/package.json +1 -1
|
@@ -1370,10 +1370,6 @@ var featuresFor = (array = []) => Object.values({
|
|
|
1370
1370
|
},
|
|
1371
1371
|
{ denied: {}, granted: {} }
|
|
1372
1372
|
);
|
|
1373
|
-
var overage = (actionCents) => ({
|
|
1374
|
-
actionCents,
|
|
1375
|
-
overages: { actions: String(actionCents) }
|
|
1376
|
-
});
|
|
1377
1373
|
var all = {
|
|
1378
1374
|
features: (array = []) => featuresFor([
|
|
1379
1375
|
connection.attentive.key,
|
|
@@ -1455,7 +1451,7 @@ var plans = {
|
|
|
1455
1451
|
["Storage", "10GB"]
|
|
1456
1452
|
]
|
|
1457
1453
|
},
|
|
1458
|
-
|
|
1454
|
+
actionCents: 2.5,
|
|
1459
1455
|
title: "Starter",
|
|
1460
1456
|
conversion: 2
|
|
1461
1457
|
},
|
|
@@ -1486,7 +1482,7 @@ var plans = {
|
|
|
1486
1482
|
["Storage", "20GB"]
|
|
1487
1483
|
]
|
|
1488
1484
|
},
|
|
1489
|
-
|
|
1485
|
+
actionCents: 2,
|
|
1490
1486
|
title: "Pro",
|
|
1491
1487
|
conversion: 1.5
|
|
1492
1488
|
},
|
|
@@ -1517,7 +1513,7 @@ var plans = {
|
|
|
1517
1513
|
["Storage", "50GB"]
|
|
1518
1514
|
]
|
|
1519
1515
|
},
|
|
1520
|
-
|
|
1516
|
+
actionCents: 1.85,
|
|
1521
1517
|
title: "Premium",
|
|
1522
1518
|
conversion: 1
|
|
1523
1519
|
},
|
|
@@ -1548,7 +1544,7 @@ var plans = {
|
|
|
1548
1544
|
["Storage", "100GB"]
|
|
1549
1545
|
]
|
|
1550
1546
|
},
|
|
1551
|
-
|
|
1547
|
+
actionCents: 1.5,
|
|
1552
1548
|
title: "Elite",
|
|
1553
1549
|
conversion: 0.5
|
|
1554
1550
|
}
|
|
@@ -1570,12 +1566,10 @@ var resolvePlan = (subscription) => {
|
|
|
1570
1566
|
// silently lacking it would be a support ticket, not a pricing decision.
|
|
1571
1567
|
features: all.features([organization.networking.key, ...((_a = custom.features) == null ? void 0 : _a.granted) || []]),
|
|
1572
1568
|
limits: all.limits(((_b = custom.limits) == null ? void 0 : _b.organization) || {}),
|
|
1573
|
-
// A custom plan stores its overage BARE on `custom.overages
|
|
1574
|
-
//
|
|
1575
|
-
//
|
|
1576
|
-
// (it bills nothing) rather than becoming NaN.
|
|
1569
|
+
// A custom plan stores its overage BARE on `custom.overages`. Number() so
|
|
1570
|
+
// a deal stored as a string still resolves to cents-per-action; an unnamed
|
|
1571
|
+
// overage stays undefined (it bills nothing) rather than becoming NaN.
|
|
1577
1572
|
actionCents: custom.overages == null ? void 0 : Number(custom.overages),
|
|
1578
|
-
overages: { actions: custom.overages },
|
|
1579
1573
|
title: custom.title || "Custom"
|
|
1580
1574
|
};
|
|
1581
1575
|
};
|
|
@@ -1296,10 +1296,6 @@ var featuresFor = (array = []) => Object.values({
|
|
|
1296
1296
|
},
|
|
1297
1297
|
{ denied: {}, granted: {} }
|
|
1298
1298
|
);
|
|
1299
|
-
var overage = (actionCents) => ({
|
|
1300
|
-
actionCents,
|
|
1301
|
-
overages: { actions: String(actionCents) }
|
|
1302
|
-
});
|
|
1303
1299
|
var all = {
|
|
1304
1300
|
features: (array = []) => featuresFor([
|
|
1305
1301
|
connection.attentive.key,
|
|
@@ -1381,7 +1377,7 @@ var plans = {
|
|
|
1381
1377
|
["Storage", "10GB"]
|
|
1382
1378
|
]
|
|
1383
1379
|
},
|
|
1384
|
-
|
|
1380
|
+
actionCents: 2.5,
|
|
1385
1381
|
title: "Starter",
|
|
1386
1382
|
conversion: 2
|
|
1387
1383
|
},
|
|
@@ -1412,7 +1408,7 @@ var plans = {
|
|
|
1412
1408
|
["Storage", "20GB"]
|
|
1413
1409
|
]
|
|
1414
1410
|
},
|
|
1415
|
-
|
|
1411
|
+
actionCents: 2,
|
|
1416
1412
|
title: "Pro",
|
|
1417
1413
|
conversion: 1.5
|
|
1418
1414
|
},
|
|
@@ -1443,7 +1439,7 @@ var plans = {
|
|
|
1443
1439
|
["Storage", "50GB"]
|
|
1444
1440
|
]
|
|
1445
1441
|
},
|
|
1446
|
-
|
|
1442
|
+
actionCents: 1.85,
|
|
1447
1443
|
title: "Premium",
|
|
1448
1444
|
conversion: 1
|
|
1449
1445
|
},
|
|
@@ -1474,7 +1470,7 @@ var plans = {
|
|
|
1474
1470
|
["Storage", "100GB"]
|
|
1475
1471
|
]
|
|
1476
1472
|
},
|
|
1477
|
-
|
|
1473
|
+
actionCents: 1.5,
|
|
1478
1474
|
title: "Elite",
|
|
1479
1475
|
conversion: 0.5
|
|
1480
1476
|
}
|
|
@@ -1496,12 +1492,10 @@ var resolvePlan = (subscription) => {
|
|
|
1496
1492
|
// silently lacking it would be a support ticket, not a pricing decision.
|
|
1497
1493
|
features: all.features([organization.networking.key, ...((_a = custom.features) == null ? void 0 : _a.granted) || []]),
|
|
1498
1494
|
limits: all.limits(((_b = custom.limits) == null ? void 0 : _b.organization) || {}),
|
|
1499
|
-
// A custom plan stores its overage BARE on `custom.overages
|
|
1500
|
-
//
|
|
1501
|
-
//
|
|
1502
|
-
// (it bills nothing) rather than becoming NaN.
|
|
1495
|
+
// A custom plan stores its overage BARE on `custom.overages`. Number() so
|
|
1496
|
+
// a deal stored as a string still resolves to cents-per-action; an unnamed
|
|
1497
|
+
// overage stays undefined (it bills nothing) rather than becoming NaN.
|
|
1503
1498
|
actionCents: custom.overages == null ? void 0 : Number(custom.overages),
|
|
1504
|
-
overages: { actions: custom.overages },
|
|
1505
1499
|
title: custom.title || "Custom"
|
|
1506
1500
|
};
|
|
1507
1501
|
};
|
package/dist/plans.cjs
CHANGED
|
@@ -291,10 +291,6 @@ var featuresFor = (array = []) => Object.values({
|
|
|
291
291
|
},
|
|
292
292
|
{ denied: {}, granted: {} }
|
|
293
293
|
);
|
|
294
|
-
var overage = (actionCents) => ({
|
|
295
|
-
actionCents,
|
|
296
|
-
overages: { actions: String(actionCents) }
|
|
297
|
-
});
|
|
298
294
|
var unitAmountDecimal = (cents) => {
|
|
299
295
|
const value = Number(cents);
|
|
300
296
|
if (cents == null || !Number.isFinite(value)) throw new Error(`Invalid overage rate: ${cents}`);
|
|
@@ -381,7 +377,7 @@ var plans = {
|
|
|
381
377
|
["Storage", "10GB"]
|
|
382
378
|
]
|
|
383
379
|
},
|
|
384
|
-
|
|
380
|
+
actionCents: 2.5,
|
|
385
381
|
title: "Starter",
|
|
386
382
|
conversion: 2
|
|
387
383
|
},
|
|
@@ -412,7 +408,7 @@ var plans = {
|
|
|
412
408
|
["Storage", "20GB"]
|
|
413
409
|
]
|
|
414
410
|
},
|
|
415
|
-
|
|
411
|
+
actionCents: 2,
|
|
416
412
|
title: "Pro",
|
|
417
413
|
conversion: 1.5
|
|
418
414
|
},
|
|
@@ -443,7 +439,7 @@ var plans = {
|
|
|
443
439
|
["Storage", "50GB"]
|
|
444
440
|
]
|
|
445
441
|
},
|
|
446
|
-
|
|
442
|
+
actionCents: 1.85,
|
|
447
443
|
title: "Premium",
|
|
448
444
|
conversion: 1
|
|
449
445
|
},
|
|
@@ -474,7 +470,7 @@ var plans = {
|
|
|
474
470
|
["Storage", "100GB"]
|
|
475
471
|
]
|
|
476
472
|
},
|
|
477
|
-
|
|
473
|
+
actionCents: 1.5,
|
|
478
474
|
title: "Elite",
|
|
479
475
|
conversion: 0.5
|
|
480
476
|
}
|
|
@@ -496,12 +492,10 @@ var resolvePlan = (subscription) => {
|
|
|
496
492
|
// silently lacking it would be a support ticket, not a pricing decision.
|
|
497
493
|
features: all.features([organization.networking.key, ...((_a = custom.features) == null ? void 0 : _a.granted) || []]),
|
|
498
494
|
limits: all.limits(((_b = custom.limits) == null ? void 0 : _b.organization) || {}),
|
|
499
|
-
// A custom plan stores its overage BARE on `custom.overages
|
|
500
|
-
//
|
|
501
|
-
//
|
|
502
|
-
// (it bills nothing) rather than becoming NaN.
|
|
495
|
+
// A custom plan stores its overage BARE on `custom.overages`. Number() so
|
|
496
|
+
// a deal stored as a string still resolves to cents-per-action; an unnamed
|
|
497
|
+
// overage stays undefined (it bills nothing) rather than becoming NaN.
|
|
503
498
|
actionCents: custom.overages == null ? void 0 : Number(custom.overages),
|
|
504
|
-
overages: { actions: custom.overages },
|
|
505
499
|
title: custom.title || "Custom"
|
|
506
500
|
};
|
|
507
501
|
};
|
package/dist/plans.d.cts
CHANGED
|
@@ -31,21 +31,12 @@ const featuresFor = ( array = [] ) => Object.values({
|
|
|
31
31
|
{ denied : {}, granted : {} }
|
|
32
32
|
);
|
|
33
33
|
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
// 2026-05-31 the same rate was encoded as dollars-per-1,000-actions, Stripe read
|
|
38
|
-
// it as per-action, and every paid tier billed 10x. `overages.actions` — the
|
|
39
|
-
// string this used to be the only name for — is kept as a byte-identical alias
|
|
40
|
-
// so consumers can be pin-bumped in any order; drop it once they all read
|
|
41
|
-
// `actionCents`.
|
|
34
|
+
// Each paid plan's `actionCents` is CENTS PER ACTION, as a number: 2.5 = $0.025.
|
|
35
|
+
// Before 2026-05-31 the rate was encoded as dollars-per-1,000-actions, Stripe
|
|
36
|
+
// read it as per-action, and every paid tier billed 10x.
|
|
42
37
|
//
|
|
43
38
|
// Sub-cent precision is deliberate: Premium's 1.85¢ does not fit integer cents
|
|
44
39
|
// (2¢ is +8%, 1¢ is −46%).
|
|
45
|
-
const overage = ( actionCents ) => ({
|
|
46
|
-
actionCents,
|
|
47
|
-
overages : { actions : String( actionCents ) }
|
|
48
|
-
});
|
|
49
40
|
|
|
50
41
|
// Cents → Stripe's `unit_amount_decimal`.
|
|
51
42
|
//
|
|
@@ -148,7 +139,7 @@ const plans = {
|
|
|
148
139
|
[ 'Storage', '10GB' ]
|
|
149
140
|
]
|
|
150
141
|
},
|
|
151
|
-
|
|
142
|
+
actionCents : 2.5,
|
|
152
143
|
title : 'Starter',
|
|
153
144
|
conversion : 2
|
|
154
145
|
},
|
|
@@ -179,7 +170,7 @@ const plans = {
|
|
|
179
170
|
[ 'Storage', '20GB' ]
|
|
180
171
|
]
|
|
181
172
|
},
|
|
182
|
-
|
|
173
|
+
actionCents : 2,
|
|
183
174
|
title : 'Pro',
|
|
184
175
|
conversion : 1.5
|
|
185
176
|
},
|
|
@@ -210,7 +201,7 @@ const plans = {
|
|
|
210
201
|
[ 'Storage', '50GB' ]
|
|
211
202
|
]
|
|
212
203
|
},
|
|
213
|
-
|
|
204
|
+
actionCents : 1.85,
|
|
214
205
|
title : 'Premium',
|
|
215
206
|
conversion : 1
|
|
216
207
|
},
|
|
@@ -241,7 +232,7 @@ const plans = {
|
|
|
241
232
|
[ 'Storage', '100GB' ]
|
|
242
233
|
]
|
|
243
234
|
},
|
|
244
|
-
|
|
235
|
+
actionCents : 1.5,
|
|
245
236
|
title : 'Elite',
|
|
246
237
|
conversion : 0.5
|
|
247
238
|
},
|
|
@@ -279,12 +270,10 @@ const resolvePlan = ( subscription ) => {
|
|
|
279
270
|
// silently lacking it would be a support ticket, not a pricing decision.
|
|
280
271
|
features : all.features([ organization.networking.key, ...( custom.features?.granted || [] ) ]),
|
|
281
272
|
limits : all.limits( custom.limits?.organization || {} ),
|
|
282
|
-
// A custom plan stores its overage BARE on `custom.overages
|
|
283
|
-
//
|
|
284
|
-
//
|
|
285
|
-
// (it bills nothing) rather than becoming NaN.
|
|
273
|
+
// A custom plan stores its overage BARE on `custom.overages`. Number() so
|
|
274
|
+
// a deal stored as a string still resolves to cents-per-action; an unnamed
|
|
275
|
+
// overage stays undefined (it bills nothing) rather than becoming NaN.
|
|
286
276
|
actionCents : custom.overages == null ? undefined : Number( custom.overages ),
|
|
287
|
-
overages : { actions : custom.overages },
|
|
288
277
|
title : custom.title || 'Custom'
|
|
289
278
|
};
|
|
290
279
|
|
package/dist/plans.d.ts
CHANGED
|
@@ -31,21 +31,12 @@ const featuresFor = ( array = [] ) => Object.values({
|
|
|
31
31
|
{ denied : {}, granted : {} }
|
|
32
32
|
);
|
|
33
33
|
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
// 2026-05-31 the same rate was encoded as dollars-per-1,000-actions, Stripe read
|
|
38
|
-
// it as per-action, and every paid tier billed 10x. `overages.actions` — the
|
|
39
|
-
// string this used to be the only name for — is kept as a byte-identical alias
|
|
40
|
-
// so consumers can be pin-bumped in any order; drop it once they all read
|
|
41
|
-
// `actionCents`.
|
|
34
|
+
// Each paid plan's `actionCents` is CENTS PER ACTION, as a number: 2.5 = $0.025.
|
|
35
|
+
// Before 2026-05-31 the rate was encoded as dollars-per-1,000-actions, Stripe
|
|
36
|
+
// read it as per-action, and every paid tier billed 10x.
|
|
42
37
|
//
|
|
43
38
|
// Sub-cent precision is deliberate: Premium's 1.85¢ does not fit integer cents
|
|
44
39
|
// (2¢ is +8%, 1¢ is −46%).
|
|
45
|
-
const overage = ( actionCents ) => ({
|
|
46
|
-
actionCents,
|
|
47
|
-
overages : { actions : String( actionCents ) }
|
|
48
|
-
});
|
|
49
40
|
|
|
50
41
|
// Cents → Stripe's `unit_amount_decimal`.
|
|
51
42
|
//
|
|
@@ -148,7 +139,7 @@ const plans = {
|
|
|
148
139
|
[ 'Storage', '10GB' ]
|
|
149
140
|
]
|
|
150
141
|
},
|
|
151
|
-
|
|
142
|
+
actionCents : 2.5,
|
|
152
143
|
title : 'Starter',
|
|
153
144
|
conversion : 2
|
|
154
145
|
},
|
|
@@ -179,7 +170,7 @@ const plans = {
|
|
|
179
170
|
[ 'Storage', '20GB' ]
|
|
180
171
|
]
|
|
181
172
|
},
|
|
182
|
-
|
|
173
|
+
actionCents : 2,
|
|
183
174
|
title : 'Pro',
|
|
184
175
|
conversion : 1.5
|
|
185
176
|
},
|
|
@@ -210,7 +201,7 @@ const plans = {
|
|
|
210
201
|
[ 'Storage', '50GB' ]
|
|
211
202
|
]
|
|
212
203
|
},
|
|
213
|
-
|
|
204
|
+
actionCents : 1.85,
|
|
214
205
|
title : 'Premium',
|
|
215
206
|
conversion : 1
|
|
216
207
|
},
|
|
@@ -241,7 +232,7 @@ const plans = {
|
|
|
241
232
|
[ 'Storage', '100GB' ]
|
|
242
233
|
]
|
|
243
234
|
},
|
|
244
|
-
|
|
235
|
+
actionCents : 1.5,
|
|
245
236
|
title : 'Elite',
|
|
246
237
|
conversion : 0.5
|
|
247
238
|
},
|
|
@@ -279,12 +270,10 @@ const resolvePlan = ( subscription ) => {
|
|
|
279
270
|
// silently lacking it would be a support ticket, not a pricing decision.
|
|
280
271
|
features : all.features([ organization.networking.key, ...( custom.features?.granted || [] ) ]),
|
|
281
272
|
limits : all.limits( custom.limits?.organization || {} ),
|
|
282
|
-
// A custom plan stores its overage BARE on `custom.overages
|
|
283
|
-
//
|
|
284
|
-
//
|
|
285
|
-
// (it bills nothing) rather than becoming NaN.
|
|
273
|
+
// A custom plan stores its overage BARE on `custom.overages`. Number() so
|
|
274
|
+
// a deal stored as a string still resolves to cents-per-action; an unnamed
|
|
275
|
+
// overage stays undefined (it bills nothing) rather than becoming NaN.
|
|
286
276
|
actionCents : custom.overages == null ? undefined : Number( custom.overages ),
|
|
287
|
-
overages : { actions : custom.overages },
|
|
288
277
|
title : custom.title || 'Custom'
|
|
289
278
|
};
|
|
290
279
|
|
package/dist/plans.js
CHANGED
|
@@ -252,10 +252,6 @@ var featuresFor = (array = []) => Object.values({
|
|
|
252
252
|
},
|
|
253
253
|
{ denied: {}, granted: {} }
|
|
254
254
|
);
|
|
255
|
-
var overage = (actionCents) => ({
|
|
256
|
-
actionCents,
|
|
257
|
-
overages: { actions: String(actionCents) }
|
|
258
|
-
});
|
|
259
255
|
var unitAmountDecimal = (cents) => {
|
|
260
256
|
const value = Number(cents);
|
|
261
257
|
if (cents == null || !Number.isFinite(value)) throw new Error(`Invalid overage rate: ${cents}`);
|
|
@@ -342,7 +338,7 @@ var plans = {
|
|
|
342
338
|
["Storage", "10GB"]
|
|
343
339
|
]
|
|
344
340
|
},
|
|
345
|
-
|
|
341
|
+
actionCents: 2.5,
|
|
346
342
|
title: "Starter",
|
|
347
343
|
conversion: 2
|
|
348
344
|
},
|
|
@@ -373,7 +369,7 @@ var plans = {
|
|
|
373
369
|
["Storage", "20GB"]
|
|
374
370
|
]
|
|
375
371
|
},
|
|
376
|
-
|
|
372
|
+
actionCents: 2,
|
|
377
373
|
title: "Pro",
|
|
378
374
|
conversion: 1.5
|
|
379
375
|
},
|
|
@@ -404,7 +400,7 @@ var plans = {
|
|
|
404
400
|
["Storage", "50GB"]
|
|
405
401
|
]
|
|
406
402
|
},
|
|
407
|
-
|
|
403
|
+
actionCents: 1.85,
|
|
408
404
|
title: "Premium",
|
|
409
405
|
conversion: 1
|
|
410
406
|
},
|
|
@@ -435,7 +431,7 @@ var plans = {
|
|
|
435
431
|
["Storage", "100GB"]
|
|
436
432
|
]
|
|
437
433
|
},
|
|
438
|
-
|
|
434
|
+
actionCents: 1.5,
|
|
439
435
|
title: "Elite",
|
|
440
436
|
conversion: 0.5
|
|
441
437
|
}
|
|
@@ -457,12 +453,10 @@ var resolvePlan = (subscription) => {
|
|
|
457
453
|
// silently lacking it would be a support ticket, not a pricing decision.
|
|
458
454
|
features: all.features([organization.networking.key, ...((_a = custom.features) == null ? void 0 : _a.granted) || []]),
|
|
459
455
|
limits: all.limits(((_b = custom.limits) == null ? void 0 : _b.organization) || {}),
|
|
460
|
-
// A custom plan stores its overage BARE on `custom.overages
|
|
461
|
-
//
|
|
462
|
-
//
|
|
463
|
-
// (it bills nothing) rather than becoming NaN.
|
|
456
|
+
// A custom plan stores its overage BARE on `custom.overages`. Number() so
|
|
457
|
+
// a deal stored as a string still resolves to cents-per-action; an unnamed
|
|
458
|
+
// overage stays undefined (it bills nothing) rather than becoming NaN.
|
|
464
459
|
actionCents: custom.overages == null ? void 0 : Number(custom.overages),
|
|
465
|
-
overages: { actions: custom.overages },
|
|
466
460
|
title: custom.title || "Custom"
|
|
467
461
|
};
|
|
468
462
|
};
|
package/dist/pricing.cjs
CHANGED
|
@@ -303,10 +303,6 @@ var featuresFor = (array = []) => Object.values({
|
|
|
303
303
|
},
|
|
304
304
|
{ denied: {}, granted: {} }
|
|
305
305
|
);
|
|
306
|
-
var overage = (actionCents) => ({
|
|
307
|
-
actionCents,
|
|
308
|
-
overages: { actions: String(actionCents) }
|
|
309
|
-
});
|
|
310
306
|
var unitAmountDecimal = (cents) => {
|
|
311
307
|
const value = Number(cents);
|
|
312
308
|
if (cents == null || !Number.isFinite(value)) throw new Error(`Invalid overage rate: ${cents}`);
|
|
@@ -393,7 +389,7 @@ var plans = {
|
|
|
393
389
|
["Storage", "10GB"]
|
|
394
390
|
]
|
|
395
391
|
},
|
|
396
|
-
|
|
392
|
+
actionCents: 2.5,
|
|
397
393
|
title: "Starter",
|
|
398
394
|
conversion: 2
|
|
399
395
|
},
|
|
@@ -424,7 +420,7 @@ var plans = {
|
|
|
424
420
|
["Storage", "20GB"]
|
|
425
421
|
]
|
|
426
422
|
},
|
|
427
|
-
|
|
423
|
+
actionCents: 2,
|
|
428
424
|
title: "Pro",
|
|
429
425
|
conversion: 1.5
|
|
430
426
|
},
|
|
@@ -455,7 +451,7 @@ var plans = {
|
|
|
455
451
|
["Storage", "50GB"]
|
|
456
452
|
]
|
|
457
453
|
},
|
|
458
|
-
|
|
454
|
+
actionCents: 1.85,
|
|
459
455
|
title: "Premium",
|
|
460
456
|
conversion: 1
|
|
461
457
|
},
|
|
@@ -486,7 +482,7 @@ var plans = {
|
|
|
486
482
|
["Storage", "100GB"]
|
|
487
483
|
]
|
|
488
484
|
},
|
|
489
|
-
|
|
485
|
+
actionCents: 1.5,
|
|
490
486
|
title: "Elite",
|
|
491
487
|
conversion: 0.5
|
|
492
488
|
}
|
|
@@ -508,12 +504,10 @@ var resolvePlan = (subscription) => {
|
|
|
508
504
|
// silently lacking it would be a support ticket, not a pricing decision.
|
|
509
505
|
features: all.features([organization.networking.key, ...((_a = custom.features) == null ? void 0 : _a.granted) || []]),
|
|
510
506
|
limits: all.limits(((_b = custom.limits) == null ? void 0 : _b.organization) || {}),
|
|
511
|
-
// A custom plan stores its overage BARE on `custom.overages
|
|
512
|
-
//
|
|
513
|
-
//
|
|
514
|
-
// (it bills nothing) rather than becoming NaN.
|
|
507
|
+
// A custom plan stores its overage BARE on `custom.overages`. Number() so
|
|
508
|
+
// a deal stored as a string still resolves to cents-per-action; an unnamed
|
|
509
|
+
// overage stays undefined (it bills nothing) rather than becoming NaN.
|
|
515
510
|
actionCents: custom.overages == null ? void 0 : Number(custom.overages),
|
|
516
|
-
overages: { actions: custom.overages },
|
|
517
511
|
title: custom.title || "Custom"
|
|
518
512
|
};
|
|
519
513
|
};
|
package/dist/pricing.d.cts
CHANGED
|
@@ -25,8 +25,6 @@ import '@drawbridge/drawbridge-telemetry';
|
|
|
25
25
|
// actionCents CENTS per action, number. 2.5 = $0.025.
|
|
26
26
|
// Starter 2.5 · Pro 2 · Premium 1.85 · Elite 1.5.
|
|
27
27
|
// Free has none — it is hard-capped at 200 actions.
|
|
28
|
-
// overages.actions DEPRECATED alias of actionCents, as a string. Kept for
|
|
29
|
-
// one release so consumers pin-bump in any order.
|
|
30
28
|
// unitAmountDecimal() cents → Stripe `unit_amount_decimal` (a cents string).
|
|
31
29
|
// plans[ id ].conversion PERCENT of order value taken as the conversion fee.
|
|
32
30
|
// limits.* counts, except storage which is BYTES.
|
package/dist/pricing.d.ts
CHANGED
|
@@ -25,8 +25,6 @@ import '@drawbridge/drawbridge-telemetry';
|
|
|
25
25
|
// actionCents CENTS per action, number. 2.5 = $0.025.
|
|
26
26
|
// Starter 2.5 · Pro 2 · Premium 1.85 · Elite 1.5.
|
|
27
27
|
// Free has none — it is hard-capped at 200 actions.
|
|
28
|
-
// overages.actions DEPRECATED alias of actionCents, as a string. Kept for
|
|
29
|
-
// one release so consumers pin-bump in any order.
|
|
30
28
|
// unitAmountDecimal() cents → Stripe `unit_amount_decimal` (a cents string).
|
|
31
29
|
// plans[ id ].conversion PERCENT of order value taken as the conversion fee.
|
|
32
30
|
// limits.* counts, except storage which is BYTES.
|
package/dist/pricing.js
CHANGED
|
@@ -252,10 +252,6 @@ var featuresFor = (array = []) => Object.values({
|
|
|
252
252
|
},
|
|
253
253
|
{ denied: {}, granted: {} }
|
|
254
254
|
);
|
|
255
|
-
var overage = (actionCents) => ({
|
|
256
|
-
actionCents,
|
|
257
|
-
overages: { actions: String(actionCents) }
|
|
258
|
-
});
|
|
259
255
|
var unitAmountDecimal = (cents) => {
|
|
260
256
|
const value = Number(cents);
|
|
261
257
|
if (cents == null || !Number.isFinite(value)) throw new Error(`Invalid overage rate: ${cents}`);
|
|
@@ -342,7 +338,7 @@ var plans = {
|
|
|
342
338
|
["Storage", "10GB"]
|
|
343
339
|
]
|
|
344
340
|
},
|
|
345
|
-
|
|
341
|
+
actionCents: 2.5,
|
|
346
342
|
title: "Starter",
|
|
347
343
|
conversion: 2
|
|
348
344
|
},
|
|
@@ -373,7 +369,7 @@ var plans = {
|
|
|
373
369
|
["Storage", "20GB"]
|
|
374
370
|
]
|
|
375
371
|
},
|
|
376
|
-
|
|
372
|
+
actionCents: 2,
|
|
377
373
|
title: "Pro",
|
|
378
374
|
conversion: 1.5
|
|
379
375
|
},
|
|
@@ -404,7 +400,7 @@ var plans = {
|
|
|
404
400
|
["Storage", "50GB"]
|
|
405
401
|
]
|
|
406
402
|
},
|
|
407
|
-
|
|
403
|
+
actionCents: 1.85,
|
|
408
404
|
title: "Premium",
|
|
409
405
|
conversion: 1
|
|
410
406
|
},
|
|
@@ -435,7 +431,7 @@ var plans = {
|
|
|
435
431
|
["Storage", "100GB"]
|
|
436
432
|
]
|
|
437
433
|
},
|
|
438
|
-
|
|
434
|
+
actionCents: 1.5,
|
|
439
435
|
title: "Elite",
|
|
440
436
|
conversion: 0.5
|
|
441
437
|
}
|
|
@@ -457,12 +453,10 @@ var resolvePlan = (subscription) => {
|
|
|
457
453
|
// silently lacking it would be a support ticket, not a pricing decision.
|
|
458
454
|
features: all.features([organization.networking.key, ...((_a = custom.features) == null ? void 0 : _a.granted) || []]),
|
|
459
455
|
limits: all.limits(((_b = custom.limits) == null ? void 0 : _b.organization) || {}),
|
|
460
|
-
// A custom plan stores its overage BARE on `custom.overages
|
|
461
|
-
//
|
|
462
|
-
//
|
|
463
|
-
// (it bills nothing) rather than becoming NaN.
|
|
456
|
+
// A custom plan stores its overage BARE on `custom.overages`. Number() so
|
|
457
|
+
// a deal stored as a string still resolves to cents-per-action; an unnamed
|
|
458
|
+
// overage stays undefined (it bills nothing) rather than becoming NaN.
|
|
464
459
|
actionCents: custom.overages == null ? void 0 : Number(custom.overages),
|
|
465
|
-
overages: { actions: custom.overages },
|
|
466
460
|
title: custom.title || "Custom"
|
|
467
461
|
};
|
|
468
462
|
};
|
package/dist/providers.cjs
CHANGED
|
@@ -1276,10 +1276,6 @@ var featuresFor = (array = []) => Object.values({
|
|
|
1276
1276
|
},
|
|
1277
1277
|
{ denied: {}, granted: {} }
|
|
1278
1278
|
);
|
|
1279
|
-
var overage = (actionCents) => ({
|
|
1280
|
-
actionCents,
|
|
1281
|
-
overages: { actions: String(actionCents) }
|
|
1282
|
-
});
|
|
1283
1279
|
var all = {
|
|
1284
1280
|
features: (array = []) => featuresFor([
|
|
1285
1281
|
connection.attentive.key,
|
|
@@ -1361,7 +1357,7 @@ var plans = {
|
|
|
1361
1357
|
["Storage", "10GB"]
|
|
1362
1358
|
]
|
|
1363
1359
|
},
|
|
1364
|
-
|
|
1360
|
+
actionCents: 2.5,
|
|
1365
1361
|
title: "Starter",
|
|
1366
1362
|
conversion: 2
|
|
1367
1363
|
},
|
|
@@ -1392,7 +1388,7 @@ var plans = {
|
|
|
1392
1388
|
["Storage", "20GB"]
|
|
1393
1389
|
]
|
|
1394
1390
|
},
|
|
1395
|
-
|
|
1391
|
+
actionCents: 2,
|
|
1396
1392
|
title: "Pro",
|
|
1397
1393
|
conversion: 1.5
|
|
1398
1394
|
},
|
|
@@ -1423,7 +1419,7 @@ var plans = {
|
|
|
1423
1419
|
["Storage", "50GB"]
|
|
1424
1420
|
]
|
|
1425
1421
|
},
|
|
1426
|
-
|
|
1422
|
+
actionCents: 1.85,
|
|
1427
1423
|
title: "Premium",
|
|
1428
1424
|
conversion: 1
|
|
1429
1425
|
},
|
|
@@ -1454,7 +1450,7 @@ var plans = {
|
|
|
1454
1450
|
["Storage", "100GB"]
|
|
1455
1451
|
]
|
|
1456
1452
|
},
|
|
1457
|
-
|
|
1453
|
+
actionCents: 1.5,
|
|
1458
1454
|
title: "Elite",
|
|
1459
1455
|
conversion: 0.5
|
|
1460
1456
|
}
|
|
@@ -1476,12 +1472,10 @@ var resolvePlan = (subscription) => {
|
|
|
1476
1472
|
// silently lacking it would be a support ticket, not a pricing decision.
|
|
1477
1473
|
features: all.features([organization.networking.key, ...((_a = custom.features) == null ? void 0 : _a.granted) || []]),
|
|
1478
1474
|
limits: all.limits(((_b = custom.limits) == null ? void 0 : _b.organization) || {}),
|
|
1479
|
-
// A custom plan stores its overage BARE on `custom.overages
|
|
1480
|
-
//
|
|
1481
|
-
//
|
|
1482
|
-
// (it bills nothing) rather than becoming NaN.
|
|
1475
|
+
// A custom plan stores its overage BARE on `custom.overages`. Number() so
|
|
1476
|
+
// a deal stored as a string still resolves to cents-per-action; an unnamed
|
|
1477
|
+
// overage stays undefined (it bills nothing) rather than becoming NaN.
|
|
1483
1478
|
actionCents: custom.overages == null ? void 0 : Number(custom.overages),
|
|
1484
|
-
overages: { actions: custom.overages },
|
|
1485
1479
|
title: custom.title || "Custom"
|
|
1486
1480
|
};
|
|
1487
1481
|
};
|
package/dist/providers.js
CHANGED
|
@@ -1232,10 +1232,6 @@ var featuresFor = (array = []) => Object.values({
|
|
|
1232
1232
|
},
|
|
1233
1233
|
{ denied: {}, granted: {} }
|
|
1234
1234
|
);
|
|
1235
|
-
var overage = (actionCents) => ({
|
|
1236
|
-
actionCents,
|
|
1237
|
-
overages: { actions: String(actionCents) }
|
|
1238
|
-
});
|
|
1239
1235
|
var all = {
|
|
1240
1236
|
features: (array = []) => featuresFor([
|
|
1241
1237
|
connection.attentive.key,
|
|
@@ -1317,7 +1313,7 @@ var plans = {
|
|
|
1317
1313
|
["Storage", "10GB"]
|
|
1318
1314
|
]
|
|
1319
1315
|
},
|
|
1320
|
-
|
|
1316
|
+
actionCents: 2.5,
|
|
1321
1317
|
title: "Starter",
|
|
1322
1318
|
conversion: 2
|
|
1323
1319
|
},
|
|
@@ -1348,7 +1344,7 @@ var plans = {
|
|
|
1348
1344
|
["Storage", "20GB"]
|
|
1349
1345
|
]
|
|
1350
1346
|
},
|
|
1351
|
-
|
|
1347
|
+
actionCents: 2,
|
|
1352
1348
|
title: "Pro",
|
|
1353
1349
|
conversion: 1.5
|
|
1354
1350
|
},
|
|
@@ -1379,7 +1375,7 @@ var plans = {
|
|
|
1379
1375
|
["Storage", "50GB"]
|
|
1380
1376
|
]
|
|
1381
1377
|
},
|
|
1382
|
-
|
|
1378
|
+
actionCents: 1.85,
|
|
1383
1379
|
title: "Premium",
|
|
1384
1380
|
conversion: 1
|
|
1385
1381
|
},
|
|
@@ -1410,7 +1406,7 @@ var plans = {
|
|
|
1410
1406
|
["Storage", "100GB"]
|
|
1411
1407
|
]
|
|
1412
1408
|
},
|
|
1413
|
-
|
|
1409
|
+
actionCents: 1.5,
|
|
1414
1410
|
title: "Elite",
|
|
1415
1411
|
conversion: 0.5
|
|
1416
1412
|
}
|
|
@@ -1432,12 +1428,10 @@ var resolvePlan = (subscription) => {
|
|
|
1432
1428
|
// silently lacking it would be a support ticket, not a pricing decision.
|
|
1433
1429
|
features: all.features([organization.networking.key, ...((_a = custom.features) == null ? void 0 : _a.granted) || []]),
|
|
1434
1430
|
limits: all.limits(((_b = custom.limits) == null ? void 0 : _b.organization) || {}),
|
|
1435
|
-
// A custom plan stores its overage BARE on `custom.overages
|
|
1436
|
-
//
|
|
1437
|
-
//
|
|
1438
|
-
// (it bills nothing) rather than becoming NaN.
|
|
1431
|
+
// A custom plan stores its overage BARE on `custom.overages`. Number() so
|
|
1432
|
+
// a deal stored as a string still resolves to cents-per-action; an unnamed
|
|
1433
|
+
// overage stays undefined (it bills nothing) rather than becoming NaN.
|
|
1439
1434
|
actionCents: custom.overages == null ? void 0 : Number(custom.overages),
|
|
1440
|
-
overages: { actions: custom.overages },
|
|
1441
1435
|
title: custom.title || "Custom"
|
|
1442
1436
|
};
|
|
1443
1437
|
};
|
package/dist/twilio.cjs
CHANGED
|
@@ -107,28 +107,37 @@ var twilio = {
|
|
|
107
107
|
// NUMBER LIFECYCLE, same transport discipline as sms above. Endpoints and
|
|
108
108
|
// parameter names are Twilio's own, from the AvailablePhoneNumbers and
|
|
109
109
|
// IncomingPhoneNumbers resource docs:
|
|
110
|
-
// GET /2010-04-01/Accounts/{AccountSid}/AvailablePhoneNumbers/{CountryCode}/
|
|
111
|
-
// query: SmsEnabled,
|
|
110
|
+
// GET /2010-04-01/Accounts/{AccountSid}/AvailablePhoneNumbers/{CountryCode}/TollFree.json
|
|
111
|
+
// query: SmsEnabled, Contains, PageSize (1-1000)
|
|
112
112
|
// POST /2010-04-01/Accounts/{AccountSid}/IncomingPhoneNumbers.json
|
|
113
113
|
// body: PhoneNumber (E.164), SmsUrl, SmsMethod
|
|
114
114
|
// DELETE /2010-04-01/Accounts/{AccountSid}/IncomingPhoneNumbers/{Sid}.json
|
|
115
115
|
// Basic auth throughout, like Messages.
|
|
116
|
-
|
|
116
|
+
//
|
|
117
|
+
// TOLL-FREE, not Local, and that is a carrier-registration decision rather
|
|
118
|
+
// than an inventory one. A US local number may not send A2P SMS without full
|
|
119
|
+
// 10DLC brand-and-campaign registration; a toll-free number needs only
|
|
120
|
+
// Toll-Free Verification — one API submission per number (see
|
|
121
|
+
// submitVerification below), no TCR brand, no campaign fees. Until a
|
|
122
|
+
// verification is approved, Twilio blocks the number's US-bound messages
|
|
123
|
+
// outright — which is why a purchased number starts `pending` and the send
|
|
124
|
+
// path refuses anything but `verified`.
|
|
125
|
+
searchNumbers: async ({ accountSid, authToken, contains, country = "US", cursor, limit = 10, request: request2 = request }) => {
|
|
117
126
|
if (!accountSid || !authToken) throw new Error("Twilio credentials missing \u2014 accountSid, authToken");
|
|
118
127
|
const page = Number(cursor) || 0;
|
|
119
128
|
const result = await request2({
|
|
120
129
|
method: "GET",
|
|
121
|
-
url: "https://api.twilio.com/2010-04-01/Accounts/" + accountSid + "/AvailablePhoneNumbers/" + country + "/
|
|
130
|
+
url: "https://api.twilio.com/2010-04-01/Accounts/" + accountSid + "/AvailablePhoneNumbers/" + country + "/TollFree.json",
|
|
122
131
|
headers: {
|
|
123
132
|
"Authorization": "Basic " + Buffer.from(accountSid + ":" + authToken).toString("base64")
|
|
124
133
|
},
|
|
125
134
|
query: {
|
|
126
135
|
SmsEnabled: true,
|
|
127
136
|
PageSize: limit,
|
|
128
|
-
...areaCode && { AreaCode: areaCode },
|
|
129
137
|
// `Contains` is the vendor's matching-pattern filter — digits
|
|
130
|
-
// match anywhere in the number, so one search box serves "
|
|
131
|
-
//
|
|
138
|
+
// match anywhere in the number, so one search box serves "an 888
|
|
139
|
+
// prefix" and "ends in 7777" alike. (AreaCode is a Local-only
|
|
140
|
+
// filter and went with Local.json.)
|
|
132
141
|
...contains && { Contains: contains },
|
|
133
142
|
...page && { Page: page }
|
|
134
143
|
}
|
|
@@ -175,6 +184,84 @@ var twilio = {
|
|
|
175
184
|
"Authorization": "Basic " + Buffer.from(accountSid + ":" + authToken).toString("base64")
|
|
176
185
|
}
|
|
177
186
|
});
|
|
187
|
+
},
|
|
188
|
+
// TOLL-FREE VERIFICATION — the carrier registration that lets a purchased
|
|
189
|
+
// number actually deliver. From the Tollfree Verification resource docs
|
|
190
|
+
// (messaging.twilio.com, docs/messaging/api/tollfree-verification-resource):
|
|
191
|
+
// POST /v1/Tollfree/Verifications
|
|
192
|
+
// required: BusinessName, BusinessWebsite, NotificationEmail,
|
|
193
|
+
// UseCaseCategories (array), UseCaseSummary,
|
|
194
|
+
// ProductionMessageSample, OptInImageUrls (array), OptInType,
|
|
195
|
+
// MessageVolume, TollfreePhoneNumberSid (PN…)
|
|
196
|
+
// GET /v1/Tollfree/Verifications/{Sid}
|
|
197
|
+
// status: PENDING_REVIEW | IN_REVIEW | TWILIO_APPROVED |
|
|
198
|
+
// TWILIO_REJECTED, with rejection_reason beside a rejection.
|
|
199
|
+
// Basic auth, form-encoded, like everything above. There is no status
|
|
200
|
+
// callback on this resource — the caller polls the fetch.
|
|
201
|
+
//
|
|
202
|
+
// THE END BUSINESS'S DETAILS, never the platform's. Twilio rejects ISV
|
|
203
|
+
// submissions carrying the ISV's own identity (Toll-Free Verification for
|
|
204
|
+
// ISVs, support.twilio.com) — so businessName, website and email here are
|
|
205
|
+
// the merchant's, collected at purchase.
|
|
206
|
+
//
|
|
207
|
+
// The use-case fields are PLATFORM facts and live here beside the message
|
|
208
|
+
// composition for the same reason it does: how Drawbridge collects opt-in
|
|
209
|
+
// (its own hosted entry forms) and what its messages look like are
|
|
210
|
+
// properties of sending through Drawbridge, not of any one merchant.
|
|
211
|
+
submitVerification: async ({
|
|
212
|
+
accountSid,
|
|
213
|
+
authToken,
|
|
214
|
+
businessName,
|
|
215
|
+
email,
|
|
216
|
+
numberSid,
|
|
217
|
+
optInImage,
|
|
218
|
+
request: request2 = request,
|
|
219
|
+
volume,
|
|
220
|
+
website
|
|
221
|
+
}) => {
|
|
222
|
+
const missing = Object.entries({ accountSid, authToken, businessName, email, numberSid, optInImage, website }).filter(([, value]) => !value).map(([name]) => name);
|
|
223
|
+
if (missing.length) throw new Error("Toll-free verification submission missing \u2014 " + missing.join(", "));
|
|
224
|
+
const body = new URLSearchParams();
|
|
225
|
+
body.append("BusinessName", businessName);
|
|
226
|
+
body.append("BusinessWebsite", website);
|
|
227
|
+
body.append("NotificationEmail", email);
|
|
228
|
+
body.append("UseCaseCategories", "MARKETING");
|
|
229
|
+
body.append("UseCaseSummary", "Prize-draw entry confirmations, winner notifications and campaign updates to consumers who opted in on this business's Drawbridge-hosted entry form.");
|
|
230
|
+
body.append("ProductionMessageSample", businessName + "\nThanks for entering! We'll text you here about your entry.\nReply STOP to opt out");
|
|
231
|
+
body.append("OptInImageUrls", optInImage);
|
|
232
|
+
body.append("OptInType", "WEB_FORM");
|
|
233
|
+
body.append("MessageVolume", volume || "1,000");
|
|
234
|
+
body.append("TollfreePhoneNumberSid", numberSid);
|
|
235
|
+
const result = await request2({
|
|
236
|
+
method: "POST",
|
|
237
|
+
type: "form",
|
|
238
|
+
url: "https://messaging.twilio.com/v1/Tollfree/Verifications",
|
|
239
|
+
headers: {
|
|
240
|
+
"Authorization": "Basic " + Buffer.from(accountSid + ":" + authToken).toString("base64")
|
|
241
|
+
},
|
|
242
|
+
body
|
|
243
|
+
});
|
|
244
|
+
return {
|
|
245
|
+
sid: result == null ? void 0 : result.sid,
|
|
246
|
+
status: result == null ? void 0 : result.status
|
|
247
|
+
};
|
|
248
|
+
},
|
|
249
|
+
fetchVerification: async ({ accountSid, authToken, request: request2 = request, sid }) => {
|
|
250
|
+
if (!accountSid || !authToken) throw new Error("Twilio credentials missing \u2014 accountSid, authToken");
|
|
251
|
+
if (!sid) throw new Error("Twilio verification fetch needs the verification sid");
|
|
252
|
+
const result = await request2({
|
|
253
|
+
method: "GET",
|
|
254
|
+
url: "https://messaging.twilio.com/v1/Tollfree/Verifications/" + sid,
|
|
255
|
+
headers: {
|
|
256
|
+
"Authorization": "Basic " + Buffer.from(accountSid + ":" + authToken).toString("base64")
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
return {
|
|
260
|
+
// TWILIO_APPROVED → verified, TWILIO_REJECTED → rejected, both
|
|
261
|
+
// review states → pending.
|
|
262
|
+
outcome: (result == null ? void 0 : result.status) === "TWILIO_APPROVED" ? "approved" : (result == null ? void 0 : result.status) === "TWILIO_REJECTED" ? "rejected" : "pending",
|
|
263
|
+
reason: (result == null ? void 0 : result.rejection_reason) || null
|
|
264
|
+
};
|
|
178
265
|
}
|
|
179
266
|
};
|
|
180
267
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/twilio.d.cts
CHANGED
|
@@ -70,14 +70,23 @@ const twilio = {
|
|
|
70
70
|
// NUMBER LIFECYCLE, same transport discipline as sms above. Endpoints and
|
|
71
71
|
// parameter names are Twilio's own, from the AvailablePhoneNumbers and
|
|
72
72
|
// IncomingPhoneNumbers resource docs:
|
|
73
|
-
// GET /2010-04-01/Accounts/{AccountSid}/AvailablePhoneNumbers/{CountryCode}/
|
|
74
|
-
// query: SmsEnabled,
|
|
73
|
+
// GET /2010-04-01/Accounts/{AccountSid}/AvailablePhoneNumbers/{CountryCode}/TollFree.json
|
|
74
|
+
// query: SmsEnabled, Contains, PageSize (1-1000)
|
|
75
75
|
// POST /2010-04-01/Accounts/{AccountSid}/IncomingPhoneNumbers.json
|
|
76
76
|
// body: PhoneNumber (E.164), SmsUrl, SmsMethod
|
|
77
77
|
// DELETE /2010-04-01/Accounts/{AccountSid}/IncomingPhoneNumbers/{Sid}.json
|
|
78
78
|
// Basic auth throughout, like Messages.
|
|
79
|
+
//
|
|
80
|
+
// TOLL-FREE, not Local, and that is a carrier-registration decision rather
|
|
81
|
+
// than an inventory one. A US local number may not send A2P SMS without full
|
|
82
|
+
// 10DLC brand-and-campaign registration; a toll-free number needs only
|
|
83
|
+
// Toll-Free Verification — one API submission per number (see
|
|
84
|
+
// submitVerification below), no TCR brand, no campaign fees. Until a
|
|
85
|
+
// verification is approved, Twilio blocks the number's US-bound messages
|
|
86
|
+
// outright — which is why a purchased number starts `pending` and the send
|
|
87
|
+
// path refuses anything but `verified`.
|
|
79
88
|
|
|
80
|
-
searchNumbers : async ({ accountSid,
|
|
89
|
+
searchNumbers : async ({ accountSid, authToken, contains, country = 'US', cursor, limit = 10, request: request$1 = request }) => {
|
|
81
90
|
|
|
82
91
|
if( ! accountSid || ! authToken ) throw new Error( 'Twilio credentials missing — accountSid, authToken' );
|
|
83
92
|
|
|
@@ -93,17 +102,17 @@ const twilio = {
|
|
|
93
102
|
|
|
94
103
|
const result = await request$1({
|
|
95
104
|
method : 'GET',
|
|
96
|
-
url : 'https://api.twilio.com/2010-04-01/Accounts/' + accountSid + '/AvailablePhoneNumbers/' + country + '/
|
|
105
|
+
url : 'https://api.twilio.com/2010-04-01/Accounts/' + accountSid + '/AvailablePhoneNumbers/' + country + '/TollFree.json',
|
|
97
106
|
headers : {
|
|
98
107
|
'Authorization' : 'Basic ' + Buffer.from( accountSid + ':' + authToken ).toString( 'base64' )
|
|
99
108
|
},
|
|
100
109
|
query : {
|
|
101
110
|
SmsEnabled : true,
|
|
102
111
|
PageSize : limit,
|
|
103
|
-
...( areaCode && { AreaCode : areaCode } ),
|
|
104
112
|
// `Contains` is the vendor's matching-pattern filter — digits
|
|
105
|
-
// match anywhere in the number, so one search box serves "
|
|
106
|
-
//
|
|
113
|
+
// match anywhere in the number, so one search box serves "an 888
|
|
114
|
+
// prefix" and "ends in 7777" alike. (AreaCode is a Local-only
|
|
115
|
+
// filter and went with Local.json.)
|
|
107
116
|
...( contains && { Contains : contains } ),
|
|
108
117
|
...( page && { Page : page } )
|
|
109
118
|
}
|
|
@@ -163,6 +172,111 @@ const twilio = {
|
|
|
163
172
|
}
|
|
164
173
|
});
|
|
165
174
|
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
// TOLL-FREE VERIFICATION — the carrier registration that lets a purchased
|
|
178
|
+
// number actually deliver. From the Tollfree Verification resource docs
|
|
179
|
+
// (messaging.twilio.com, docs/messaging/api/tollfree-verification-resource):
|
|
180
|
+
// POST /v1/Tollfree/Verifications
|
|
181
|
+
// required: BusinessName, BusinessWebsite, NotificationEmail,
|
|
182
|
+
// UseCaseCategories (array), UseCaseSummary,
|
|
183
|
+
// ProductionMessageSample, OptInImageUrls (array), OptInType,
|
|
184
|
+
// MessageVolume, TollfreePhoneNumberSid (PN…)
|
|
185
|
+
// GET /v1/Tollfree/Verifications/{Sid}
|
|
186
|
+
// status: PENDING_REVIEW | IN_REVIEW | TWILIO_APPROVED |
|
|
187
|
+
// TWILIO_REJECTED, with rejection_reason beside a rejection.
|
|
188
|
+
// Basic auth, form-encoded, like everything above. There is no status
|
|
189
|
+
// callback on this resource — the caller polls the fetch.
|
|
190
|
+
//
|
|
191
|
+
// THE END BUSINESS'S DETAILS, never the platform's. Twilio rejects ISV
|
|
192
|
+
// submissions carrying the ISV's own identity (Toll-Free Verification for
|
|
193
|
+
// ISVs, support.twilio.com) — so businessName, website and email here are
|
|
194
|
+
// the merchant's, collected at purchase.
|
|
195
|
+
//
|
|
196
|
+
// The use-case fields are PLATFORM facts and live here beside the message
|
|
197
|
+
// composition for the same reason it does: how Drawbridge collects opt-in
|
|
198
|
+
// (its own hosted entry forms) and what its messages look like are
|
|
199
|
+
// properties of sending through Drawbridge, not of any one merchant.
|
|
200
|
+
submitVerification : async ({
|
|
201
|
+
accountSid,
|
|
202
|
+
authToken,
|
|
203
|
+
businessName,
|
|
204
|
+
email,
|
|
205
|
+
numberSid,
|
|
206
|
+
optInImage,
|
|
207
|
+
request: request$1 = request,
|
|
208
|
+
volume,
|
|
209
|
+
website
|
|
210
|
+
}) => {
|
|
211
|
+
|
|
212
|
+
const missing = Object.entries({ accountSid, authToken, businessName, email, numberSid, optInImage, website })
|
|
213
|
+
.filter( ( [ , value ] ) => ! value )
|
|
214
|
+
.map( ( [ name ] ) => name );
|
|
215
|
+
|
|
216
|
+
if( missing.length ) throw new Error( 'Toll-free verification submission missing — ' + missing.join( ', ' ) );
|
|
217
|
+
|
|
218
|
+
// URLSearchParams rather than an object: UseCaseCategories and
|
|
219
|
+
// OptInImageUrls are array parameters, which Twilio takes as REPEATED
|
|
220
|
+
// form keys — an object body would comma-join them into one value.
|
|
221
|
+
const body = new URLSearchParams();
|
|
222
|
+
|
|
223
|
+
body.append( 'BusinessName', businessName );
|
|
224
|
+
body.append( 'BusinessWebsite', website );
|
|
225
|
+
body.append( 'NotificationEmail', email );
|
|
226
|
+
body.append( 'UseCaseCategories', 'MARKETING' );
|
|
227
|
+
body.append( 'UseCaseSummary', 'Prize-draw entry confirmations, winner notifications and campaign updates to consumers who opted in on this business\'s Drawbridge-hosted entry form.' );
|
|
228
|
+
// Mirrors the sms() composition above — newline-separated, brand first,
|
|
229
|
+
// opt-out line appended — so the reviewed sample is the sent shape.
|
|
230
|
+
body.append( 'ProductionMessageSample', businessName + '\nThanks for entering! We\'ll text you here about your entry.\nReply STOP to opt out' );
|
|
231
|
+
body.append( 'OptInImageUrls', optInImage );
|
|
232
|
+
// The entry form is a web form; the phone field is beside the consent
|
|
233
|
+
// language on the merchant's campaign page.
|
|
234
|
+
body.append( 'OptInType', 'WEB_FORM' );
|
|
235
|
+
body.append( 'MessageVolume', volume || '1,000' );
|
|
236
|
+
body.append( 'TollfreePhoneNumberSid', numberSid );
|
|
237
|
+
|
|
238
|
+
const result = await request$1({
|
|
239
|
+
method : 'POST',
|
|
240
|
+
type : 'form',
|
|
241
|
+
url : 'https://messaging.twilio.com/v1/Tollfree/Verifications',
|
|
242
|
+
headers : {
|
|
243
|
+
'Authorization' : 'Basic ' + Buffer.from( accountSid + ':' + authToken ).toString( 'base64' )
|
|
244
|
+
},
|
|
245
|
+
body
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
return {
|
|
249
|
+
sid : result?.sid,
|
|
250
|
+
status : result?.status
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
},
|
|
254
|
+
|
|
255
|
+
fetchVerification : async ({ accountSid, authToken, request: request$1 = request, sid }) => {
|
|
256
|
+
|
|
257
|
+
if( ! accountSid || ! authToken ) throw new Error( 'Twilio credentials missing — accountSid, authToken' );
|
|
258
|
+
if( ! sid ) throw new Error( 'Twilio verification fetch needs the verification sid' );
|
|
259
|
+
|
|
260
|
+
const result = await request$1({
|
|
261
|
+
method : 'GET',
|
|
262
|
+
url : 'https://messaging.twilio.com/v1/Tollfree/Verifications/' + sid,
|
|
263
|
+
headers : {
|
|
264
|
+
'Authorization' : 'Basic ' + Buffer.from( accountSid + ':' + authToken ).toString( 'base64' )
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
// The caller-facing shape is ours; the vendor's vocabulary stays here.
|
|
269
|
+
return {
|
|
270
|
+
// TWILIO_APPROVED → verified, TWILIO_REJECTED → rejected, both
|
|
271
|
+
// review states → pending.
|
|
272
|
+
outcome : result?.status === 'TWILIO_APPROVED'
|
|
273
|
+
? 'approved'
|
|
274
|
+
: result?.status === 'TWILIO_REJECTED'
|
|
275
|
+
? 'rejected'
|
|
276
|
+
: 'pending',
|
|
277
|
+
reason : result?.rejection_reason || null
|
|
278
|
+
};
|
|
279
|
+
|
|
166
280
|
}
|
|
167
281
|
|
|
168
282
|
};
|
package/dist/twilio.d.ts
CHANGED
|
@@ -70,14 +70,23 @@ const twilio = {
|
|
|
70
70
|
// NUMBER LIFECYCLE, same transport discipline as sms above. Endpoints and
|
|
71
71
|
// parameter names are Twilio's own, from the AvailablePhoneNumbers and
|
|
72
72
|
// IncomingPhoneNumbers resource docs:
|
|
73
|
-
// GET /2010-04-01/Accounts/{AccountSid}/AvailablePhoneNumbers/{CountryCode}/
|
|
74
|
-
// query: SmsEnabled,
|
|
73
|
+
// GET /2010-04-01/Accounts/{AccountSid}/AvailablePhoneNumbers/{CountryCode}/TollFree.json
|
|
74
|
+
// query: SmsEnabled, Contains, PageSize (1-1000)
|
|
75
75
|
// POST /2010-04-01/Accounts/{AccountSid}/IncomingPhoneNumbers.json
|
|
76
76
|
// body: PhoneNumber (E.164), SmsUrl, SmsMethod
|
|
77
77
|
// DELETE /2010-04-01/Accounts/{AccountSid}/IncomingPhoneNumbers/{Sid}.json
|
|
78
78
|
// Basic auth throughout, like Messages.
|
|
79
|
+
//
|
|
80
|
+
// TOLL-FREE, not Local, and that is a carrier-registration decision rather
|
|
81
|
+
// than an inventory one. A US local number may not send A2P SMS without full
|
|
82
|
+
// 10DLC brand-and-campaign registration; a toll-free number needs only
|
|
83
|
+
// Toll-Free Verification — one API submission per number (see
|
|
84
|
+
// submitVerification below), no TCR brand, no campaign fees. Until a
|
|
85
|
+
// verification is approved, Twilio blocks the number's US-bound messages
|
|
86
|
+
// outright — which is why a purchased number starts `pending` and the send
|
|
87
|
+
// path refuses anything but `verified`.
|
|
79
88
|
|
|
80
|
-
searchNumbers : async ({ accountSid,
|
|
89
|
+
searchNumbers : async ({ accountSid, authToken, contains, country = 'US', cursor, limit = 10, request: request$1 = request }) => {
|
|
81
90
|
|
|
82
91
|
if( ! accountSid || ! authToken ) throw new Error( 'Twilio credentials missing — accountSid, authToken' );
|
|
83
92
|
|
|
@@ -93,17 +102,17 @@ const twilio = {
|
|
|
93
102
|
|
|
94
103
|
const result = await request$1({
|
|
95
104
|
method : 'GET',
|
|
96
|
-
url : 'https://api.twilio.com/2010-04-01/Accounts/' + accountSid + '/AvailablePhoneNumbers/' + country + '/
|
|
105
|
+
url : 'https://api.twilio.com/2010-04-01/Accounts/' + accountSid + '/AvailablePhoneNumbers/' + country + '/TollFree.json',
|
|
97
106
|
headers : {
|
|
98
107
|
'Authorization' : 'Basic ' + Buffer.from( accountSid + ':' + authToken ).toString( 'base64' )
|
|
99
108
|
},
|
|
100
109
|
query : {
|
|
101
110
|
SmsEnabled : true,
|
|
102
111
|
PageSize : limit,
|
|
103
|
-
...( areaCode && { AreaCode : areaCode } ),
|
|
104
112
|
// `Contains` is the vendor's matching-pattern filter — digits
|
|
105
|
-
// match anywhere in the number, so one search box serves "
|
|
106
|
-
//
|
|
113
|
+
// match anywhere in the number, so one search box serves "an 888
|
|
114
|
+
// prefix" and "ends in 7777" alike. (AreaCode is a Local-only
|
|
115
|
+
// filter and went with Local.json.)
|
|
107
116
|
...( contains && { Contains : contains } ),
|
|
108
117
|
...( page && { Page : page } )
|
|
109
118
|
}
|
|
@@ -163,6 +172,111 @@ const twilio = {
|
|
|
163
172
|
}
|
|
164
173
|
});
|
|
165
174
|
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
// TOLL-FREE VERIFICATION — the carrier registration that lets a purchased
|
|
178
|
+
// number actually deliver. From the Tollfree Verification resource docs
|
|
179
|
+
// (messaging.twilio.com, docs/messaging/api/tollfree-verification-resource):
|
|
180
|
+
// POST /v1/Tollfree/Verifications
|
|
181
|
+
// required: BusinessName, BusinessWebsite, NotificationEmail,
|
|
182
|
+
// UseCaseCategories (array), UseCaseSummary,
|
|
183
|
+
// ProductionMessageSample, OptInImageUrls (array), OptInType,
|
|
184
|
+
// MessageVolume, TollfreePhoneNumberSid (PN…)
|
|
185
|
+
// GET /v1/Tollfree/Verifications/{Sid}
|
|
186
|
+
// status: PENDING_REVIEW | IN_REVIEW | TWILIO_APPROVED |
|
|
187
|
+
// TWILIO_REJECTED, with rejection_reason beside a rejection.
|
|
188
|
+
// Basic auth, form-encoded, like everything above. There is no status
|
|
189
|
+
// callback on this resource — the caller polls the fetch.
|
|
190
|
+
//
|
|
191
|
+
// THE END BUSINESS'S DETAILS, never the platform's. Twilio rejects ISV
|
|
192
|
+
// submissions carrying the ISV's own identity (Toll-Free Verification for
|
|
193
|
+
// ISVs, support.twilio.com) — so businessName, website and email here are
|
|
194
|
+
// the merchant's, collected at purchase.
|
|
195
|
+
//
|
|
196
|
+
// The use-case fields are PLATFORM facts and live here beside the message
|
|
197
|
+
// composition for the same reason it does: how Drawbridge collects opt-in
|
|
198
|
+
// (its own hosted entry forms) and what its messages look like are
|
|
199
|
+
// properties of sending through Drawbridge, not of any one merchant.
|
|
200
|
+
submitVerification : async ({
|
|
201
|
+
accountSid,
|
|
202
|
+
authToken,
|
|
203
|
+
businessName,
|
|
204
|
+
email,
|
|
205
|
+
numberSid,
|
|
206
|
+
optInImage,
|
|
207
|
+
request: request$1 = request,
|
|
208
|
+
volume,
|
|
209
|
+
website
|
|
210
|
+
}) => {
|
|
211
|
+
|
|
212
|
+
const missing = Object.entries({ accountSid, authToken, businessName, email, numberSid, optInImage, website })
|
|
213
|
+
.filter( ( [ , value ] ) => ! value )
|
|
214
|
+
.map( ( [ name ] ) => name );
|
|
215
|
+
|
|
216
|
+
if( missing.length ) throw new Error( 'Toll-free verification submission missing — ' + missing.join( ', ' ) );
|
|
217
|
+
|
|
218
|
+
// URLSearchParams rather than an object: UseCaseCategories and
|
|
219
|
+
// OptInImageUrls are array parameters, which Twilio takes as REPEATED
|
|
220
|
+
// form keys — an object body would comma-join them into one value.
|
|
221
|
+
const body = new URLSearchParams();
|
|
222
|
+
|
|
223
|
+
body.append( 'BusinessName', businessName );
|
|
224
|
+
body.append( 'BusinessWebsite', website );
|
|
225
|
+
body.append( 'NotificationEmail', email );
|
|
226
|
+
body.append( 'UseCaseCategories', 'MARKETING' );
|
|
227
|
+
body.append( 'UseCaseSummary', 'Prize-draw entry confirmations, winner notifications and campaign updates to consumers who opted in on this business\'s Drawbridge-hosted entry form.' );
|
|
228
|
+
// Mirrors the sms() composition above — newline-separated, brand first,
|
|
229
|
+
// opt-out line appended — so the reviewed sample is the sent shape.
|
|
230
|
+
body.append( 'ProductionMessageSample', businessName + '\nThanks for entering! We\'ll text you here about your entry.\nReply STOP to opt out' );
|
|
231
|
+
body.append( 'OptInImageUrls', optInImage );
|
|
232
|
+
// The entry form is a web form; the phone field is beside the consent
|
|
233
|
+
// language on the merchant's campaign page.
|
|
234
|
+
body.append( 'OptInType', 'WEB_FORM' );
|
|
235
|
+
body.append( 'MessageVolume', volume || '1,000' );
|
|
236
|
+
body.append( 'TollfreePhoneNumberSid', numberSid );
|
|
237
|
+
|
|
238
|
+
const result = await request$1({
|
|
239
|
+
method : 'POST',
|
|
240
|
+
type : 'form',
|
|
241
|
+
url : 'https://messaging.twilio.com/v1/Tollfree/Verifications',
|
|
242
|
+
headers : {
|
|
243
|
+
'Authorization' : 'Basic ' + Buffer.from( accountSid + ':' + authToken ).toString( 'base64' )
|
|
244
|
+
},
|
|
245
|
+
body
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
return {
|
|
249
|
+
sid : result?.sid,
|
|
250
|
+
status : result?.status
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
},
|
|
254
|
+
|
|
255
|
+
fetchVerification : async ({ accountSid, authToken, request: request$1 = request, sid }) => {
|
|
256
|
+
|
|
257
|
+
if( ! accountSid || ! authToken ) throw new Error( 'Twilio credentials missing — accountSid, authToken' );
|
|
258
|
+
if( ! sid ) throw new Error( 'Twilio verification fetch needs the verification sid' );
|
|
259
|
+
|
|
260
|
+
const result = await request$1({
|
|
261
|
+
method : 'GET',
|
|
262
|
+
url : 'https://messaging.twilio.com/v1/Tollfree/Verifications/' + sid,
|
|
263
|
+
headers : {
|
|
264
|
+
'Authorization' : 'Basic ' + Buffer.from( accountSid + ':' + authToken ).toString( 'base64' )
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
// The caller-facing shape is ours; the vendor's vocabulary stays here.
|
|
269
|
+
return {
|
|
270
|
+
// TWILIO_APPROVED → verified, TWILIO_REJECTED → rejected, both
|
|
271
|
+
// review states → pending.
|
|
272
|
+
outcome : result?.status === 'TWILIO_APPROVED'
|
|
273
|
+
? 'approved'
|
|
274
|
+
: result?.status === 'TWILIO_REJECTED'
|
|
275
|
+
? 'rejected'
|
|
276
|
+
: 'pending',
|
|
277
|
+
reason : result?.rejection_reason || null
|
|
278
|
+
};
|
|
279
|
+
|
|
166
280
|
}
|
|
167
281
|
|
|
168
282
|
};
|
package/dist/twilio.js
CHANGED
|
@@ -82,28 +82,37 @@ var twilio = {
|
|
|
82
82
|
// NUMBER LIFECYCLE, same transport discipline as sms above. Endpoints and
|
|
83
83
|
// parameter names are Twilio's own, from the AvailablePhoneNumbers and
|
|
84
84
|
// IncomingPhoneNumbers resource docs:
|
|
85
|
-
// GET /2010-04-01/Accounts/{AccountSid}/AvailablePhoneNumbers/{CountryCode}/
|
|
86
|
-
// query: SmsEnabled,
|
|
85
|
+
// GET /2010-04-01/Accounts/{AccountSid}/AvailablePhoneNumbers/{CountryCode}/TollFree.json
|
|
86
|
+
// query: SmsEnabled, Contains, PageSize (1-1000)
|
|
87
87
|
// POST /2010-04-01/Accounts/{AccountSid}/IncomingPhoneNumbers.json
|
|
88
88
|
// body: PhoneNumber (E.164), SmsUrl, SmsMethod
|
|
89
89
|
// DELETE /2010-04-01/Accounts/{AccountSid}/IncomingPhoneNumbers/{Sid}.json
|
|
90
90
|
// Basic auth throughout, like Messages.
|
|
91
|
-
|
|
91
|
+
//
|
|
92
|
+
// TOLL-FREE, not Local, and that is a carrier-registration decision rather
|
|
93
|
+
// than an inventory one. A US local number may not send A2P SMS without full
|
|
94
|
+
// 10DLC brand-and-campaign registration; a toll-free number needs only
|
|
95
|
+
// Toll-Free Verification — one API submission per number (see
|
|
96
|
+
// submitVerification below), no TCR brand, no campaign fees. Until a
|
|
97
|
+
// verification is approved, Twilio blocks the number's US-bound messages
|
|
98
|
+
// outright — which is why a purchased number starts `pending` and the send
|
|
99
|
+
// path refuses anything but `verified`.
|
|
100
|
+
searchNumbers: async ({ accountSid, authToken, contains, country = "US", cursor, limit = 10, request: request2 = request }) => {
|
|
92
101
|
if (!accountSid || !authToken) throw new Error("Twilio credentials missing \u2014 accountSid, authToken");
|
|
93
102
|
const page = Number(cursor) || 0;
|
|
94
103
|
const result = await request2({
|
|
95
104
|
method: "GET",
|
|
96
|
-
url: "https://api.twilio.com/2010-04-01/Accounts/" + accountSid + "/AvailablePhoneNumbers/" + country + "/
|
|
105
|
+
url: "https://api.twilio.com/2010-04-01/Accounts/" + accountSid + "/AvailablePhoneNumbers/" + country + "/TollFree.json",
|
|
97
106
|
headers: {
|
|
98
107
|
"Authorization": "Basic " + Buffer.from(accountSid + ":" + authToken).toString("base64")
|
|
99
108
|
},
|
|
100
109
|
query: {
|
|
101
110
|
SmsEnabled: true,
|
|
102
111
|
PageSize: limit,
|
|
103
|
-
...areaCode && { AreaCode: areaCode },
|
|
104
112
|
// `Contains` is the vendor's matching-pattern filter — digits
|
|
105
|
-
// match anywhere in the number, so one search box serves "
|
|
106
|
-
//
|
|
113
|
+
// match anywhere in the number, so one search box serves "an 888
|
|
114
|
+
// prefix" and "ends in 7777" alike. (AreaCode is a Local-only
|
|
115
|
+
// filter and went with Local.json.)
|
|
107
116
|
...contains && { Contains: contains },
|
|
108
117
|
...page && { Page: page }
|
|
109
118
|
}
|
|
@@ -150,6 +159,84 @@ var twilio = {
|
|
|
150
159
|
"Authorization": "Basic " + Buffer.from(accountSid + ":" + authToken).toString("base64")
|
|
151
160
|
}
|
|
152
161
|
});
|
|
162
|
+
},
|
|
163
|
+
// TOLL-FREE VERIFICATION — the carrier registration that lets a purchased
|
|
164
|
+
// number actually deliver. From the Tollfree Verification resource docs
|
|
165
|
+
// (messaging.twilio.com, docs/messaging/api/tollfree-verification-resource):
|
|
166
|
+
// POST /v1/Tollfree/Verifications
|
|
167
|
+
// required: BusinessName, BusinessWebsite, NotificationEmail,
|
|
168
|
+
// UseCaseCategories (array), UseCaseSummary,
|
|
169
|
+
// ProductionMessageSample, OptInImageUrls (array), OptInType,
|
|
170
|
+
// MessageVolume, TollfreePhoneNumberSid (PN…)
|
|
171
|
+
// GET /v1/Tollfree/Verifications/{Sid}
|
|
172
|
+
// status: PENDING_REVIEW | IN_REVIEW | TWILIO_APPROVED |
|
|
173
|
+
// TWILIO_REJECTED, with rejection_reason beside a rejection.
|
|
174
|
+
// Basic auth, form-encoded, like everything above. There is no status
|
|
175
|
+
// callback on this resource — the caller polls the fetch.
|
|
176
|
+
//
|
|
177
|
+
// THE END BUSINESS'S DETAILS, never the platform's. Twilio rejects ISV
|
|
178
|
+
// submissions carrying the ISV's own identity (Toll-Free Verification for
|
|
179
|
+
// ISVs, support.twilio.com) — so businessName, website and email here are
|
|
180
|
+
// the merchant's, collected at purchase.
|
|
181
|
+
//
|
|
182
|
+
// The use-case fields are PLATFORM facts and live here beside the message
|
|
183
|
+
// composition for the same reason it does: how Drawbridge collects opt-in
|
|
184
|
+
// (its own hosted entry forms) and what its messages look like are
|
|
185
|
+
// properties of sending through Drawbridge, not of any one merchant.
|
|
186
|
+
submitVerification: async ({
|
|
187
|
+
accountSid,
|
|
188
|
+
authToken,
|
|
189
|
+
businessName,
|
|
190
|
+
email,
|
|
191
|
+
numberSid,
|
|
192
|
+
optInImage,
|
|
193
|
+
request: request2 = request,
|
|
194
|
+
volume,
|
|
195
|
+
website
|
|
196
|
+
}) => {
|
|
197
|
+
const missing = Object.entries({ accountSid, authToken, businessName, email, numberSid, optInImage, website }).filter(([, value]) => !value).map(([name]) => name);
|
|
198
|
+
if (missing.length) throw new Error("Toll-free verification submission missing \u2014 " + missing.join(", "));
|
|
199
|
+
const body = new URLSearchParams();
|
|
200
|
+
body.append("BusinessName", businessName);
|
|
201
|
+
body.append("BusinessWebsite", website);
|
|
202
|
+
body.append("NotificationEmail", email);
|
|
203
|
+
body.append("UseCaseCategories", "MARKETING");
|
|
204
|
+
body.append("UseCaseSummary", "Prize-draw entry confirmations, winner notifications and campaign updates to consumers who opted in on this business's Drawbridge-hosted entry form.");
|
|
205
|
+
body.append("ProductionMessageSample", businessName + "\nThanks for entering! We'll text you here about your entry.\nReply STOP to opt out");
|
|
206
|
+
body.append("OptInImageUrls", optInImage);
|
|
207
|
+
body.append("OptInType", "WEB_FORM");
|
|
208
|
+
body.append("MessageVolume", volume || "1,000");
|
|
209
|
+
body.append("TollfreePhoneNumberSid", numberSid);
|
|
210
|
+
const result = await request2({
|
|
211
|
+
method: "POST",
|
|
212
|
+
type: "form",
|
|
213
|
+
url: "https://messaging.twilio.com/v1/Tollfree/Verifications",
|
|
214
|
+
headers: {
|
|
215
|
+
"Authorization": "Basic " + Buffer.from(accountSid + ":" + authToken).toString("base64")
|
|
216
|
+
},
|
|
217
|
+
body
|
|
218
|
+
});
|
|
219
|
+
return {
|
|
220
|
+
sid: result == null ? void 0 : result.sid,
|
|
221
|
+
status: result == null ? void 0 : result.status
|
|
222
|
+
};
|
|
223
|
+
},
|
|
224
|
+
fetchVerification: async ({ accountSid, authToken, request: request2 = request, sid }) => {
|
|
225
|
+
if (!accountSid || !authToken) throw new Error("Twilio credentials missing \u2014 accountSid, authToken");
|
|
226
|
+
if (!sid) throw new Error("Twilio verification fetch needs the verification sid");
|
|
227
|
+
const result = await request2({
|
|
228
|
+
method: "GET",
|
|
229
|
+
url: "https://messaging.twilio.com/v1/Tollfree/Verifications/" + sid,
|
|
230
|
+
headers: {
|
|
231
|
+
"Authorization": "Basic " + Buffer.from(accountSid + ":" + authToken).toString("base64")
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
return {
|
|
235
|
+
// TWILIO_APPROVED → verified, TWILIO_REJECTED → rejected, both
|
|
236
|
+
// review states → pending.
|
|
237
|
+
outcome: (result == null ? void 0 : result.status) === "TWILIO_APPROVED" ? "approved" : (result == null ? void 0 : result.status) === "TWILIO_REJECTED" ? "rejected" : "pending",
|
|
238
|
+
reason: (result == null ? void 0 : result.rejection_reason) || null
|
|
239
|
+
};
|
|
153
240
|
}
|
|
154
241
|
};
|
|
155
242
|
export {
|
package/package.json
CHANGED