@drawbridge/drawbridge-utils 0.0.133 → 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/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
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