kapso-client-ruby 1.0.1 → 1.0.2
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +81 -81
- data/CHANGELOG.md +262 -91
- data/Gemfile +20 -20
- data/RAILS_INTEGRATION.md +477 -477
- data/README.md +1053 -752
- data/Rakefile +40 -40
- data/TEMPLATE_TOOLS_GUIDE.md +120 -120
- data/WHATSAPP_24_HOUR_GUIDE.md +133 -133
- data/examples/advanced_features.rb +352 -349
- data/examples/advanced_messaging.rb +241 -0
- data/examples/basic_messaging.rb +139 -136
- data/examples/enhanced_interactive.rb +400 -0
- data/examples/flows_usage.rb +307 -0
- data/examples/interactive_messages.rb +343 -0
- data/examples/media_management.rb +256 -253
- data/examples/rails/jobs.rb +387 -387
- data/examples/rails/models.rb +239 -239
- data/examples/rails/notifications_controller.rb +226 -226
- data/examples/template_management.rb +393 -390
- data/kapso-ruby-logo.jpg +0 -0
- data/lib/kapso_client_ruby/client.rb +321 -316
- data/lib/kapso_client_ruby/errors.rb +348 -329
- data/lib/kapso_client_ruby/rails/generators/install_generator.rb +75 -75
- data/lib/kapso_client_ruby/rails/generators/templates/env.erb +20 -20
- data/lib/kapso_client_ruby/rails/generators/templates/initializer.rb.erb +32 -32
- data/lib/kapso_client_ruby/rails/generators/templates/message_service.rb.erb +137 -137
- data/lib/kapso_client_ruby/rails/generators/templates/webhook_controller.rb.erb +61 -61
- data/lib/kapso_client_ruby/rails/railtie.rb +54 -54
- data/lib/kapso_client_ruby/rails/service.rb +188 -188
- data/lib/kapso_client_ruby/rails/tasks.rake +166 -166
- data/lib/kapso_client_ruby/resources/calls.rb +172 -172
- data/lib/kapso_client_ruby/resources/contacts.rb +190 -190
- data/lib/kapso_client_ruby/resources/conversations.rb +103 -103
- data/lib/kapso_client_ruby/resources/flows.rb +382 -0
- data/lib/kapso_client_ruby/resources/media.rb +205 -205
- data/lib/kapso_client_ruby/resources/messages.rb +760 -380
- data/lib/kapso_client_ruby/resources/phone_numbers.rb +85 -85
- data/lib/kapso_client_ruby/resources/templates.rb +283 -283
- data/lib/kapso_client_ruby/types.rb +348 -262
- data/lib/kapso_client_ruby/version.rb +5 -5
- data/lib/kapso_client_ruby.rb +75 -74
- data/scripts/.env.example +17 -17
- data/scripts/kapso_template_finder.rb +91 -91
- data/scripts/sdk_setup.rb +404 -404
- data/scripts/test.rb +60 -60
- metadata +12 -3
data/README.md
CHANGED
|
@@ -1,753 +1,1054 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
client.messages.
|
|
169
|
-
phone_number_id: 'phone_id',
|
|
170
|
-
to: '+1234567890',
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
)
|
|
254
|
-
|
|
255
|
-
#
|
|
256
|
-
client.
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
)
|
|
272
|
-
|
|
273
|
-
#
|
|
274
|
-
client.messages.
|
|
275
|
-
phone_number_id: 'phone_id',
|
|
276
|
-
to: '+1234567890'
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
```ruby
|
|
285
|
-
#
|
|
286
|
-
|
|
287
|
-
phone_number_id: 'phone_id',
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
)
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
client
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
#
|
|
464
|
-
client
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
#
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
#
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img src="kapso-ruby-logo.jpg" alt="Kapso Ruby Client" width="400">
|
|
3
|
+
</div>
|
|
4
|
+
|
|
5
|
+
# Kapso API Ruby SDK
|
|
6
|
+
|
|
7
|
+
[](https://badge.fury.io/rb/kapso-client-ruby)
|
|
8
|
+
[](https://www.ruby-lang.org/)
|
|
9
|
+
[](https://opensource.org/licenses/MIT)
|
|
10
|
+
|
|
11
|
+
A comprehensive Ruby client library for the [WhatsApp Business Cloud API](https://developers.facebook.com/docs/whatsapp/cloud-api/). This SDK provides a complete interface for sending messages, managing media, templates, and more, with built-in error handling, retry logic, and debug capabilities.
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- 🚀 **Complete API Coverage**: All Kapso Cloud API endpoints supported
|
|
16
|
+
- 📱 **Rich Message Types**: Text, media, templates, interactive messages, and more
|
|
17
|
+
- 🔐 **Dual Authentication**: Meta Graph API and Kapso Proxy support
|
|
18
|
+
- 🛡️ **Smart Error Handling**: Comprehensive error categorization and retry logic
|
|
19
|
+
- 📊 **Advanced Features**: Message history, analytics, and contact management (via Kapso)
|
|
20
|
+
- 🔍 **Debug Support**: Detailed logging and request/response tracing
|
|
21
|
+
- 📚 **Type Safety**: Structured response objects and validation
|
|
22
|
+
- ⚡ **Performance**: HTTP connection pooling and efficient request handling
|
|
23
|
+
- 🛤️ **Rails Integration**: First-class Rails support with generators, service classes, and background jobs
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
Add this line to your application's Gemfile:
|
|
28
|
+
|
|
29
|
+
```ruby
|
|
30
|
+
gem 'kapso-client-ruby'
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
And then execute:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
$ bundle install
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Or install it yourself as:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
$ gem install kapso-client-ruby
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Rails Integration
|
|
46
|
+
|
|
47
|
+
For Rails applications, use the built-in generator to set up everything automatically:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
rails generate kapso_client_ruby:install
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
This creates:
|
|
54
|
+
- Configuration initializer
|
|
55
|
+
- Webhook controller
|
|
56
|
+
- Service class for messaging
|
|
57
|
+
- Background job examples
|
|
58
|
+
- Routes for webhooks
|
|
59
|
+
|
|
60
|
+
See the [Rails Integration Guide](RAILS_INTEGRATION.md) for detailed Rails-specific documentation.
|
|
61
|
+
|
|
62
|
+
## Quick Start
|
|
63
|
+
|
|
64
|
+
### Basic Setup
|
|
65
|
+
|
|
66
|
+
```ruby
|
|
67
|
+
require 'kapso_client_api'
|
|
68
|
+
|
|
69
|
+
# Initialize client with Meta Graph API access token
|
|
70
|
+
client = KapsoClientRuby::Client.new(
|
|
71
|
+
access_token: 'your_access_token'
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# Send a text message
|
|
75
|
+
response = client.messages.send_text(
|
|
76
|
+
phone_number_id: 'your_phone_number_id',
|
|
77
|
+
to: '+1234567890',
|
|
78
|
+
body: 'Hello from Ruby!'
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
puts "Message sent: #{response.messages.first.id}"
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Using Kapso Proxy (for enhanced features)
|
|
85
|
+
|
|
86
|
+
```ruby
|
|
87
|
+
# Initialize client with Kapso API key for enhanced features
|
|
88
|
+
kapso_client = KapsoClientRuby::Client.new(
|
|
89
|
+
kapso_api_key: 'your_kapso_api_key',
|
|
90
|
+
base_url: 'https://app.kapso.ai/api/meta'
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
# Access message history and analytics
|
|
94
|
+
messages = kapso_client.messages.query(
|
|
95
|
+
phone_number_id: 'your_phone_number_id',
|
|
96
|
+
direction: 'inbound',
|
|
97
|
+
limit: 10
|
|
98
|
+
)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## API Reference
|
|
102
|
+
|
|
103
|
+
### Flows
|
|
104
|
+
|
|
105
|
+
Create and manage interactive WhatsApp Flows for rich data collection:
|
|
106
|
+
|
|
107
|
+
#### Create and Deploy a Flow
|
|
108
|
+
|
|
109
|
+
```ruby
|
|
110
|
+
# Idempotent deployment (recommended)
|
|
111
|
+
deployment = client.flows.deploy(
|
|
112
|
+
business_account_id: 'your_business_id',
|
|
113
|
+
name: 'appointment_booking',
|
|
114
|
+
categories: ['APPOINTMENT_BOOKING'],
|
|
115
|
+
flow_json: {
|
|
116
|
+
version: '3.0',
|
|
117
|
+
screens: [
|
|
118
|
+
{
|
|
119
|
+
id: 'APPOINTMENT',
|
|
120
|
+
title: 'Book Appointment',
|
|
121
|
+
layout: {
|
|
122
|
+
type: 'SingleColumnLayout',
|
|
123
|
+
children: [
|
|
124
|
+
{
|
|
125
|
+
type: 'Form',
|
|
126
|
+
name: 'appointment_form',
|
|
127
|
+
children: [
|
|
128
|
+
{
|
|
129
|
+
type: 'TextInput',
|
|
130
|
+
name: 'customer_name',
|
|
131
|
+
label: 'Full Name',
|
|
132
|
+
required: true
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
type: 'DatePicker',
|
|
136
|
+
name: 'appointment_date',
|
|
137
|
+
label: 'Preferred Date',
|
|
138
|
+
required: true
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
type: 'Footer',
|
|
142
|
+
label: 'Submit',
|
|
143
|
+
on_click_action: {
|
|
144
|
+
name: 'complete',
|
|
145
|
+
payload: {
|
|
146
|
+
customer_name: '${form.customer_name}',
|
|
147
|
+
appointment_date: '${form.appointment_date}'
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
]
|
|
152
|
+
}
|
|
153
|
+
]
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
]
|
|
157
|
+
}
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
puts "Flow ID: #{deployment[:id]}"
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
#### Send a Flow Message
|
|
164
|
+
|
|
165
|
+
```ruby
|
|
166
|
+
require 'securerandom'
|
|
167
|
+
|
|
168
|
+
client.messages.send_flow(
|
|
169
|
+
phone_number_id: 'phone_id',
|
|
170
|
+
to: '+1234567890',
|
|
171
|
+
flow_id: 'your_flow_id',
|
|
172
|
+
flow_cta: 'Book Appointment',
|
|
173
|
+
flow_token: SecureRandom.uuid,
|
|
174
|
+
header: {
|
|
175
|
+
type: 'text',
|
|
176
|
+
text: 'Appointment Booking'
|
|
177
|
+
},
|
|
178
|
+
body_text: 'Book your appointment in just a few taps!',
|
|
179
|
+
footer_text: 'Available slots fill up fast'
|
|
180
|
+
)
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
#### Handle Flow Webhooks
|
|
184
|
+
|
|
185
|
+
```ruby
|
|
186
|
+
# In your webhook endpoint
|
|
187
|
+
def handle_flow_webhook(encrypted_request)
|
|
188
|
+
private_key = OpenSSL::PKey::RSA.new(File.read('private_key.pem'))
|
|
189
|
+
|
|
190
|
+
# Decrypt incoming Flow event
|
|
191
|
+
flow_event = client.flows.receive_flow_event(
|
|
192
|
+
encrypted_request: encrypted_request,
|
|
193
|
+
private_key: private_key
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
# Process form data
|
|
197
|
+
case flow_event.action
|
|
198
|
+
when 'INIT'
|
|
199
|
+
response_data = {
|
|
200
|
+
version: flow_event.version,
|
|
201
|
+
screen: 'APPOINTMENT',
|
|
202
|
+
data: { available_dates: ['2024-01-15', '2024-01-16'] }
|
|
203
|
+
}
|
|
204
|
+
when 'data_exchange'
|
|
205
|
+
# Save appointment data
|
|
206
|
+
customer_name = flow_event.data['customer_name']
|
|
207
|
+
appointment_date = flow_event.data['appointment_date']
|
|
208
|
+
|
|
209
|
+
response_data = {
|
|
210
|
+
version: flow_event.version,
|
|
211
|
+
screen: 'SUCCESS',
|
|
212
|
+
data: { success: true }
|
|
213
|
+
}
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Encrypt and return response
|
|
217
|
+
client.flows.respond_to_flow(
|
|
218
|
+
response_data: response_data,
|
|
219
|
+
private_key: private_key
|
|
220
|
+
)
|
|
221
|
+
end
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
#### Manage Flows
|
|
225
|
+
|
|
226
|
+
```ruby
|
|
227
|
+
# List all Flows
|
|
228
|
+
flows = client.flows.list(business_account_id: 'business_id')
|
|
229
|
+
|
|
230
|
+
# Get Flow details
|
|
231
|
+
flow = client.flows.get(flow_id: 'flow_id')
|
|
232
|
+
|
|
233
|
+
# Update Flow
|
|
234
|
+
client.flows.update(
|
|
235
|
+
flow_id: 'flow_id',
|
|
236
|
+
categories: ['APPOINTMENT_BOOKING', 'CUSTOMER_SUPPORT']
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
# Update Flow JSON
|
|
240
|
+
asset_response = client.flows.update_asset(
|
|
241
|
+
flow_id: 'flow_id',
|
|
242
|
+
asset: flow_json_hash
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
# Publish Flow
|
|
246
|
+
client.flows.publish(flow_id: 'flow_id')
|
|
247
|
+
|
|
248
|
+
# Get preview URL
|
|
249
|
+
preview = client.flows.preview(flow_id: 'flow_id')
|
|
250
|
+
puts preview.preview_url
|
|
251
|
+
|
|
252
|
+
# Deprecate Flow
|
|
253
|
+
client.flows.deprecate(flow_id: 'flow_id')
|
|
254
|
+
|
|
255
|
+
# Delete Flow
|
|
256
|
+
client.flows.delete(flow_id: 'flow_id')
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Messages
|
|
260
|
+
|
|
261
|
+
Send various types of messages with the Messages resource:
|
|
262
|
+
|
|
263
|
+
#### Text Messages
|
|
264
|
+
|
|
265
|
+
```ruby
|
|
266
|
+
# Simple text message
|
|
267
|
+
client.messages.send_text(
|
|
268
|
+
phone_number_id: 'phone_id',
|
|
269
|
+
to: '+1234567890',
|
|
270
|
+
body: 'Hello World!'
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
# Text with URL preview
|
|
274
|
+
client.messages.send_text(
|
|
275
|
+
phone_number_id: 'phone_id',
|
|
276
|
+
to: '+1234567890',
|
|
277
|
+
body: 'Check this out: https://example.com',
|
|
278
|
+
preview_url: true
|
|
279
|
+
)
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
#### Media Messages
|
|
283
|
+
|
|
284
|
+
```ruby
|
|
285
|
+
# Send image
|
|
286
|
+
client.messages.send_image(
|
|
287
|
+
phone_number_id: 'phone_id',
|
|
288
|
+
to: '+1234567890',
|
|
289
|
+
image: {
|
|
290
|
+
link: 'https://example.com/image.jpg',
|
|
291
|
+
caption: 'Beautiful sunset'
|
|
292
|
+
}
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
# Send document
|
|
296
|
+
client.messages.send_document(
|
|
297
|
+
phone_number_id: 'phone_id',
|
|
298
|
+
to: '+1234567890',
|
|
299
|
+
document: {
|
|
300
|
+
id: 'media_id', # or link: 'https://...'
|
|
301
|
+
filename: 'report.pdf',
|
|
302
|
+
caption: 'Monthly report'
|
|
303
|
+
}
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
# Send audio
|
|
307
|
+
client.messages.send_audio(
|
|
308
|
+
phone_number_id: 'phone_id',
|
|
309
|
+
to: '+1234567890',
|
|
310
|
+
audio: { id: 'audio_media_id' }
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
# Send voice note (OGG/OPUS format recommended)
|
|
314
|
+
client.messages.send_audio(
|
|
315
|
+
phone_number_id: 'phone_id',
|
|
316
|
+
to: '+1234567890',
|
|
317
|
+
audio: { link: 'https://example.com/voice-note.ogg' },
|
|
318
|
+
voice: true # Marks as voice note
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
# Send video
|
|
322
|
+
client.messages.send_video(
|
|
323
|
+
phone_number_id: 'phone_id',
|
|
324
|
+
to: '+1234567890',
|
|
325
|
+
video: {
|
|
326
|
+
link: 'https://example.com/video.mp4',
|
|
327
|
+
caption: 'Tutorial video'
|
|
328
|
+
}
|
|
329
|
+
)
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
#### Group Messaging
|
|
333
|
+
|
|
334
|
+
Send messages to WhatsApp groups by setting `recipient_type: 'group'`:
|
|
335
|
+
|
|
336
|
+
```ruby
|
|
337
|
+
# Send text to group
|
|
338
|
+
client.messages.send_text(
|
|
339
|
+
phone_number_id: 'phone_id',
|
|
340
|
+
to: '120363XXXXXXXXX@g.us', # Group ID format
|
|
341
|
+
body: 'Hello everyone!',
|
|
342
|
+
recipient_type: 'group'
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
# Send image to group
|
|
346
|
+
client.messages.send_image(
|
|
347
|
+
phone_number_id: 'phone_id',
|
|
348
|
+
to: '120363XXXXXXXXX@g.us',
|
|
349
|
+
image: { link: 'https://example.com/team-photo.jpg' },
|
|
350
|
+
caption: 'Team photo from our event',
|
|
351
|
+
recipient_type: 'group'
|
|
352
|
+
)
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
**Note:** Group messaging works with all message types (text, images, videos, documents, interactive messages, etc.)
|
|
356
|
+
|
|
357
|
+
**Group ID Format:** `XXXXXXXXX@g.us` (WhatsApp group identifier)
|
|
358
|
+
|
|
359
|
+
#### Location Messages
|
|
360
|
+
|
|
361
|
+
```ruby
|
|
362
|
+
# Send location
|
|
363
|
+
client.messages.send_location(
|
|
364
|
+
phone_number_id: 'phone_id',
|
|
365
|
+
to: '+1234567890',
|
|
366
|
+
latitude: 37.7749,
|
|
367
|
+
longitude: -122.4194,
|
|
368
|
+
name: 'Our Office',
|
|
369
|
+
address: '123 Main St, San Francisco, CA'
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
# Request user's location
|
|
373
|
+
client.messages.send_interactive_location_request(
|
|
374
|
+
phone_number_id: 'phone_id',
|
|
375
|
+
to: '+1234567890',
|
|
376
|
+
body_text: 'Please share your location for delivery',
|
|
377
|
+
footer_text: 'Your privacy is important to us'
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
# Request location with header
|
|
381
|
+
client.messages.send_interactive_location_request(
|
|
382
|
+
phone_number_id: 'phone_id',
|
|
383
|
+
to: '+1234567890',
|
|
384
|
+
header: {
|
|
385
|
+
type: 'image',
|
|
386
|
+
image: { link: 'https://example.com/map-icon.png' }
|
|
387
|
+
},
|
|
388
|
+
body_text: 'Share your location to find the nearest store',
|
|
389
|
+
footer_text: 'Tap to share'
|
|
390
|
+
)
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
#### Interactive Messages
|
|
394
|
+
|
|
395
|
+
```ruby
|
|
396
|
+
# Button interactive message
|
|
397
|
+
client.messages.send_interactive_buttons(
|
|
398
|
+
phone_number_id: 'phone_id',
|
|
399
|
+
to: '+1234567890',
|
|
400
|
+
body_text: 'Choose an option:',
|
|
401
|
+
buttons: [
|
|
402
|
+
{
|
|
403
|
+
type: 'reply',
|
|
404
|
+
reply: {
|
|
405
|
+
id: 'option_1',
|
|
406
|
+
title: 'Option 1'
|
|
407
|
+
}
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
type: 'reply',
|
|
411
|
+
reply: {
|
|
412
|
+
id: 'option_2',
|
|
413
|
+
title: 'Option 2'
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
],
|
|
417
|
+
header: {
|
|
418
|
+
type: 'text',
|
|
419
|
+
text: 'Menu'
|
|
420
|
+
}
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
# List interactive message
|
|
424
|
+
client.messages.send_interactive_list(
|
|
425
|
+
phone_number_id: 'phone_id',
|
|
426
|
+
to: '+1234567890',
|
|
427
|
+
body_text: 'Please select from the list:',
|
|
428
|
+
button_text: 'View Options',
|
|
429
|
+
sections: [
|
|
430
|
+
{
|
|
431
|
+
title: 'Section 1',
|
|
432
|
+
rows: [
|
|
433
|
+
{
|
|
434
|
+
id: 'item_1',
|
|
435
|
+
title: 'Item 1',
|
|
436
|
+
description: 'Description 1'
|
|
437
|
+
}
|
|
438
|
+
]
|
|
439
|
+
}
|
|
440
|
+
]
|
|
441
|
+
)
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
#### Interactive CTA URL Messages
|
|
445
|
+
|
|
446
|
+
Send messages with Call-to-Action buttons that open URLs:
|
|
447
|
+
|
|
448
|
+
```ruby
|
|
449
|
+
# With image header
|
|
450
|
+
client.messages.send_interactive_cta_url(
|
|
451
|
+
phone_number_id: 'phone_id',
|
|
452
|
+
to: '+1234567890',
|
|
453
|
+
header: {
|
|
454
|
+
type: 'image',
|
|
455
|
+
image: { link: 'https://example.com/banner.jpg' }
|
|
456
|
+
},
|
|
457
|
+
body_text: 'Get 25% off your first purchase!',
|
|
458
|
+
display_text: 'Shop Now', # Max 20 characters
|
|
459
|
+
url: 'https://shop.example.com?utm_source=whatsapp',
|
|
460
|
+
footer_text: 'Limited time offer'
|
|
461
|
+
)
|
|
462
|
+
|
|
463
|
+
# With text header
|
|
464
|
+
client.messages.send_interactive_cta_url(
|
|
465
|
+
phone_number_id: 'phone_id',
|
|
466
|
+
to: '+1234567890',
|
|
467
|
+
header: {
|
|
468
|
+
type: 'text',
|
|
469
|
+
text: 'Special Offer'
|
|
470
|
+
},
|
|
471
|
+
body_text: 'Join our exclusive members club!',
|
|
472
|
+
display_text: 'Join Now',
|
|
473
|
+
url: 'https://members.example.com/signup'
|
|
474
|
+
)
|
|
475
|
+
|
|
476
|
+
# With video or document header
|
|
477
|
+
client.messages.send_interactive_cta_url(
|
|
478
|
+
phone_number_id: 'phone_id',
|
|
479
|
+
to: '+1234567890',
|
|
480
|
+
header: {
|
|
481
|
+
type: 'video',
|
|
482
|
+
video: { link: 'https://example.com/demo.mp4' }
|
|
483
|
+
},
|
|
484
|
+
body_text: 'Watch our product in action!',
|
|
485
|
+
display_text: 'Learn More',
|
|
486
|
+
url: 'https://example.com/product'
|
|
487
|
+
)
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
**Validations:**
|
|
491
|
+
- `body_text`: Max 1024 characters
|
|
492
|
+
- `display_text`: Max 20 characters
|
|
493
|
+
- `url`: Must be valid HTTP/HTTPS URL
|
|
494
|
+
- `footer_text`: Max 60 characters (optional)
|
|
495
|
+
- `header`: Supports text, image, video, or document
|
|
496
|
+
|
|
497
|
+
#### Catalog Messages
|
|
498
|
+
|
|
499
|
+
Send your product catalog directly in WhatsApp:
|
|
500
|
+
|
|
501
|
+
```ruby
|
|
502
|
+
client.messages.send_interactive_catalog_message(
|
|
503
|
+
phone_number_id: 'phone_id',
|
|
504
|
+
to: '+1234567890',
|
|
505
|
+
body_text: 'Browse our entire product catalog!',
|
|
506
|
+
thumbnail_product_retailer_id: 'SKU-001', # Product SKU for thumbnail
|
|
507
|
+
footer_text: 'Tap to explore'
|
|
508
|
+
)
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
**Parameters:**
|
|
512
|
+
- `body_text`: Max 1024 characters
|
|
513
|
+
- `thumbnail_product_retailer_id`: Product SKU to display as thumbnail (required)
|
|
514
|
+
- `footer_text`: Max 60 characters (optional)
|
|
515
|
+
|
|
516
|
+
#### Template Messages
|
|
517
|
+
|
|
518
|
+
```ruby
|
|
519
|
+
# Simple template
|
|
520
|
+
client.messages.send_template(
|
|
521
|
+
phone_number_id: 'phone_id',
|
|
522
|
+
to: '+1234567890',
|
|
523
|
+
name: 'hello_world',
|
|
524
|
+
language: 'en_US'
|
|
525
|
+
)
|
|
526
|
+
|
|
527
|
+
# Template with parameters
|
|
528
|
+
client.messages.send_template(
|
|
529
|
+
phone_number_id: 'phone_id',
|
|
530
|
+
to: '+1234567890',
|
|
531
|
+
name: 'appointment_reminder',
|
|
532
|
+
language: 'en_US',
|
|
533
|
+
components: [
|
|
534
|
+
{
|
|
535
|
+
type: 'body',
|
|
536
|
+
parameters: [
|
|
537
|
+
{ type: 'text', text: 'John Doe' },
|
|
538
|
+
{ type: 'text', text: 'Tomorrow at 2 PM' }
|
|
539
|
+
]
|
|
540
|
+
}
|
|
541
|
+
]
|
|
542
|
+
)
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
#### Message Reactions
|
|
546
|
+
|
|
547
|
+
```ruby
|
|
548
|
+
# Add reaction
|
|
549
|
+
client.messages.send_reaction(
|
|
550
|
+
phone_number_id: 'phone_id',
|
|
551
|
+
to: '+1234567890',
|
|
552
|
+
message_id: 'message_to_react_to',
|
|
553
|
+
emoji: '👍'
|
|
554
|
+
)
|
|
555
|
+
|
|
556
|
+
# Remove reaction
|
|
557
|
+
client.messages.send_reaction(
|
|
558
|
+
phone_number_id: 'phone_id',
|
|
559
|
+
to: '+1234567890',
|
|
560
|
+
message_id: 'message_to_react_to',
|
|
561
|
+
emoji: nil
|
|
562
|
+
)
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
#### Message Status
|
|
566
|
+
|
|
567
|
+
```ruby
|
|
568
|
+
# Mark message as read
|
|
569
|
+
client.messages.mark_read(
|
|
570
|
+
phone_number_id: 'phone_id',
|
|
571
|
+
message_id: 'message_id'
|
|
572
|
+
)
|
|
573
|
+
|
|
574
|
+
# Send typing indicator
|
|
575
|
+
client.messages.send_typing_indicator(
|
|
576
|
+
phone_number_id: 'phone_id',
|
|
577
|
+
to: '+1234567890'
|
|
578
|
+
)
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
### Media Management
|
|
582
|
+
|
|
583
|
+
Handle media uploads, downloads, and management:
|
|
584
|
+
|
|
585
|
+
```ruby
|
|
586
|
+
# Upload media
|
|
587
|
+
upload_response = client.media.upload(
|
|
588
|
+
phone_number_id: 'phone_id',
|
|
589
|
+
type: 'image',
|
|
590
|
+
file: '/path/to/image.jpg'
|
|
591
|
+
)
|
|
592
|
+
|
|
593
|
+
media_id = upload_response.id
|
|
594
|
+
|
|
595
|
+
# Get media metadata
|
|
596
|
+
metadata = client.media.get(media_id: media_id)
|
|
597
|
+
puts "File size: #{metadata.file_size} bytes"
|
|
598
|
+
puts "MIME type: #{metadata.mime_type}"
|
|
599
|
+
|
|
600
|
+
# Download media
|
|
601
|
+
content = client.media.download(
|
|
602
|
+
media_id: media_id,
|
|
603
|
+
as: :binary
|
|
604
|
+
)
|
|
605
|
+
|
|
606
|
+
# Save media to file
|
|
607
|
+
client.media.save_to_file(
|
|
608
|
+
media_id: media_id,
|
|
609
|
+
filepath: '/path/to/save/file.jpg'
|
|
610
|
+
)
|
|
611
|
+
|
|
612
|
+
# Delete media
|
|
613
|
+
client.media.delete(media_id: media_id)
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
### Template Management
|
|
617
|
+
|
|
618
|
+
Create, manage, and use message templates:
|
|
619
|
+
|
|
620
|
+
```ruby
|
|
621
|
+
# List templates
|
|
622
|
+
templates = client.templates.list(
|
|
623
|
+
business_account_id: 'your_business_id',
|
|
624
|
+
status: 'APPROVED'
|
|
625
|
+
)
|
|
626
|
+
|
|
627
|
+
# Create marketing template
|
|
628
|
+
template_data = client.templates.build_marketing_template(
|
|
629
|
+
name: 'summer_sale',
|
|
630
|
+
language: 'en_US',
|
|
631
|
+
body: 'Hi {{1}}! Our summer sale is here with {{2}} off!',
|
|
632
|
+
header: {
|
|
633
|
+
type: 'HEADER',
|
|
634
|
+
format: 'TEXT',
|
|
635
|
+
text: 'Summer Sale 🌞'
|
|
636
|
+
},
|
|
637
|
+
footer: 'Limited time offer',
|
|
638
|
+
buttons: [
|
|
639
|
+
{
|
|
640
|
+
type: 'URL',
|
|
641
|
+
text: 'Shop Now',
|
|
642
|
+
url: 'https://shop.example.com'
|
|
643
|
+
}
|
|
644
|
+
],
|
|
645
|
+
body_example: {
|
|
646
|
+
body_text: [['John', '25%']]
|
|
647
|
+
}
|
|
648
|
+
)
|
|
649
|
+
|
|
650
|
+
response = client.templates.create(
|
|
651
|
+
business_account_id: 'your_business_id',
|
|
652
|
+
**template_data
|
|
653
|
+
)
|
|
654
|
+
|
|
655
|
+
# Create authentication template
|
|
656
|
+
auth_template = client.templates.build_authentication_template(
|
|
657
|
+
name: 'verify_code',
|
|
658
|
+
language: 'en_US',
|
|
659
|
+
ttl_seconds: 300
|
|
660
|
+
)
|
|
661
|
+
|
|
662
|
+
client.templates.create(
|
|
663
|
+
business_account_id: 'your_business_id',
|
|
664
|
+
**auth_template
|
|
665
|
+
)
|
|
666
|
+
|
|
667
|
+
# Delete template
|
|
668
|
+
client.templates.delete(
|
|
669
|
+
business_account_id: 'your_business_id',
|
|
670
|
+
name: 'old_template',
|
|
671
|
+
language: 'en_US'
|
|
672
|
+
)
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
### Advanced Features (Kapso Proxy)
|
|
676
|
+
|
|
677
|
+
Access enhanced features with Kapso proxy:
|
|
678
|
+
|
|
679
|
+
```ruby
|
|
680
|
+
# Initialize Kapso client
|
|
681
|
+
kapso_client = KapsoClientRuby::Client.new(
|
|
682
|
+
kapso_api_key: 'your_kapso_key',
|
|
683
|
+
base_url: 'https://app.kapso.ai/api/meta'
|
|
684
|
+
)
|
|
685
|
+
|
|
686
|
+
# Message history
|
|
687
|
+
messages = kapso_client.messages.query(
|
|
688
|
+
phone_number_id: 'phone_id',
|
|
689
|
+
direction: 'inbound',
|
|
690
|
+
since: '2024-01-01T00:00:00Z',
|
|
691
|
+
limit: 50
|
|
692
|
+
)
|
|
693
|
+
|
|
694
|
+
# Conversation management
|
|
695
|
+
conversations = kapso_client.conversations.list(
|
|
696
|
+
phone_number_id: 'phone_id',
|
|
697
|
+
status: 'active'
|
|
698
|
+
)
|
|
699
|
+
|
|
700
|
+
conversation = kapso_client.conversations.get(
|
|
701
|
+
conversation_id: conversations.data.first.id
|
|
702
|
+
)
|
|
703
|
+
|
|
704
|
+
# Update conversation status
|
|
705
|
+
kapso_client.conversations.update_status(
|
|
706
|
+
conversation_id: conversation.id,
|
|
707
|
+
status: 'archived'
|
|
708
|
+
)
|
|
709
|
+
|
|
710
|
+
# Contact management
|
|
711
|
+
contacts = kapso_client.contacts.list(
|
|
712
|
+
phone_number_id: 'phone_id',
|
|
713
|
+
limit: 100
|
|
714
|
+
)
|
|
715
|
+
|
|
716
|
+
# Update contact metadata
|
|
717
|
+
kapso_client.contacts.update(
|
|
718
|
+
phone_number_id: 'phone_id',
|
|
719
|
+
wa_id: 'contact_wa_id',
|
|
720
|
+
metadata: {
|
|
721
|
+
tags: ['premium', 'customer'],
|
|
722
|
+
source: 'website'
|
|
723
|
+
}
|
|
724
|
+
)
|
|
725
|
+
|
|
726
|
+
# Search contacts
|
|
727
|
+
results = kapso_client.contacts.search(
|
|
728
|
+
phone_number_id: 'phone_id',
|
|
729
|
+
query: 'john',
|
|
730
|
+
search_in: ['profile_name', 'phone_number']
|
|
731
|
+
)
|
|
732
|
+
```
|
|
733
|
+
|
|
734
|
+
## Configuration
|
|
735
|
+
|
|
736
|
+
### Global Configuration
|
|
737
|
+
|
|
738
|
+
```ruby
|
|
739
|
+
KapsoClientRuby.configure do |config|
|
|
740
|
+
config.debug = true
|
|
741
|
+
config.timeout = 60
|
|
742
|
+
config.open_timeout = 10
|
|
743
|
+
config.max_retries = 3
|
|
744
|
+
config.retry_delay = 1.0
|
|
745
|
+
end
|
|
746
|
+
```
|
|
747
|
+
|
|
748
|
+
### Client Configuration
|
|
749
|
+
|
|
750
|
+
```ruby
|
|
751
|
+
client = KapsoClientRuby::Client.new(
|
|
752
|
+
access_token: 'token',
|
|
753
|
+
debug: true,
|
|
754
|
+
timeout: 30,
|
|
755
|
+
logger: Logger.new('whatsapp.log')
|
|
756
|
+
)
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
### Debug Logging
|
|
760
|
+
|
|
761
|
+
Enable debug logging to see detailed HTTP requests and responses:
|
|
762
|
+
|
|
763
|
+
```ruby
|
|
764
|
+
# Enable debug mode
|
|
765
|
+
client = KapsoClientRuby::Client.new(
|
|
766
|
+
access_token: 'token',
|
|
767
|
+
debug: true
|
|
768
|
+
)
|
|
769
|
+
|
|
770
|
+
# Custom logger
|
|
771
|
+
logger = Logger.new(STDOUT)
|
|
772
|
+
logger.level = Logger::DEBUG
|
|
773
|
+
|
|
774
|
+
client = KapsoClientRuby::Client.new(
|
|
775
|
+
access_token: 'token',
|
|
776
|
+
logger: logger
|
|
777
|
+
)
|
|
778
|
+
```
|
|
779
|
+
|
|
780
|
+
## Error Handling
|
|
781
|
+
|
|
782
|
+
The SDK provides comprehensive error handling with detailed categorization:
|
|
783
|
+
|
|
784
|
+
```ruby
|
|
785
|
+
begin
|
|
786
|
+
client.messages.send_text(
|
|
787
|
+
phone_number_id: 'phone_id',
|
|
788
|
+
to: 'invalid_number',
|
|
789
|
+
body: 'Test'
|
|
790
|
+
)
|
|
791
|
+
rescue KapsoClientRuby::Errors::GraphApiError => e
|
|
792
|
+
puts "Error: #{e.message}"
|
|
793
|
+
puts "Category: #{e.category}"
|
|
794
|
+
puts "HTTP Status: #{e.http_status}"
|
|
795
|
+
puts "Code: #{e.code}"
|
|
796
|
+
|
|
797
|
+
# Check error type
|
|
798
|
+
case e.category
|
|
799
|
+
when :authorization
|
|
800
|
+
puts "Authentication failed - check your access token"
|
|
801
|
+
when :parameter
|
|
802
|
+
puts "Invalid parameter - check phone number format"
|
|
803
|
+
when :throttling
|
|
804
|
+
puts "Rate limited - wait before retrying"
|
|
805
|
+
if e.retry_hint[:retry_after_ms]
|
|
806
|
+
sleep(e.retry_hint[:retry_after_ms] / 1000.0)
|
|
807
|
+
end
|
|
808
|
+
when :template
|
|
809
|
+
puts "Template error - check template name and parameters"
|
|
810
|
+
when :media
|
|
811
|
+
puts "Media error - check file format and size"
|
|
812
|
+
end
|
|
813
|
+
|
|
814
|
+
# Check retry recommendations
|
|
815
|
+
case e.retry_hint[:action]
|
|
816
|
+
when :retry
|
|
817
|
+
puts "Safe to retry this request"
|
|
818
|
+
when :retry_after
|
|
819
|
+
puts "Retry after specified delay: #{e.retry_hint[:retry_after_ms]}ms"
|
|
820
|
+
when :do_not_retry
|
|
821
|
+
puts "Do not retry - permanent error"
|
|
822
|
+
when :fix_and_retry
|
|
823
|
+
puts "Fix the request and retry"
|
|
824
|
+
when :refresh_token
|
|
825
|
+
puts "Access token needs to be refreshed"
|
|
826
|
+
end
|
|
827
|
+
end
|
|
828
|
+
```
|
|
829
|
+
|
|
830
|
+
### Error Categories
|
|
831
|
+
|
|
832
|
+
- `:authorization` - Authentication and token errors
|
|
833
|
+
- `:permission` - Permission and access errors
|
|
834
|
+
- `:parameter` - Invalid parameters or format errors
|
|
835
|
+
- `:throttling` - Rate limiting errors
|
|
836
|
+
- `:template` - Template-related errors
|
|
837
|
+
- `:media` - Media upload/download errors
|
|
838
|
+
- `:phone_registration` - Phone number registration errors
|
|
839
|
+
- `:integrity` - Message integrity errors
|
|
840
|
+
- `:business_eligibility` - Business account eligibility errors
|
|
841
|
+
- `:reengagement_window` - 24-hour messaging window errors
|
|
842
|
+
- `:waba_config` - WhatsApp Business Account configuration errors
|
|
843
|
+
- `:flow` - WhatsApp Flow errors
|
|
844
|
+
- `:synchronization` - Data synchronization errors
|
|
845
|
+
- `:server` - Server-side errors
|
|
846
|
+
- `:unknown` - Unclassified errors
|
|
847
|
+
|
|
848
|
+
### Automatic Retry Logic
|
|
849
|
+
|
|
850
|
+
```ruby
|
|
851
|
+
def send_with_retry(client, max_retries = 3)
|
|
852
|
+
retries = 0
|
|
853
|
+
|
|
854
|
+
begin
|
|
855
|
+
client.messages.send_text(
|
|
856
|
+
phone_number_id: 'phone_id',
|
|
857
|
+
to: '+1234567890',
|
|
858
|
+
body: 'Test message'
|
|
859
|
+
)
|
|
860
|
+
rescue KapsoClientRuby::Errors::GraphApiError => e
|
|
861
|
+
retries += 1
|
|
862
|
+
|
|
863
|
+
case e.retry_hint[:action]
|
|
864
|
+
when :retry
|
|
865
|
+
if retries <= max_retries
|
|
866
|
+
sleep(retries * 2) # Exponential backoff
|
|
867
|
+
retry
|
|
868
|
+
end
|
|
869
|
+
when :retry_after
|
|
870
|
+
if e.retry_hint[:retry_after_ms] && retries <= max_retries
|
|
871
|
+
sleep(e.retry_hint[:retry_after_ms] / 1000.0)
|
|
872
|
+
retry
|
|
873
|
+
end
|
|
874
|
+
end
|
|
875
|
+
|
|
876
|
+
raise # Re-raise if no retry
|
|
877
|
+
end
|
|
878
|
+
end
|
|
879
|
+
```
|
|
880
|
+
|
|
881
|
+
## Webhook Handling
|
|
882
|
+
|
|
883
|
+
Handle incoming webhooks from WhatsApp:
|
|
884
|
+
|
|
885
|
+
```ruby
|
|
886
|
+
# Verify webhook signature
|
|
887
|
+
def verify_webhook_signature(payload, signature, app_secret)
|
|
888
|
+
require 'openssl'
|
|
889
|
+
|
|
890
|
+
sig_hash = signature.sub('sha256=', '')
|
|
891
|
+
expected_sig = OpenSSL::HMAC.hexdigest('sha256', app_secret, payload)
|
|
892
|
+
|
|
893
|
+
sig_hash == expected_sig
|
|
894
|
+
end
|
|
895
|
+
|
|
896
|
+
# In your webhook endpoint
|
|
897
|
+
def handle_webhook(request)
|
|
898
|
+
payload = request.body.read
|
|
899
|
+
signature = request.headers['X-Hub-Signature-256']
|
|
900
|
+
|
|
901
|
+
unless verify_webhook_signature(payload, signature, ENV['WHATSAPP_APP_SECRET'])
|
|
902
|
+
return [401, {}, ['Unauthorized']]
|
|
903
|
+
end
|
|
904
|
+
|
|
905
|
+
webhook_data = JSON.parse(payload)
|
|
906
|
+
|
|
907
|
+
# Process webhook data
|
|
908
|
+
webhook_data['entry'].each do |entry|
|
|
909
|
+
entry['changes'].each do |change|
|
|
910
|
+
if change['field'] == 'messages'
|
|
911
|
+
messages = change['value']['messages'] || []
|
|
912
|
+
messages.each do |message|
|
|
913
|
+
handle_incoming_message(message)
|
|
914
|
+
end
|
|
915
|
+
end
|
|
916
|
+
end
|
|
917
|
+
end
|
|
918
|
+
|
|
919
|
+
[200, {}, ['OK']]
|
|
920
|
+
end
|
|
921
|
+
|
|
922
|
+
def handle_incoming_message(message)
|
|
923
|
+
case message['type']
|
|
924
|
+
when 'text'
|
|
925
|
+
puts "Received text: #{message['text']['body']}"
|
|
926
|
+
when 'image'
|
|
927
|
+
puts "Received image: #{message['image']['id']}"
|
|
928
|
+
when 'interactive'
|
|
929
|
+
puts "Received interactive response: #{message['interactive']}"
|
|
930
|
+
end
|
|
931
|
+
end
|
|
932
|
+
```
|
|
933
|
+
|
|
934
|
+
## Testing
|
|
935
|
+
|
|
936
|
+
Run the test suite:
|
|
937
|
+
|
|
938
|
+
```bash
|
|
939
|
+
# Install development dependencies
|
|
940
|
+
bundle install
|
|
941
|
+
|
|
942
|
+
# Run tests
|
|
943
|
+
bundle exec rspec
|
|
944
|
+
|
|
945
|
+
# Run tests with coverage
|
|
946
|
+
bundle exec rspec --format documentation
|
|
947
|
+
|
|
948
|
+
# Run rubocop for style checking
|
|
949
|
+
bundle exec rubocop
|
|
950
|
+
```
|
|
951
|
+
|
|
952
|
+
### Testing with VCR
|
|
953
|
+
|
|
954
|
+
The SDK includes VCR cassettes for testing without making real API calls:
|
|
955
|
+
|
|
956
|
+
```ruby
|
|
957
|
+
# spec/spec_helper.rb
|
|
958
|
+
require 'vcr'
|
|
959
|
+
|
|
960
|
+
VCR.configure do |config|
|
|
961
|
+
config.cassette_library_dir = 'spec/vcr_cassettes'
|
|
962
|
+
config.hook_into :webmock
|
|
963
|
+
config.configure_rspec_metadata!
|
|
964
|
+
|
|
965
|
+
# Filter sensitive data
|
|
966
|
+
config.filter_sensitive_data('<ACCESS_TOKEN>') { ENV['WHATSAPP_ACCESS_TOKEN'] }
|
|
967
|
+
config.filter_sensitive_data('<PHONE_NUMBER_ID>') { ENV['PHONE_NUMBER_ID'] }
|
|
968
|
+
end
|
|
969
|
+
|
|
970
|
+
# In your tests
|
|
971
|
+
RSpec.describe 'Messages' do
|
|
972
|
+
it 'sends text message', :vcr do
|
|
973
|
+
client = KapsoClientRuby::Client.new(access_token: 'test_token')
|
|
974
|
+
|
|
975
|
+
response = client.messages.send_text(
|
|
976
|
+
phone_number_id: 'test_phone_id',
|
|
977
|
+
to: '+1234567890',
|
|
978
|
+
body: 'Test message'
|
|
979
|
+
)
|
|
980
|
+
|
|
981
|
+
expect(response.messages.first.id).to be_present
|
|
982
|
+
end
|
|
983
|
+
end
|
|
984
|
+
```
|
|
985
|
+
|
|
986
|
+
## Examples
|
|
987
|
+
|
|
988
|
+
See the [examples](examples/) directory for comprehensive usage examples:
|
|
989
|
+
|
|
990
|
+
- [Basic Messaging](examples/basic_messaging.rb) - Text, media, and template messages
|
|
991
|
+
- [Media Management](examples/media_management.rb) - Upload, download, and manage media
|
|
992
|
+
- [Template Management](examples/template_management.rb) - Create and manage templates
|
|
993
|
+
- [Advanced Features](examples/advanced_features.rb) - Kapso proxy features and analytics
|
|
994
|
+
|
|
995
|
+
## Requirements
|
|
996
|
+
|
|
997
|
+
- Ruby >= 2.7.0
|
|
998
|
+
- Faraday >= 2.0
|
|
999
|
+
- A WhatsApp Business Account with Cloud API access
|
|
1000
|
+
- Valid access token from Meta or Kapso API key
|
|
1001
|
+
|
|
1002
|
+
## Contributing
|
|
1003
|
+
|
|
1004
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/gokapso/whatsapp-cloud-api-ruby.
|
|
1005
|
+
|
|
1006
|
+
### Development Setup
|
|
1007
|
+
|
|
1008
|
+
```bash
|
|
1009
|
+
git clone https://github.com/gokapso/whatsapp-cloud-api-ruby.git
|
|
1010
|
+
cd whatsapp-cloud-api-ruby
|
|
1011
|
+
bundle install
|
|
1012
|
+
```
|
|
1013
|
+
|
|
1014
|
+
### Running Tests
|
|
1015
|
+
|
|
1016
|
+
```bash
|
|
1017
|
+
# Run all tests
|
|
1018
|
+
bundle exec rspec
|
|
1019
|
+
|
|
1020
|
+
# Run specific test file
|
|
1021
|
+
bundle exec rspec spec/client_spec.rb
|
|
1022
|
+
|
|
1023
|
+
# Run with coverage
|
|
1024
|
+
COVERAGE=true bundle exec rspec
|
|
1025
|
+
```
|
|
1026
|
+
|
|
1027
|
+
### Code Style
|
|
1028
|
+
|
|
1029
|
+
```bash
|
|
1030
|
+
# Check style
|
|
1031
|
+
bundle exec rubocop
|
|
1032
|
+
|
|
1033
|
+
# Auto-fix issues
|
|
1034
|
+
bundle exec rubocop -A
|
|
1035
|
+
```
|
|
1036
|
+
|
|
1037
|
+
## License
|
|
1038
|
+
|
|
1039
|
+
This gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
1040
|
+
|
|
1041
|
+
## Support
|
|
1042
|
+
|
|
1043
|
+
- 📖 [WhatsApp Cloud API Documentation](https://developers.facebook.com/docs/whatsapp/cloud-api/)
|
|
1044
|
+
- 🌐 [Kapso Platform](https://kapso.ai/) for enhanced features
|
|
1045
|
+
- 🐛 [Issue Tracker](https://github.com/PabloB07/kapso-client-ruby/issues)
|
|
1046
|
+
- 📧 Email: support@kapso.ai
|
|
1047
|
+
|
|
1048
|
+
## Changelog
|
|
1049
|
+
|
|
1050
|
+
See [CHANGELOG.md](CHANGELOG.md) for version history and updates.
|
|
1051
|
+
|
|
1052
|
+
---
|
|
1053
|
+
|
|
753
1054
|
Built with ❤️ for the [Kapso](https://kapso.ai) team
|