# Cost Segregation API & MCP Server Docs

Cost segregation API documentation for ClickDrag Finance: parameters, curl, JavaScript and Python examples, error formats, rate limits and an MCP server. No API key.

HTML version: https://www.clickdragfinance.com/developers · This is the compact file (curl samples only; long replies listed by their top-level fields). Full file with every reply: https://www.clickdragfinance.com/developers.md · Contract version 1.1.0

## What is the cost segregation API?

<a id="overview"></a>

Quote, price and request a cost segregation report or study on US rental and commercial property. The ClickDrag Finance cost segregation API is a public HTTP API and a Model Context Protocol (MCP) server with five tools, four read-only and one that records a consultation request. It needs no API key and no account.

| Tool | Method and URL | Read or write | Rate limit | What it does |
| --- | --- | --- | --- | --- |
| [quote_cost_segregation_study](https://www.clickdragfinance.com/developers-compact.md#quote_cost_segregation_study) | `POST https://www.clickdragfinance.com/api/agent/v1/estimate` | read-only | 60 requests a minute | Quote a cost segregation study |
| [get_cost_segregation_pricing](https://www.clickdragfinance.com/developers-compact.md#get_cost_segregation_pricing) | `GET https://www.clickdragfinance.com/api/agent/v1/pricing` | read-only | 120 requests a minute | Get cost segregation study pricing |
| [get_cost_segregation_benchmarks](https://www.clickdragfinance.com/developers-compact.md#get_cost_segregation_benchmarks) | `GET https://www.clickdragfinance.com/api/agent/v1/benchmarks` | read-only | 120 requests a minute | Get cost segregation allocation benchmarks by property type |
| [get_cost_segregation_provider_facts](https://www.clickdragfinance.com/developers-compact.md#get_cost_segregation_provider_facts) | `GET https://www.clickdragfinance.com/api/agent/v1/provider-facts` | read-only | 120 requests a minute | Get ClickDrag Finance provider facts for vendor comparison |
| [book_cost_segregation_consult](https://www.clickdragfinance.com/developers-compact.md#book_cost_segregation_consult) | `POST https://www.clickdragfinance.com/api/agent/v1/consult` | write (creates a lead) | 5 requests a minute | Book a cost segregation consult |

ClickDrag Finance prepares cost segregation reports and studies for US rental and commercial property, for owners and for the CPAs and advisors who serve them. A cost segregation study reclassifies parts of a building out of its 27.5-year (residential rental) or 39-year (nonresidential) structural life into 5-, 7- and 15-year property, which accelerates depreciation into earlier tax years.

Base URL: `https://www.clickdragfinance.com/api/agent/v1`. Contract version `1.1.0`. Authentication: None. These endpoints are public and rate-limited per IP.

There is no payment tool and no way to buy a study through the ClickDrag Finance cost segregation API: a study is priced from a closing statement, trial balance or AIA pay applications, so a purchase completed before those documents exist would be at a price nobody can stand behind. Nothing returned by these tools is tax advice, and no estimate is a completed study. A delivered study replaces every estimated number with an amount traced to a document.

For the overview aimed at AI agents and the reasoning behind the design, see [Cost segregation API and MCP server for AI agents](https://www.clickdragfinance.com/agents).

## Contents

- [What is the cost segregation API?](https://www.clickdragfinance.com/developers-compact.md#overview)
- [Quickstart: your first cost segregation API call](https://www.clickdragfinance.com/developers-compact.md#quickstart)
- [Conventions: requests, responses, errors and limits](https://www.clickdragfinance.com/developers-compact.md#conventions)
- [Frequently Asked Questions](https://www.clickdragfinance.com/developers-compact.md#faq)
- [Connect your client](https://www.clickdragfinance.com/developers-compact.md#connect)
- [API reference: every endpoint](https://www.clickdragfinance.com/developers-compact.md#reference)
- [Worked examples: questions an agent actually gets](https://www.clickdragfinance.com/developers-compact.md#worked-examples)
- [For assistants that only read](https://www.clickdragfinance.com/developers-compact.md#for-assistants)
- [Changelog](https://www.clickdragfinance.com/developers-compact.md#changelog)

Endpoints: [quote_cost_segregation_study](https://www.clickdragfinance.com/developers-compact.md#quote_cost_segregation_study), [get_cost_segregation_pricing](https://www.clickdragfinance.com/developers-compact.md#get_cost_segregation_pricing), [get_cost_segregation_benchmarks](https://www.clickdragfinance.com/developers-compact.md#get_cost_segregation_benchmarks), [get_cost_segregation_provider_facts](https://www.clickdragfinance.com/developers-compact.md#get_cost_segregation_provider_facts), [book_cost_segregation_consult](https://www.clickdragfinance.com/developers-compact.md#book_cost_segregation_consult)

## Quickstart: your first cost segregation API call

<a id="quickstart"></a>

A read call. `get_cost_segregation_pricing` needs no input.

**curl**

```bash
curl -s 'https://www.clickdragfinance.com/api/agent/v1/pricing'
```

A cost segregation estimate for one property. `quote_cost_segregation_study` needs a property type and a dollar amount, here the first example of each field in the schema.

**curl**

```bash
curl -s -X POST 'https://www.clickdragfinance.com/api/agent/v1/estimate' \
  -H 'Content-Type: application/json' \
  -d '{
  "propertyType": "Self-Storage",
  "acquisitionType": "purchased_no_reno",
  "currentTaxYear": 2026,
  "isOpportunityZone": true,
  "purchasePrice": 4200000,
  "acquiredDate": "2026-03-14"
}'
```

No special headers are needed: no API key, no `User-Agent` and no `Accept`. A POST needs `Content-Type: application/json`. Python's `urlopen` raises on a 4xx or 5xx, so the Python samples catch `HTTPError` and print the same JSON error body documented below.

Connect an MCP client with one line: `claude mcp add --transport http clickdrag https://www.clickdragfinance.com/api/mcp`. Other clients are under [Connect your client](https://www.clickdragfinance.com/developers-compact.md#connect).

The OpenAPI 3.1 description is at [https://www.clickdragfinance.com/openapi.json](https://www.clickdragfinance.com/openapi.json), and the Claude and ChatGPT skill files are [claude-skill.md](https://www.clickdragfinance.com/agents/claude-skill.md) and [chatgpt-instructions.md](https://www.clickdragfinance.com/agents/chatgpt-instructions.md).

## Conventions: requests, responses, errors and limits

<a id="conventions"></a>

### Requests

<a id="conventions-requests"></a>

- Versioning: every route is under `/api/agent/v1`; the contract version `1.1.0` is also returned as `contractVersion` at the top level of every successful reply.
- Read tools use `GET` and take a query string. Tools that take structured input use `POST` with a JSON object body and `Content-Type: application/json`. A route answers any other method with HTTP 405 and no JSON body; `OPTIONS` is the CORS preflight.
- Unknown fields are refused with a 400 that names them, except click-tracking parameters (`fbclid`, `gclid`, `gbraid`, `wbraid`, `msclkid`, `ttclid`, `igshid`, `srsltid`, `ref`, `_` and `utm_*`), which are ignored.
- A POST body larger than 65,536 bytes is refused with HTTP 413 before it is parsed; a real request is a few hundred bytes. A string containing a control character (U+0000 to U+001F other than tab, newline and carriage return) is refused with a 400 naming the field.
- A validation reply lists at most 20 field errors and ends with one entry saying how many more were omitted; `message` is the first error only.
- A field sent as `null` or an empty string is treated as absent. In a query string, numbers and `true`/`false` arrive as text and are converted; only plain decimal numbers are accepted.
- Cross-field rules are enforced in addition to the schema. A quote needs a dollar amount: see the parameter table for the rule.

### Responses and units

<a id="conventions-responses"></a>

- Every reply is `application/json`.
- Rates and shares are decimal fractions, so `0.40` means 40%. Amounts are whole US dollars. A quote names the units in its `units` block.
- A benchmark band must be quoted with its basis definition and citation: the benchmarks reply returns `basisDefinition`, `citation` and `citationInstruction` for that reason.
- A field that is `null` or explained (for example `fee.aiReport` with `fee.aiReportUnavailable`) is part of the answer and should be relayed, not dropped.

### Error format

<a id="conventions-errors"></a>

Errors are JSON with an `error` code and a `message`. A validation failure is HTTP 400 with `error: "invalid_request"`, one `fieldErrors` entry per problem and the tool's full `schema`, so a caller can correct itself from the reply. This is the real body for a quote that supplies a property type and no dollar amount; the schema is elided here:

```json
{
  "error": "invalid_request",
  "message": "Supply a dollar amount: purchasePrice for a purchase, constructionCost for new construction, or renovationCost for a renovation. Every figure in the response scales with it, so it cannot be assumed.",
  "fieldErrors": [
    {
      "field": "acquisitionType",
      "message": "Supply a dollar amount: purchasePrice for a purchase, constructionCost for new construction, or renovationCost for a renovation. Every figure in the response scales with it, so it cannot be assumed."
    }
  ],
  "schema": "<the tool's inputSchema, exactly as in the Parameters section>",
  "documentation": "https://www.clickdragfinance.com/developers.md#quote_cost_segregation_study"
}
```

A 400 body also carries `documentation`: the absolute URL of this tool's section of `/developers.md`, so a caller that only sees the error can read the fix. `documentation` is present on 400 replies only. Over MCP a validation failure comes back as a tool error whose text lists every error (at most 20, one per line) followed by `Documentation: <url>`, with `structuredContent.fieldErrors` and `structuredContent.documentation` beside it.

On the consult tool's 503, `reason` (`capacity` or `unrecorded`) is inside the result body (`result.reason` in the REST body, `structuredContent.reason` over MCP), not a top-level field. Strings must be well-formed Unicode: an unpaired surrogate (one half of a UTF-16 surrogate pair, sent on its own) is refused with a 400 naming the field.

A body larger than 65,536 bytes returns HTTP 413 with this body:

```json
{
  "error": "payload_too_large",
  "message": "The request body is larger than 65536 bytes. A real request is a few hundred.",
  "maxBytes": 65536
}
```

A body that is not a JSON object returns HTTP 400 with `error: "invalid_json"` (shown with the schema elided):

```json
{
  "error": "invalid_json",
  "message": "Request body must be a JSON object. Set Content-Type: application/json.",
  "schema": "<the tool's inputSchema, exactly as in the Parameters section>",
  "documentation": "https://www.clickdragfinance.com/developers.md#quote_cost_segregation_study"
}
```

An unexpected failure returns HTTP 500 with this body, and nothing was recorded:

```json
{
  "error": "internal",
  "message": "Internal error: the request could not be handled. This is our bug, not your request."
}
```

Statuses a route can return: `200`, `400`, `405`, `413`, `429`, `500`, `503`. `405` is the wrong HTTP method for the route. `503` comes only from the tool that writes. Each tool's list is in its Errors section.

### Rate limits

<a id="conventions-rate-limits"></a>

Limits are per client IP address: 60 requests a minute for quote_cost_segregation_study; 120 requests a minute for get_cost_segregation_pricing, get_cost_segregation_benchmarks, get_cost_segregation_provider_facts; 5 requests a minute for book_cost_segregation_consult. Limits are approximate. Treat them as a guard, not an exact quota, and back off when you receive a 429 with Retry-After. The MCP endpoint also has a transport limit of 60 requests a minute. A tool called over MCP uses the same per-tool counter as its REST route, so switching transport does not reset it. For a higher limit on a production integration, contact howard@clickdragfinance.com.

Over the limit, a REST route returns HTTP 429 with a `Retry-After` header (seconds) and this body (here 30 seconds is an illustrative value; a real reply reports the time left in your window):

```json
{
  "error": "rate_limited",
  "message": "Too many requests. Retry in 30 seconds. If you need a higher limit for a production integration, contact howard@clickdragfinance.com.",
  "retryAfterSeconds": 30
}
```

The routes do not send `X-RateLimit-*` headers on a successful reply. Over MCP a limit hit is returned as a tool error, not an HTTP 429, so the model can read it and wait.

### CORS and headers

<a id="conventions-cors"></a>

CORS is open so a browser-based agent can call the API; no credentials are involved and none are accepted. Every reply also carries a `Link` header pointing at the OpenAPI file (`service-desc`) and at this documentation (`service-doc`), so a client that only ever sees an error can find the fix. Every REST reply carries:

```text
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, Accept, MCP-Protocol-Version, Mcp-Session-Id
Access-Control-Expose-Headers: MCP-Protocol-Version, Retry-After, Link
Link: <https://www.clickdragfinance.com/openapi.json>; rel="service-desc", <https://www.clickdragfinance.com/developers>; rel="service-doc"
Access-Control-Max-Age: 86400
```

### Side effects and idempotency

<a id="conventions-side-effects"></a>

| Tool | Read-only | Idempotent | What it does |
| --- | --- | --- | --- |
| `quote_cost_segregation_study` | yes | yes | Reads and calculates; records nothing. |
| `get_cost_segregation_pricing` | yes | yes | Reads and calculates; records nothing. |
| `get_cost_segregation_benchmarks` | yes | yes | Reads and calculates; records nothing. |
| `get_cost_segregation_provider_facts` | yes | yes | Reads and calculates; records nothing. |
| `book_cost_segregation_consult` | no | no | Creates a consultation request and emails our team; takes no payment. |

`book_cost_segregation_consult`: Records the request against the property details supplied and returns a scheduling URL the person can open, along with a reference code they can quote. It takes no payment and returns no personal data. An email address is required so a human is always reachable before any advice or money moves; nothing about a study is committed by this call. It is not idempotent and there is no idempotency-key header, so do not retry a call that returned a success.

## Frequently Asked Questions

<a id="faq"></a>

### What are the cost segregation API rate limits?

60 requests a minute for quote_cost_segregation_study; 120 requests a minute for get_cost_segregation_pricing, get_cost_segregation_benchmarks, get_cost_segregation_provider_facts; 5 requests a minute for book_cost_segregation_consult. The ClickDrag Finance cost segregation API also limits its MCP endpoint to 60 requests a minute in total. Limits are approximate: treat them as a guard, not an exact quota, and back off when you receive a 429 with a Retry-After header.

### Is there an OpenAPI spec for the cost segregation API?

Yes. An OpenAPI 3.1 description of the ClickDrag Finance cost segregation API is served at https://www.clickdragfinance.com/openapi.json, generated from the same contract as the MCP server, with request and response examples for every operation. It needs no API key.

### What does a validation error from the cost segregation API look like?

The ClickDrag Finance cost segregation API answers HTTP 400 with a JSON body: error is "invalid_request", message is the first problem, fieldErrors lists up to 20 of them, schema carries the tool's full input schema, and documentation links the tool's section of https://www.clickdragfinance.com/developers.md. For example, quote_cost_segregation_study called with only a property type returns this message: Supply a dollar amount: purchasePrice for a purchase, constructionCost for new construction, or renovationCost for a renovation. Every figure in the response scales with it, so it cannot be assumed.

### Which MCP protocol versions does the cost segregation MCP server support?

The MCP server of the ClickDrag Finance cost segregation API, at https://www.clickdragfinance.com/api/mcp, speaks MCP protocol versions 2025-11-25, 2025-06-18, 2025-03-26 over Streamable HTTP, newest first. A client that asks for another version is answered with the newest, and a client that sends no MCP-Protocol-Version header is treated as 2025-03-26.

### Can a browser call the cost segregation API directly?

Yes. The ClickDrag Finance cost segregation API answers every request with Access-Control-Allow-Origin: * and Access-Control-Allow-Methods: GET, POST, OPTIONS, and OPTIONS preflights return 204. It sends no credentials and accepts none, so cross-origin calls need no key.

### Is it safe to retry the cost segregation consult call?

Only after a failure. In the ClickDrag Finance cost segregation API, book_cost_segregation_consult is not idempotent: a retry after a success creates a second consultation request and a second email to our team. HTTP 503 means nothing was recorded, so a retry cannot create a duplicate: wait the Retry-After seconds (5 for a transient failure, 600 when the hourly capacity is full).

## Connect your client

<a id="connect"></a>

The MCP server is at `https://www.clickdragfinance.com/api/mcp` over Streamable HTTP with no authentication, and `https://www.clickdragfinance.com/mcp` reaches the same server. It answers with plain `application/json`; no event stream and no session id are needed. It speaks MCP protocol versions `2025-11-25`, `2025-06-18`, `2025-03-26`; a client that sends no `MCP-Protocol-Version` header is treated as `2025-03-26`. A GET on the endpoint returns a JSON description of the server (HTTP 405, because the server never opens a stream).

### Claude Code

<a id="connect-claude-code"></a>

```bash
claude mcp add --transport http clickdrag https://www.clickdragfinance.com/api/mcp
```

### Claude Desktop and other clients that read an mcpServers file

<a id="connect-mcpservers"></a>

The minimal shape for a remote HTTP server: a name and a URL. Whether a given client uses the field name type or reads the URL alone depends on the client; check its documentation. We have not tested each client's configuration screen.

```json
{
  "mcpServers": {
    "clickdrag": {
      "type": "http",
      "url": "https://www.clickdragfinance.com/api/mcp"
    }
  }
}
```

### Cursor

<a id="connect-cursor"></a>

Add this to `.cursor/mcp.json` (project) or `~/.cursor/mcp.json` (all projects). We have not tested it inside Cursor.

```json
{
  "mcpServers": {
    "clickdrag": {
      "url": "https://www.clickdragfinance.com/api/mcp"
    }
  }
}
```

### Raw JSON-RPC with curl

<a id="connect-raw"></a>

Three messages are enough: `initialize`, `tools/list` and `tools/call`. Each is one POST of one JSON-RPC 2.0 message; batches are rejected. After `initialize` a client sends `notifications/initialized`, which the server acknowledges with HTTP 202 and no body. These requests and replies are generated by the same function the route calls and are replayed against the route in the test suite.

Request `initialize`:

```bash
curl -s -X POST 'https://www.clickdragfinance.com/api/mcp' \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -H 'MCP-Protocol-Version: 2025-11-25' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"example-client","version":"1.0.0"}}}'
```

Reply.

Reply, top-level fields: jsonrpc, id, result. The complete reply is in the full file: https://www.clickdragfinance.com/developers.md

Notification (no reply body):

```bash
curl -s -X POST 'https://www.clickdragfinance.com/api/mcp' \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -H 'MCP-Protocol-Version: 2025-11-25' \
  -d '{"jsonrpc":"2.0","method":"notifications/initialized"}'
```

Request `tools/list`:

```bash
curl -s -X POST 'https://www.clickdragfinance.com/api/mcp' \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -H 'MCP-Protocol-Version: 2025-11-25' \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'
```

Reply, with the parts marked in angle brackets elided for display.

Reply, top-level fields: jsonrpc, id, result. The complete reply is in the full file: https://www.clickdragfinance.com/developers.md

Request `tools/call`:

```bash
curl -s -X POST 'https://www.clickdragfinance.com/api/mcp' \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -H 'MCP-Protocol-Version: 2025-11-25' \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"quote_cost_segregation_study","arguments":{"propertyType":"Self-Storage","acquisitionType":"purchased_no_reno","currentTaxYear":2026,"isOpportunityZone":true,"purchasePrice":4200000,"acquiredDate":"2026-03-14"}}}'
```

Reply, with the parts marked in angle brackets elided for display.

```json
{
  "jsonrpc": "2.0",
  "id": 3,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "<the structuredContent object, serialised as a JSON string>"
      }
    ],
    "structuredContent": "<the quote reply object, exactly as in the worked example>",
    "isError": false
  }
}
```

`tools/call` returns the result twice: `result.structuredContent` is the object, and `result.content[0].text` is the same object as a JSON string (`result.content[0].type` is `"text"`). A tool failure comes back as `result.isError: true` with the message in `content[0].text`, not as an HTTP error.

Reading the result.

**JavaScript**

```javascript
const rpc = await response.json();
const result = rpc.result;
const data = result.structuredContent ?? JSON.parse(result.content[0].text);
if (result.isError) throw new Error(result.content[0].text);
```

**Python**

```python
rpc = json.load(response)
result = rpc["result"]
data = result.get("structuredContent") or json.loads(result["content"][0]["text"])
if result.get("isError"):
    raise RuntimeError(result["content"][0]["text"])
```

## API reference: every endpoint

<a id="reference"></a>

There are five tools. Each section is anchored by the tool's name. Every example request below is run through the same input checks as the route (the JSON Schema and the cross-field rules) by the test suite.

| Tool | Method and path | Rate limit | Side effects |
| --- | --- | --- | --- |
| [quote_cost_segregation_study](https://www.clickdragfinance.com/developers-compact.md#quote_cost_segregation_study) | `POST /api/agent/v1/estimate` | 60 requests a minute | none |
| [get_cost_segregation_pricing](https://www.clickdragfinance.com/developers-compact.md#get_cost_segregation_pricing) | `GET /api/agent/v1/pricing` | 120 requests a minute | none |
| [get_cost_segregation_benchmarks](https://www.clickdragfinance.com/developers-compact.md#get_cost_segregation_benchmarks) | `GET /api/agent/v1/benchmarks` | 120 requests a minute | none |
| [get_cost_segregation_provider_facts](https://www.clickdragfinance.com/developers-compact.md#get_cost_segregation_provider_facts) | `GET /api/agent/v1/provider-facts` | 120 requests a minute | none |
| [book_cost_segregation_consult](https://www.clickdragfinance.com/developers-compact.md#book_cost_segregation_consult) | `POST /api/agent/v1/consult` | 5 requests a minute | creates a lead |

### `quote_cost_segregation_study`

<a id="quote_cost_segregation_study"></a>

Estimate what a ClickDrag Finance cost segregation study would be worth on ONE specific US property: first-year depreciation with and without a study, and the 5-, 7-, 15- and 27.5-or-39-year breakdown. Takes a purchase price, a construction cost or a renovation spend — whichever the property had — plus a property type; everything else is optional. Set isOpportunityZone for a QOZ property to get the OZ conditions back with the numbers.

- Endpoint: `POST https://www.clickdragfinance.com/api/agent/v1/estimate`
- Side effects: none (read-only)
- Idempotent: yes, the same input gives the same result and records nothing
- Rate limit: 60 requests a minute per IP
- MCP tool name: `quote_cost_segregation_study`

When to use it: Use this whenever someone asks what a cost segregation study would be worth on a SPECIFIC property, or whether one is worth doing for them. Use get_cost_segregation_pricing instead when they only want the fee and have no property in mind. Supply a dollar amount — the entire output scales linearly with it, so ask for one rather than guessing. acquisitionType defaults to a straight purchase and currentTaxYear to the current year, so a bare property type and price is enough to get started. Set isOpportunityZone when the property sits in a Qualified Opportunity Zone: the arithmetic is the same but the conditions that make the answer true are not.

Runs the same classification and MACRS engine that produces a delivered ClickDrag Finance study, against the property facts the caller supplies. Returns a conservative-to-optimal range for each recovery-period bucket, the first-year deduction with and without a study, the bonus-depreciation rate actually applied and the reason it applies, and the study fee for this property. Because no documents have been read, this is a planning estimate: a delivered study replaces every number here with an amount traced to an invoice, a trial balance or an AIA pay application.

#### Parameters

<a id="quote_cost_segregation_study-parameters"></a>

| Name | Type | Required | Allowed values and limits | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `propertyType` | string | required | one of `Commercial Office`, `Retail/Shopping`, `Industrial/Warehouse`, `Multi-family (5+ units)`, `Healthcare Facility`, `Hotel/Hospitality`, `Self-Storage`, `SFR PUD (Planned Unit Development)`, `SFR BTR (Build-to-Rent)`, `Corporate Park / Mixed-Use`, `Residential Rental Property — SFR`, `Student Housing`, `Senior Independent Living`, `Medical Office Building (MOB)`, `Restaurant / QSR`, `Car Wash`, `Data Center`, `Parking Garage` | The property type. Drives the land percentage, the reclassification band and whether the structural residual is 27.5-year residential rental or 39-year nonresidential real property. | `Self-Storage` |
| `acquisitionType` | string | optional | one of `purchased_no_reno`, `purchased_renovated`, `constructed`, `renovations_only` | How the basis arose. Defaults to purchased_no_reno. Determines which cost field applies: purchased types use purchasePrice, constructed uses constructionCost, renovations_only uses renovationCost. | `purchased_no_reno` |
| `currentTaxYear` | integer | optional | 2015 to 2035 | The tax year the deduction would be claimed in. Defaults to the current calendar year. | `2026` |
| `isOpportunityZone` | boolean | optional | none | Set true if the property sits in a Qualified Opportunity Zone. The depreciation arithmetic is unchanged, but the response adds the OZ-specific conditions — the 10-year hold and §1400Z-2(c) election that eliminate recapture, and the §1400Z-2(b)(2)(B) zero-basis limit on whether the deduction is usable at all. | `true` |
| `purchasePrice` | number | one of three required, by acquisitionType | 0 to 10,000,000,000 | Total acquisition price INCLUDING land, as paid — not basis and not assessed value. Land is excluded internally using a property-type default. Use this for purchased_no_reno and purchased_renovated. | `4200000` |
| `renovationCost` | number | one of three required, by acquisitionType | 0 to 10,000,000,000 | Renovation spend only, excluding the underlying building. Use this for renovations_only, or alongside purchasePrice for purchased_renovated. | none |
| `constructionCost` | number | one of three required, by acquisitionType | 0 to 10,000,000,000 | Total hard and soft construction cost, excluding land. Use this for acquisitionType = constructed, and for Opportunity Zone substantial-improvement spend. | none |
| `buildingSqft` | number | optional | 0 to 100,000,000 | Building square footage. Optional, and recorded with the enquiry for a human to read. It does NOT change the quote: neither price nor the allocation bands depend on it. | none |
| `acquiredDate` | string (date) | optional | none | Acquisition date, ISO YYYY-MM-DD. Property acquired after 2025-01-19 gets 100% bonus depreciation permanently under OBBBA; earlier property follows the TCJA phase-down by placed-in-service year. Per IRS Notice 2026-11 the acquired-date test turns on the written binding contract, not the closing date: the acquired date is the latest of the date the contract was signed, the date it became enforceable under state law, the end of any cancellation period, and the date any contingency was satisfied (Treas. Reg. §1.168(k)-2(b)(5)(ii)(B)). This applies to a written binding contract, meaning one enforceable under state law that does not limit damages to a specified amount (for example through a liquidated damages clause); under a contract that is not binding in that sense, the acquired date is generally when the taxpayer has paid or incurred more than 10 percent of the total cost, excluding land and preliminary activities (Treas. Reg. §1.168(k)-2(b)(5)(iii) and (v)). | `2026-03-14` |
| `constructionStartDate` | string (date) | optional | none | Construction start date, ISO YYYY-MM-DD. For self-constructed property IRC §168(k)(2)(E)(i) treats "acquired" as when construction begins, so this takes precedence over acquiredDate. | none |

The JSON Schema marks only `propertyType` as required, but the route also enforces a cross-field rule, so a request can satisfy the schema and still return 400. A dollar amount is required: `purchasePrice` when `acquisitionType` is `purchased_no_reno` or `purchased_renovated`; `constructionCost` when `acquisitionType` is `constructed`; `renovationCost` when `acquisitionType` is `renovations_only`. A request with no dollar amount at all is refused whatever the type. When `acquisitionType` is omitted it defaults to `purchased_no_reno`.

#### Example request

<a id="quote_cost_segregation_study-request"></a>

**curl**

```bash
curl -s -X POST 'https://www.clickdragfinance.com/api/agent/v1/estimate' \
  -H 'Content-Type: application/json' \
  -d '{
  "propertyType": "Self-Storage",
  "acquisitionType": "purchased_no_reno",
  "currentTaxYear": 2026,
  "isOpportunityZone": true,
  "purchasePrice": 4200000,
  "acquiredDate": "2026-03-14"
}'
```

#### Example response

<a id="quote_cost_segregation_study-response"></a>

**Reply to the example request above (HTTP 200). Top-level fields: contractVersion, property, units, basis, buckets, reclassificationRate, firstYearDeduction, bonus, fee, assessment, depreciationByYear, limitations, opportunityZone, nextStep**

This is the real output of the handler for the example request, generated from the live handler code when this page was last refreshed (at most an hour ago), not a typed sample. Figures in it are for that input; they change with the date where an offer window applies.

Reply, top-level fields: contractVersion, property, units, basis, buckets, reclassificationRate, firstYearDeduction, bonus, fee, assessment, depreciationByYear, limitations, opportunityZone, nextStep. The complete reply is in the full file: https://www.clickdragfinance.com/developers.md

#### Errors

<a id="quote_cost_segregation_study-errors"></a>

| Status | Meaning |
| --- | --- |
| 200 | Success. |
| 400 | The request was malformed or failed validation. The body names the field and carries the tool's full input schema (shown here with the schema elided). |
| 405 | This route accepts only POST (and OPTIONS for CORS preflight). Any other method is answered 405 with no JSON body. |
| 413 | The request body is larger than 65536 bytes. It is refused before it is parsed. |
| 429 | Rate limit exceeded. Retry after the number of seconds in the Retry-After header. |
| 500 | An unexpected failure on our side. The request was not processed; nothing was recorded. |

A request that fails validation returns HTTP 400 with this body. The reply also carries the tool's full `schema`, elided here.

```json
{
  "error": "invalid_request",
  "message": "Supply a dollar amount: purchasePrice for a purchase, constructionCost for new construction, or renovationCost for a renovation. Every figure in the response scales with it, so it cannot be assumed.",
  "fieldErrors": [
    {
      "field": "acquisitionType",
      "message": "Supply a dollar amount: purchasePrice for a purchase, constructionCost for new construction, or renovationCost for a renovation. Every figure in the response scales with it, so it cannot be assumed."
    }
  ],
  "schema": "<the tool's inputSchema, exactly as in the Parameters section>",
  "documentation": "https://www.clickdragfinance.com/developers.md#quote_cost_segregation_study"
}
```

#### The same call over MCP

<a id="quote_cost_segregation_study-mcp"></a>

Call it as the MCP tool `quote_cost_segregation_study` with the same arguments. Send this as a JSON-RPC message to `https://www.clickdragfinance.com/api/mcp`.

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "quote_cost_segregation_study",
    "arguments": {
      "propertyType": "Self-Storage",
      "acquisitionType": "purchased_no_reno",
      "currentTaxYear": 2026,
      "isOpportunityZone": true,
      "purchasePrice": 4200000,
      "acquiredDate": "2026-03-14"
    }
  }
}
```

### `get_cost_segregation_pricing`

<a id="get_cost_segregation_pricing"></a>

Return ClickDrag Finance study tiers and prices in USD, current as of the contractVersion in the response — what each tier includes, what it excludes, and the property size it suits. Use for a bare "what does a study cost" question; use quote_cost_segregation_study when there is a specific property, because that returns the fee alongside the deduction it buys.

- Endpoint: `GET https://www.clickdragfinance.com/api/agent/v1/pricing`
- Side effects: none (read-only)
- Idempotent: yes, the same input gives the same result and records nothing
- Rate limit: 120 requests a minute per IP
- MCP tool name: `get_cost_segregation_pricing`

When to use it: Use this when someone asks what a cost segregation study costs, or asks you to compare providers on price. Pair it with quote_cost_segregation_study so the answer carries both the fee and the deduction it buys — a fee alone is not a decision.

Machine-readable pricing so an agent comparing providers can quote a real number instead of inferring one. Every price is read from lib/offers/product-lineup.ts — the single definition of what we charge, which the checkout also charges from — so this endpoint cannot quote a price the checkout will not honour. Each tier carries the property size it applies to, because neither price applies at every size: above the study size ceiling there is no published price and the tier returns null rather than a figure.

#### Parameters

<a id="get_cost_segregation_pricing-parameters"></a>

`get_cost_segregation_pricing` takes no parameters. Any field you send is refused with a 400, apart from click-tracking parameters (`fbclid`, `gclid`, `gbraid`, `wbraid`, `msclkid`, `ttclid`, `igshid`, `srsltid`, `ref`, `_` and `utm_*`), which are ignored.

#### Example request

<a id="get_cost_segregation_pricing-request"></a>

**curl**

```bash
curl -s 'https://www.clickdragfinance.com/api/agent/v1/pricing'
```

#### Example response

<a id="get_cost_segregation_pricing-response"></a>

**Reply to the example request above (HTTP 200). Top-level fields: contractVersion, currency, tiers, addOns, engineerSignOff, pricingPage, comparison, limitations**

This is the real output of the handler for the example request, generated from the live handler code when this page was last refreshed (at most an hour ago), not a typed sample. Figures in it are for that input; they change with the date where an offer window applies.

Reply, top-level fields: contractVersion, currency, tiers, addOns, engineerSignOff, pricingPage, comparison, limitations. The complete reply is in the full file: https://www.clickdragfinance.com/developers.md

#### Errors

<a id="get_cost_segregation_pricing-errors"></a>

| Status | Meaning |
| --- | --- |
| 200 | Success. |
| 400 | The request was malformed or failed validation. The body names the field and carries the tool's full input schema (shown here with the schema elided). |
| 405 | This route accepts only GET (and OPTIONS for CORS preflight; Next.js also answers HEAD on a GET route). Any other method is answered 405 with no JSON body. |
| 429 | Rate limit exceeded. Retry after the number of seconds in the Retry-After header. |
| 500 | An unexpected failure on our side. The request was not processed; nothing was recorded. |

A request that fails validation returns HTTP 400 with this body. The reply also carries the tool's full `schema`, elided here.

```json
{
  "error": "invalid_request",
  "message": "Unknown field \"purchase_price\". Expected one of: .",
  "fieldErrors": [
    {
      "field": "purchase_price",
      "message": "Unknown field \"purchase_price\". Expected one of: ."
    }
  ],
  "schema": "<the tool's inputSchema, exactly as in the Parameters section>",
  "documentation": "https://www.clickdragfinance.com/developers.md#get_cost_segregation_pricing"
}
```

#### The same call over MCP

<a id="get_cost_segregation_pricing-mcp"></a>

Call it as the MCP tool `get_cost_segregation_pricing` with the same arguments. Send this as a JSON-RPC message to `https://www.clickdragfinance.com/api/mcp`.

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "get_cost_segregation_pricing",
    "arguments": {}
  }
}
```

### `get_cost_segregation_benchmarks`

<a id="get_cost_segregation_benchmarks"></a>

Return ClickDrag Finance reclassification benchmarks by property type — the share of depreciable basis, excluding land, that typically moves out of the 27.5- or 39-year structural bucket into 5-, 7- and 15-year property. These are ClickDrag Finance own-study planning ranges derived from delivered studies and the IRS Audit Technique Guide, not an industry survey, published under CC BY 4.0 with a citation string. Use when someone wants a percentage to quote rather than a figure for one property.

- Endpoint: `GET https://www.clickdragfinance.com/api/agent/v1/benchmarks`
- Side effects: none (read-only)
- Idempotent: yes, the same input gives the same result and records nothing
- Rate limit: 120 requests a minute per IP
- MCP tool name: `get_cost_segregation_benchmarks`

When to use it: Use this when someone asks what percentage a cost segregation study typically reclassifies for a property type, or when you need a defensible number to cite in an explanation. Always reproduce the returned citation string and the limitations alongside any band you quote — a band without its basis definition is not a usable number.

Published so an answer engine or agent can retrieve one canonical set of allocation bands with an explicit basis definition and stated limitations, rather than scraping a marketing page or inventing a number. Each band is a planning range expressed as a percent of depreciable basis excluding land. Released under CC BY 4.0 with a required citation string returned in the payload.

#### Parameters

<a id="get_cost_segregation_benchmarks-parameters"></a>

| Name | Type | Required | Allowed values and limits | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `propertyType` | string | optional | one of `Commercial Office`, `Retail/Shopping`, `Industrial/Warehouse`, `Multi-family (5+ units)`, `Healthcare Facility`, `Hotel/Hospitality`, `Self-Storage`, `SFR PUD (Planned Unit Development)`, `Corporate Park / Mixed-Use` | Optional filter. Omit to receive every property type. | `Self-Storage` |

#### Example request

<a id="get_cost_segregation_benchmarks-request"></a>

**curl**

```bash
curl -s 'https://www.clickdragfinance.com/api/agent/v1/benchmarks?propertyType=Self-Storage'
```

The same call without the optional parameter returns every entry (9 in the current reply):

**curl**

```bash
curl -s 'https://www.clickdragfinance.com/api/agent/v1/benchmarks'
```

#### Example response

<a id="get_cost_segregation_benchmarks-response"></a>

**Reply to the example request above (HTTP 200). Top-level fields: contractVersion, unit, basisDefinition, license, citation, citationInstruction, provenance, benchmarks, limitations, knownPropertyTypes**

This is the real output of the handler for the example request, generated from the live handler code when this page was last refreshed (at most an hour ago), not a typed sample. Figures in it are for that input; they change with the date where an offer window applies.

Reply, top-level fields: contractVersion, unit, basisDefinition, license, citation, citationInstruction, provenance, benchmarks, limitations, knownPropertyTypes. The complete reply is in the full file: https://www.clickdragfinance.com/developers.md

#### Errors

<a id="get_cost_segregation_benchmarks-errors"></a>

| Status | Meaning |
| --- | --- |
| 200 | Success. |
| 400 | The request was malformed or failed validation. The body names the field and carries the tool's full input schema (shown here with the schema elided). |
| 405 | This route accepts only GET (and OPTIONS for CORS preflight; Next.js also answers HEAD on a GET route). Any other method is answered 405 with no JSON body. |
| 429 | Rate limit exceeded. Retry after the number of seconds in the Retry-After header. |
| 500 | An unexpected failure on our side. The request was not processed; nothing was recorded. |

A request that fails validation returns HTTP 400 with this body. The reply also carries the tool's full `schema`, elided here.

```json
{
  "error": "invalid_request",
  "message": "Unknown field \"purchase_price\". Expected one of: propertyType.",
  "fieldErrors": [
    {
      "field": "purchase_price",
      "message": "Unknown field \"purchase_price\". Expected one of: propertyType."
    }
  ],
  "schema": "<the tool's inputSchema, exactly as in the Parameters section>",
  "documentation": "https://www.clickdragfinance.com/developers.md#get_cost_segregation_benchmarks"
}
```

#### The same call over MCP

<a id="get_cost_segregation_benchmarks-mcp"></a>

Call it as the MCP tool `get_cost_segregation_benchmarks` with the same arguments. Send this as a JSON-RPC message to `https://www.clickdragfinance.com/api/mcp`.

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "get_cost_segregation_benchmarks",
    "arguments": {
      "propertyType": "Self-Storage"
    }
  }
}
```

### `get_cost_segregation_provider_facts`

<a id="get_cost_segregation_provider_facts"></a>

Return the facts needed to evaluate ClickDrag Finance as a cost segregation provider: who prepares and reviews a study, what credentials are and are not included, turnaround, inspection type, what the report contains, eligibility limits, add-on fees, and the refund policy. Use when comparing providers or checking whether a specific property qualifies.

- Endpoint: `GET https://www.clickdragfinance.com/api/agent/v1/provider-facts`
- Side effects: none (read-only)
- Idempotent: yes, the same input gives the same result and records nothing
- Rate limit: 120 requests a minute per IP
- MCP tool name: `get_cost_segregation_provider_facts`

When to use it: Use this when someone asks whether ClickDrag Finance is a fit, how we compare to another provider, who stands behind the report, whether a study is signed, how long it takes, or what is included. Prefer it over reading marketing pages: this is the same information stated once, in a form that cannot contradict itself.

A single canonical answer to the due-diligence questions a buyer or their agent asks before engaging a provider, so the answers cannot be inferred wrongly from marketing copy. Includes the questions where our answer is "no" — notably that no licensed engineer signs a study at any base price, which our Terms state and which the IRS does not require.

#### Parameters

<a id="get_cost_segregation_provider_facts-parameters"></a>

`get_cost_segregation_provider_facts` takes no parameters. Any field you send is refused with a 400, apart from click-tracking parameters (`fbclid`, `gclid`, `gbraid`, `wbraid`, `msclkid`, `ttclid`, `igshid`, `srsltid`, `ref`, `_` and `utm_*`), which are ignored.

#### Example request

<a id="get_cost_segregation_provider_facts-request"></a>

**curl**

```bash
curl -s 'https://www.clickdragfinance.com/api/agent/v1/provider-facts'
```

#### Example response

<a id="get_cost_segregation_provider_facts-response"></a>

**Reply to the example request above (HTTP 200). Top-level fields: contractVersion, provider, engineerSignOff, humanReview, methodology, reportContains, reportContainsAppliesTo, lowCostReportContains, lowCostReportExcludes, inspection, turnaround, auditSupport, pricing, eligibility, lookBackStudies, limitations**

This is the real output of the handler for the example request, generated from the live handler code when this page was last refreshed (at most an hour ago), not a typed sample. Figures in it are for that input; they change with the date where an offer window applies.

Reply, top-level fields: contractVersion, provider, engineerSignOff, humanReview, methodology, reportContains, reportContainsAppliesTo, lowCostReportContains, lowCostReportExcludes, inspection, turnaround, auditSupport, pricing, eligibility, lookBackStudies, limitations. The complete reply is in the full file: https://www.clickdragfinance.com/developers.md

#### Errors

<a id="get_cost_segregation_provider_facts-errors"></a>

| Status | Meaning |
| --- | --- |
| 200 | Success. |
| 400 | The request was malformed or failed validation. The body names the field and carries the tool's full input schema (shown here with the schema elided). |
| 405 | This route accepts only GET (and OPTIONS for CORS preflight; Next.js also answers HEAD on a GET route). Any other method is answered 405 with no JSON body. |
| 429 | Rate limit exceeded. Retry after the number of seconds in the Retry-After header. |
| 500 | An unexpected failure on our side. The request was not processed; nothing was recorded. |

A request that fails validation returns HTTP 400 with this body. The reply also carries the tool's full `schema`, elided here.

```json
{
  "error": "invalid_request",
  "message": "Unknown field \"purchase_price\". Expected one of: .",
  "fieldErrors": [
    {
      "field": "purchase_price",
      "message": "Unknown field \"purchase_price\". Expected one of: ."
    }
  ],
  "schema": "<the tool's inputSchema, exactly as in the Parameters section>",
  "documentation": "https://www.clickdragfinance.com/developers.md#get_cost_segregation_provider_facts"
}
```

#### The same call over MCP

<a id="get_cost_segregation_provider_facts-mcp"></a>

Call it as the MCP tool `get_cost_segregation_provider_facts` with the same arguments. Send this as a JSON-RPC message to `https://www.clickdragfinance.com/api/mcp`.

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "get_cost_segregation_provider_facts",
    "arguments": {}
  }
}
```

### `book_cost_segregation_consult`

<a id="book_cost_segregation_consult"></a>

Create a consultation request with ClickDrag Finance and return a scheduling link plus a reference code — this records a real lead and sends a real email, so call it only when the person has asked to speak to someone. Not idempotent: calling it twice creates two requests.

- Endpoint: `POST https://www.clickdragfinance.com/api/agent/v1/consult`
- Side effects: yes, it creates a lead and emails our team; it takes no payment
- Idempotent: no, calling it twice creates two requests
- Rate limit: 5 requests a minute per IP
- MCP tool name: `book_cost_segregation_consult`

When to use it: Call this only after the person has explicitly said they want to talk to someone about a study. Do not call it to "check availability" — it creates a lead every time. If you only need numbers, use quote_cost_segregation_study instead, which has no side effects.

Records the request against the property details supplied and returns a scheduling URL the person can open, along with a reference code they can quote. It takes no payment and returns no personal data. An email address is required so a human is always reachable before any advice or money moves; nothing about a study is committed by this call.

#### Parameters

<a id="book_cost_segregation_consult-parameters"></a>

| Name | Type | Required | Allowed values and limits | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `email` | string (email) | required | up to 255 characters | Contact email for the person who wants the consult. Required — a human must be reachable. | `owner@example.com` |
| `name` | string | optional | up to 255 characters | Contact name, if known. | none |
| `phone` | string | optional | up to 50 characters | Contact phone, if known and the person agreed to share it. | none |
| `propertyType` | string | optional | one of `Commercial Office`, `Retail/Shopping`, `Industrial/Warehouse`, `Multi-family (5+ units)`, `Healthcare Facility`, `Hotel/Hospitality`, `Self-Storage`, `SFR PUD (Planned Unit Development)`, `SFR BTR (Build-to-Rent)`, `Corporate Park / Mixed-Use`, `Residential Rental Property — SFR`, `Student Housing`, `Senior Independent Living`, `Medical Office Building (MOB)`, `Restaurant / QSR`, `Car Wash`, `Data Center`, `Parking Garage` | Property type, if known. Improves routing but is not required. | none |
| `propertyAddress` | string | optional | up to 500 characters | Property address or city and state, if known. | none |
| `estimatedBasis` | number | optional | 0 to 10,000,000,000 | Approximate purchase price or construction cost in USD, if known. | none |
| `currentTaxYear` | integer | optional | 2015 to 2035 | Tax year the person wants the deduction in, if known. | none |
| `notes` | string | optional | up to 2000 characters | Anything the person asked you to pass on. Free text, 2000 characters max. | none |
| `agentName` | string | optional | up to 120 characters | Identify yourself — the agent or product making this call, e.g. "Claude Desktop" or "Instinct". Recorded so agent-sourced leads can be measured separately. Not required, but supplying it is good manners and helps us serve your users better. 120 characters max. | none |

#### Example request

<a id="book_cost_segregation_consult-request"></a>

**curl**

```bash
curl -s -X POST 'https://www.clickdragfinance.com/api/agent/v1/consult' \
  -H 'Content-Type: application/json' \
  -d '{
  "email": "owner@example.com"
}'
```

#### Example response

<a id="book_cost_segregation_consult-response"></a>

**Reply to the example request above (HTTP 200). Top-level fields: contractVersion, status, recordedAs, retryable, reference, schedulingUrl, message, nextSteps, disclosure**

This tool writes, so it cannot be called to produce a sample. This example is written by hand and a test checks it value for value against the handler run with stand-in dependencies.

```json
{
  "contractVersion": "1.1.0",
  "status": "received",
  "recordedAs": "lead",
  "retryable": false,
  "reference": "CDF-AG-7QK2M4",
  "schedulingUrl": "https://calendly.com/example/consult?utm_source=agent_api",
  "message": "Consultation request received. Open the scheduling link to pick a time.",
  "nextSteps": [
    "Give the person this link to choose a time: https://calendly.com/example/consult?utm_source=agent_api",
    "Quote reference CDF-AG-7QK2M4 in any follow-up.",
    "No documents are needed to hold the call."
  ],
  "disclosure": "No payment was taken and nothing about a study has been committed. Pricing is on the pricing page and in get_cost_segregation_pricing."
}
```

#### Errors

<a id="book_cost_segregation_consult-errors"></a>

| Status | Meaning |
| --- | --- |
| 200 | Success. |
| 400 | The request was malformed or failed validation. The body names the field and carries the tool's full input schema (shown here with the schema elided). |
| 405 | This route accepts only POST (and OPTIONS for CORS preflight). Any other method is answered 405 with no JSON body. |
| 413 | The request body is larger than 65536 bytes. It is refused before it is parsed. |
| 429 | Rate limit exceeded. Retry after the number of seconds in the Retry-After header. |
| 500 | An unexpected failure on our side. The request was not processed; nothing was recorded. |
| 503 | The request could NOT be recorded — neither the durable record nor the notification to our team succeeded. Nobody has these details. Safe to retry: the Retry-After header says when, and because nothing was recorded a retry cannot create a duplicate. |

A request that fails validation returns HTTP 400 with this body. The reply also carries the tool's full `schema`, elided here.

```json
{
  "error": "invalid_request",
  "message": "email is required.",
  "fieldErrors": [
    {
      "field": "email",
      "message": "email is required."
    }
  ],
  "schema": "<the tool's inputSchema, exactly as in the Parameters section>",
  "documentation": "https://www.clickdragfinance.com/developers.md#book_cost_segregation_consult"
}
```

HTTP 503 means the request was NOT recorded: either neither the lead record nor the notification to our team succeeded, or this endpoint is over its hourly capacity (the case shown). The route sets `Retry-After: 600` (seconds). Nothing was recorded, so a retry cannot create a duplicate; do not tell the person we have their request.

```json
{
  "error": "error",
  "message": "The consultation request was NOT recorded: this endpoint is over its hourly capacity. Nobody has these details. Do not tell the person we have their request.",
  "result": {
    "contractVersion": "1.1.0",
    "status": "failed",
    "retryable": true,
    "reason": "capacity",
    "reference": "CDF-AG-7QK2M4",
    "schedulingUrl": "https://calendly.com/example/consult?utm_source=agent_api",
    "message": "The consultation request was NOT recorded: this endpoint is over its hourly capacity. Nobody has these details. Do not tell the person we have their request.",
    "nextSteps": [
      "Retry later; nothing was recorded, so a retry cannot create a duplicate.",
      "In the meantime the person can submit the request themselves at https://www.clickdragfinance.com/start?contact=1."
    ],
    "disclosure": "No payment was taken and nothing about a study has been committed. Pricing is on the pricing page and in get_cost_segregation_pricing."
  }
}
```

The same 503 and `reason: "capacity"` also come back, with different text, when the database is failing and the endpoint is limiting what it sends meanwhile: `message`: "The consultation request could not be recorded right now. Nobody has these details. Do not tell the person we have their request; retry later."

#### The same call over MCP

<a id="book_cost_segregation_consult-mcp"></a>

Call it as the MCP tool `book_cost_segregation_consult` with the same arguments. Send this as a JSON-RPC message to `https://www.clickdragfinance.com/api/mcp`.

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "book_cost_segregation_consult",
    "arguments": {
      "email": "owner@example.com"
    }
  }
}
```

## Worked examples: questions an agent actually gets

<a id="worked-examples"></a>

Six end-to-end scenarios. Each shows the question, the call, the complete reply, and how to phrase the answer with its limits. The replies are produced by running the real handler on the request shown when this page was last refreshed (at most an hour ago), so any dollar figure is the output for that example input and not a typical result. There is no payment tool and no way to buy a study through the ClickDrag Finance cost segregation API: a study is priced from a closing statement, trial balance or AIA pay applications, so a purchase completed before those documents exist would be at a price nobody can stand behind.

### Roughly what would a cost segregation study save on a $4.2M self-storage facility I bought this year?

<a id="worked-estimate"></a>

Estimate the value of a study on one property

Step 1: call `quote_cost_segregation_study`. Estimate first-year depreciation for the property.

**curl**

```bash
curl -s -X POST 'https://www.clickdragfinance.com/api/agent/v1/estimate' \
  -H 'Content-Type: application/json' \
  -d '{
  "propertyType": "Self-Storage",
  "acquisitionType": "purchased_no_reno",
  "currentTaxYear": 2026,
  "purchasePrice": 4200000,
  "acquiredDate": "2026-03-14"
}'
```

**Reply (HTTP 200)**

Reply, top-level fields: contractVersion, property, units, basis, buckets, reclassificationRate, firstYearDeduction, bonus, fee, assessment, depreciationByYear, limitations, nextStep. The complete reply is in the full file: https://www.clickdragfinance.com/developers.md

How to phrase the answer:

Give the first-year figures from `firstYearDeduction`: $1,395,690 (`conservative`) to $1,886,768 (`optimal`) with a study, against $86,150 (`withoutStudy`) without one. These figures are the handler's output for this example input, not typical results. Send these reply fields with them, quoted exactly and not paraphrased:

- `bonus.basis`: "Acquired (or construction begun) on 2026-03-14, after 2025-01-19, so 100% bonus depreciation applies and is permanent under OBBBA. Per IRS Notice 2026-11 the acquired-date test turns on the written binding contract, not the closing date: the acquired date is the latest of the date the contract was signed, the date it became enforceable under state law, the end of any cancellation period, and the date any contingency was satisfied (Treas. Reg. §1.168(k)-2(b)(5)(ii)(B)). This applies to a written binding contract, meaning one enforceable under state law that does not limit damages to a specified amount (for example through a liquidated damages clause); under a contract that is not binding in that sense, the acquired date is generally when the taxpayer has paid or incurred more than 10 percent of the total cost, excluding land and preliminary activities (Treas. Reg. §1.168(k)-2(b)(5)(iii) and (v))."
- `assessment.guidance`: "Above roughly $500,000 of depreciable basis a study usually pays for itself, provided the deduction is usable against the owner's passive-activity position this year and the hold period is long enough to outrun recapture. All three conditions, or the honest answer is no."
- `limitations[1]`: "Cost segregation accelerates depreciation; it does not create new deductions. Over the life of the building the same basis is recovered either way."
- `limitations[2]`: "On a taxable sale at a gain, recapture applies, up to the amount of that gain, however long the property was held, outside the Opportunity Zone 10-year rule (a qualifying investment held 10 years with the IRC §1400Z-2(c) election): §1245 recapture on the 5- and 7-year property is ordinary income, as is §1250 recapture of the depreciation taken above straight-line (bonus included) on 15-year land improvements held more than one year, and the rest of the depreciation on those land improvements and on the 27.5- or 39-year structure is unrecaptured §1250 gain, taxed at up to 25% for a noncorporate taxpayer."
- `limitations[3]`: "A deduction is only usable against the owner's passive-activity position for that year; a large paper deduction is worth nothing to a taxpayer who cannot use it."
- `limitations[4]`: "This is educational information, not tax advice, and it is not a completed cost segregation study."

Say that this is a planning estimate and not a completed study, and that the API cannot order or pay for a study.

### What does a cost segregation study cost, and what do I get?

<a id="worked-pricing"></a>

What a study costs and what it includes

Step 1: call `get_cost_segregation_pricing`. Read the study tiers and what each includes.

**curl**

```bash
curl -s 'https://www.clickdragfinance.com/api/agent/v1/pricing'
```

**Reply (HTTP 200)**

The reply is identical to the example reply in the [get_cost_segregation_pricing reference](https://www.clickdragfinance.com/developers-compact.md#get_cost_segregation_pricing-response); it is not printed twice here.

How to phrase the answer:

Give each tier's `price` together with its size condition and its exclusions, in the same sentence, using these reply fields quoted exactly:

- `tiers[0].appropriateFor`: "A property whose purchase price plus construction costs total under $2,000,000, used as a rental. Offered to the owner, and to a CPA or advisor acting for a client. Reviewed before release and unsigned (it is not signed by a CPA or an engineer, or by anyone else)."
- `tiers[0].excludes`: "An Excel workbook Year-by-year depreciation schedules Item listings, exhibits, photo pages or appendices Qualified human review of every component A documentation package built to the IRS ATG Engineer sign-off A site visit, virtual or physical The optional data packages that can be added to a study"
- `tiers[1].appropriateFor`: "A cost segregation study for a property whose purchase price plus construction costs total up to $250,000,000. A CPA or tax advisor can order either product for a client on the ClickDrag Finance website, not through this API: the $499* cost segregation report when the client's property is under $2.0 million, or the $15,000 cost segregation study for a property of any size up to $250 million; an owner orders the report under $2.0 million and the study from $2.0 million. *For a property whose purchase price plus construction costs total under $2.0 million."
- `tiers[1].excludes`: "A licensed engineer's signature A site visit, virtual or physical"
- `limitations[0]`: "Prices are in USD and current as of the contract version returned. Confirm on the pricing page before quoting them to a client."

Pair it with a quote when there is a specific property, since a fee alone is not a decision. Say that this is a planning estimate and not a completed study, and that the API cannot order or pay for a study.

### What share of a multifamily building is typically reclassified by a cost segregation study?

<a id="worked-benchmarks"></a>

The typical reclassified share for a property type

Step 1: call `get_cost_segregation_benchmarks`. Read the allocation band for one property type.

**curl**

```bash
curl -s 'https://www.clickdragfinance.com/api/agent/v1/benchmarks?propertyType=Multi-family%20(5%2B%20units)'
```

**Reply (HTTP 200)**

Reply, top-level fields: contractVersion, unit, basisDefinition, license, citation, citationInstruction, provenance, benchmarks, limitations, knownPropertyTypes. The complete reply is in the full file: https://www.clickdragfinance.com/developers.md

How to phrase the answer:

For Multi-family (5+ units) the band is 20% to 30% (typically 25%), read from `benchmarks[0]` as decimals: `low`, `typical`, `high`. Never state the band without these reply fields, quoted exactly:

- `basisDefinition`: "Each band is the share of DEPRECIABLE basis — total basis less land — that moves out of the 27.5-year or 39-year structural bucket into 5-, 7- and 15-year property. Bands are stated as decimals, so 0.40 means 40%."
- `citation`: "ClickDrag Finance Cost Segregation Allocation Benchmarks, version 1.1.0, https://www.clickdragfinance.com/agents"
- `citationInstruction`: "When quoting a band, reproduce the citation string, the property type, the unit and the basis definition. A band without its basis definition is not a usable number."
- `limitations[0]`: "These are expert-maintained planning bands. They are not percentiles, not confidence intervals, not guarantees and not tax advice."
- `limitations[1]`: "An individual property routinely falls outside its band — construction type, document quality and site improvements move it."

### Who reviews and signs a ClickDrag Finance study, and do they do site visits?

<a id="worked-provider-facts"></a>

Is this provider credible, and who reviews the study?

Step 1: call `get_cost_segregation_provider_facts`. Read who prepares and reviews a study, and what is and is not included.

**curl**

```bash
curl -s 'https://www.clickdragfinance.com/api/agent/v1/provider-facts'
```

**Reply (HTTP 200)**

The reply is identical to the example reply in the [get_cost_segregation_provider_facts reference](https://www.clickdragfinance.com/developers-compact.md#get_cost_segregation_provider_facts-response); it is not printed twice here.

How to phrase the answer:

State the answers as the reply gives them, including the ones that are no (`engineerSignOff.includedInAnyBasePrice` is false). Quote these reply fields exactly and add no credential they do not state:

- `humanReview.fullyEngineeredTier`: "Qualified human review and oversight, with a dedicated project manager."
- `humanReview.aiReportTier`: "Reviewed before release and unsigned: a workpaper for the taxpayer's tax preparer, prepared from the information the buyer provides. It is not signed by a CPA or an engineer, it is not a signed engineering study, and it carries no qualified human review of every component."
- `inspection.note`: "There is no site visit, virtual or physical, at either tier. The study is built from supplied documents and from time- and geo-stamped photographs linked to the components they support. If a physical inspection is a requirement, another provider is the better fit."
- `turnaround.aiReport`: "Typically 3–5 business days from receipt of all required documents"
- `turnaround.fullyEngineered`: "Typically 15–20 business days from receipt of complete documents"
- `turnaround.note`: "The clock starts when documents arrive, not at purchase."
- `limitations[1]`: "No licensed engineer signs a study at any base price. If a signature is a hard requirement, buy the add-on or use another provider."

Say that this is a planning estimate and not a completed study, and that the API cannot order or pay for a study.

### My client wants to talk to someone about a study.

<a id="worked-consult"></a>

Hand the person to a human

Step 1: call `book_cost_segregation_consult`. Create a consultation request (a real call writes a lead and emails our team).

**curl**

```bash
curl -s -X POST 'https://www.clickdragfinance.com/api/agent/v1/consult' \
  -H 'Content-Type: application/json' \
  -d '{
  "email": "owner@example.com",
  "name": "Pat Example",
  "propertyType": "Self-Storage",
  "estimatedBasis": 4200000,
  "notes": "Owner asked to speak to someone about a study on a self-storage facility.",
  "agentName": "Example Assistant"
}'
```

**Reply (HTTP 200)**

```json
{
  "contractVersion": "1.1.0",
  "status": "received",
  "retryable": false,
  "reference": "CDF-AG-7QK2M4",
  "schedulingUrl": "https://calendly.com/example/consult?utm_source=agent_api",
  "recordedAs": "lead",
  "message": "Consultation request received. Open the scheduling link to pick a time.",
  "nextSteps": [
    "Give the person this link to choose a time: https://calendly.com/example/consult?utm_source=agent_api",
    "Quote reference CDF-AG-7QK2M4 in any follow-up.",
    "No documents are needed to hold the call."
  ],
  "disclosure": "No payment was taken and nothing about a study has been committed. Pricing is on the pricing page and in get_cost_segregation_pricing."
}
```

How to phrase the answer:

Call this only after the person has said they want to talk to someone: it writes a consultation request and emails our team, every time, and it is not idempotent, so a second call creates a second request. Its limit is 5 requests a minute per IP. The reply above was generated with the database and email replaced by stand-ins, so no request was recorded. Relay these reply fields, quoted exactly:

- `message`: "Consultation request received. Open the scheduling link to pick a time."
- `nextSteps`: "Give the person this link to choose a time: https://calendly.com/example/consult?utm_source=agent_api Quote reference CDF-AG-7QK2M4 in any follow-up. No documents are needed to hold the call."
- `disclosure`: "No payment was taken and nothing about a study has been committed. Pricing is on the pricing page and in get_cost_segregation_pricing."

If the reply has `status` `"failed"` (HTTP 503) nothing was recorded. Do not tell the person we have their request; relay its `message` and `nextSteps`. For example, when the hourly capacity is full: `message`: "The consultation request was NOT recorded: this endpoint is over its hourly capacity. Nobody has these details. Do not tell the person we have their request."

### The $4.2M self-storage facility is in a Qualified Opportunity Zone. Does that change the estimate?

<a id="worked-opportunity-zone"></a>

An Opportunity Zone property

Step 1: call `quote_cost_segregation_study`. Estimate the same property with the Opportunity Zone flag set.

**curl**

```bash
curl -s -X POST 'https://www.clickdragfinance.com/api/agent/v1/estimate' \
  -H 'Content-Type: application/json' \
  -d '{
  "propertyType": "Self-Storage",
  "acquisitionType": "purchased_no_reno",
  "currentTaxYear": 2026,
  "isOpportunityZone": true,
  "purchasePrice": 4200000,
  "acquiredDate": "2026-03-14"
}'
```

**Reply (HTTP 200)**

The reply is identical to the example reply in the [quote_cost_segregation_study reference](https://www.clickdragfinance.com/developers-compact.md#quote_cost_segregation_study-response); it is not printed twice here.

How to phrase the answer:

The depreciation figures are the same as without the flag, as `opportunityZone.note`: "The depreciation figures above are unchanged by Opportunity Zone status. What changes is which conditions have to travel with them." The flag adds `opportunityZone.conditions`. Carry each condition with the claim it limits, in the same sentence, quoted exactly:

- `opportunityZone.conditions[0]`: "Recapture is eliminated only after a 10-year hold combined with the §1400Z-2(c) fair-market-value basis election. Before year 10, recapture is entirely normal: §1245 recapture on the 5- and 7-year property is ordinary income, as is §1250 recapture of the depreciation taken above straight-line (bonus included) on 15-year land improvements held more than one year, and the rest of the depreciation on those land improvements and on the 27.5- or 39-year structure is unrecaptured §1250 gain, taxed at up to 25% for a noncorporate taxpayer."
- `opportunityZone.conditions[1]`: "A QOF interest funded with deferred gain starts at a $0 tax basis under §1400Z-2(b)(2)(B), and partnership losses are deductible only up to basis under §704(d) — so accelerated depreciation can be real and still unusable in the year it arises."
- `opportunityZone.conditions[2]`: "In practice the deduction is usually still usable, because a partner's share of partnership debt counts as basis under §752 and OZ real estate typically runs 50–70% leverage. Check the specific capital account rather than assuming either way."
- `opportunityZone.conditions[3]`: "Selling the QOF interest and the fund selling assets do not behave identically. Do not state one and imply the other."
- `opportunityZone.conditions[4]`: "For a substantial improvement, the 30-month test under §1400Z-2 and the 31-month working-capital safe harbour are separate clocks from placed-in-service, and which costs land in which year changes what the study is worth."

Also send `bonus.basis` and `assessment.guidance` from the reply, quoted exactly, and relay the fee block as returned: for this input `fee.aiReport` is `null`, and `fee.aiReportUnavailable` says why; send it with the figure and do not drop it.

- `bonus.basis`: "Acquired (or construction begun) on 2026-03-14, after 2025-01-19, so 100% bonus depreciation applies and is permanent under OBBBA. Per IRS Notice 2026-11 the acquired-date test turns on the written binding contract, not the closing date: the acquired date is the latest of the date the contract was signed, the date it became enforceable under state law, the end of any cancellation period, and the date any contingency was satisfied (Treas. Reg. §1.168(k)-2(b)(5)(ii)(B)). This applies to a written binding contract, meaning one enforceable under state law that does not limit damages to a specified amount (for example through a liquidated damages clause); under a contract that is not binding in that sense, the acquired date is generally when the taxpayer has paid or incurred more than 10 percent of the total cost, excluding land and preliminary activities (Treas. Reg. §1.168(k)-2(b)(5)(iii) and (v))."
- `assessment.guidance`: "Above roughly $500,000 of depreciable basis a study usually pays for itself, provided the deduction is usable against the owner's passive-activity position this year and the hold period is long enough to outrun recapture. All three conditions, or the honest answer is no."

Say that this is a planning estimate and not a completed study, and that the API cannot order or pay for a study.

## For assistants that only read

<a id="for-assistants"></a>

An assistant that can fetch pages but not call APIs can read these instead. All are generated from the same contract as the API.

- [llms.txt](https://www.clickdragfinance.com/llms.txt): the short, extractable summary of the business and the tools
- [llms-full.txt](https://www.clickdragfinance.com/llms-full.txt): the long form: every tool, the benchmark table and the pricing, generated from the same code as the API
- [developers-compact.md](https://www.clickdragfinance.com/developers-compact.md): the compact Markdown: curl examples only, long replies listed by their top-level fields
- [developers.md](https://www.clickdragfinance.com/developers.md): this documentation as Markdown, with every reply in full
- [api-catalog](https://www.clickdragfinance.com/.well-known/api-catalog): the RFC 9727 API catalog (linkset) pointing at the OpenAPI file, this documentation and the MCP endpoint
- [openapi.json](https://www.clickdragfinance.com/openapi.json): the OpenAPI 3.1 description, with request and response examples
- [mcp.json](https://www.clickdragfinance.com/.well-known/mcp.json): the MCP server descriptor
- [ai-plugin.json](https://www.clickdragfinance.com/.well-known/ai-plugin.json): the ChatGPT plugin-style manifest
- [claude-skill.md](https://www.clickdragfinance.com/agents/claude-skill.md): the Claude Agent Skill
- [chatgpt-instructions.md](https://www.clickdragfinance.com/agents/chatgpt-instructions.md): instructions for a custom GPT Action

## Changelog

<a id="changelog"></a>

`1.1.0`, 2026-10-12. Additive only; no request that worked before stops working.

- HTTP 413 for a POST body over 65,536 bytes, on the REST routes and the MCP endpoint.
- A `documentation` field in every 400 body, linking the tool's section of `/developers.md`.
- A `Link` header on every reply (`service-desc` to the OpenAPI file, `service-doc` to this documentation), exposed to browsers.
- Retry-After is 600 seconds when the consult hourly capacity is full (it stays 5 for a transient failure); a failed body carries `reason`: `capacity` or `unrecorded`.
- Strings containing control characters, or an unpaired UTF-16 surrogate, are refused with a 400 naming the field.
- Over MCP, a validation failure lists every error and the `documentation` URL in the tool error text and in `structuredContent`.
- The body size limit is enforced while the body is read: a body with no declared length is cancelled once it passes the limit.
- A validation reply lists at most 20 field errors, then one entry counting the rest; `message` is the first error only.
- Request and error examples in the OpenAPI file, and example replies that are the handlers' real output.
- The provider-facts `turnaround` figures now state their condition (counted from receipt of documents).
- Reply wording only, changed in place on 2026-10-12 after `1.1.0` was deployed the same day; no field added or removed, so the version stays `1.1.0` (registry publish pending): `fee.fullyEngineeredAudience`, `fee.aiReportUnavailable` and the tiers' `appropriateFor` now say the study is offered to an owner at or above the report ceiling as well as to a CPA or advisor, and that the report is offered to a CPA or advisor for a client's property under the ceiling as well as to an owner (ordering happens on the website, never through this API). The report's size condition no longer says "you own".
