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.
- checksums.yaml +7 -0
- data/.env.example +12 -0
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.tool-versions +1 -0
- data/.travis.yml +9 -0
- data/CHANGELOG.md +149 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +882 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/followupboss_client.gemspec +43 -0
- data/lib/followupboss_client.rb +16 -0
- data/lib/fub_client/action_plan.rb +5 -0
- data/lib/fub_client/appointment.rb +42 -0
- data/lib/fub_client/appointment_outcome.rb +51 -0
- data/lib/fub_client/appointment_type.rb +51 -0
- data/lib/fub_client/call.rb +4 -0
- data/lib/fub_client/client.rb +200 -0
- data/lib/fub_client/compatibility.rb +18 -0
- data/lib/fub_client/configuration.rb +54 -0
- data/lib/fub_client/cookie_client.rb +190 -0
- data/lib/fub_client/custom_field.rb +5 -0
- data/lib/fub_client/deal.rb +41 -0
- data/lib/fub_client/deal_attachment.rb +61 -0
- data/lib/fub_client/deal_custom_field.rb +47 -0
- data/lib/fub_client/em_event.rb +5 -0
- data/lib/fub_client/email_template.rb +5 -0
- data/lib/fub_client/event.rb +8 -0
- data/lib/fub_client/group.rb +58 -0
- data/lib/fub_client/her_patch.rb +101 -0
- data/lib/fub_client/identity.rb +33 -0
- data/lib/fub_client/message.rb +41 -0
- data/lib/fub_client/middleware/authentication.rb +26 -0
- data/lib/fub_client/middleware/cookie_authentication.rb +61 -0
- data/lib/fub_client/middleware/parser.rb +59 -0
- data/lib/fub_client/middleware.rb +8 -0
- data/lib/fub_client/note.rb +4 -0
- data/lib/fub_client/people_relationship.rb +34 -0
- data/lib/fub_client/person.rb +5 -0
- data/lib/fub_client/person_attachment.rb +50 -0
- data/lib/fub_client/pipeline.rb +45 -0
- data/lib/fub_client/property.rb +26 -0
- data/lib/fub_client/rails8_patch.rb +39 -0
- data/lib/fub_client/resource.rb +33 -0
- data/lib/fub_client/shared_inbox.rb +389 -0
- data/lib/fub_client/smart_list.rb +5 -0
- data/lib/fub_client/stage.rb +39 -0
- data/lib/fub_client/task.rb +18 -0
- data/lib/fub_client/team.rb +65 -0
- data/lib/fub_client/team_inbox.rb +65 -0
- data/lib/fub_client/text_message.rb +46 -0
- data/lib/fub_client/text_message_template.rb +49 -0
- data/lib/fub_client/user.rb +4 -0
- data/lib/fub_client/version.rb +3 -0
- data/lib/fub_client/webhook.rb +47 -0
- data/lib/fub_client.rb +61 -0
- data/scripts/test_api.rb +110 -0
- data/scripts/test_shared_inbox.rb +90 -0
- metadata +335 -0
data/README.md
ADDED
|
@@ -0,0 +1,882 @@
|
|
|
1
|
+
# FollowUpBoss Client
|
|
2
|
+
|
|
3
|
+
[](https://www.ruby-lang.org/)
|
|
4
|
+
[](https://rubyonrails.org/)
|
|
5
|
+
[](LICENSE.txt)
|
|
6
|
+
[](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**
|