> ## Documentation Index
> Fetch the complete documentation index at: https://developer.lofty.com/llms.txt
> Use this file to discover all available pages before exploring further.

# October 2026

**October 2026**

## Sales Agent — two endpoints retired

Both routes still exist but no longer do anything: every call returns **`410 Gone`** with error code **`200128`** (`ENDPOINT_RETIRED`, "This endpoint has been retired and no longer performs any action."). They have been removed from the API reference.

| Endpoint | Why | Instead |
| - | - | - |
| **`POST /v2.0/plan-tasks/create`** — batch-create plan tasks | The Sales Agent plan-task feature behind it no longer exists. | None. [`GET /v2.0/plan-tasks/lead/{leadId}`](https://developer.lofty.com/api-reference/sales-agents/get-plan-tasks-by-lead-id) still reads a lead's plan tasks. |
| **`PUT /v2.0/sales-agent/settings`** — update Sales Agent settings | Writes could change a Sales Agent the caller did not own, and a partial body reset every omitted setting to its default. | Change settings in the Lofty CRM. [`GET /v2.0/sales-agent/settings`](https://developer.lofty.com/api-reference/sales-agents/query-sales-agent-settings) is unchanged. |

In MCP, [`manage_sales_agent`](https://developer.lofty.com/mcp/tools/manage-sales-agent) no longer has the `create_plan_tasks` and `update_settings` actions. Reading settings and plan tasks through [`search_sales_agent`](https://developer.lofty.com/mcp/tools/search-sales-agent) is unchanged.

**October 2026**

## MCP — saved searches have their own tools

Writing a lead's saved searches (property alerts and market snapshots) is now five separate tools, each with only its own parameters:

| Tool | Was |
| - | - |
| [`create_saved_search`](https://developer.lofty.com/mcp/tools/create-saved-search) | `manage_leads` `action: "create_saved_search"` |
| [`update_saved_search`](https://developer.lofty.com/mcp/tools/update-saved-search) | `manage_leads` `action: "update_saved_search"` |
| [`delete_saved_search`](https://developer.lofty.com/mcp/tools/delete-saved-search) | `manage_leads` `action: "delete_saved_search"` |
| [`pause_saved_searches`](https://developer.lofty.com/mcp/tools/pause-saved-searches) | `manage_leads` `action: "pause_saved_searches"` |
| [`resume_saved_searches`](https://developer.lofty.com/mcp/tools/resume-saved-searches) | `manage_leads` `action: "resume_saved_searches"` |

The tool names match the old action names, and the arguments are the same with one exception: `pause_saved_searches` takes `categories` as a top-level argument, not `fields.categories`. `manage_leads` no longer accepts these actions — calling it with one returns an error naming the tool to use.

Reading saved searches stays in [`search_leads`](https://developer.lofty.com/mcp/tools/search-leads), which gains a `scope` parameter: pass `scope: "saved_searches"` with `lead_id`. `lead_id` on its own still works but is deprecated.

**October 2026**

## Leads — Update Lead no longer merges into an email duplicate by default

**`PUT /v1.0/leads/{leadId}`** — [Update a lead](https://developer.lofty.com/api-reference/leads/update-lead.md) — gains an optional query parameter, **`deduplicate`** (boolean, default `false`):

* **`false` or omitted** — the lead is updated in place.
* **`true`** — when duplicate-lead merging is turned on for your account, and another lead in the same ownership scope shares this lead's email address after the update, this lead is merged into that lead and is deleted.

Previously that merge ran on every update that changed a field, even one that did not touch email addresses, so a routine update could delete the lead being updated. If your integration relied on it, pass `deduplicate=true`.

**October 2026**

## Transactions — create custom fields

Two new endpoints bring transaction custom fields in line with lead custom fields, which you have been able to create through the API for some time.

* **`POST /v1.0/transaction/customfields`** — creates a transaction custom field on your team and returns it, id included, so you can populate `LeadTransaction.customFields` straight away.
* **`GET /v1.0/transaction/forms?type=`** — lists the field groups defined for a transaction type.

### Put the field in a group

A transaction custom field that belongs to no field group is invisible: it does not appear on the transaction detail page, as a list column, or in filters. Pass **`formIds`** when you create the field, using ids from `GET /v1.0/transaction/forms`:

```json theme={null}
{
  "attributeName": "Mailer Sent",
  "attributeType": "date",
  "formIds": [301]
}
```

Every id is checked before the field is created, so an unknown group or one already at its 50-field limit fails the whole request rather than leaving an unplaced field behind.

### Two differences from lead custom fields

* **`anniversary_date` and `html` are not available.** Transactions have no recurring-date type, and HTML fields are not exposed through the API. Supported types: `text`, `number`, `date`, `percentage`, `currency`, `single_select`, `multi_select`.
* **Option lists accept both shapes.** Transactions store a bare array, leads use an `option` wrapper. Either is accepted here and normalised, so `["Pending","Closed"]` and `{"option":["Pending","Closed"]}` behave identically.

Field groups are read-only over the API — create, rename and delete them in the CRM.

**October 2026**

## Narrower permissions for custom fields and notes

Two things an application used to get only as part of a much larger bundle can now be requested on their own.

| What you need | Used to require | Which also granted |
| - | - | - |
| **Create custom fields** | Team & Organization → *Create agents, accounts, offices and teams* | creating agents, accounts, offices and teams |
| **View notes** | Communication → *View call history, emails, texts and call recordings* | call, email and text history, and call recordings |

Both appear as their own line when you edit an application's permissions — inside the group they belong to, alongside the broader items, each with its own checkbox. **Communication** is now **Communication & Notes**.

If your integration creates custom fields, it no longer needs the right to create agents, accounts, offices or teams to do it.

### Nothing to do for existing applications

Every application and every issued token keeps the access it already had. The new permissions were added alongside the existing ones, no endpoint became unreachable, and you do not need to reauthorize.

New applications should ask for the narrower line rather than the bundle it was carved out of.

> [!NOTE]
>
> **Two things deliberately did not change.**
>
> *Reading* custom field definitions — `GET /v1.0/teamFeatures/listCustomField` and `GET /v1.0/transaction/customfields` — still comes with Team & Organization and Transactions view access respectively. Only creation was separated out.
>
> *Writing* notes is unchanged and still sits in that group on three separate items: `POST /v1.0/notes` shares *Log calls, emails and texts manually, and create notes*, while `PUT` and `DELETE /v1.0/notes/{id}` sit on *Update notes* and *Delete manual log types and notes*. Only reading was separated out.

**October 2026**

## Leads — Saved searches: create, update, delete, pause and resume

The saved-searches resource is now read-write. A saved search and its listing alert are one object: a buyer search sends **Property Alert** emails (`category: PROPERTY_ALERT`), a seller search sends **Market Snapshot** emails (`category: MARKET_SNAPSHOT`), and the search filters and the delivery cadence travel together.

* **`POST /v1.0/leads/{leadId}/savedSearches`** — [Create a property alert or market snapshot](https://developer.lofty.com/api-reference/leads/create-saved-search.md). Required: `category`, `name` (1–255 characters), `alertFrequency` and `criteria`. Takes the same values you would pick in the CRM: weekdays and `AM` / `PM` for `schedule`, cadence names (`INSTANTLY`, `DAILY`, `WEEKLY`, `BIWEEKLY`, `MONTHLY`, or `NEVER` to create it paused), and email addresses for `recipientEmails`, which must belong to the lead. The lead needs at least one email address, and a search with the same filters as an existing one of the lead is refused. `sendFirstEmailNow` defaults to `true` for a property alert.
* **`PUT /v1.0/leads/{leadId}/savedSearches/{savedSearchId}`** — [Update a saved search](https://developer.lofty.com/api-reference/leads/update-saved-search.md) in place. Only the fields sent change; `criteria`, when sent, replaces every writable filter at once while filters the API cannot write are kept. `category` cannot be changed, and searches the lead created on the website cannot be edited (`2000011`).
* **`DELETE /v1.0/leads/{leadId}/savedSearches/{savedSearchId}`** — [Delete a saved search](https://developer.lofty.com/api-reference/leads/delete-saved-search.md) and stop its emails for good.
* **`POST /v1.0/leads/{leadId}/savedSearches/pause`** — [Pause alerts](https://developer.lofty.com/api-reference/leads/pause-saved-searches.md) without deleting the searches. Select by `categories` or by `savedSearchIds`, not both. Pausing sets the cadence to `NEVER`, keeps the conditions and schedule, and the search reads back as `INACTIVE` with the cadence it replaced in `frequencyBeforePause`. Searches already off are reported as `ALREADY_INACTIVE`, so repeating the call is safe.
* **`POST /v1.0/leads/{leadId}/savedSearches/resume`** — [Resume alerts](https://developer.lofty.com/api-reference/leads/resume-saved-searches.md) for the given `savedSearchIds`, each at the cadence it ran at before it was paused. A search whose earlier cadence is unknown stays paused and is reported as `PRIOR_FREQUENCY_UNKNOWN`; set `alertFrequency` through the update endpoint instead.

Pause and resume return one `results` entry per selected search with `result` (`PAUSED`, `RESUMED`, `ALREADY_INACTIVE`, `ALREADY_ACTIVE`, `PRIOR_FREQUENCY_UNKNOWN`, `NOT_CHANGED`), `previousAlertFrequency`, `alertFrequency` and `status`.

### Criteria rules

`criteria` is shared by reads and writes. `locations` takes either named locations (`cities`, `zipCodes`, …) or `mapCircles` (centre, `radius`, `unit` of `miles` or `kilometers`, at most 100 miles), not both; omitting `locations` means no location filter. `priceMin` / `priceMax`, `daysOnSite`, `purchaseTypes` and `includeOpenHouse` apply to `PROPERTY_ALERT` only; `soldPriceMin` / `soldPriceMax` and `daysSold` apply to `MARKET_SNAPSHOT` only. Dropdown values such as `propertyTypes` and `listingStatuses` must match the options Lofty shows for the lead's site exactly, including letter case. `listingStatuses` defaults to `Active` for a property alert and `Sold` for a market snapshot.

### Errors

A request rejected by validation or by a business rule answers HTTP `200` with `{"code": <error code>, "message": "..."}` in place of the saved search — check `code` in the body, not the HTTP status. Unknown ids answer `404`; leads the token's user cannot see answer `403`.

| code | meaning |
| - | - |
| `20012` | A field is missing, invalid or not allowed; `message` names it |
| `2000001` | A saved search of the lead already has the same filters |
| `2000005` | The lead has no email address to send alerts to |
| `2000011` | The saved search cannot be edited (created by the lead on the website) |
| `100000` | Lofty could not complete the change; try again later |

### Read endpoint changes

**`GET /v1.0/leads/{leadId}/savedSearches`** now returns `frequencyBeforePause` on an `INACTIVE` search — the cadence the resume endpoint restores — and documents the `TERMINATED` / `REFUSED` cadences left by older Lofty features, which can appear when reading but cannot be written. `mapCircles[].unit` is normalised to `miles` or `kilometers`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.