# API Report Card: Follow Up Boss (FUB) REST API v1

Reconciled result of three grading runs on one frozen evidence packet. Run 1 performed discovery and the live read-path battery; runs 2 and 3 were independent graders working only from the frozen packet. Check-level marks were compared and every disagreement was resolved against the packet (see "Independent grading runs and reconciliation").

## Run metadata
- Methodology version: 1.1
- Evaluating model: Claude Fable 5.1 (run 1: discovery, live battery and reconciliation; runs 2 and 3: independent grader instances of the same model, each with a fresh copy of the prompt and no access to the other runs' marks)
- Date run: 2026-09-22
- Provisional evidence-packet version or ID: v1 (frozen 2026-09-22 16:45 UTC; 222 first-party sources; `evidence/02-manifest-v1.md`)
- Final evidence-packet version or ID: v2 (frozen 2026-09-22 17:05 UTC; v1 plus the amendment log below; `evidence/03-amendment-log-and-final-packet-v2.md`). Runs 2 and 3 graded v2 unchanged.
- Evidence-discovery mode: tool-enabled discovery (run 1); supplied final packet (runs 2 and 3)
- Evidence tier: baseline verified
- Live-write method and safety: none — writes documentation-graded. FUB offers no sandbox mode, and controlled live-data write testing was not authorized in writing for this session, so no create, update, delete, or webhook registration was performed.
- Minimum live-test battery: steps 1–5 complete against the operator's production account using the supplied user API key (admin/broker-role user, HTTP Basic). Step 6 not run (no sandbox; no recorded write authorization). Step 7 N-A (the API documents no idempotency-key mechanism). Step 8 not run (registering a webhook is a write that also requires the account-owner key, a registered `X-System`/`X-System-Key` pair, and an operator-controlled HTTPS endpoint).
- Live tests performed (all read-only, 2026-09-22 16:35–16:42 UTC, `https://api.followupboss.com/v1/...`; full log in `evidence/live-headers/LIVE-TEST-LOG.md`): `GET /me`, `GET /identity` (auth); `GET /people?limit=3&fields=...` page 1, page 2 via `next`, and via `offset=3`; `GET /people/{id}?fields=allFields`; `GET /deals`, `/pipelines`, `/stages`, `/customFields`, `/actionPlans`, `/smartLists`, `/automations` (403), `/webhooks` (400 without `system`, 200 with `?system=`); incremental filters `updatedAfter`, `updatedAfter`+`updatedBefore`, `stage=Lead`, `sort=-updated` on `/people`, `updatedAfter` on `/deals`; deliberate errors `GET /people/999999999` (404), `GET /notARealResource` (404), invalid key (401), `limit=abc` (200), `limit=101` (200, clamped to 100), `updatedAfter=notadate` (200); response-header capture on every call.
- Live tests not possible: step 6 (create/update a core resource), step 8 (register and trigger a webhook)
- Documentation-graded checks (baseline verified): C1.2, C1.3, C2.4, C2.8
- Independent grading runs: 3 (run 1 = 84/B; run 2 = 78/C+; run 3 = 77/C+; reconciled = 79/C+). Runs 2 and 3 are saved as `runs/run2-independent-grader.md` and `runs/run3-independent-grader.md`; the check-level comparison is `evidence/04-run-comparison.md`.

## Final evidence packet manifest
Docs domain (`https://docs.followupboss.com`; every page also retrieved as Markdown by appending `.md`):
- `/llms.txt` (documentation index) and `/openapi/58b53c341065f9c438aa1f7e` (vendor OpenAPI 3.1 JSON, 156 operations)
- `/reference/getting-started`, `/reference/fub-api-tou`, `/reference/identification`, `/reference/authentication`, `/reference/requests-and-responses`, `/reference/error-responses`, `/reference/searching`, `/reference/pagination`, `/reference/rate-limiting`, `/reference/common-filters`, `/reference/webhooks-guide`, `/reference/common-issues`, `/reference/api-change-requests`, `/reference/support-and-help`, `/reference/send-in-a-lead`, `/reference/embedded-apps`, `/reference/idx-integration`, `/reference/merge-fields`, `/reference/follow-up-bot`
- `/docs/start-here-brand-new-integration`, `/docs/lead-provider-integration-guide`, `/docs/email-marketing-integration-guide`, `/docs/getting-started-with-oauth`, `/docs/oauth-authentication-and-authorization`, `/docs/oauth-token-lifetimes`, `/docs/oauth-token-exchange-response`, `/docs/oauth-error-codes`, the seven `/docs/inbox-apps-*` pages
- All 156 endpoint reference pages listed in `llms.txt` (people, events, deals, pipelines, stages, customFields, dealCustomFields, tasks, notes, calls, textMessages, appointments, appointmentTypes/Outcomes, actionPlans, actionPlansPeople, automations, automationsPeople, webhooks, webhookEvents, users, teams, groups, ponds, smartLists, templates, textMessageTemplates, emEvents, emCampaigns, peopleRelationships, personAttachments, dealAttachments, reactions, threadedReplies, timeframes, identity, me, rateLimit/usage, rateLimit/limits, inboxApps). Full URL list: `evidence/02-manifest-v1.md`.
Other first-party:
- `https://www.followupboss.com/` (home and navigation), `/pricing`, `/pro`, `/security`, `/integrations`, `/legal-pages/terms-of-service`, `/legal-pages/acceptable-use-policy`, `/sitemap.xml`
- `https://apps.followupboss.com/system-registration` (registered-system form)
- Help center (retrieved through the public Zendesk article API because the HTML pages block non-browser clients): `API Key` (360014289393), `Power-Up: API Key Restrictions` (15512708762135), `Follow Up Boss Open API` (7787906777751), `Follow Up Boss Plan Breakdown` (4414062754071), `All Property Management` (360034680274), `Automations Overview` (360048951553), `Automations 2.0 Migration` (33217901299095), `Automations 2.0 Overview` (33056241868311), `Users, Roles & Permissions` (4402370636567)
- `https://followupboss.statuspage.io/` plus `/history`, `/uptime`, `/api/v2/summary.json`, `/api/v2/incidents.json`, `/api/v2/components.json`
- `https://updates.followupboss.com/en` (product news feed and changelog)
- `https://github.com/FollowUpBoss/fub-api-examples` (README) and the FollowUpBoss GitHub organization repository list (`api.github.com/orgs/FollowUpBoss/repos`)
- Live read-only battery against `https://api.followupboss.com/v1` with the operator-supplied key (headers and status codes preserved in `evidence/live-headers/`; response bodies containing contact data were not retained)

## Evidence-amendment log
Controlled verification pass (run 1) run once for every check marked no or partial. No mark changed.
- C3.2: added help article `Users, Roles & Permissions` (confirms role-based access only; no key-level scopes).
- C1.2 / C1.4: added help article `Automations 2.0 Overview` (owner/admin create automations; triggers are Stage Change, Tag Added, Deal Stage Change, Appointment).
- C2.12: added the status page's public `incidents.json` and `components.json` (50 incidents listed; "API" is a monitored component).
- C4.4: attempted the updates feed's RSS/Atom endpoints (`/en/rss`, `/rss`, `/en/feed` return HTML; `*.xml` return 500); the feed is Beamer-hosted and rendered client-side.
- C4.3: third retry of the 19 documentation pages whose `.md` URL returns the HTML application shell; all 19 still failed.
- Targeted first-party domain searches (followupboss.com) run once each for C1.4, C2.2, C2.3, C2.4, C2.7, C2.9, C2.10, C2.11, C3.1/C3.2 and C4.4 surfaced no further sources.
Runs 2 and 3 added no sources (supplied final packet).

## API eligibility
- Qualifying API: yes
- API operator: Follow Up Boss, LLC ("FUB") makes the FUB API available [`/reference/fub-api-tou`, preamble]; the API "is the property of MFTB HoldCo Inc." [same, §9]; the platform Terms of Service are Zillow's and govern API use ("Zillow may suspend or terminate your access to the FUB API") [`/legal-pages/terms-of-service` §4.5]
- Access or credential issuer: user API keys are generated inside the customer's own account at Admin > API [`/reference/authentication`, "Basic Authentication"; help `API Key`]; partner system credentials (`X-System`, `X-System-Key`) are issued through the self-serve registration form [`/reference/identification`; `apps.followupboss.com/system-registration`]; OAuth client apps are created by registered systems via `POST /v1/oauthApps` and grants are revoked with `DELETE /v1/oauthApps/revokeAccess` [`/docs/getting-started-with-oauth`; `/docs/oauth-authentication-and-authorization`, Step 6]
- Eligibility basis: a first-party REST API at `https://api.followupboss.com/v1` documented with 156 operations in the vendor's OpenAPI file [`/reference/getting-started`, "API Endpoint"; `/openapi/58b53c341065f9c438aa1f7e`]; live authentication with the supplied key succeeded on 2026-09-22 (`GET /v1/me` → 200, `GET /v1/identity` → 200 returning the account and user identity)

## Context
- Software category: workflow or CRM tool
- What the API is for and its core objects and workflows: Follow Up Boss is a real-estate lead-management CRM, and its API exposes contacts ("people"), lead events, deals, pipelines and stages, tasks, notes, calls, texts, appointments, custom fields, action-plan and Automations 2.0 enrollment, users, teams, groups, ponds, smart lists, and webhooks. For this category the core objects are records (people and deals), automations/triggers, pipelines, and custom fields, and the core workflows are creating or updating records and firing or receiving triggers.

## Provider and property-management fit
- What this product is: a hosted CRM for real-estate agents and teams that pulls leads in from 200+ sources and automates follow-up ("The Real Estate Team OS") [`www.followupboss.com/` navigation; `/pricing`]
- Bank status, when relevant: N-A (software product; holds no customer funds) [`/pricing`; `/legal-pages/terms-of-service`]
- Who provides any bank account or regulated banking service: N-A
- What the customer actually receives: a subscription to the FUB CRM web and mobile apps (Grow $69 per user per month, Pro $499 per month, Platform $1,000 per month; unlimited contacts, lead sources and integrations; 14-day free trial, no contract) [`/pricing`]
- Property-management fit: general-purpose. The site, navigation and documentation target real-estate sales teams (team leads, ISAs, agents); the only "property management" material is a lead-provider integration page for the "All Property Management" referral site, which is a lead source rather than a PM workflow [`www.followupboss.com/` "Who we serve"; help `All Property Management`; `/integrations`]
- Documented PM-specific workflows: none found
- Trust or fiduciary workflow support, when relevant: N-A (the product does not hold, move or account for funds)
- Operational role and dependencies: a contact and lead CRM that would sit beside, not replace, a property-management system, accounting/trust ledger, and maintenance tooling; PM concepts (owners, tenants, units, leases) would have to be modeled with custom fields, tags, stages and deal pipelines.

## Coverage classification (fixed before inspection)
Recorded at 2026-09-22 16:30 UTC before any documentation was opened (`evidence/00-coverage-classification-FIXED.md`). Default Workflow/CRM classification; no deviation. Used unchanged by all three runs.

| Object or workflow | Class | Weight | Present / read-only / absent |
|---|---|---|---|
| Records (people; deals) | critical | 3 | present (list, get, create, update, delete) |
| Automations / triggers (action plans; Automations 2.0) | critical | 3 | present for enrollment and control (list; enroll/trigger; pause/resume); definitions are read-only and UI-managed; Automations 2.0 routes require a registered system and a migrated account |
| Boards / pipelines (pipelines, pipeline stages, people stages) | important | 2 | present (create, update, delete; owner-only for pipelines) |
| Custom fields (people and deal) | important | 2 | present (create, update, delete; owner-only writes) |
| Reporting / exports | optional | 1 | partial (no reporting endpoints; export only by paging list endpoints; smart lists readable) |
| Workflow: create/update records | critical | 3 | present (`POST/PUT /people`, `POST /events`, `POST/PUT /deals`) |
| Workflow: fire and receive triggers | critical | 3 | present (`POST /events`, `POST /actionPlansPeople`, `POST /automationsPeople`; webhooks) |
| Workflow: manage pipelines/stages | important | 2 | present |
| Workflow: manage custom fields | important | 2 | present |
| Lifecycle: records (delete/archive; stage change) | critical | 3 | present (`DELETE /people/{id}`; `stage`/`stageId` on `PUT`; Trash stage) |
| Lifecycle: automations (enroll, unenroll, pause, resume) | critical | 3 | partial (enroll, pause and resume present; no unenroll/remove operation) |
| Lifecycle: pipelines (move stage, close, delete) | important | 2 | present (`stageId` on `PUT /deals/{id}`; `closedStage` stages; `DELETE /deals`, `/stages`, `/pipelines`) |
| Lifecycle: custom fields (delete) | important | 2 | present (`DELETE /customFields/{id}`) |
| Lifecycle: reporting/exports | optional | — | N-A |

## Functional coverage map
- Core objects: records present (weight 3); automations/triggers present for enrollment and control, read-only for definitions (weight 3); pipelines present (weight 2); custom fields present (weight 2); reporting/exports partial (weight 1).
- Primary operational workflows: create/update records; fire and receive triggers; manage pipelines and stages; manage custom fields.
- Principal lifecycle changes: delete or trash a person; change a person's stage; pause/resume an action plan or automation for a person (no unenroll); move a deal between stages or into a closed stage; delete a deal, stage or pipeline; delete a custom field.

## Category 1: Functional Coverage and Usefulness: 11.3/15
- C1.1 Object coverage: **partial** — weighted coverage = 82% (9.0 of 11). Records 1.0×3: `GET/POST /people`, `GET/PUT/DELETE /people/{id}`, `GET/POST /deals`, `PUT/DELETE /deals/{id}` [OpenAPI paths; `/reference/people-post`, `/reference/people-id-put`, `/reference/deals-post`]; live `GET /v1/people` returned 2,269 records with `_metadata` and `GET /v1/deals` 156 (2026-09-22). Automations/triggers 0.5×3: enrollment and control are writable (`GET/POST /actionPlansPeople`, `PUT /actionPlansPeople/{id}` [`/reference/actionplanspeople-post`]; `GET/POST /automationsPeople`, `PUT /automationsPeople/{id}` [`/reference/automationspeople-1`, `/reference/automationspeopleid`]; live `GET /v1/actionPlans` → 200 with 30 plans), but automation definitions are read-only through the API (only `GET /automations` and `GET /automations/{id}` exist; no create, update or delete of a rule in the 156-operation file) and those two routes are "for customers with Automations 2.0 only" and "restricted to registered systems only" [`/reference/automations`, `/reference/automationsid`]; live `GET /v1/automations` with the operator's own key → 403 `{"errorMessage":"You do not have access to this API endpoint."}`; the legacy action-plan endpoints carry a deprecation notice with no date [`/reference/actionplanspeople-post`]. Under the check's wording (present but missing a non-critical operation) this scores 0.5; run 1 had scored 1.0 and was overruled on reconciliation. Pipelines 1.0×2: `GET/POST /pipelines`, `PUT/DELETE /pipelines/{id}` (owner only), `GET/POST /stages`, `PUT/DELETE /stages/{id}` [`/reference/pipelines-post`, `/reference/stages-post`]; live `GET /v1/pipelines` → 6, `GET /v1/stages` → 29. Custom fields 1.0×2: `GET/POST /customFields`, `PUT/DELETE /customFields/{id}` (owner only) and `/dealCustomFields` [`/reference/customfields-post`, `/reference/customfields-id-delete`]; live `GET /v1/customFields` → 25. Reporting/exports 0.5×1: no report endpoints; `GET /smartLists` is read-only and datasets are exported only by paging list endpoints [`/reference/smartlists-get`; `/reference/pagination`]. No critical object absent; 0.818 falls in the 0.50–0.84 band.
- C1.2 Core operational actions: **yes** (documentation-graded) — weighted coverage = 100% (10 of 10 over the mutable items; 91% if the non-mutable reporting/exports item is counted at 0, as runs 2 and 3 did; same mark either way). Create/update records: `POST /people` and `PUT /people/{id}` (`firstName`, `stage`, `tags`, `emails`, `custom*` fields) [`/reference/people-post`; OpenAPI `PUT /people/{id}`], `POST /events` for lead intake with dedupe and automation triggering [`/reference/events-post`, "Sending Leads and Activity"], `POST /deals`, `PUT /deals/{id}` [`/reference/deals-post`]. Fire and receive triggers: `POST /events` with type `Registration`, `Property Inquiry`, `Seller Inquiry` or `General Inquiry` starts action plans and automations [`/reference/events-post`, "Action Plan and Automation Triggers"]; `POST /actionPlansPeople` applies a plan [`/reference/actionplanspeople-post`]; `POST /automationsPeople` "Manually trigger an Automation for a specified Person" for registered systems [`/reference/automationspeople-1`]; tag and stage changes through `PUT /people/{id}` fire Tag Added and Stage Change triggers [help `Automations 2.0 Overview`]; receiving is by webhooks [`/reference/webhooks-guide`]. Pipelines and custom fields: `POST /pipelines`, `PUT /pipelines/{id}`, `POST /stages`, `POST /customFields`, `PUT /customFields/{id}` [OpenAPI]. Limitations disclosed: `POST /actionPlansPeople` is "planned for deprecation as part of the Automations 2.0 rollout" with no date; after an account migrates, new automations must be triggered from the API by adding a tag that a tag trigger watches [help `Automations 2.0 Migration`, "API"]. Live corroboration only: people records in the account carry `createdVia: "API"` (`GET /v1/people/{id}`, 2026-09-22).
- C1.3 Delete or lifecycle actions: **yes** (documentation-graded) — weighted coverage = 85% (8.5 of 10), exactly at the threshold. Records 1.0×3: `DELETE /people/{id}` [OpenAPI; llms.txt entry], stage change via `stage`/`stageId` on `PUT /people/{id}` and the `Trash` stage [`/reference/people-get`, "Trash Stage"; OpenAPI]. Automations 0.5×3: enroll (`POST /actionPlansPeople`, `POST /automationsPeople`), pause and resume (`status` = `Running` or `Paused` on `PUT /actionPlansPeople/{id}` [OpenAPI] and `PUT /automationsPeople/{id}` [`/reference/automationspeopleid`]), and `contacted: true` on `PUT /people/{id}` pauses action plans [`/reference/people-id-put`]; no unenroll or remove operation exists (no `DELETE` on either pairing resource), and the fixed classification lists unenroll as a principal lifecycle change. Pipelines 1.0×2: `stageId` on `PUT /deals/{id}` moves a deal, pipeline stages carry a `closedStage` flag so moving a deal into such a stage closes it, and `DELETE /deals/{id}`, `DELETE /stages/{id}` (not for `isProtected` stages) and `DELETE /pipelines/{id}` exist [`/reference/deals-id-put`; `/reference/pipelines-post` (request schema `closedStage`); `/reference/stage-id-delete`]. Deals also expose an `Archived` status in `GET /deals` filters but no documented way to set it through the API [OpenAPI `GET /deals` parameters `status`, `includeArchived`]. Custom fields 1.0×2: `DELETE /customFields/{id}` (owner only) [`/reference/customfields-id-delete`].
- C1.4 Change notification: **partial** — weighted push coverage = 70% (7 of 10). Webhook events cover records (`peopleCreated`, `peopleUpdated`, `peopleDeleted`, `peopleStageUpdated`, `peopleTagsCreated`, `dealsCreated/Updated/Deleted`; weight 3), pipelines (`pipelineCreated/Updated/Deleted`, `pipelineStageCreated/Updated/Deleted`, `stageCreated/Updated/Deleted`; weight 2) and custom-field definitions (`customFieldsCreated/Updated/Deleted`, `dealCustomFieldsCreated/Updated/Deleted`; weight 2; person custom-field values fire `peopleUpdated`) [`/reference/webhooks-guide`, "Supported webhook events"]. No event exists for automation or action-plan enrollment or firing (weight 3); those can only be polled through `GET /actionPlansPeople` or `GET /automationsPeople?status=` [`/reference/actionplanspeople-get`; `/reference/automationspeople`]. Efficient incremental polling exists for people (`updatedAfter`, honored live) but not deals [`/reference/common-filters`; live `s3_deals_updatedAfter` total 0 of 156]. Webhooks require the account-owner key and a registered system [`/reference/webhooks-guide`, "Owner Permissions Required", "X-System Header is Required"]. Note: the `POST /webhooks` OpenAPI enum omits the pipeline, pipeline-stage and custom-field events that the guide documents.
Score math: earned 3.0 of 4 applicable checks; unrounded fraction = 0.75; category points = 11.25 → 11.3/15; verification coverage = 100% (4 of 4).
What this means for you: nearly everything a CRM holds is readable and writable through the API, including contacts, deals, pipelines, stages and custom fields, and you can drop leads in, start or pause an action plan or automation for a contact, and move deals between stages. Two limits shape what you can build: the automation rules themselves can only be built and edited in the FUB screens, and the newer Automations 2.0 endpoints answer only to a registered "system", so your own key gets a 403 until you register one. There is also no event when an automation starts or finishes, so you would watch for its side effects or poll.

## Category 2: API Design, Reliability, and Operability: 7.1/10
- C2.1 Modern API conventions: **yes** — resource-oriented REST over HTTPS with JSON bodies and standard verbs (`GET`, `POST`, `PUT`, `DELETE`), documented in an OpenAPI 3.1 file [`/reference/getting-started`, "API Endpoint"; `/reference/requests-and-responses`; `/openapi/58b53c341065f9c438aa1f7e`]; confirmed live (`GET /v1/people/{id}` → 200 JSON, 2026-09-22).
- C2.2 Consistent typing: **partial** — core identifiers and timestamps are consistently typed (`id` integer, `created`/`updated` ISO 8601 UTC strings, `stageId` integer, `price` integer or null) in the schemas and in live reads [`/reference/requests-and-responses`, "Date Format"; live `GET /v1/people/{id}?fields=allFields`]. Inconsistencies: `contacted` is `boolean` in the `POST /people`, `PUT /people/{id}` and `POST /events` request schemas but an integer `0`/`1` in responses (documented in the `/people/claim` response schema `"contacted": {"type": "integer", "example": 0}` and observed live as a number); `phones[].isPrimary` and `emails[].isPrimary` are integers `1` in responses; the "include archived" concept is a boolean on `GET /people` (`includeTrash`) but an integer "Set to `1`" on `GET /deals` (`includeArchived`, `includeDeleted`) [OpenAPI parameters]. These are confined to flag fields.
- C2.3 Structured errors: **partial** — errors are JSON with correct HTTP status and a human-readable `errorMessage`, but no stable machine-readable error code [`/reference/error-responses`]. Observed live 2026-09-22: `GET /v1/people/999999999` → 404 `{"errorMessage":"Requested resource was not found."}`; invalid key → 401 `{"errorMessage":"Invalid API Key or authentication credentials..."}`; `GET /v1/webhooks` without `system` → 400 `{"errorMessage":"Missing required field in the request: system."}`; `GET /v1/notARealResource` → 404 with a descriptive message. Error shape also varies: the `/rateLimit/*` 403 example uses `error` rather than `errorMessage` [`/reference/ratelimit-usage-get`]. Caveat: invalid query values are silently ignored rather than rejected — `limit=abc` and `updatedAfter=notadate` both returned 200 with default paging and an unfiltered collection (2,269 records), which is undocumented.
- C2.4 Duplicate prevention: **partial** (documentation-graded) — no idempotency keys or request identifiers are documented anywhere in the reference or OpenAPI file. Natural idempotency covers lead intake: `POST /events` "will automatically de-duplicate people based on their phone number or email address" and accepts `person.id` to bind to an existing contact [`/reference/events-post`]; `POST /people?deduplicate=true` returns the existing person instead of creating one [OpenAPI `POST /people` parameter `deduplicate`]; `GET /people/checkDuplicate` exists [`/reference/people-checkduplicate`]. Retried `POST /notes`, `/tasks`, `/deals`, `/appointments` or `/calls` have no documented protection, and a retried `POST /events` still records a duplicate event.
- C2.5 Graceful handling under load: **yes** — sliding 10-second window; every response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Window`, `X-RateLimit-Context`; exhausted limits return `429 Too Many Requests` with a `Retry-After` header in seconds, with published defaults (global 250 per 10 s for registered systems, 125 unregistered) [`/reference/rate-limiting`]. Observed live on every response 2026-09-22 (`x-ratelimit-limit: 125`, `x-ratelimit-remaining: 124…106`, `x-ratelimit-window: 10`, `x-ratelimit-context: global`). A 429 was not deliberately triggered because the Terms of Service prohibit exceeding published limits [`/legal-pages/terms-of-service` §4.5]. Registered systems can also read `GET /v1/rateLimit/usage` and `/limits` [`/reference/ratelimit-usage-get`].
- C2.6 Pagination for large collections: **yes** — `limit` (default 10, max 100) with either `offset` or the recommended keyset `next` token; `_metadata` returns `total`, `next` and `nextLink`; default ordering is documented ("reverse order by id"), and deep pagination enforces `next` [`/reference/pagination`]. Observed live 2026-09-22: page 1 `{"offset":0,"limit":3,"total":2269,"next":"eyJ…"}`, page 2 fetched via `next` and via `offset=3` returned the same next three ids, `limit=101` silently clamped to `limit: 100` consistent with the documented maximum.
- C2.7 Bulk or incremental export: **partial** — incremental sync is documented and honored for people (`updatedAfter`/`updatedBefore`, `createdAfter`/`createdBefore`, `idGreaterThan`/`idLessThan`) [`/reference/common-filters`]; live `GET /v1/people?updatedAfter=2026-09-15T00:00:00Z` returned 10 records all updated on or after the cutoff, and a two-sided window returned 74 records inside it (2026-09-22). Limitations: the docs warn "the updated fields may not be accurate for all records" because related records do not bump the parent [same page]; deal objects carry no `updated` field, `GET /deals` defines no `updatedAfter` parameter, and `GET /v1/deals?updatedAfter=…` returned 0 of 156 deals live; notes have no list endpoint (only `GET /notes/{id}`) [llms.txt]; there is no bulk or asynchronous export path, so full extracts are 100 records per call.
- C2.8 Webhook security and delivery reliability: **yes** (documentation-graded; runs 1 and 2 yes, run 3 partial — see reconciliation) — every delivery carries a `FUB-Signature` header, an HMAC-SHA256 of the base64-encoded payload keyed by the registered `X-System-Key`, with sample verification code; failed deliveries (non-2xx) are retried on a published schedule (1, 5, 5, 10 and 30 minutes) and webhooks with more than 50% failures over 48 hours are auto-disabled; each event carries a unique `eventId`, missed events can be re-requested from `GET /v1/webhookEvents/:id`, and consumers are told to fetch the resource `uri`, reconcile against their own store, and keep an events table as "the source of truth as to what webhook events have been received and processed" [`/reference/webhooks-guide`, "Verify the request", "Retrying webhook events", "Requesting webhook events", "Handling webhook events", "Webhook best practices"]. Disclosed weaknesses: the same page says retries continue "up to 5 times" (about 51 minutes in total) and elsewhere "for up to 8 hours"; the missed-event section refers to events "older than 3 days"; the docs never state outright that deliveries may repeat. Not live-tested (step 8 not run).
- C2.9 Concurrency and conflict control: **partial** — documented conflict semantics exist for one write only: `POST /people/claim` returns `409 Conflict` when the lead was already claimed [`/reference/people-claim`]. No ETag/If-Match, version field or 409 is documented for ordinary updates, and `PUT /people/{id}` overwrites the `tags` and `phones` arrays wholesale unless `mergeTags=true` [`/reference/people-id-put`]; no concurrency limits are documented (an `etag` header was observed on live GET responses but is undocumented and could not be exercised without a write). All three runs marked partial; two noted that "no" is also defensible.
- C2.10 Versioning and backward compatibility: **partial** — explicit path version `/v1` ("currently v1 is the only version") and an informal promise that the API "will be extended … while keeping it backward compatible" [`/reference/getting-started`]; the API Terms say "New versions may not be compatible with your previous implementation" and FUB may require the newest version [`/reference/fub-api-tou` §1]; deprecations are announced as page banners ("planned for deprecation … no date has been set") [`/reference/actionplanspeople-post`; `/docs/oauth-authentication-and-authorization`, "Deprecated GET endpoint"]. There is no written policy defining breaking versus non-breaking changes or deprecation windows.
- C2.11 Request traceability: **partial** — no request or correlation identifier is documented; every live response carries CloudFront's `x-amz-cf-id` (observed 2026-09-22), which FUB does not document or state as usable with support; support is by email only [`/reference/support-and-help`]. All three runs marked partial; two noted that "no" is also defensible.
- C2.12 Service availability and status transparency: **yes** — public status page "Follow Up Boss Status" with an "API" component, an incident-history page and an uptime-history page ("Uptime over the past 90 days. View historical uptime.") [`followupboss.statuspage.io`, `/history`, `/uptime`]; the public status API listed 50 incidents including "Widespread Service Disruption" (major, 2026-08-31, resolved) and showed the API component in `partial_outage` at run time [`/api/v2/incidents.json`, `/api/v2/components.json`, 2026-09-22]; the site links to it ("You can view our system status at any time") and publishes an uptime figure, although inconsistently: "99.5% System Uptime" on the security page and "99.95% System Uptime" on the Terms of Service page [`www.followupboss.com/security`; `/legal-pages/terms-of-service`]. No contractual SLA (service is "AS-IS") [`/legal-pages/terms-of-service` §11].
Score math: earned 8.5 of 12 applicable checks; unrounded fraction = 0.708333; category points = 7.083 → 7.1/10; verification coverage = 100% (12 of 12).
What this means for you: the API behaves like a normal modern REST API, with clean JSON, cursor paging, clear rate-limit headers and a real status page, so an AI agent or a no-code tool will not be surprised by its shape. The rough edges are on the operations side: errors come as messages rather than codes, bad query parameters are silently ignored, only people can be synced by "changed since", nothing protects you from double-creating notes or tasks on a retry, and there is no way to detect that someone else edited a contact between your read and your write.

## Category 3: Access Control and Safe Automation: 3.5/5
- C3.1 Read-only credentials: **no** — "API key has the same access level as the user whom the key belongs to" and "provides the same privileges as the user's login credentials"; every documented role (Owner, Admin, Agent, Lender) can write [`/reference/authentication`, "Basic Authentication", "Permission Levels"; help `API Key`, `Users, Roles & Permissions`]. No read-only key or role is documented, and the OAuth authorization request defines no scope parameter [`/docs/oauth-authentication-and-authorization`, Step 1].
- C3.2 Scoped credentials: **partial** — scoping is by user role only: an Agent key reaches only assigned contacts and has "restricted access to things like action plans", a Lender key fewer actions still, an Admin cannot use webhooks, and pipelines, custom fields and webhooks are owner-only [`/reference/authentication`, "Permission Levels"; `/reference/pipelines-post`; `/reference/customfields-post`; `/reference/webhooks-guide`]. The API Key Restrictions power-up controls who may create keys, not what a key can do [help `Power-Up: API Key Restrictions`]; OAuth defines no scopes.
- C3.3 Multiple keys: **yes** — any number of named keys per user, listed with created and last-used times and the integrations that used each [help `API Key`, "Creating an API Key", "How can I see a list of my account's API keys?"]; the live `/me` body exposes a `canCreateApiKeys` flag (field name only).
- C3.4 Rotation and revocation: **yes** — self-serve delete ("Integrations using that API key will lose access … and stop working immediately") and self-serve creation of a replacement [help `API Key`, "How do I delete an API key?"]; keys are shown once at creation [`/docs/start-here-brand-new-integration`]; OAuth grants can be revoked with `DELETE /v1/oauthApps/revokeAccess` [`/docs/oauth-authentication-and-authorization`, Step 6].
- C3.5 Test and production isolation: **yes** (run 1 yes, run 2 partial, run 3 N-A — see reconciliation) — the vendor's integration guide directs developers to a separate free 14-day trial account ("a full-featured account", created "so that you can see the result of your API requests") that can be converted to a non-expiring dev account by emailing product@followupboss.com, with its own API keys generated at Admin > API [`/docs/start-here-brand-new-integration`, "Creating a trial Follow Up Boss account", "Generate an API Key in your Follow Up Boss account"]. Because it is a separate account, its credentials and data are isolated from the operator's live account. Limitation: this is a separate production tenant rather than a sandbox mode of the operator's own account, and the dev-account conversion is email-mediated.
Score math: earned 3.5 of 5 applicable checks; unrounded fraction = 0.70; category points = 3.5/5; verification coverage = 100% (5 of 5).
What this means for you: you can mint a separate, named key for every tool or agent and kill any one of them instantly, and you can test in a separate trial account. What you cannot do is hand an agent a read-only or narrowly scoped key: every key carries the full rights of the user who made it, so a key made by the owner can do anything the owner can, including deleting contacts. Give an AI agent a key from a low-privilege user rather than the owner.

## Category 4: Documentation and AI-Agent Readiness: 2.5/5
- C4.1 Complete self-serve reference: **partial** — a public reference covers authentication, every endpoint, parameters and request schemas, with guides for pagination, filters, errors, rate limits and webhooks [`/reference/getting-started` and linked pages; `llms.txt`]. Worked examples and response definitions are uneven: 38 of 156 operations carry only a `{}` placeholder as their 2xx example, including core writes `POST /events`, `POST /people`, `PUT /people/{id}`, `DELETE /people/{id}`, `POST /notes`, `POST /actionPlansPeople` and `GET /automations`; the 2xx examples for `GET /people`, `GET /people/{id}` and `GET /deals` are not valid JSON (truncated with ellipses or trailing commas); request-body examples exist for 23 operations; the OAuth `oauthApps` endpoints appear only in guides [`/openapi/58b53c341065f9c438aa1f7e`, computed 2026-09-22].
- C4.2 Reliable machine-consumable integration path: **partial** (run 1 yes, runs 2 and 3 partial — resolved to partial on the evidence) — a published OpenAPI 3.1 JSON covers all 156 operations with typed parameters and request bodies [`/openapi/58b53c341065f9c438aa1f7e`; listed at `/openapi`], but it is incomplete where it matters most for code or tool generation: 67 of 156 operations have no typed 2xx response schema (schema absent or with zero properties), including `GET /people`, `GET /people/{id}`, `GET /deals`, `POST /events`, `POST /people`, `PUT /people/{id}` and `DELETE /people/{id}`, so a generated client has no response types for the two critical record types; one path is `//textMessageTemplates`, the two rate-limit paths are written `/v1/rateLimit/...` under a server URL that already ends in `/v1`, `/webhookEvents/:id` uses colon syntax without a declared path parameter, and some enums embed quotation marks. No official SDK exists: the FollowUpBoss GitHub organization's only API repository, `fub-api-examples`, holds bash and PHP lead-submission samples, is archived, was last pushed 2023-04-03, and its README links to a legacy documentation URL [`api.github.com/orgs/FollowUpBoss/repos`; README]. No first-party MCP server was found; the MCP servers located are third-party.
- C4.3 AI-readable documentation: **partial** — `/llms.txt` indexes every guide and endpoint page with descriptions and instructs "Append .md to any documentation page URL to get its markdown version" [`/llms.txt`]; 174 of 193 pages rendered as Markdown, but 19 endpoint pages (mostly `/{id}` operations, including `GET /people/{id}`, `DELETE /people/{id}`, `GET /tasks/{id}`, `PUT /tasks/{id}`, `GET /stages/{id}`, `GET /customFields/{id}`) returned the 580 KB HTML application shell on three attempts (2026-09-22); no `llms-full.txt` exists (404).
- C4.4 Kept current: **partial** — a public product "news feed and changelog" exists but is product-level, has no API category, and is rendered client-side [`updates.followupboss.com/en`]; the docs carry deprecation banners with no dates [`/reference/actionplanspeople-post`]; API responses tell unregistered callers that registered systems "will be notified about important changes to the API" (observed in `_metadata.notice`, 2026-09-22), so change notices go by email to registered systems; the newest pages (`/reference/ratelimit-usage-get`) use 2026 dates and help articles carry recent update stamps (`Automations 2.0 Overview` 2026-07-22; `Plan Breakdown` 2026-09-11). There is no API changelog (`docs.followupboss.com/changelog` → 404).
Score math: earned 2.0 of 4 applicable checks; unrounded fraction = 0.50; category points = 2.5/5; verification coverage = 100% (4 of 4).
What this means for you: an AI coding tool can pull the whole reference as Markdown from one index file and read every endpoint's parameters from the OpenAPI file, which is better than most vendors in this space. Expect to fill gaps yourself: the OpenAPI file gives no typed responses for contacts or deals and needs path fixes before you generate a client from it, many write endpoints show no example response, about one page in ten will not render as Markdown, there is no SDK, and there is no place to watch for API changes other than deprecation banners and emails to registered systems.

## Category 5: Accessibility and Cost: 15.0/15
- C5.1 Self-serve API key: **yes** — "Go to Admin > API, Click Create API Key … By default, anyone can create an API key" [help `API Key`, "Creating an API Key"; `/docs/start-here-brand-new-integration`, "Generate an API Key"]; confirmed by live authentication with an operator-created key (2026-09-22). Note: webhooks, Automations 2.0 routes and the rate-limit endpoints additionally require a registered system, obtained through a self-serve web form (system name, system ID, email, name, organization, terms checkbox) [`apps.followupboss.com/system-registration`]; whether the system key is issued instantly was not verified.
- C5.3 Not commercially gated: **yes** — API keys are generated "within your FUB account" with no plan condition [help `API Key`]; all plans include "Unlimited contacts, lead sources and integrations" and the site markets the "Open API" for custom integrations [`/pricing`; `/pro`, "Integrations"]; the plan breakdown lists no API restriction [help `Follow Up Boss Plan Breakdown`]. Caveat: Automations 2.0 must be enabled by the account owner in an irreversible but free migration, and its API routes are open only to registered systems [help `Automations 2.0 Migration`; `/reference/automations`].
Score math: earned 2 of 2 applicable checks; unrounded fraction = 1.0; category points = 15.0/15; verification coverage = 100% (2 of 2).
What this means for you: you can start building today on the plan you already have, with no sales call and no upgrade. Register a free "system" with FUB before you need webhooks or the newer automation endpoints.

## Independent grading runs and reconciliation
Procedure. Run 1 performed discovery, froze packet v2, and ran the live read battery. Two independent grader instances (runs 2 and 3) then graded packet v2 with a fresh copy of the methodology prompt, no network access, and run 1's marks, report and the API key removed from their reach. Each produced a full cited report ending in a machine-readable marks block; the blocks were diffed by script (`evidence/04-run-comparison.md`). Twenty-three of 27 checks agreed across all three runs. The four disagreements were resolved against the packet as follows.

| Check | Run 1 | Run 2 | Run 3 | Reconciled | Basis |
|---|---|---|---|---|---|
| C1.1 | yes (95%) | partial (82%) | partial (82%) | **partial (82%)** | Automation definitions are read-only via API (`GET` only), gated to registered systems and migrated accounts (live 403 with the operator's key), and the writable legacy path is deprecated without a date; the check scores a present object that is missing a non-critical operation at 0.5. Run 1's 1.0 is overruled. |
| C2.8 | yes | yes | partial | **yes** | Signature, retry schedule and replay path (`webhookEvents`, `eventId`, processed-events table guidance) are all documented; run 3's objection is that the docs never explicitly say deliveries may repeat and the retry duration is stated two ways. Recorded as a residual judgment call (−0.42 raw if partial). |
| C3.5 | yes | partial | N-A | **yes** | The vendor documents a separate trial/dev account with its own keys and data as the way to see the results of API requests; that satisfies "separate test and live credentials documented and operationally isolated". "Partial" does not fit because credential isolation is clear, and "N-A" would require that no separate test environment exist. Recorded as a residual judgment call (−0.38 raw if N-A; −0.50 if partial). |
| C4.2 | yes | partial | partial | **partial** | Verified on the OpenAPI file: 67 of 156 operations lack a typed 2xx response schema, including every core people and deals read; core examples are invalid JSON; four paths are malformed; the only sample repository is archived (last pushed 2023-04-03); no SDK or first-party MCP. Run 1 credited breadth without checking schema depth and is overruled. |

Sub-item differences that did not change a mark: C1.2 was 100% (run 1, non-mutable reporting item excluded) versus 91% (runs 2 and 3, counted at 0); C1.3 sub-scores differed (run 2 scored automations 0.5 for the missing unenroll and pipelines 1.0; run 3 scored automations 1.0 and pipelines 0.5 for the missing deal close/archive) and the reconciled map takes automations 0.5 (unenroll is on the fixed list) and pipelines 1.0 (`closedStage` stages close a deal), giving 85%; C1.4 was 70% in runs 1 and 3 and 78% in run 2 through an arithmetic slip in the denominator.

Per-run totals: run 1 raw 41.83 → 84 (B); run 2 raw 38.83 → 78 (C+); run 3 raw 38.54 → 77 (C+); reconciled raw 39.33 → 79 (C+).

Corrections to run 1's report adopted from the independent runs: the examples repository is archived and was last pushed 2023-04-03 (run 1 had cited the repository's metadata date of 2026-09-16); the security page states 99.5% uptime and the Terms page 99.95% (run 1 attributed 99.95% to the security page); the OpenAPI response-schema count in run 1 included schemas with zero properties.

## Total
- Raw: 39.33 / 50 (11.25 + 7.083 + 3.5 + 2.5 + 15.0)
- Normalized before rounding: 78.67 / 100
- Published numeric score: 79 / 100
- Letter grade: C+
- Evidence tier: baseline verified (write checks C1.2, C1.3, C2.4 and C2.8 graded from first-party documentation)
- Overall verification coverage: 100% (27 of 27 applicable checks verified; no check unverified or N-A) (gate: no category Unable to verify; overall ≥ 80%)
- Partial-result flag: yes. Operator-authorized controlled live-data write testing on labeled fixtures (a test contact and test deal), plus a registered system key, an owner API key and an operator-controlled HTTPS endpoint for a webhook subscription, would upgrade the run to fully verified — controlled live and move C1.2, C1.3, C2.4 and C2.8 from documentation-graded to observed.
- Unresolved evaluator disagreements: two definition-level judgment calls remain after reconciliation. C2.8 (yes vs partial, run 3 dissenting) is worth −0.42 raw; C3.5 (yes vs N-A or partial, runs 2 and 3 dissenting) is worth −0.38 or −0.50 raw. Applying either or both gives 77–78, still C+, so the letter grade does not depend on them. Two further alternatives were flagged by graders but adopted by none (C2.9 and C2.11 as "no", −0.42 raw each); applying every downward alternative gives 75 (C). Run 1's original readings of C1.1 and C4.2 would give 84 (B) but were overruled on verified evidence.

## Bottom line for a property manager
Follow Up Boss has a genuinely open, self-serve REST API: with a key you make yourself on any plan, you or an AI agent can read and write contacts, deals, pipelines, stages, tasks, notes and custom fields, push leads in, start or pause follow-up automations for a contact, and receive signed webhooks when records change. Its biggest strengths are accessibility and the shape of the API itself (no gating, cursor paging, clear rate-limit headers, a status page, and Markdown docs with an index file). Its biggest limitations are safety and depth: every key carries the full rights of its user with no read-only or scoped option, automation rules can only be built in the FUB screens and the newer automation endpoints require registering a "system" first, the OpenAPI file gives no typed responses for contacts or deals, retries can double-create activity records, and only contacts can be synced by change date. Product fit is a separate question from API quality: FUB is a real-estate sales CRM with no property-management workflows, so a property manager would use it for owner, tenant and prospect relationships and follow-up cadences while keeping the property-management system, trust accounting and maintenance tooling elsewhere. The published 79 (C+) is the reconciled result of three grading runs and is baseline verified: reads were tested live against the operator's account, and the write-path checks were graded from the vendor's documentation pending authorized fixture testing.
