followupboss_client 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.
Files changed (65) hide show
  1. checksums.yaml +7 -0
  2. data/.env.example +12 -0
  3. data/.gitignore +18 -0
  4. data/.rspec +2 -0
  5. data/.ruby-gemset +1 -0
  6. data/.ruby-version +1 -0
  7. data/.tool-versions +1 -0
  8. data/.travis.yml +9 -0
  9. data/CHANGELOG.md +149 -0
  10. data/CODE_OF_CONDUCT.md +49 -0
  11. data/Gemfile +4 -0
  12. data/LICENSE.txt +22 -0
  13. data/README.md +882 -0
  14. data/Rakefile +6 -0
  15. data/bin/console +14 -0
  16. data/bin/setup +8 -0
  17. data/followupboss_client.gemspec +43 -0
  18. data/lib/followupboss_client.rb +16 -0
  19. data/lib/fub_client/action_plan.rb +5 -0
  20. data/lib/fub_client/appointment.rb +42 -0
  21. data/lib/fub_client/appointment_outcome.rb +51 -0
  22. data/lib/fub_client/appointment_type.rb +51 -0
  23. data/lib/fub_client/call.rb +4 -0
  24. data/lib/fub_client/client.rb +200 -0
  25. data/lib/fub_client/compatibility.rb +18 -0
  26. data/lib/fub_client/configuration.rb +54 -0
  27. data/lib/fub_client/cookie_client.rb +190 -0
  28. data/lib/fub_client/custom_field.rb +5 -0
  29. data/lib/fub_client/deal.rb +41 -0
  30. data/lib/fub_client/deal_attachment.rb +61 -0
  31. data/lib/fub_client/deal_custom_field.rb +47 -0
  32. data/lib/fub_client/em_event.rb +5 -0
  33. data/lib/fub_client/email_template.rb +5 -0
  34. data/lib/fub_client/event.rb +8 -0
  35. data/lib/fub_client/group.rb +58 -0
  36. data/lib/fub_client/her_patch.rb +101 -0
  37. data/lib/fub_client/identity.rb +33 -0
  38. data/lib/fub_client/message.rb +41 -0
  39. data/lib/fub_client/middleware/authentication.rb +26 -0
  40. data/lib/fub_client/middleware/cookie_authentication.rb +61 -0
  41. data/lib/fub_client/middleware/parser.rb +59 -0
  42. data/lib/fub_client/middleware.rb +8 -0
  43. data/lib/fub_client/note.rb +4 -0
  44. data/lib/fub_client/people_relationship.rb +34 -0
  45. data/lib/fub_client/person.rb +5 -0
  46. data/lib/fub_client/person_attachment.rb +50 -0
  47. data/lib/fub_client/pipeline.rb +45 -0
  48. data/lib/fub_client/property.rb +26 -0
  49. data/lib/fub_client/rails8_patch.rb +39 -0
  50. data/lib/fub_client/resource.rb +33 -0
  51. data/lib/fub_client/shared_inbox.rb +389 -0
  52. data/lib/fub_client/smart_list.rb +5 -0
  53. data/lib/fub_client/stage.rb +39 -0
  54. data/lib/fub_client/task.rb +18 -0
  55. data/lib/fub_client/team.rb +65 -0
  56. data/lib/fub_client/team_inbox.rb +65 -0
  57. data/lib/fub_client/text_message.rb +46 -0
  58. data/lib/fub_client/text_message_template.rb +49 -0
  59. data/lib/fub_client/user.rb +4 -0
  60. data/lib/fub_client/version.rb +3 -0
  61. data/lib/fub_client/webhook.rb +47 -0
  62. data/lib/fub_client.rb +61 -0
  63. data/scripts/test_api.rb +110 -0
  64. data/scripts/test_shared_inbox.rb +90 -0
  65. metadata +335 -0
