waba-toolkit 0.3.0 → 0.3.1

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.
@@ -0,0 +1,635 @@
1
+ # Sending Messages Guide
2
+
3
+ Send WhatsApp messages programmatically with type safety.
4
+
5
+ ---
6
+
7
+ ## Quick Start
8
+
9
+ > **Prefer CLI?** See [CLI Guide: Sending Messages](CLI.md#sending-messages)
10
+
11
+ ```typescript
12
+ import { WABAApiClient } from 'waba-toolkit';
13
+
14
+ const client = new WABAApiClient({
15
+ accessToken: process.env.META_ACCESS_TOKEN,
16
+ phoneNumberId: '1234567890', // Your business phone number ID
17
+ });
18
+
19
+ // Send text
20
+ const response = await client.sendTextMessage(
21
+ '1234567890',
22
+ 'Hello from waba-toolkit!'
23
+ );
24
+
25
+ console.log('Message ID:', response.messages[0].id);
26
+ ```
27
+
28
+ ---
29
+
30
+ ## Initialize Client
31
+
32
+ ```typescript
33
+ import { WABAApiClient } from 'waba-toolkit';
34
+
35
+ const client = new WABAApiClient({
36
+ accessToken: string, // Required: Meta access token
37
+ phoneNumberId: string, // Required: Your business phone number ID
38
+ apiVersion?: string, // Optional: Default 'v22.0'
39
+ baseUrl?: string, // Optional: Default 'https://graph.facebook.com'
40
+ });
41
+ ```
42
+
43
+ ---
44
+
45
+ ## Text Messages
46
+
47
+ ### Basic Text
48
+
49
+ ```typescript
50
+ await client.sendTextMessage(
51
+ '1234567890', // Recipient phone number
52
+ 'Hello World' // Message text
53
+ );
54
+ ```
55
+
56
+ ### With URL Preview
57
+
58
+ ```typescript
59
+ await client.sendTextMessage(
60
+ '1234567890',
61
+ 'Check this out: https://example.com',
62
+ { previewUrl: true }
63
+ );
64
+ ```
65
+
66
+ ### Reply to Message
67
+
68
+ ```typescript
69
+ await client.sendTextMessage(
70
+ '1234567890',
71
+ 'Got your message!',
72
+ {
73
+ context: {
74
+ message_id: 'wamid.HBgLMTY...', // Original message ID
75
+ },
76
+ }
77
+ );
78
+ ```
79
+
80
+ ---
81
+
82
+ ## Template Messages
83
+
84
+ Send pre-approved template messages:
85
+
86
+ ```typescript
87
+ await client.sendTemplateMessage('1234567890', {
88
+ name: 'hello_world', // Template name
89
+ language: { code: 'en_US' }, // Language code
90
+ components: [ // Optional: Parameters
91
+ {
92
+ type: 'body',
93
+ parameters: [
94
+ { type: 'text', text: 'John Doe' },
95
+ { type: 'text', text: 'Order #12345' },
96
+ ],
97
+ },
98
+ ],
99
+ });
100
+ ```
101
+
102
+ ### Template with Header Image
103
+
104
+ ```typescript
105
+ await client.sendTemplateMessage('1234567890', {
106
+ name: 'order_confirmation',
107
+ language: { code: 'en' },
108
+ components: [
109
+ {
110
+ type: 'header',
111
+ parameters: [
112
+ {
113
+ type: 'image',
114
+ image: {
115
+ link: 'https://example.com/product.jpg',
116
+ },
117
+ },
118
+ ],
119
+ },
120
+ {
121
+ type: 'body',
122
+ parameters: [
123
+ { type: 'text', text: 'John' },
124
+ { type: 'text', text: 'ORD-12345' },
125
+ ],
126
+ },
127
+ ],
128
+ });
129
+ ```
130
+
131
+ ### Template with Buttons
132
+
133
+ ```typescript
134
+ await client.sendTemplateMessage('1234567890', {
135
+ name: 'appointment_reminder',
136
+ language: { code: 'en_US' },
137
+ components: [
138
+ {
139
+ type: 'body',
140
+ parameters: [
141
+ { type: 'text', text: 'Tomorrow at 3 PM' },
142
+ ],
143
+ },
144
+ {
145
+ type: 'button',
146
+ sub_type: 'url',
147
+ index: '0',
148
+ parameters: [
149
+ {
150
+ type: 'text',
151
+ text: 'abc123', // Dynamic URL parameter
152
+ },
153
+ ],
154
+ },
155
+ ],
156
+ });
157
+ ```
158
+
159
+ ---
160
+
161
+ ## Media Messages
162
+
163
+ ### Image
164
+
165
+ ```typescript
166
+ await client.sendMessage({
167
+ messaging_product: 'whatsapp',
168
+ to: '1234567890',
169
+ type: 'image',
170
+ image: {
171
+ link: 'https://example.com/photo.jpg',
172
+ caption: 'Amazing view!',
173
+ },
174
+ });
175
+ ```
176
+
177
+ ### Video
178
+
179
+ ```typescript
180
+ await client.sendMessage({
181
+ messaging_product: 'whatsapp',
182
+ to: '1234567890',
183
+ type: 'video',
184
+ video: {
185
+ link: 'https://example.com/video.mp4',
186
+ caption: 'Check out this video',
187
+ },
188
+ });
189
+ ```
190
+
191
+ ### Document
192
+
193
+ ```typescript
194
+ await client.sendMessage({
195
+ messaging_product: 'whatsapp',
196
+ to: '1234567890',
197
+ type: 'document',
198
+ document: {
199
+ link: 'https://example.com/invoice.pdf',
200
+ caption: 'Your invoice',
201
+ filename: 'invoice-12345.pdf',
202
+ },
203
+ });
204
+ ```
205
+
206
+ ### Audio
207
+
208
+ ```typescript
209
+ await client.sendMessage({
210
+ messaging_product: 'whatsapp',
211
+ to: '1234567890',
212
+ type: 'audio',
213
+ audio: {
214
+ link: 'https://example.com/audio.mp3',
215
+ },
216
+ });
217
+ ```
218
+
219
+ ---
220
+
221
+ ## Interactive Messages
222
+
223
+ ### Buttons
224
+
225
+ ```typescript
226
+ await client.sendMessage({
227
+ messaging_product: 'whatsapp',
228
+ to: '1234567890',
229
+ type: 'interactive',
230
+ interactive: {
231
+ type: 'button',
232
+ body: {
233
+ text: 'Would you like to continue?',
234
+ },
235
+ action: {
236
+ buttons: [
237
+ {
238
+ type: 'reply',
239
+ reply: {
240
+ id: 'yes',
241
+ title: 'Yes',
242
+ },
243
+ },
244
+ {
245
+ type: 'reply',
246
+ reply: {
247
+ id: 'no',
248
+ title: 'No',
249
+ },
250
+ },
251
+ ],
252
+ },
253
+ },
254
+ });
255
+ ```
256
+
257
+ ### List
258
+
259
+ ```typescript
260
+ await client.sendMessage({
261
+ messaging_product: 'whatsapp',
262
+ to: '1234567890',
263
+ type: 'interactive',
264
+ interactive: {
265
+ type: 'list',
266
+ header: {
267
+ type: 'text',
268
+ text: 'Menu',
269
+ },
270
+ body: {
271
+ text: 'Choose an option',
272
+ },
273
+ footer: {
274
+ text: 'Powered by waba-toolkit',
275
+ },
276
+ action: {
277
+ button: 'View Options',
278
+ sections: [
279
+ {
280
+ title: 'Main Options',
281
+ rows: [
282
+ {
283
+ id: 'option1',
284
+ title: 'Option 1',
285
+ description: 'Description for option 1',
286
+ },
287
+ {
288
+ id: 'option2',
289
+ title: 'Option 2',
290
+ description: 'Description for option 2',
291
+ },
292
+ ],
293
+ },
294
+ ],
295
+ },
296
+ },
297
+ });
298
+ ```
299
+
300
+ ---
301
+
302
+ ## Location Messages
303
+
304
+ ```typescript
305
+ await client.sendMessage({
306
+ messaging_product: 'whatsapp',
307
+ to: '1234567890',
308
+ type: 'location',
309
+ location: {
310
+ latitude: 37.7749,
311
+ longitude: -122.4194,
312
+ name: 'San Francisco',
313
+ address: 'California, USA',
314
+ },
315
+ });
316
+ ```
317
+
318
+ ---
319
+
320
+ ## Contact Messages
321
+
322
+ ```typescript
323
+ await client.sendMessage({
324
+ messaging_product: 'whatsapp',
325
+ to: '1234567890',
326
+ type: 'contacts',
327
+ contacts: [
328
+ {
329
+ name: {
330
+ formatted_name: 'John Doe',
331
+ first_name: 'John',
332
+ last_name: 'Doe',
333
+ },
334
+ phones: [
335
+ {
336
+ phone: '+1 555 0100',
337
+ type: 'MOBILE',
338
+ },
339
+ ],
340
+ emails: [
341
+ {
342
+ email: 'john@example.com',
343
+ type: 'WORK',
344
+ },
345
+ ],
346
+ },
347
+ ],
348
+ });
349
+ ```
350
+
351
+ ---
352
+
353
+ ## Reaction Messages
354
+
355
+ React to a message:
356
+
357
+ ```typescript
358
+ await client.sendMessage({
359
+ messaging_product: 'whatsapp',
360
+ to: '1234567890',
361
+ type: 'reaction',
362
+ reaction: {
363
+ message_id: 'wamid.HBgLMTY...', // Message to react to
364
+ emoji: '👍',
365
+ },
366
+ });
367
+ ```
368
+
369
+ Remove reaction:
370
+
371
+ ```typescript
372
+ await client.sendMessage({
373
+ messaging_product: 'whatsapp',
374
+ to: '1234567890',
375
+ type: 'reaction',
376
+ reaction: {
377
+ message_id: 'wamid.HBgLMTY...',
378
+ emoji: '', // Empty emoji removes reaction
379
+ },
380
+ });
381
+ ```
382
+
383
+ ---
384
+
385
+ ## Error Handling
386
+
387
+ ```typescript
388
+ import { WABAAuthError, WABASendError } from 'waba-toolkit';
389
+
390
+ try {
391
+ await client.sendTextMessage('1234567890', 'Hello');
392
+ } catch (error) {
393
+ if (error instanceof WABAAuthError) {
394
+ console.error('Authentication failed:', error.statusCode);
395
+ // Check your access token
396
+ } else if (error instanceof WABASendError) {
397
+ console.error('Send failed:', error.statusCode);
398
+ console.error('Details:', error.errorPayload);
399
+ // Check error code and message
400
+ } else {
401
+ console.error('Unknown error:', error);
402
+ }
403
+ }
404
+ ```
405
+
406
+ ### Common Error Codes
407
+
408
+ | Code | Description | Solution |
409
+ |------|-------------|----------|
410
+ | 100 | Invalid parameter | Check phone number format |
411
+ | 131047 | Re-engagement message | Need approved template (>24hrs) |
412
+ | 131053 | Recipient not on WhatsApp | Verify phone number |
413
+ | 190 | Access token expired | Refresh token |
414
+ | 368 | Temporarily blocked | Reduce send rate |
415
+
416
+ > **More help?** See [Troubleshooting: Message Sending Issues](TROUBLESHOOTING.md#message-sending-issues)
417
+
418
+ ---
419
+
420
+ ## Response Format
421
+
422
+ All send methods return:
423
+
424
+ ```typescript
425
+ {
426
+ messaging_product: 'whatsapp',
427
+ contacts: [
428
+ {
429
+ input: '1234567890', // Phone as you provided
430
+ wa_id: '1234567890', // WhatsApp ID (normalized)
431
+ },
432
+ ],
433
+ messages: [
434
+ {
435
+ id: 'wamid.HBgLMTY...', // Message ID (WAMID)
436
+ },
437
+ ],
438
+ }
439
+ ```
440
+
441
+ Use the message ID to track delivery status.
442
+
443
+ ---
444
+
445
+ ## Phone Management
446
+
447
+ ### Register Phone
448
+
449
+ ```typescript
450
+ await client.registerPhone('123456'); // 6-digit PIN
451
+ ```
452
+
453
+ ### Deregister Phone
454
+
455
+ ```typescript
456
+ await client.deregisterPhone();
457
+ ```
458
+
459
+ ### List Phone Numbers
460
+
461
+ ```typescript
462
+ const phones = await client.listPhoneNumbers('YOUR_WABA_ID');
463
+
464
+ phones.data.forEach((phone) => {
465
+ console.log('ID:', phone.id);
466
+ console.log('Display:', phone.display_phone_number);
467
+ console.log('Quality:', phone.quality_rating);
468
+ console.log('Name:', phone.verified_name);
469
+ });
470
+ ```
471
+
472
+ ---
473
+
474
+ ## Best Practices
475
+
476
+ ### 1. Template Messages for First Contact
477
+
478
+ Send templates if >24 hours since last customer message:
479
+
480
+ ```typescript
481
+ async function sendMessage(to: string, text: string) {
482
+ const lastMessageTime = await getLastMessageTime(to);
483
+ const hoursSince = (Date.now() - lastMessageTime) / 1000 / 60 / 60;
484
+
485
+ if (hoursSince > 24) {
486
+ // Use template
487
+ await client.sendTemplateMessage(to, {
488
+ name: 're_engagement',
489
+ language: { code: 'en' },
490
+ });
491
+ } else {
492
+ // Use text
493
+ await client.sendTextMessage(to, text);
494
+ }
495
+ }
496
+ ```
497
+
498
+ ### 2. Rate Limiting
499
+
500
+ Respect Meta's rate limits:
501
+
502
+ ```typescript
503
+ import pLimit from 'p-limit';
504
+
505
+ const limit = pLimit(10); // Max 10 concurrent requests
506
+
507
+ const recipients = ['123', '456', '789'];
508
+ const promises = recipients.map((to) =>
509
+ limit(() => client.sendTextMessage(to, 'Hello'))
510
+ );
511
+
512
+ await Promise.all(promises);
513
+ ```
514
+
515
+ ### 3. Store Message IDs
516
+
517
+ Track delivery status:
518
+
519
+ ```typescript
520
+ const response = await client.sendTextMessage(to, text);
521
+ const messageId = response.messages[0].id;
522
+
523
+ await db.saveMessage({
524
+ messageId,
525
+ recipient: to,
526
+ text,
527
+ sentAt: new Date(),
528
+ status: 'sent',
529
+ });
530
+ ```
531
+
532
+ ### 4. Handle Retries
533
+
534
+ Retry on transient errors:
535
+
536
+ ```typescript
537
+ import pRetry from 'p-retry';
538
+
539
+ await pRetry(
540
+ () => client.sendTextMessage(to, text),
541
+ {
542
+ retries: 3,
543
+ onFailedAttempt: (error) => {
544
+ console.log(`Attempt ${error.attemptNumber} failed`);
545
+ },
546
+ }
547
+ );
548
+ ```
549
+
550
+ ---
551
+
552
+ ## Examples
553
+
554
+ ### Send OTP Template
555
+
556
+ ```typescript
557
+ await client.sendTemplateMessage(phoneNumber, {
558
+ name: 'otp_template',
559
+ language: { code: 'en' },
560
+ components: [
561
+ {
562
+ type: 'body',
563
+ parameters: [
564
+ { type: 'text', text: otpCode },
565
+ ],
566
+ },
567
+ {
568
+ type: 'button',
569
+ sub_type: 'url',
570
+ index: '0',
571
+ parameters: [
572
+ { type: 'text', text: otpCode }, // Autofill code
573
+ ],
574
+ },
575
+ ],
576
+ });
577
+ ```
578
+
579
+ ### Send Order Confirmation
580
+
581
+ ```typescript
582
+ await client.sendTemplateMessage(customerId, {
583
+ name: 'order_confirmation',
584
+ language: { code: 'en' },
585
+ components: [
586
+ {
587
+ type: 'header',
588
+ parameters: [
589
+ {
590
+ type: 'image',
591
+ image: { link: productImageUrl },
592
+ },
593
+ ],
594
+ },
595
+ {
596
+ type: 'body',
597
+ parameters: [
598
+ { type: 'text', text: customerName },
599
+ { type: 'text', text: orderId },
600
+ { type: 'text', text: deliveryDate },
601
+ ],
602
+ },
603
+ ],
604
+ });
605
+ ```
606
+
607
+ ### Send Delivery Update with Location
608
+
609
+ ```typescript
610
+ // Send text
611
+ await client.sendTextMessage(
612
+ customerId,
613
+ 'Your order is out for delivery!'
614
+ );
615
+
616
+ // Send driver location
617
+ await client.sendMessage({
618
+ messaging_product: 'whatsapp',
619
+ to: customerId,
620
+ type: 'location',
621
+ location: {
622
+ latitude: driverLat,
623
+ longitude: driverLng,
624
+ name: 'Driver Location',
625
+ },
626
+ });
627
+ ```
628
+
629
+ ---
630
+
631
+ ## Next Steps
632
+
633
+ - [Webhook Processing](WEBHOOKS.md) - Handle incoming messages
634
+ - [CLI Guide](CLI.md) - Use CLI to send messages
635
+ - [API Reference](../API_REFERENCE.md) - Complete type documentation