@xenterprises/fastify-xtwilio 1.0.0

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/API.md ADDED
@@ -0,0 +1,973 @@
1
+ # xTwilio API Reference
2
+
3
+ Complete API documentation for xTwilio - the Fastify plugin for Twilio communications (SMS, Conversations, RCS) and SendGrid email services.
4
+
5
+ ## Table of Contents
6
+
7
+ 1. [SMS Service](#sms-service)
8
+ 2. [Conversations Service](#conversations-service)
9
+ 3. [RCS Service](#rcs-service)
10
+ 4. [Email Service](#email-service)
11
+ 5. [Configuration](#configuration)
12
+ 6. [Error Handling](#error-handling)
13
+ 7. [Usage Examples](#usage-examples)
14
+ 8. [Rate Limiting](#rate-limiting)
15
+ 9. [Webhook Handling](#webhook-handling)
16
+
17
+ ---
18
+
19
+ ## SMS Service
20
+
21
+ The SMS service allows you to send text messages, multimedia messages (MMS), and manage message history via Twilio.
22
+
23
+ ### Methods Overview
24
+
25
+ | Method | Description |
26
+ |--------|-------------|
27
+ | `send()` | Send SMS message |
28
+ | `sendMMS()` | Send multimedia message |
29
+ | `schedule()` | Schedule message for future delivery |
30
+ | `cancelScheduled()` | Cancel scheduled message |
31
+ | `get()` | Get message details |
32
+ | `getStatus()` | Get message delivery status |
33
+ | `list()` | List messages with filters |
34
+ | `delete()` | Delete message |
35
+ | `getMedia()` | Get media from MMS |
36
+ | `validatePhoneNumber()` | Validate phone number |
37
+ | `sendBulk()` | Send bulk SMS |
38
+
39
+ ### send()
40
+
41
+ Send a text message to a phone number.
42
+
43
+ **Signature:**
44
+ ```typescript
45
+ send(to: string, body: string, options?: SMSOptions): Promise<SMSResult>
46
+ ```
47
+
48
+ **Parameters:**
49
+ - `to` (string, required) - Recipient phone number in E.164 format (+1234567890)
50
+ - `body` (string, required) - Message content (max 160 characters for single SMS)
51
+ - `options` (object, optional) - Additional options
52
+
53
+ **Options:**
54
+ ```typescript
55
+ {
56
+ accountSid?: string; // Override default account SID
57
+ mediaUrls?: string[]; // Media URLs for MMS (if needed)
58
+ attributes?: {}; // Custom attributes
59
+ attemptNumber?: number; // Retry attempt number
60
+ }
61
+ ```
62
+
63
+ **Response:**
64
+ ```typescript
65
+ {
66
+ sid: string; // Twilio message SID
67
+ status: string; // Message status (queued, sending, sent, failed, etc.)
68
+ to: string; // Recipient phone number
69
+ body: string; // Message text
70
+ numSegments: number; // Number of SMS segments
71
+ price?: number; // Message price
72
+ priceUnit?: string; // Currency (e.g., USD)
73
+ errorCode?: string; // Error code if failed
74
+ errorMessage?: string; // Error message if failed
75
+ dateCreated: Date; // When message was created
76
+ }
77
+ ```
78
+
79
+ **Example:**
80
+ ```javascript
81
+ const result = await fastify.sms.send(
82
+ '+1234567890',
83
+ 'Hello! Your verification code is 123456'
84
+ );
85
+
86
+ console.log(result.sid); // SM1234567890abcdef1234567890abcdef
87
+ console.log(result.status); // queued
88
+ ```
89
+
90
+ ### sendMMS()
91
+
92
+ Send a multimedia message (image, video, etc.).
93
+
94
+ **Signature:**
95
+ ```typescript
96
+ sendMMS(
97
+ to: string,
98
+ body: string,
99
+ mediaUrl: string,
100
+ options?: SMSOptions
101
+ ): Promise<SMSResult>
102
+ ```
103
+
104
+ **Parameters:**
105
+ - `to` (string, required) - Recipient phone number
106
+ - `body` (string, required) - Message text
107
+ - `mediaUrl` (string, required) - URL of media file (must be HTTPS)
108
+ - `options` (object, optional) - Additional options
109
+
110
+ **Supported Media Types:**
111
+ - Images: JPEG, PNG, GIF, WEBP
112
+ - Video: MP4, 3GP
113
+ - Audio: WAV, MP3, OGG
114
+
115
+ **Example:**
116
+ ```javascript
117
+ const result = await fastify.sms.sendMMS(
118
+ '+1234567890',
119
+ 'Check out this photo!',
120
+ 'https://example.com/photo.jpg'
121
+ );
122
+ ```
123
+
124
+ ### schedule()
125
+
126
+ Schedule an SMS for future delivery.
127
+
128
+ **Signature:**
129
+ ```typescript
130
+ schedule(
131
+ to: string,
132
+ body: string,
133
+ sendAt: Date | string,
134
+ options?: SMSOptions
135
+ ): Promise<SMSResult>
136
+ ```
137
+
138
+ **Parameters:**
139
+ - `to` (string, required) - Recipient phone number
140
+ - `body` (string, required) - Message content
141
+ - `sendAt` (Date | string, required) - When to send (ISO 8601 format or Date object)
142
+ - `options` (object, optional) - Additional options
143
+
144
+ **Example:**
145
+ ```javascript
146
+ // Schedule for 1 hour from now
147
+ const futureDate = new Date(Date.now() + 3600000);
148
+
149
+ const result = await fastify.sms.schedule(
150
+ '+1234567890',
151
+ 'Reminder: Your appointment is tomorrow',
152
+ futureDate
153
+ );
154
+
155
+ // Or using ISO 8601 string
156
+ const result = await fastify.sms.schedule(
157
+ '+1234567890',
158
+ 'Reminder: Your appointment is tomorrow',
159
+ '2024-12-31T10:00:00Z'
160
+ );
161
+ ```
162
+
163
+ ### cancelScheduled()
164
+
165
+ Cancel a previously scheduled message.
166
+
167
+ **Signature:**
168
+ ```typescript
169
+ cancelScheduled(messageSid: string): Promise<{ success: boolean }>
170
+ ```
171
+
172
+ **Parameters:**
173
+ - `messageSid` (string, required) - SID of the scheduled message to cancel
174
+
175
+ **Example:**
176
+ ```javascript
177
+ const result = await fastify.sms.cancelScheduled('SMxxxxxxxxxxxxxxxxxxxxxxxxxx');
178
+ console.log(result.success); // true
179
+ ```
180
+
181
+ ### getStatus()
182
+
183
+ Get the delivery status of a message.
184
+
185
+ **Signature:**
186
+ ```typescript
187
+ getStatus(messageSid: string): Promise<SMSStatus>
188
+ ```
189
+
190
+ **Response:**
191
+ ```typescript
192
+ {
193
+ sid: string; // Message SID
194
+ status: string; // Current status
195
+ errorCode?: number; // Error code if failed
196
+ errorMessage?: string; // Error message if failed
197
+ }
198
+ ```
199
+
200
+ **Status Values:**
201
+ - `queued` - Message is queued for delivery
202
+ - `sending` - Message is being sent
203
+ - `sent` - Message was sent successfully
204
+ - `delivered` - Message was delivered to recipient
205
+ - `failed` - Message delivery failed
206
+ - `undelivered` - Message could not be delivered
207
+
208
+ **Example:**
209
+ ```javascript
210
+ const status = await fastify.sms.getStatus('SMxxxxxxxxxxxxxxxxxxxxxxxxxx');
211
+ console.log(status.status); // "delivered"
212
+ ```
213
+
214
+ ### list()
215
+
216
+ List SMS messages with optional filters.
217
+
218
+ **Signature:**
219
+ ```typescript
220
+ list(filters?: {
221
+ to?: string;
222
+ from?: string;
223
+ status?: string;
224
+ limit?: number;
225
+ }): Promise<SMSResult[]>
226
+ ```
227
+
228
+ **Example:**
229
+ ```javascript
230
+ // Get last 10 messages to a phone number
231
+ const messages = await fastify.sms.list({
232
+ to: '+1234567890',
233
+ limit: 10
234
+ });
235
+
236
+ // Get failed messages
237
+ const failedMessages = await fastify.sms.list({
238
+ status: 'failed',
239
+ limit: 50
240
+ });
241
+ ```
242
+
243
+ ### validatePhoneNumber()
244
+
245
+ Validate and get information about a phone number.
246
+
247
+ **Signature:**
248
+ ```typescript
249
+ validatePhoneNumber(
250
+ phoneNumber: string,
251
+ options?: { countryCode?: string }
252
+ ): Promise<PhoneValidationResult>
253
+ ```
254
+
255
+ **Response:**
256
+ ```typescript
257
+ {
258
+ phoneNumber: string; // Validated phone in E.164 format
259
+ isValid: boolean; // Is the number valid?
260
+ countryCode?: string; // Country code (e.g., US)
261
+ numberType?: string; // mobile, fixed-line, voip, etc.
262
+ carrier?: string; // Carrier name
263
+ error?: string; // Error message if invalid
264
+ }
265
+ ```
266
+
267
+ **Example:**
268
+ ```javascript
269
+ const validation = await fastify.sms.validatePhoneNumber('+14155552671');
270
+
271
+ if (validation.isValid) {
272
+ console.log(`${validation.carrier} - ${validation.numberType}`);
273
+ // Output: Verizon - mobile
274
+ }
275
+ ```
276
+
277
+ ### sendBulk()
278
+
279
+ Send SMS to multiple recipients in a single operation.
280
+
281
+ **Signature:**
282
+ ```typescript
283
+ sendBulk(messages: BulkSMSMessage[]): Promise<SMSResult[]>
284
+ ```
285
+
286
+ **Parameters:**
287
+ ```typescript
288
+ messages: Array<{
289
+ to: string; // Recipient phone number
290
+ body: string; // Message body
291
+ mediaUrls?: string[]; // Optional media for MMS
292
+ options?: SMSOptions; // Optional per-message options
293
+ }>
294
+ ```
295
+
296
+ **Example:**
297
+ ```javascript
298
+ const results = await fastify.sms.sendBulk([
299
+ {
300
+ to: '+1234567890',
301
+ body: 'Hello Alice! Your code: 111111'
302
+ },
303
+ {
304
+ to: '+0987654321',
305
+ body: 'Hello Bob! Your code: 222222'
306
+ },
307
+ {
308
+ to: '+1111111111',
309
+ body: 'Hello Charlie! Your code: 333333'
310
+ }
311
+ ]);
312
+
313
+ results.forEach(result => {
314
+ console.log(`${result.to}: ${result.status}`);
315
+ });
316
+ ```
317
+
318
+ ---
319
+
320
+ ## Conversations Service
321
+
322
+ Conversations provide multi-channel messaging combining SMS, Email, WhatsApp, and more.
323
+
324
+ ### Methods Overview
325
+
326
+ | Method | Description |
327
+ |--------|-------------|
328
+ | `create()` | Create new conversation |
329
+ | `get()` | Get conversation details |
330
+ | `update()` | Update conversation |
331
+ | `list()` | List conversations |
332
+ | `delete()` | Delete conversation |
333
+ | `addParticipant()` | Add participant |
334
+ | `listParticipants()` | List participants |
335
+ | `removeParticipant()` | Remove participant |
336
+ | `sendMessage()` | Send message to conversation |
337
+ | `sendMediaMessage()` | Send media message |
338
+ | `getMessages()` | Get conversation messages |
339
+ | `getMessage()` | Get specific message |
340
+ | `deleteMessage()` | Delete message |
341
+ | `getWebhooks()` | Get webhook configuration |
342
+ | `updateWebhooks()` | Update webhooks |
343
+
344
+ ### create()
345
+
346
+ Create a new conversation.
347
+
348
+ **Signature:**
349
+ ```typescript
350
+ create(
351
+ friendlyName: string,
352
+ attributes?: Record<string, any>
353
+ ): Promise<ConversationInfo>
354
+ ```
355
+
356
+ **Example:**
357
+ ```javascript
358
+ const conversation = await fastify.conversations.create(
359
+ 'Support Chat - Order #12345',
360
+ {
361
+ order_id: '12345',
362
+ customer_id: 'CUST_789',
363
+ priority: 'high'
364
+ }
365
+ );
366
+
367
+ console.log(conversation.sid); // IV1234567890abcdef1234567890abcdef
368
+ ```
369
+
370
+ ### addParticipant()
371
+
372
+ Add a participant to a conversation.
373
+
374
+ **Signature:**
375
+ ```typescript
376
+ addParticipant(
377
+ conversationSid: string,
378
+ identity?: string,
379
+ messagingBindingAddress?: string
380
+ ): Promise<ConversationParticipant>
381
+ ```
382
+
383
+ **Parameters:**
384
+ - `conversationSid` (string, required) - Conversation ID
385
+ - `identity` (string, optional) - Participant identity (user ID, email, etc.)
386
+ - `messagingBindingAddress` (string, optional) - SMS number, email, WhatsApp ID
387
+
388
+ **Example:**
389
+ ```javascript
390
+ // Add customer via SMS
391
+ const participant1 = await fastify.conversations.addParticipant(
392
+ 'IV1234567890abcdef1234567890abcdef',
393
+ 'customer_user_123',
394
+ '+1234567890' // SMS number
395
+ );
396
+
397
+ // Add agent via email
398
+ const participant2 = await fastify.conversations.addParticipant(
399
+ 'IV1234567890abcdef1234567890abcdef',
400
+ 'agent_support_456',
401
+ 'support@example.com' // Email
402
+ );
403
+ ```
404
+
405
+ ### sendMessage()
406
+
407
+ Send a message to a conversation.
408
+
409
+ **Signature:**
410
+ ```typescript
411
+ sendMessage(
412
+ conversationSid: string,
413
+ body: string,
414
+ author: string,
415
+ attributes?: Record<string, any>
416
+ ): Promise<ConversationMessage>
417
+ ```
418
+
419
+ **Example:**
420
+ ```javascript
421
+ const message = await fastify.conversations.sendMessage(
422
+ 'IV1234567890abcdef1234567890abcdef',
423
+ 'Thank you for contacting support. How can we help?',
424
+ 'agent_support_456',
425
+ { message_type: 'greeting' }
426
+ );
427
+ ```
428
+
429
+ ### getMessages()
430
+
431
+ Get all messages in a conversation.
432
+
433
+ **Signature:**
434
+ ```typescript
435
+ getMessages(
436
+ conversationSid: string,
437
+ options?: { limit?: number; index?: number }
438
+ ): Promise<ConversationMessage[]>
439
+ ```
440
+
441
+ **Example:**
442
+ ```javascript
443
+ const messages = await fastify.conversations.getMessages(
444
+ 'IV1234567890abcdef1234567890abcdef',
445
+ { limit: 50 }
446
+ );
447
+
448
+ messages.forEach(msg => {
449
+ console.log(`${msg.author}: ${msg.body}`);
450
+ });
451
+ ```
452
+
453
+ ---
454
+
455
+ ## RCS Service
456
+
457
+ RCS (Rich Communication Services) provides rich messaging capabilities with cards, buttons, and media.
458
+
459
+ ### Methods Overview
460
+
461
+ | Method | Description |
462
+ |--------|-------------|
463
+ | `send()` | Send basic RCS message |
464
+ | `sendMedia()` | Send with media |
465
+ | `sendTemplate()` | Send from template |
466
+ | `sendRichCard()` | Send rich card |
467
+ | `sendCarousel()` | Send carousel of cards |
468
+ | `sendQuickReplies()` | Send with quick replies |
469
+ | `getStatus()` | Get message status |
470
+ | `listTemplates()` | List templates |
471
+ | `getTemplate()` | Get template details |
472
+ | `deleteTemplate()` | Delete template |
473
+
474
+ ### sendRichCard()
475
+
476
+ Send a rich card with title, description, image, and actions.
477
+
478
+ **Signature:**
479
+ ```typescript
480
+ sendRichCard(to: string, card: RCSRichCard): Promise<RCSSendResult>
481
+ ```
482
+
483
+ **Card Structure:**
484
+ ```typescript
485
+ {
486
+ title: string; // Card title
487
+ description: string; // Card description
488
+ mediaUrl?: string; // Image/video URL
489
+ actions?: Array<{
490
+ type: 'url' | 'phone' | 'open_app' | 'create_calendar_event' | 'reply';
491
+ text: string; // Button text
492
+ url?: string; // For url action
493
+ phoneNumber?: string; // For phone action
494
+ payload?: string; // For reply action
495
+ }>;
496
+ }
497
+ ```
498
+
499
+ **Example:**
500
+ ```javascript
501
+ const result = await fastify.rcs.sendRichCard(
502
+ '+1234567890',
503
+ {
504
+ title: 'Summer Sale',
505
+ description: '50% off all items this weekend only',
506
+ mediaUrl: 'https://example.com/sale-banner.jpg',
507
+ actions: [
508
+ {
509
+ type: 'url',
510
+ text: 'Shop Now',
511
+ url: 'https://example.com/sale'
512
+ },
513
+ {
514
+ type: 'phone',
515
+ text: 'Call Us',
516
+ phoneNumber: '+1234567890'
517
+ },
518
+ {
519
+ type: 'reply',
520
+ text: 'More Info',
521
+ payload: 'sale_info'
522
+ }
523
+ ]
524
+ }
525
+ );
526
+ ```
527
+
528
+ ### sendCarousel()
529
+
530
+ Send multiple rich cards in a carousel.
531
+
532
+ **Signature:**
533
+ ```typescript
534
+ sendCarousel(to: string, cards: RCSRichCard[]): Promise<RCSSendResult>
535
+ ```
536
+
537
+ **Example:**
538
+ ```javascript
539
+ const result = await fastify.rcs.sendCarousel(
540
+ '+1234567890',
541
+ [
542
+ {
543
+ title: 'Product 1',
544
+ description: '$29.99',
545
+ mediaUrl: 'https://example.com/product1.jpg',
546
+ actions: [{
547
+ type: 'url',
548
+ text: 'View',
549
+ url: 'https://example.com/product/1'
550
+ }]
551
+ },
552
+ {
553
+ title: 'Product 2',
554
+ description: '$39.99',
555
+ mediaUrl: 'https://example.com/product2.jpg',
556
+ actions: [{
557
+ type: 'url',
558
+ text: 'View',
559
+ url: 'https://example.com/product/2'
560
+ }]
561
+ }
562
+ ]
563
+ );
564
+ ```
565
+
566
+ ### sendQuickReplies()
567
+
568
+ Send a message with quick reply options.
569
+
570
+ **Signature:**
571
+ ```typescript
572
+ sendQuickReplies(
573
+ to: string,
574
+ body: string,
575
+ replies: RCSQuickReply[]
576
+ ): Promise<RCSSendResult>
577
+ ```
578
+
579
+ **Example:**
580
+ ```javascript
581
+ const result = await fastify.rcs.sendQuickReplies(
582
+ '+1234567890',
583
+ 'How can we help you today?',
584
+ [
585
+ { text: 'Product Question', payload: 'product_q' },
586
+ { text: 'Order Status', payload: 'order_status' },
587
+ { text: 'Complaint', payload: 'complaint' }
588
+ ]
589
+ );
590
+ ```
591
+
592
+ ---
593
+
594
+ ## Email Service
595
+
596
+ Send emails via SendGrid with templates, attachments, and contact management.
597
+
598
+ ### Methods Overview
599
+
600
+ | Method | Description |
601
+ |--------|-------------|
602
+ | `send()` | Send email |
603
+ | `sendTemplate()` | Send from template |
604
+ | `sendWithAttachments()` | Send with file attachments |
605
+ | `sendBulk()` | Send bulk email |
606
+ | `sendPersonalizedBulk()` | Send personalized bulk |
607
+ | `validate()` | Validate email address |
608
+ | `addContact()` | Add contact |
609
+ | `searchContact()` | Search contact |
610
+ | `deleteContact()` | Delete contact |
611
+ | `createList()` | Create contact list |
612
+ | `getLists()` | Get contact lists |
613
+ | `deleteList()` | Delete list |
614
+
615
+ ### send()
616
+
617
+ Send a simple email.
618
+
619
+ **Signature:**
620
+ ```typescript
621
+ send(
622
+ to: string,
623
+ subject: string,
624
+ html: string,
625
+ text?: string,
626
+ options?: EmailOptions
627
+ ): Promise<EmailSendResult>
628
+ ```
629
+
630
+ **Example:**
631
+ ```javascript
632
+ const result = await fastify.email.send(
633
+ 'customer@example.com',
634
+ 'Welcome to Our Service!',
635
+ `<h1>Welcome</h1>
636
+ <p>Thank you for signing up!</p>
637
+ <a href="https://example.com/get-started">Get Started</a>`,
638
+ 'Welcome to Our Service! Thank you for signing up.'
639
+ );
640
+
641
+ console.log(result.messageId); // Message ID from SendGrid
642
+ ```
643
+
644
+ ### sendTemplate()
645
+
646
+ Send email using a SendGrid template.
647
+
648
+ **Signature:**
649
+ ```typescript
650
+ sendTemplate(
651
+ to: string,
652
+ subject: string,
653
+ templateId: string,
654
+ dynamicData: Record<string, any>,
655
+ options?: EmailOptions
656
+ ): Promise<EmailSendResult>
657
+ ```
658
+
659
+ **Example:**
660
+ ```javascript
661
+ const result = await fastify.email.sendTemplate(
662
+ 'user@example.com',
663
+ 'Welcome Email',
664
+ 'd-1234567890abcdef1234567890abcdef', // Template ID from SendGrid
665
+ {
666
+ first_name: 'John',
667
+ activation_link: 'https://example.com/activate/abc123'
668
+ }
669
+ );
670
+ ```
671
+
672
+ ### sendWithAttachments()
673
+
674
+ Send email with file attachments.
675
+
676
+ **Signature:**
677
+ ```typescript
678
+ sendWithAttachments(
679
+ to: string,
680
+ subject: string,
681
+ html: string,
682
+ attachments: Array<{
683
+ content: string; // Base64 encoded
684
+ filename: string;
685
+ type: string; // MIME type
686
+ }>,
687
+ options?: EmailOptions
688
+ ): Promise<EmailSendResult>
689
+ ```
690
+
691
+ **Example:**
692
+ ```javascript
693
+ const fs = require('fs');
694
+
695
+ const pdfContent = fs.readFileSync('invoice.pdf').toString('base64');
696
+
697
+ const result = await fastify.email.sendWithAttachments(
698
+ 'customer@example.com',
699
+ 'Your Invoice',
700
+ '<p>Your invoice is attached.</p>',
701
+ [
702
+ {
703
+ content: pdfContent,
704
+ filename: 'invoice_12345.pdf',
705
+ type: 'application/pdf'
706
+ }
707
+ ]
708
+ );
709
+ ```
710
+
711
+ ### validate()
712
+
713
+ Check if an email address is valid and deliverable.
714
+
715
+ **Signature:**
716
+ ```typescript
717
+ validate(email: string): Promise<{
718
+ isValid: boolean;
719
+ suggestion?: string;
720
+ error?: string;
721
+ }>
722
+ ```
723
+
724
+ **Example:**
725
+ ```javascript
726
+ const validation = await fastify.email.validate('user@example.com');
727
+
728
+ if (validation.isValid) {
729
+ console.log('Email is valid and deliverable');
730
+ } else {
731
+ console.log('Validation error:', validation.error);
732
+ if (validation.suggestion) {
733
+ console.log('Did you mean:', validation.suggestion);
734
+ }
735
+ }
736
+ ```
737
+
738
+ ### addContact()
739
+
740
+ Add a contact to SendGrid.
741
+
742
+ **Signature:**
743
+ ```typescript
744
+ addContact(
745
+ email: string,
746
+ data: Record<string, string>,
747
+ listIds?: string[]
748
+ ): Promise<EmailContact>
749
+ ```
750
+
751
+ **Example:**
752
+ ```javascript
753
+ const contact = await fastify.email.addContact(
754
+ 'john@example.com',
755
+ {
756
+ firstName: 'John',
757
+ lastName: 'Doe',
758
+ phone: '+1234567890',
759
+ company: 'Acme Inc'
760
+ },
761
+ ['list_id_123'] // Add to lists
762
+ );
763
+ ```
764
+
765
+ ### sendBulk()
766
+
767
+ Send email to multiple recipients.
768
+
769
+ **Signature:**
770
+ ```typescript
771
+ sendBulk(
772
+ to: string[],
773
+ subject: string,
774
+ html: string,
775
+ options?: EmailOptions
776
+ ): Promise<EmailSendResult[]>
777
+ ```
778
+
779
+ **Example:**
780
+ ```javascript
781
+ const recipients = [
782
+ 'user1@example.com',
783
+ 'user2@example.com',
784
+ 'user3@example.com'
785
+ ];
786
+
787
+ const results = await fastify.email.sendBulk(
788
+ recipients,
789
+ 'Newsletter - December',
790
+ '<h1>December Newsletter</h1><p>...'
791
+ );
792
+
793
+ results.forEach((result, idx) => {
794
+ console.log(`${recipients[idx]}: ${result.status}`);
795
+ });
796
+ ```
797
+
798
+ ---
799
+
800
+ ## Configuration
801
+
802
+ ### Plugin Registration
803
+
804
+ ```javascript
805
+ import Fastify from 'fastify';
806
+ import xTwilio from '@xenterprises/fastify-xtwilio';
807
+
808
+ const fastify = Fastify();
809
+
810
+ await fastify.register(xTwilio, {
811
+ twilio: {
812
+ accountSid: process.env.TWILIO_ACCOUNT_SID,
813
+ authToken: process.env.TWILIO_AUTH_TOKEN,
814
+ phoneNumber: process.env.TWILIO_PHONE_NUMBER,
815
+ messagingServiceSid: process.env.TWILIO_MESSAGING_SERVICE_SID,
816
+ active: true, // Enable Twilio
817
+ },
818
+ sendgrid: {
819
+ apiKey: process.env.SENDGRID_API_KEY,
820
+ fromEmail: process.env.SENDGRID_FROM_EMAIL,
821
+ active: true, // Enable SendGrid
822
+ },
823
+ logRequests: process.env.NODE_ENV === 'development',
824
+ });
825
+
826
+ await fastify.listen({ port: 3000 });
827
+ ```
828
+
829
+ ---
830
+
831
+ ## Error Handling
832
+
833
+ All methods throw errors with descriptive messages. Use try-catch blocks:
834
+
835
+ ```javascript
836
+ try {
837
+ await fastify.sms.send('+1234567890', 'Hello');
838
+ } catch (error) {
839
+ console.error('SMS failed:', error.message);
840
+ // Handle specific error
841
+ }
842
+ ```
843
+
844
+ ---
845
+
846
+ ## Usage Examples
847
+
848
+ ### Complete SMS Flow
849
+
850
+ ```javascript
851
+ // 1. Validate phone number
852
+ const validation = await fastify.sms.validatePhoneNumber('+1234567890');
853
+ if (!validation.isValid) {
854
+ throw new Error('Invalid phone number');
855
+ }
856
+
857
+ // 2. Send SMS
858
+ const result = await fastify.sms.send(
859
+ '+1234567890',
860
+ 'Your verification code is 123456'
861
+ );
862
+
863
+ // 3. Check status
864
+ const status = await fastify.sms.getStatus(result.sid);
865
+ console.log('Status:', status.status);
866
+
867
+ // 4. List recent messages
868
+ const messages = await fastify.sms.list({ limit: 10 });
869
+ ```
870
+
871
+ ### Customer Conversation
872
+
873
+ ```javascript
874
+ // 1. Create conversation
875
+ const conversation = await fastify.conversations.create('Customer Support');
876
+
877
+ // 2. Add customer (SMS)
878
+ await fastify.conversations.addParticipant(
879
+ conversation.sid,
880
+ 'customer_123',
881
+ '+1234567890'
882
+ );
883
+
884
+ // 3. Add agent (Email)
885
+ await fastify.conversations.addParticipant(
886
+ conversation.sid,
887
+ 'agent_456',
888
+ 'support@company.com'
889
+ );
890
+
891
+ // 4. Send greeting
892
+ await fastify.conversations.sendMessage(
893
+ conversation.sid,
894
+ 'Hello! How can we help you?',
895
+ 'agent_456'
896
+ );
897
+
898
+ // 5. Get conversation history
899
+ const messages = await fastify.conversations.getMessages(
900
+ conversation.sid
901
+ );
902
+ ```
903
+
904
+ ### Email Campaign with Personaliz ation
905
+
906
+ ```javascript
907
+ const customers = [
908
+ { email: 'alice@example.com', name: 'Alice', orderAmount: '$100' },
909
+ { email: 'bob@example.com', name: 'Bob', orderAmount: '$250' }
910
+ ];
911
+
912
+ const results = await fastify.email.sendPersonalizedBulk(
913
+ customers.map(customer => ({
914
+ to: customer.email,
915
+ subject: `Thank you for your ${customer.orderAmount} order, ${customer.name}!`,
916
+ html: `<p>Hi ${customer.name},</p><p>Thank you for ordering ${customer.orderAmount}!</p>`,
917
+ personalData: {
918
+ name: customer.name,
919
+ amount: customer.orderAmount
920
+ }
921
+ }))
922
+ );
923
+ ```
924
+
925
+ ---
926
+
927
+ ## Rate Limiting
928
+
929
+ Implement rate limiting to prevent abuse:
930
+
931
+ ```bash
932
+ # Environment variables
933
+ SMS_RATE_LIMIT_PER_MINUTE=10
934
+ EMAIL_RATE_LIMIT_PER_MINUTE=5
935
+ ```
936
+
937
+ ---
938
+
939
+ ## Webhook Handling
940
+
941
+ Configure webhooks in your Twilio account to receive delivery status updates, inbound messages, and conversation events.
942
+
943
+ **Webhook Signature Validation:**
944
+ ```javascript
945
+ import crypto from 'crypto';
946
+
947
+ fastify.post('/webhooks/twilio', async (request, reply) => {
948
+ const signature = request.headers['x-twilio-signature'];
949
+ const body = request.rawBody || JSON.stringify(request.body);
950
+
951
+ const expectedSignature = crypto
952
+ .createHmac('sha1', process.env.TWILIO_AUTH_TOKEN)
953
+ .update(`${process.env.WEBHOOK_URL}${body}`)
954
+ .digest('base64');
955
+
956
+ if (signature !== expectedSignature) {
957
+ return reply.code(403).send({ error: 'Unauthorized' });
958
+ }
959
+
960
+ // Process webhook
961
+ console.log('Webhook received:', request.body);
962
+ reply.code(200).send({ status: 'received' });
963
+ });
964
+ ```
965
+
966
+ ---
967
+
968
+ ## Related Documentation
969
+
970
+ - [Twilio API Docs](https://www.twilio.com/docs/api)
971
+ - [SendGrid Email API](https://docs.sendgrid.com/api-reference/mail-send)
972
+ - [SECURITY.md](./SECURITY.md) - Security best practices
973
+ - [CHANGELOG.md](./CHANGELOG.md) - Version history and changes