How to Fix Close.io (Close) API Connection Issues in Ruby
When integrating the Close (formerly Close.io) API with Ruby, one of the most common frustrations developers face is updating Lead custom fields without accidentally deleting others or triggering confusing Ruby syntax errors.
If you have ever seen:
- Custom fields being erased after updating another one
NameError: undefined local variable or method 'custom'- Syntax errors pointing at a colon after
field_name:
This article explains exactly why it happens and provides a complete working Ruby example.
The Problem
According to Close’s documentation:
To update a single custom field without removing others, use
custom.field_name: updated_value
instead ofcustom: { all: 'fields', listed: 'here' }
But when trying to use that syntax in Ruby, you run into:
custom.field_name: some_value
Which throws a syntax error.
Or:
custom.field_name => some_value
Which throws:
NameError: undefined local variable or method `custom' for main:Object
Meanwhile, using:
custom: { kk_referral: 2.0 }
works but then updating another field removes the first one.
Understanding the Root Cause
There are two separate issues here:
Ruby Syntax vs API Syntax:
The Close API documentation shows this:
custom.field_name: value
That syntax works in JSON — not in Ruby.
Ruby does not allow dots in symbol-style hash keys like:
custom.field_name: 2.0 # Invalid Ruby
Ruby thinks you’re calling a method named custom, then calling field_name, and then adding a label — which is invalid.
That’s why you get syntax errors.
Why Updating One Field Deletes the Others
When you do this:
Closeio::Lead.update(
lead_id,
custom: { kk_referral: 2.0 }
)
Depending on the wrapper implementation, the custom object may be treated as a full replacement, not a partial patch.
So when you later run:
Closeio::Lead.update(
lead_id,
custom: { kk_blog_posts_submitted: 3.0 }
)
The API receives only that one field and the previous kk_referral value is overwritten.
That’s why it appears as if fields are being deleted.
The Correct Way to Update a Single Custom Field in Ruby
Instead of using nested custom: {} syntax, use a flat hash with a string key.
Ruby allows string keys with dots.
Correct Syntax:
Closeio::Lead.update(
lead_id,
"custom.kk_referral" => 2.0
)
That matches the API format while remaining valid Ruby.
Complete Working Example
Let’s walk through a clean, production-safe implementation.
Find the Lead:
lead = Closeio::Lead.where(
query: "email:['#{current_user.email}']"
).firstraise "Lead not found" unless leadlead_id = lead["id"]
Calculate Your Values:
referral_count = Referral.where(
user_id: current_user.id
).count.to_fcomment_count = Comment.where(
user_id: current_user.id
).count.to_f
Update Multiple Custom Fields Safely (Recommended):
Instead of making two separate API calls, send everything at once.
Closeio::Lead.update(
lead_id,
{
"custom.kk_referral" => referral_count,
"custom.kk_blog_posts_submitted" => comment_count
}
)
This:
- Prevents overwriting issues
- Reduces API calls
- Avoids race conditions
If You Must Update Fields Separately
This also works safely:
Closeio::Lead.update(
lead_id,
"custom.kk_referral" => referral_count
)Closeio::Lead.update(
lead_id,
"custom.kk_blog_posts_submitted" => comment_count
)
The key is:
Use
"custom.FIELD_NAME"as a STRING key not nested insidecustom: {}
Important Close Now Recommends Using Field IDs
Close’s newer API versions prefer:
custom.cf_abc123XYZ
Instead of:
custom.kk_referral
Example:
Closeio::Lead.update(
lead_id,
"custom.cf_v6S011I6MqcbVvB2FA5Nk8dr5MkL8sWuCiG8cUleO9c" => referral_count
)
Using field IDs:
- Avoids problems if field names change
- Is future-proof
- Matches the latest API recommendations
Why Your Earlier Attempts Failed
This fails (invalid Ruby):
custom.kk_blog_posts_submitted: value
Reason: Ruby does not allow dotted keys in label syntax.
This fails (NameError):
custom.kk_blog_posts_submitted => value
Reason: Ruby thinks custom is a variable or method which doesn’t exist.
This may overwrite fields:
custom: { kk_blog_posts_submitted: value }
Reason: Some wrappers treat custom as a full object replacement.
This works:
"custom.kk_blog_posts_submitted" => value
Final Best-Practice Version (Production Ready)
class CloseLeadUpdater
def self.sync_user_stats(user)
lead = Closeio::Lead.where(
query: "email:['#{user.email}']"
).first return unless lead lead_id = lead["id"] referral_count = Referral.where(user_id: user.id).count.to_f
comment_count = Comment.where(user_id: user.id).count.to_f Closeio::Lead.update(
lead_id,
{
"custom.kk_referral" => referral_count,
"custom.kk_blog_posts_submitted" => comment_count
}
)
end
end
Clean. Safe. Predictable.
| Problem | Cause | Solution |
|---|---|---|
Syntax error after field_name: | Ruby doesn’t allow dotted label keys | Use string keys |
undefined local variable custom | Ruby thinks custom is a metho | Use "custom.field" as string |
| Other custom fields erased | Nested custom: {} replaces object | Use flat "custom.field" keys |
| Want future-proof integration | Field names deprecated | Use custom.cf_FIELDID |
Final Takeaway
When working with Close’s API in Ruby:
Always use flat string keys like
"custom.field_name" => value
Never try to directly copy JSON-style dotted syntax into Ruby hash labels.