Parsing JSON Responses in the Follow Up Boss API
The Follow Up Boss API returns standard JSON objects. List endpoints wrap results in a top-level array key (e.g., people) alongside a _metadata object for pagination. Single-resource calls return the object directly. Custom fields — including Ace Score fields written by Follow Up Ace — appear as named properties such as customAceScore and customAceTier on the contact object.
How the Follow Up Boss REST API returns data
Follow Up Boss exposes a REST API at https://api.followupboss.com/v1/. Every response body is JSON. The content type is always application/json. There is no XML option, no GraphQL endpoint, and no streaming format — just straightforward request/response JSON over HTTPS.
The response shape depends on whether you are fetching a collection or a single resource:
- Collection endpoints (e.g.,
GET /v1/people) return a JSON object with a named array key matching the resource type, plus a_metadataobject containing pagination details. - Single-resource endpoints (e.g.,
GET /v1/people/{id}) return the contact object directly at the root level, with no wrapping array. - Write responses (POST, PUT) return the created or updated resource, same shape as the single-resource GET.
- Error responses return a JSON body with an
errorCodeandmessagefield alongside the HTTP status code.
Authentication before you can parse anything
The API uses HTTP Basic Authentication. Your API key is the username; the password field is left blank. Every request must include an Authorization header or the server returns 401 before you even see a JSON body.
# Example: Base64-encode API_KEY: (note the trailing colon, no password)
# Then set the Authorization header on every request:
Authorization: Basic <base64(API_KEY:)>
Content-Type: application/json
Most HTTP libraries handle the base64 encoding automatically when you supply a username and empty password. Never embed the raw API key in client-side code or commit it to source control — store it in an environment variable.
JSON response structure for contacts and leads
The /v1/people endpoint is the primary resource for contacts and leads. A collection response looks like this (illustrative example — field names and shape match FUB's public documentation):
{
"people": [
{
"id": 12345,
"firstName": "Jordan",
"lastName": "Rivera",
"stage": "New Lead",
"source": "Website",
"created": "2026-06-01T09:22:00Z",
"updated": "2026-06-20T14:05:00Z",
"emails": [
{ "value": "[email protected]", "type": "home" }
],
"phones": [
{ "value": "5551234567", "type": "mobile" }
],
"customAceScore": 74,
"customAceTier": "Warm",
"customAceStatus": "Engaged",
"customAceResponseTime": 8,
"customAceVelocityScore": 61,
"customAceDaysSinceInbound": 3,
"customAcePreferredChannel": "sms"
}
],
"_metadata": {
"total": 842,
"limit": 25,
"offset": 0
}
}
Key fields to know when parsing contact objects:
| Field | Type | Notes |
|---|---|---|
id |
integer | Stable unique identifier; use this for updates and lookups |
firstName / lastName |
string | May be null if the contact was created from a phone number only |
emails / phones |
array of objects | Each item has value and type; a contact may have multiple entries |
stage |
string | Pipeline stage label; values depend on your FUB configuration |
source |
string | Lead origin; drives action plan assignment in FUB |
created / updated |
ISO 8601 string | UTC timestamps; parse with your language's date library |
custom* |
varies | Custom fields appear as top-level properties with their configured name |
Handling pagination in Follow Up Boss API responses
All list endpoints are paginated. The _metadata object in every collection response tells you how many total records exist and where your current page falls. Use limit and offset query parameters to walk through pages.
JavaScript pagination example
// Illustrative example — walks all contacts page by page
async function fetchAllPeople(apiKey) {
const BASE = 'https://api.followupboss.com/v1/people';
const auth = Buffer.from(`${apiKey}:`).toString('base64');
const headers = { Authorization: `Basic ${auth}` };
const limit = 100;
let offset = 0;
let total = Infinity;
const results = [];
while (offset < total) {
const url = `${BASE}?limit=${limit}&offset=${offset}`;
const res = await fetch(url, { headers });
if (!res.ok) throw new Error(`FUB API error: ${res.status}`);
const data = await res.json();
total = data._metadata.total;
results.push(...data.people);
offset += limit;
}
return results;
}
Python pagination example
# Illustrative example using the requests library
import requests, os
def fetch_all_people(api_key):
session = requests.Session()
session.auth = (api_key, '') # HTTP Basic Auth — password blank
url = 'https://api.followupboss.com/v1/people'
limit = 100
offset = 0
results = []
while True:
resp = session.get(url, params={'limit': limit, 'offset': offset})
resp.raise_for_status()
data = resp.json()
results.extend(data['people'])
total = data['_metadata']['total']
offset += limit
if offset >= total:
break
return results
A few practical notes on pagination:
- The common maximum
limitis 100 per page — check the docs for resource-specific limits. - Do not parallelize page fetches aggressively; the API rate limit applies per key, and concurrent requests exhaust it quickly on large datasets.
- For real-time sync, prefer webhooks (below) over polling every page of contacts on a timer.
Webhook JSON payloads from Follow Up Boss
Webhooks deliver event data to your endpoint as an HTTP POST with a JSON body the moment something changes in FUB — a contact is created, a stage transitions, a note is added. This is the right pattern for any integration that needs to react in near real-time rather than polling.
A personCreated webhook payload looks similar to the single-resource contact GET response, wrapped in an event envelope:
{
"event": "personCreated",
"eventId": "evt_abc123",
"uri": "https://api.followupboss.com/v1/people/12345",
"data": {
"id": 12345,
"firstName": "Jordan",
"lastName": "Rivera",
"stage": "New Lead",
"source": "Zillow",
"created": "2026-06-26T10:00:00Z"
}
}
Steps for reliable webhook processing:
- Register the webhook via
POST /v1/webhookswith yoururl,eventtype, and optionally asecretfor signature verification. - Respond with 200 immediately. FUB expects a response within a few seconds. If you do not respond in time, FUB will retry delivery. Accept the payload, enqueue it to a background job, and return 200 before doing any processing.
- Verify the signature if you configured a secret — compare the HMAC digest in the request header against your own computation of the payload body.
- Parse
datathe same way you parse a single-resource contact response — same field names, same types. - Handle duplicates using
eventId. Webhook delivery is at-least-once; occasional duplicates are normal. Store processed event IDs and skip duplicates.
Parsing custom fields in the API response
Custom fields in Follow Up Boss are extensible properties you define in your account. When you retrieve a contact via the API, custom field values appear as top-level properties on the contact object, using the field's configured name as the key.
For example, if you have a custom field named customBuyerTimeline, its value appears directly on the contact JSON at the root level:
{
"id": 12345,
"firstName": "Jordan",
"customBuyerTimeline": "3-6 months",
"customBudgetRange": "600000-800000"
}
To write a custom field value, include it by name in your POST or PUT body. To discover what custom fields exist in an account, call GET /v1/customFields — this returns the field definitions including name, type, and available choices for dropdown fields.
Ace Score fields in the FUB API response
Follow Up Ace writes AI-generated scoring data back to FUB as custom fields. If Follow Up Ace is connected to an account, these fields appear in the contact JSON response alongside any other custom fields. They are created automatically by Follow Up Ace and populated via webhook-driven analysis.
The pre-consolidation free Ace Score fields (retired June 2026 — see the update note above; the current free set is customAceWinScore + customAceChurnRisk):
| Field name in API | Type | Description |
|---|---|---|
customAceScore |
number (0–100) | Lead engagement score, updated live via webhooks |
customAceTier |
string (dropdown) | Hot, Warm, Cool, Cold, or Dormant — banded from the score |
customAceStatus |
string (dropdown) | Pipeline status: New, Contacted, Engaged, Active Client, Closed, Nurturing, or Dormant |
customAceResponseTime |
number | Minutes to first agent response after lead creation |
customAceVelocityScore |
number (0–100) | Engagement velocity derived from event volume over 7-day and 30-day windows |
customAceDaysSinceInbound |
number | Days since this contact last sent an inbound message |
customAcePreferredChannel |
string (dropdown) | sms, email, or call — the channel the contact most recently engages on |
Paid Ace Trove accounts also receive deeper intelligence fields including customAceLeadType, customAcePropertyProfile, customAceSearchArea, customAceBuyerReadiness, customAceLeadSummary, customAceNextAction, and the seller propensity tier field (customAceSellerTier). The underlying 0–100 Seller Score itself lives in the Seller Radar view rather than a FUB custom field.
Reading them in JavaScript is no different from reading any other field:
// After fetching a contact object from GET /v1/people/{id}:
const contact = await fetchContact(id);
const aceScore = contact.customAceScore; // number, e.g. 74
const aceTier = contact.customAceTier; // string, e.g. "Warm"
const aceDays = contact.customAceDaysSinceInbound; // number, e.g. 3
const aceChannel = contact.customAcePreferredChannel; // string, e.g. "sms"
// Null-safe access if the field may not be set yet:
const tier = contact.customAceTier ?? 'Unknown';
Using an AI connector instead of raw JSON parsing
If the goal is natural-language access to contact data rather than a deterministic data pipeline, parsing raw JSON yourself is the wrong level of abstraction. Follow Up Ace exposes a Model Context Protocol (MCP) connector at https://followupace.com/mcp that allows an AI assistant like Claude to search contacts, read scores, log notes, and trigger actions through conversation — no JSON parsing required on your end.
The Agentic connector sits on top of the same FUB API endpoints described in this article; it just handles authentication, pagination, and field mapping for you. For teams that want both layers — a reliable ETL pipeline for data sync and a conversational interface for ad-hoc queries — the two approaches complement each other well.
Common parsing pitfalls and how to avoid them
- Assuming fields are always present. Optional fields (especially custom fields and
customAceDaysSinceInbound, which is hidden when null) may be absent from the JSON object entirely. Always use null-safe access (?.fieldin JS,.get('field')in Python) rather than assuming every key exists. - Treating
emailsandphonesas strings. Both are arrays of objects. Iterate them rather than accessingcontact.emailsas a single string value. - Ignoring
_metadata.total. A single page of results is not all records. Always check whether there are more pages before treating the response as complete. - Not handling 429 rate-limit responses. If your code errors on non-200 status codes without inspecting the code, a rate limit will look identical to an authentication failure. Check
res.statusand back off with exponential delay on 429. - Parsing webhook payloads synchronously. If your webhook handler awaits a database write or external API call before returning, you risk timing out and triggering FUB retries. Enqueue, acknowledge, process asynchronously.
- Assuming ISO timestamps are in local time. All FUB timestamps are UTC. Parse them explicitly —
new Date(contact.created)in JavaScript returns a UTC-interpreted Date; formatting it in a local timezone requires explicit conversion.
Further reading
- How to use the Follow Up Boss API docs for integration
- MCP protocol: the technology making real estate AI actually useful
- Follow Up Ace Agentic — natural-language access to your CRM
- Ace Trove — automated lead scoring and intelligence fields
Try Follow Up Ace in your Follow Up Boss
Free to start, no sales call. Connect Follow Up Boss in one click and Ace works inside your CRM.
Get Started Free