data/README.md ADDED
@@ -0,0 +1,882 @@
1
+ # FollowUpBoss Client
2
+
3
+ [![Ruby](https://img.shields.io/badge/ruby-%3E%3D%202.7-red.svg)](https://www.ruby-lang.org/)
4
+ [![Rails](https://img.shields.io/badge/rails-%3E%3D%207.1-red.svg)](https://rubyonrails.org/)
5
+ [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE.txt)
6
+ [![Gem Version](https://badge.fury.io/rb/followupboss_client.svg)](https://badge.fury.io/rb/followupboss_client)
7
+
8
+ A comprehensive Ruby client for the [Follow Up Boss API](https://api.followupboss.com/api-documentation/). This gem provides Rails-like models and methods for seamless integration with Follow Up Boss CRM.
9
+
10
+ **Enhanced Features:**
11
+ - 🚀 Rails 8 compatibility with automatic patches
12
+ - 🔐 Secure cookie authentication for SharedInbox
13
+ - 📧 Advanced inbox management
14
+ - 🛡️ Security-first architecture
15
+ - 📱 Comprehensive API coverage (25+ resources)
16
+
17
+ ## Table of Contents
18
+
19
+ - [Installation](#installation)
20
+ - [Quick Start](#quick-start)
21
+ - [Authentication](#authentication)
22
+ - [Configuration](#configuration)
23
+ - [API Reference](#api-reference)
24
+ - [Core Resources](#core-resources)
25
+ - [Communications](#communications)
26
+ - [Tasks & Events](#tasks--events)
27
+ - [Templates & Automation](#templates--automation)
28
+ - [Teams & Users](#teams--users)
29
+ - [Pipelines & Stages](#pipelines--stages)
30
+ - [Attachments & Files](#attachments--files)
31
+ - [Custom Fields](#custom-fields)
32
+ - [Inboxes](#inboxes)
33
+ - [Integration & Webhooks](#integration--webhooks)
34
+ - [Advanced Features](#advanced-features)
35
+ - [Error Handling](#error-handling)
36
+ - [Security Considerations](#security-considerations)
37
+ - [Troubleshooting](#troubleshooting)
38
+ - [Development](#development)
39
+ - [Contributing](#contributing)
40
+ - [License](#license)
41
+
42
+ ## Installation
43
+
44
+ Add this line to your application's Gemfile:
45
+
46
+ ```ruby
47
+ gem 'followupboss_client'
48
+ ```
49
+
50
+ And then execute:
51
+
52
+ ```bash
53
+ $ bundle install
54
+ ```
55
+
56
+ Or install it directly:
57
+
58
+ ```bash
59
+ $ gem install followupboss_client
60
+ ```
61
+
62
+ ## Quick Start
63
+
64
+ ```ruby
65
+ require 'followupboss_client'
66
+
67
+ # Configure with API key
68
+ FubClient.configure do |config|
69
+ config.api_key = 'your_api_key'
70
+ end
71
+
72
+ # Start using the API
73
+ people = FubClient::Person.all
74
+ deals = FubClient::Deal.active
75
+ events = FubClient::Event.find(123)
76
+ ```
77
+
78
+ ## Authentication
79
+
80
+ FubClient supports two authentication methods:
81
+
82
+ ### 1. API Key Authentication (Recommended)
83
+
84
+ The primary authentication method for most API operations.
85
+
86
+ **Environment Variable:**
87
+ ```bash
88
+ export FUB_API_KEY=your_api_key
89
+ ```
90
+
91
+ **Configuration Block:**
92
+ ```ruby
93
+ FubClient.configure do |config|
94
+ config.api_key = 'your_api_key'
95
+ end
96
+ ```
97
+
98
+ **Direct Assignment:**
99
+ ```ruby
100
+ FubClient::Client.instance.api_key = 'your_api_key'
101
+ ```
102
+
103
+ ### 2. Cookie Authentication (For Advanced Features)
104
+
105
+ Required for SharedInbox functionality and other advanced features.
106
+
107
+ ```ruby
108
+ # Using CookieClient with GIST encryption
109
+ cookie_client = FubClient::CookieClient.new(
110
+ subdomain: 'your_subdomain',
111
+ gist_url: 'https://gist.githubusercontent.com/...',
112
+ encryption_key: 'your_encryption_key'
113
+ )
114
+
115
+ # Or with direct cookie
116
+ cookie_client = FubClient::CookieClient.new(
117
+ subdomain: 'your_subdomain',
118
+ cookie: 'your_cookie_string'
119
+ )
120
+ ```
121
+
122
+ #### Using sync-my-cookie Browser Extension for Cookie Encryption
123
+
124
+ For secure cookie storage, you can use the [sync-my-cookie browser extension](https://github.com/Andiedie/sync-my-cookie) which uses kevast encryption in the backend:
125
+
126
+ **Step 1: Install the sync-my-cookie browser extension**
127
+ - Install from the Chrome Web Store or Firefox Add-ons
128
+ - Or install from source: https://github.com/Andiedie/sync-my-cookie
129
+
130
+ **Step 2: Configure and sync your cookies**
131
+ 1. Navigate to your FollowUpBoss subdomain and log in
132
+ 2. Open the sync-my-cookie browser extension
133
+ 3. Set up your encryption password/key
134
+ 4. Configure the extension to sync cookies to a GitHub GIST
135
+ 5. The extension will automatically encrypt and upload your cookies
136
+
137
+ **Step 3: Get your GIST URL**
138
+ The sync-my-cookie extension will create a GitHub GIST with encrypted cookie data in this format:
139
+ ```json
140
+ {
141
+ "followupboss.com": "encrypted_hex_string_from_kevast"
142
+ }
143
+ ```
144
+
145
+ **Step 4: Use with FubClient**
146
+ ```ruby
147
+ cookie_client = FubClient::CookieClient.new(
148
+ subdomain: 'your_subdomain',
149
+ gist_url: 'https://gist.githubusercontent.com/username/gist_id/raw/filename.json',
150
+ encryption_key: 'your_encryption_key' # Same key used in sync-my-cookie
151
+ )
152
+ ```
153
+
154
+ The FubClient will automatically:
155
+ - Fetch the encrypted data from the GIST
156
+ - Decrypt it using the same kevast algorithm as sync-my-cookie (AES-128-CBC)
157
+ - Process the cookie data for authentication
158
+
159
+ This approach provides secure, encrypted storage of sensitive cookie data while keeping it accessible for your applications. The sync-my-cookie extension handles the complex cookie extraction, encryption, and GIST synchronization process automatically.
160
+
161
+ ## Configuration
162
+
163
+ ### Global Configuration
164
+
165
+ ```ruby
166
+ FubClient.configure do |config|
167
+ # API Key Authentication
168
+ config.api_key = 'your_api_key'
169
+
170
+ # Cookie Authentication (for SharedInbox)
171
+ config.subdomain = 'your_subdomain'
172
+ config.gist_url = 'https://gist.githubusercontent.com/...'
173
+ config.encryption_key = 'your_encryption_key'
174
+ config.cookie = 'direct_cookie_string' # Alternative to GIST
175
+ end
176
+ ```
177
+
178
+ ### Environment Variables
179
+
180
+ Create a `.env` file:
181
+
182
+ ```bash
183
+ # API Key Authentication
184
+ FUB_API_KEY=your_api_key
185
+
186
+ # Cookie Authentication
187
+ FUB_SUBDOMAIN=your_subdomain
188
+ FUB_GIST_URL=https://gist.githubusercontent.com/...
189
+ FUB_ENCRYPTION_KEY=your_encryption_key
190
+ FUB_COOKIE=direct_cookie_string
191
+ ```
192
+
193
+ ## API Reference
194
+
195
+ ### Core Resources
196
+
197
+ #### Person
198
+ Manage contacts and leads.
199
+
200
+ ```ruby
201
+ # Basic operations
202
+ people = FubClient::Person.all
203
+ person = FubClient::Person.find(123)
204
+ person = FubClient::Person.create(name: 'John Doe', email: 'john@example.com')
205
+
206
+ # Pagination
207
+ people = FubClient::Person.by_page(2, 10) # page 2, 10 per page
208
+
209
+ # Total count
210
+ total = FubClient::Person.total
211
+ ```
212
+
213
+ #### Deal
214
+ Manage deals and opportunities.
215
+
216
+ ```ruby
217
+ # Deal status filtering
218
+ active_deals = FubClient::Deal.active
219
+ won_deals = FubClient::Deal.won
220
+ lost_deals = FubClient::Deal.lost
221
+
222
+ # Relationships
223
+ deal = FubClient::Deal.find(123)
224
+ people = deal.people
225
+ property = deal.property
226
+ ```
227
+
228
+ #### Property
229
+ Manage property listings.
230
+
231
+ ```ruby
232
+ properties = FubClient::Property.all
233
+ property = FubClient::Property.find(123)
234
+ property = FubClient::Property.create(
235
+ address: '123 Main St',
236
+ city: 'Anytown',
237
+ state: 'CA',
238
+ zip: '12345'
239
+ )
240
+ ```
241
+
242
+ ### Communications
243
+
244
+ #### Message
245
+ Handle email communications.
246
+
247
+ ```ruby
248
+ # Get messages
249
+ messages = FubClient::Message.all
250
+ unread = FubClient::Message.unread
251
+ with_attachments = FubClient::Message.with_attachments
252
+
253
+ # Mark as read
254
+ message = FubClient::Message.find(123)
255
+ message.mark_as_read
256
+
257
+ # Relationships
258
+ person = message.person
259
+ user = message.user
260
+ ```
261
+
262
+ #### TextMessage
263
+ Manage SMS communications.
264
+
265
+ ```ruby
266
+ # Get text messages
267
+ texts = FubClient::TextMessage.all
268
+ unread = FubClient::TextMessage.unread
269
+
270
+ # Send a text message
271
+ FubClient::TextMessage.send_message(
272
+ person_id: 123,
273
+ message: 'Hello from Follow Up Boss!',
274
+ user_id: 456
275
+ )
276
+
277
+ # Mark as read
278
+ text = FubClient::TextMessage.find(123)
279
+ text.mark_as_read
280
+ ```
281
+
282
+ #### Call
283
+ Track phone calls.
284
+
285
+ ```ruby
286
+ calls = FubClient::Call.all
287
+ call = FubClient::Call.find(123)
288
+ call = FubClient::Call.create(
289
+ person_id: 123,
290
+ user_id: 456,
291
+ duration: 300,
292
+ notes: 'Great conversation about their home search'
293
+ )
294
+ ```
295
+
296
+ ### Tasks & Events
297
+
298
+ #### Task
299
+ Manage tasks and to-dos.
300
+
301
+ ```ruby
302
+ # Get tasks
303
+ tasks = FubClient::Task.all
304
+ overdue = FubClient::Task.overdue
305
+
306
+ # Create a task
307
+ task = FubClient::Task.create(
308
+ person_id: 123,
309
+ user_id: 456,
310
+ subject: 'Follow up on property showing',
311
+ due_date: Date.tomorrow
312
+ )
313
+ ```
314
+
315
+ #### Event
316
+ Track events and activities.
317
+
318
+ ```ruby
319
+ events = FubClient::Event.all
320
+ event = FubClient::Event.find(123)
321
+
322
+ # Get total events
323
+ total = FubClient::Event.total
324
+ ```
325
+
326
+ #### Appointment
327
+ Manage appointments and showings.
328
+
329
+ ```ruby
330
+ # Get appointments
331
+ upcoming = FubClient::Appointment.upcoming
332
+ past = FubClient::Appointment.past
333
+
334
+ # Create appointment
335
+ appointment = FubClient::Appointment.create(
336
+ person_id: 123,
337
+ user_id: 456,
338
+ start_time: Time.now + 1.day,
339
+ end_time: Time.now + 1.day + 1.hour,
340
+ subject: 'Property showing'
341
+ )
342
+
343
+ # Complete appointment
344
+ appointment.complete(outcome_id: 1, notes: 'Successful showing')
345
+
346
+ # Relationships
347
+ person = appointment.person
348
+ user = appointment.user
349
+ ```
350
+
351
+ ### Templates & Automation
352
+
353
+ #### EmailTemplate
354
+ Manage email templates.
355
+
356
+ ```ruby
357
+ templates = FubClient::EmailTemplate.all
358
+ template = FubClient::EmailTemplate.find(123)
359
+
360
+ # Get total templates
361
+ total = FubClient::EmailTemplate.total
362
+ ```
363
+
364
+ #### TextMessageTemplate
365
+ Manage SMS templates.
366
+
367
+ ```ruby
368
+ # Get templates
369
+ active = FubClient::TextMessageTemplate.active
370
+ inactive = FubClient::TextMessageTemplate.inactive
371
+
372
+ # Use template
373
+ template = FubClient::TextMessageTemplate.find(123)
374
+ merged = template.merge(person_id: 456) # Merge with person data
375
+ template.send_to(person_id: 456, user_id: 789) # Send directly
376
+ ```
377
+
378
+ #### ActionPlan
379
+ Manage automated action plans.
380
+
381
+ ```ruby
382
+ plans = FubClient::ActionPlan.all
383
+ plan = FubClient::ActionPlan.find(123)
384
+ ```
385
+
386
+ ### Teams & Users
387
+
388
+ #### User
389
+ Manage users and agents.
390
+
391
+ ```ruby
392
+ users = FubClient::User.all
393
+ user = FubClient::User.find(123)
394
+ ```
395
+
396
+ #### Team
397
+ Manage teams and groups.
398
+
399
+ ```ruby
400
+ # Get teams
401
+ teams = FubClient::Team.all
402
+ active_teams = FubClient::Team.active
403
+
404
+ # Team management
405
+ team = FubClient::Team.find(123)
406
+ members = team.members
407
+ stats = team.stats
408
+
409
+ # Add/remove members
410
+ team.add_user(user_id: 456, team_leader: true)
411
+ team.remove_user(user_id: 456)
412
+ ```
413
+
414
+ #### Group
415
+ Manage user groups.
416
+
417
+ ```ruby
418
+ # Get groups
419
+ groups = FubClient::Group.all
420
+ active = FubClient::Group.active
421
+ round_robin = FubClient::Group.round_robin
422
+
423
+ # Group management
424
+ group = FubClient::Group.find(123)
425
+ members = group.members
426
+ group.add_user(456)
427
+ group.remove_user(456)
428
+ ```
429
+
430
+ #### Identity
431
+ Get current user information.
432
+
433
+ ```ruby
434
+ current = FubClient::Identity.current
435
+ user = current.user
436
+ teams = current.teams
437
+
438
+ # Check permissions
439
+ has_permission = current.has_permission?('manage_deals')
440
+ ```
441
+
442
+ ### Pipelines & Stages
443
+
444
+ #### Pipeline
445
+ Manage deal pipelines.
446
+
447
+ ```ruby
448
+ # Get pipelines
449
+ pipelines = FubClient::Pipeline.all
450
+ active = FubClient::Pipeline.active
451
+ inactive = FubClient::Pipeline.inactive
452
+
453
+ # Pipeline details
454
+ pipeline = FubClient::Pipeline.find(123)
455
+ stages = pipeline.stages
456
+ deals = pipeline.deals
457
+ stats = pipeline.stats
458
+
459
+ # Reorder pipeline
460
+ pipeline.move_to_position(2)
461
+
462
+ # Update stages
463
+ pipeline.update_stages([
464
+ { id: 1, name: 'Lead', position: 1 },
465
+ { id: 2, name: 'Qualified', position: 2 }
466
+ ])
467
+ ```
468
+
469
+ #### Stage
470
+ Manage pipeline stages.
471
+
472
+ ```ruby
473
+ # Get stages
474
+ stages = FubClient::Stage.all
475
+ active = FubClient::Stage.active
476
+ inactive = FubClient::Stage.inactive
477
+
478
+ # Stage details
479
+ stage = FubClient::Stage.find(123)
480
+ deals = stage.deals
481
+ count = stage.deal_count
482
+
483
+ # Reorder stage
484
+ stage.move_to_position(3)
485
+ ```
486
+
487
+ ### Attachments & Files
488
+
489
+ #### PersonAttachment
490
+ Manage person attachments.
491
+
492
+ ```ruby
493
+ # Upload attachment
494
+ attachment = FubClient::PersonAttachment.upload(
495
+ person_id: 123,
496
+ file_path: '/path/to/file.pdf',
497
+ name: 'Contract',
498
+ type: 'application/pdf',
499
+ description: 'Signed contract'
500
+ )
501
+
502
+ # Download attachment
503
+ attachment = FubClient::PersonAttachment.find(123)
504
+ file_data = attachment.download
505
+
506
+ # Get person
507
+ person = attachment.person
508
+
509
+ # Delete attachment
510
+ attachment.delete
511
+ ```
512
+
513
+ #### DealAttachment
514
+ Manage deal attachments.
515
+
516
+ ```ruby
517
+ # Upload attachment
518
+ attachment = FubClient::DealAttachment.upload(
519
+ deal_id: 123,
520
+ file_path: '/path/to/document.pdf',
521
+ name: 'Purchase Agreement',
522
+ type: 'application/pdf'
523
+ )
524
+
525
+ # Download and delete
526
+ file_data = attachment.download
527
+ attachment.delete
528
+
529
+ # Get associated deal
530
+ deal = attachment.deal
531
+ ```
532
+
533
+ ### Custom Fields
534
+
535
+ #### CustomField
536
+ Manage custom field definitions.
537
+
538
+ ```ruby
539
+ fields = FubClient::CustomField.all
540
+ field = FubClient::CustomField.find(123)
541
+ ```
542
+
543
+ #### DealCustomField
544
+ Manage deal-specific custom fields.
545
+
546
+ ```ruby
547
+ # Get custom fields
548
+ active = FubClient::DealCustomField.active
549
+ inactive = FubClient::DealCustomField.inactive
550
+
551
+ # Update custom field
552
+ field = FubClient::DealCustomField.find(123)
553
+ field.update(name: 'Updated Field Name', required: true)
554
+
555
+ # Delete custom field
556
+ field.delete
557
+ ```
558
+
559
+ ### Inboxes
560
+
561
+ #### SharedInbox
562
+ Manage shared team inboxes (requires cookie authentication).
563
+
564
+ ```ruby
565
+ # Setup cookie authentication first
566
+ cookie_client = FubClient::CookieClient.new(
567
+ subdomain: 'your_subdomain',
568
+ gist_url: 'your_gist_url',
569
+ encryption_key: 'your_key'
570
+ )
571
+
572
+ # Get all shared inboxes
573
+ inboxes = FubClient::SharedInbox.all_inboxes
574
+
575
+ # Get specific inbox
576
+ inbox = FubClient::SharedInbox.get_inbox(123)
577
+
578
+ # Inbox operations
579
+ messages = inbox.messages(limit: 10, offset: 0)
580
+ conversations = inbox.conversations(limit: 10, offset: 0)
581
+ settings = inbox.settings
582
+
583
+ # Update settings
584
+ inbox.update_settings({
585
+ auto_assign: true,
586
+ notification_email: 'team@example.com'
587
+ })
588
+ ```
589
+
590
+ #### TeamInbox
591
+ Manage team-specific inboxes.
592
+
593
+ ```ruby
594
+ # Get all team inboxes
595
+ inboxes = FubClient::TeamInbox.all_inboxes
596
+
597
+ # Inbox operations
598
+ inbox = FubClient::TeamInbox.find(123)
599
+ team = inbox.team
600
+ messages = inbox.messages(limit: 10)
601
+
602
+ # Conversation management
603
+ participants = inbox.participants(conversation_id: 456)
604
+ inbox.add_message(
605
+ conversation_id: 456,
606
+ content: 'Thanks for your inquiry!',
607
+ user_id: 789
608
+ )
609
+ ```
610
+
611
+ ### Integration & Webhooks
612
+
613
+ #### Webhook
614
+ Manage webhooks for real-time notifications.
615
+
616
+ ```ruby
617
+ # Get webhooks
618
+ webhooks = FubClient::Webhook.all
619
+ active = FubClient::Webhook.active
620
+ inactive = FubClient::Webhook.inactive
621
+
622
+ # Webhook management
623
+ webhook = FubClient::Webhook.find(123)
624
+ events = webhook.events(limit: 10)
625
+
626
+ # Control webhook status
627
+ webhook.activate
628
+ webhook.deactivate
629
+ webhook.test # Send test event
630
+ ```
631
+
632
+ ### Smart Lists
633
+
634
+ #### SmartList
635
+ Manage dynamic contact lists.
636
+
637
+ ```ruby
638
+ lists = FubClient::SmartList.all
639
+ list = FubClient::SmartList.find(123)
640
+ ```
641
+
642
+ ## Advanced Features
643
+
644
+ ### Rails 8 Compatibility
645
+
646
+ FubClient includes automatic compatibility patches for Rails 8:
647
+
648
+ - ActiveSupport::BasicObject compatibility
649
+ - Her gem middleware compatibility
650
+ - JSON parsing compatibility
651
+
652
+ No additional configuration required - patches are applied automatically.
653
+
654
+ ### Pagination and Bulk Operations
655
+
656
+ ```ruby
657
+ # Pagination with Her::Model::Relation
658
+ people = FubClient::Person.by_page(1, 50)
659
+ people.each { |person| puts person.name }
660
+
661
+ # Safe operations (with error handling)
662
+ people = FubClient::Person.safe_all
663
+ total = FubClient::Person.total
664
+
665
+ # Method chaining
666
+ active_deals = FubClient::Deal.active.where(stage: 'qualified')
667
+ ```
668
+
669
+ ### Batch Processing
670
+
671
+ ```ruby
672
+ # Process large datasets efficiently
673
+ FubClient::Person.by_page(1, 100).each do |person|
674
+ # Process each person
675
+ puts "Processing #{person.name}"
676
+ end
677
+ ```
678
+
679
+ ## Error Handling
680
+
681
+ ```ruby
682
+ begin
683
+ person = FubClient::Person.find(123)
684
+ rescue Her::Errors::ResourceNotFound
685
+ puts "Person not found"
686
+ rescue Her::Errors::TimeoutError
687
+ puts "Request timed out"
688
+ rescue StandardError => e
689
+ puts "Error: #{e.message}"
690
+ end
691
+
692
+ # Safe operations
693
+ people = FubClient::Person.safe_all
694
+ if people.nil?
695
+ puts "Failed to fetch people"
696
+ else
697
+ puts "Found #{people.count} people"
698
+ end
699
+ ```
700
+
701
+ ## Security Considerations
702
+
703
+ ### API Key Security
704
+
705
+ - **Never commit API keys to version control**
706
+ - Use environment variables or secure credential storage
707
+ - Rotate API keys regularly
708
+ - Limit API key permissions to minimum required
709
+
710
+ ### Cookie Authentication Security
711
+
712
+ - Cookie authentication is required for SharedInbox features
713
+ - Cookies are encrypted and stored securely
714
+ - Use HTTPS in production environments
715
+ - Implement proper session management
716
+
717
+ ### Best Practices
718
+
719
+ ```ruby
720
+ # ✅ Good - Use environment variables
721
+ FubClient.configure do |config|
722
+ config.api_key = ENV['FUB_API_KEY']
723
+ config.subdomain = ENV['FUB_SUBDOMAIN']
724
+ end
725
+
726
+ # ❌ Bad - Hard-coded credentials
727
+ FubClient.configure do |config|
728
+ config.api_key = 'hardcoded_key' # Never do this!
729
+ end
730
+ ```
731
+
732
+ ## Troubleshooting
733
+
734
+ ### Common Issues
735
+
736
+ #### Authentication Errors
737
+ ```ruby
738
+ # Check configuration
739
+ config = FubClient.configuration
740
+ puts config.auth_summary
741
+
742
+ # Verify API key
743
+ puts "API Key configured: #{config.has_api_key_auth?}"
744
+ puts "Cookie Auth configured: #{config.has_cookie_auth?}"
745
+ ```
746
+
747
+ #### Connection Issues
748
+ ```ruby
749
+ # Test basic connectivity
750
+ begin
751
+ FubClient::User.all
752
+ puts "Connection successful"
753
+ rescue => e
754
+ puts "Connection failed: #{e.message}"
755
+ end
756
+ ```
757
+
758
+ #### SharedInbox Issues
759
+ ```ruby
760
+ # Verify cookie authentication
761
+ cookie_client = FubClient::CookieClient.new(
762
+ subdomain: ENV['FUB_SUBDOMAIN'],
763
+ gist_url: ENV['FUB_GIST_URL'],
764
+ encryption_key: ENV['FUB_ENCRYPTION_KEY']
765
+ )
766
+
767
+ if cookie_client.cookies
768
+ puts "Cookie authentication successful"
769
+ else
770
+ puts "Cookie authentication failed"
771
+ end
772
+ ```
773
+
774
+ ### Debug Mode
775
+
776
+ Enable debug output:
777
+
778
+ ```ruby
779
+ ENV['DEBUG'] = 'true'
780
+ # Now all requests will show detailed debug information
781
+ ```
782
+
783
+ ### Rate Limiting
784
+
785
+ Follow Up Boss API has rate limits. Implement proper retry logic:
786
+
787
+ ```ruby
788
+ def with_retry(max_retries: 3)
789
+ retries = 0
790
+ begin
791
+ yield
792
+ rescue Her::Errors::TooManyRequests => e
793
+ retries += 1
794
+ if retries <= max_retries
795
+ sleep(2 ** retries) # Exponential backoff
796
+ retry
797
+ else
798
+ raise e
799
+ end
800
+ end
801
+ end
802
+
803
+ # Usage
804
+ with_retry do
805
+ people = FubClient::Person.all
806
+ end
807
+ ```
808
+
809
+ ## Development
810
+
811
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests.
812
+
813
+ ```bash
814
+ # Setup development environment
815
+ git clone https://github.com/connorgallopo/followupboss_client.git
816
+ cd followupboss_client
817
+ bin/setup
818
+
819
+ # Run tests
820
+ rake spec
821
+
822
+ # Interactive console
823
+ bin/console
824
+
825
+ # Install locally
826
+ bundle exec rake install
827
+ ```
828
+
829
+ ### Running Examples
830
+
831
+ Check out the example scripts:
832
+
833
+ ```bash
834
+ # API key authentication example
835
+ ruby scripts/test_api.rb
836
+
837
+ # SharedInbox with cookie authentication
838
+ ruby scripts/test_shared_inbox.rb
839
+ ```
840
+
841
+ ## Contributing
842
+
843
+ We welcome contributions! Please follow these steps:
844
+
845
+ 1. Fork the repository
846
+ 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
847
+ 3. Make your changes
848
+ 4. Add tests for new functionality
849
+ 5. Ensure all tests pass (`rake spec`)
850
+ 6. Commit your changes (`git commit -am 'Add amazing feature'`)
851
+ 7. Push to the branch (`git push origin feature/amazing-feature`)
852
+ 8. Open a Pull Request
853
+
854
+ ### Code Style
855
+
856
+ - Follow Ruby community style guidelines
857
+ - Add documentation for new methods
858
+ - Include examples in documentation
859
+ - Maintain backward compatibility when possible
860
+
861
+ ### Security
862
+
863
+ - Never commit sensitive credentials
864
+ - Follow secure coding practices
865
+ - Report security issues privately to maintainers
866
+
867
+ ## License
868
+
869
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
870
+
871
+ ---
872
+
873
+ ## Links
874
+
875
+ - [Follow Up Boss API Documentation](https://api.followupboss.com/api-documentation/)
876
+ - [Follow Up Boss Website](https://www.followupboss.com)
877
+ - [GitHub Repository](https://github.com/connorgallopo/followupboss_client)
878
+ - [Issue Tracker](https://github.com/connorgallopo/followupboss_client/issues)
879
+
880
+ ---
881
+
882
+ **Made with ❤️ for the real estate community**