> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://developer.fortresstech.io/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://developer.fortresstech.io/_mcp/server.

# Read agent state for a property

GET https://api.fortresstech.io/v1/agents/{agentKey}/state

Returns whether the agent is enabled for one property, and when that last changed. Staff enable or
disable the agent per property in Fortress' agent configuration. While `isEnabled` is `false`, the
agent does no work for that property.

A property that has never been configured returns `isEnabled: false` and `updatedAt: null`.

For `agentKey: cora` the response also carries `cadencePolicy`: the outreach cadence CORA executes for
the property. It is the policy staff stored for the property, or the baseline seed when the property never
set one, so a property with no CORA configuration also gets the default. Leslie responses do not carry it.
`cadencePolicy.isFallback` is read-only: `true` when the property has no stored policy and Fortress
returns its default (the baseline seed, with `expireAfterLastTouchpoint` of 7 days), `false` for the
property's stored policy. Staff set the policy in Fortress; there is no partner write. Read it as data
and do not hard-code the timing.

The [`agent.state_changed`](/webhooks/webhook-events/webhooks/agent-state-changed) webhook is sent
when staff flip the switch (`scope: PROPERTY`), including when deleting an enabled configuration
disables the agent. It is also sent when staff change the CORA cadence policy: re-read this route and
apply the new policy from the next touchpoint. Read the state on startup and after a missed webhook.

The API key needs the `agents: read` permission to call this endpoint, and the property must belong
to the key's organization.

Authenticate with the `x-api-key` header. `agentKey` is `cora` or `leslie`; any other value returns `404 AGENT_NOT_FOUND`. Clients must branch on `code`, not on `message`.

Reference: https://developer.fortresstech.io/api-reference/agents/get-agent-state

## Authentication

- `x-api-key` header (required) — API Key authentication via header

## Request

### Path parameters

- `agentKey` (string, required) — The agent, `cora` or `leslie`. Any other value returns 404 AGENT_NOT_FOUND.

### Query parameters

- `propertyId` (string, required) — The property to read the agent state for.

## Response

### 200

The agent's state for the property.

- `propertyId` (string, required) — The property the state applies to.
- `agentKey` (enum, required) — The agent, `cora` or `leslie`.
  - Allowed values: `cora`, `leslie`
- `isEnabled` (boolean, required) — Whether the agent is enabled for the property. `false`: the agent does no work for the property until it is enabled again. `false` when the property has never been configured.
- `updatedAt` (datetime, required, nullable) — When the state last changed for the property. `null` when the property has never been configured.
- `cadencePolicy` (CadencePolicy, optional) — The outreach cadence CORA executes for the property. Returned only for `agentKey: cora`: the policy staff stored for the property, or the default (the baseline seed, `isFallback: true`) when the property never set one or has no CORA configuration. Not returned for `leslie`. Staff set the policy in Fortress; there is no partner write. Read it as data, never hard-code it.

## Errors

### 400 Bad Request Error

`propertyId` is missing or not a valid UUID (INVALID_PROPERTY_ID).

- `code` (enum, required)
  - Allowed values: `AGENT_NOT_FOUND`, `PROPERTY_NOT_FOUND`
- `message` (string, optional)

### 401 Unauthorized Error

No `x-api-key` header (API Gateway, before the authorizer), or Fortress refuses a key that passed the gateway.

- `any`

### 403 Forbidden Error

The x-api-key is unknown (authorizer), or the key does not have the `agents: read` permission (authorizer).

- `any`

### 404 Not Found Error

No agent with this agentKey (AGENT_NOT_FOUND), or the property does not exist or does not belong to the key's organization (PROPERTY_NOT_FOUND).

- `code` (enum, required)
  - Allowed values: `AGENT_NOT_FOUND`, `PROPERTY_NOT_FOUND`
- `message` (string, optional)

## Types

### CadencePolicy

One outreach cadence per property, stored by Fortress and executed by CORA. The touchpoints are the whole cadence. The scope is fixed: one cadence per household; the clock starts at `application.signed` and resets to offset 0 on any applicant response or upload (`requirement.upload_received`), which does not stop it; each touchpoint sends one message per household covering all outstanding requirements. CORA stops the cadence when the application is complete (every requirement `VALIDATED`) or canceled, denied or abandoned. When a channel fails (for example an invalid phone number), CORA may move on to the next channel or touchpoint. CORA runs the cadence whenever the agent is enabled (`isEnabled`); the policy has no on/off of its own.

- `touchpoints` (list of CadenceTouchpoint, required) — Strictly increasing by total offset.
- `expireAfterLastTouchpoint` (CadencePolicyExpireAfterLastTouchpoint, required) — How long after the last touchpoint, without any applicant interaction, CORA waits before it cancels the application with `CADENCE_EXPIRED`. Non-negative integers, at least one present, total above 0. Always returned; a stored policy that did not set it reads as `{ days: 7 }`.
- `isFallback` (boolean, required) — Read-only. `true`: the property has no stored policy and Fortress returns its default (the baseline seed, with `expireAfterLastTouchpoint` of 7 days). `false`: the property's stored policy.
- `quietHours` (CadenceQuietHours, optional, nullable) — Optional. Absent or `null` means no quiet hours.

### CadenceTouchpoint

- `offset` (CadenceOffset, required) — A duration in days and/or hours; what it is measured from depends on the field. At least one of `days` and `hours` is present. The total is `days x 24 + hours` hours.
- `channels` (list of enum, required) — Channels for the touchpoint. `SITE_TASK` marks where staff escalation happens. The site-task route is out of scope, so CORA stops outreach at that touchpoint and raises no task.
  - Allowed values: `SMS`, `CALL`, `SITE_TASK`
- `purpose` (string, optional) — Optional label, stored as given. CORA uses it to pick the message template family.

### CadencePolicyExpireAfterLastTouchpoint

How long after the last touchpoint, without any applicant interaction, CORA waits before it cancels the application with `CADENCE_EXPIRED`. Non-negative integers, at least one present, total above 0. Always returned; a stored policy that did not set it reads as `{ days: 7 }`.

- `days` (integer, optional)
- `hours` (integer, optional)

### CadenceQuietHours

A touchpoint that falls inside quiet hours is deferred to `end` in `timezone`.

- `start` (string, required) — HH:mm, 00:00 to 23:59.
- `end` (string, required) — HH:mm, 00:00 to 23:59.
- `timezone` (string, required) — IANA timezone name.

### CadenceOffset

A duration in days and/or hours; what it is measured from depends on the field. At least one of `days` and `hours` is present. The total is `days x 24 + hours` hours.

- any
- any

## Examples

### CORA with the baseline seed policy (the property never set one)

**Response**

```json
{
  "propertyId": "3a1e2b4c-5d6f-4a7b-8c9d-0e1f2a3b4c5d",
  "agentKey": "cora",
  "isEnabled": true,
  "updatedAt": "2026-10-12T15:00:00.000Z",
  "cadencePolicy": {
    "touchpoints": [
      {
        "offset": null,
        "channels": [
          "SMS"
        ]
      },
      {
        "offset": null,
        "channels": [
          "SMS"
        ]
      },
      {
        "offset": null,
        "channels": [
          "SMS",
          "CALL"
        ]
      },
      {
        "offset": null,
        "channels": [
          "SMS",
          "CALL"
        ]
      },
      {
        "offset": null,
        "channels": [
          "SITE_TASK"
        ]
      }
    ],
    "expireAfterLastTouchpoint": {
      "days": 7
    },
    "isFallback": true
  }
}
```

**SDK Code**

```python CORA with the baseline seed policy (the property never set one)
import requests

url = "https://api.fortresstech.io/v1/agents/cora/state"

querystring = {"propertyId":"3a1e2b4c-5d6f-4a7b-8c9d-0e1f2a3b4c5d"}

headers = {"x-api-key": "<apiKey>"}

response = requests.get(url, headers=headers, params=querystring)

print(response.json())
```

```javascript CORA with the baseline seed policy (the property never set one)
const url = 'https://api.fortresstech.io/v1/agents/cora/state?propertyId=3a1e2b4c-5d6f-4a7b-8c9d-0e1f2a3b4c5d';
const options = {method: 'GET', headers: {'x-api-key': '<apiKey>'}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go CORA with the baseline seed policy (the property never set one)
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://api.fortresstech.io/v1/agents/cora/state?propertyId=3a1e2b4c-5d6f-4a7b-8c9d-0e1f2a3b4c5d"

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("x-api-key", "<apiKey>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby CORA with the baseline seed policy (the property never set one)
require 'uri'
require 'net/http'

url = URI("https://api.fortresstech.io/v1/agents/cora/state?propertyId=3a1e2b4c-5d6f-4a7b-8c9d-0e1f2a3b4c5d")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["x-api-key"] = '<apiKey>'

response = http.request(request)
puts response.read_body
```

```java CORA with the baseline seed policy (the property never set one)
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.get("https://api.fortresstech.io/v1/agents/cora/state?propertyId=3a1e2b4c-5d6f-4a7b-8c9d-0e1f2a3b4c5d")
  .header("x-api-key", "<apiKey>")
  .asString();
```

```php CORA with the baseline seed policy (the property never set one)
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('GET', 'https://api.fortresstech.io/v1/agents/cora/state?propertyId=3a1e2b4c-5d6f-4a7b-8c9d-0e1f2a3b4c5d', [
  'headers' => [
    'x-api-key' => '<apiKey>',
  ],
]);

echo $response->getBody();
```

```csharp CORA with the baseline seed policy (the property never set one)
using RestSharp;

var client = new RestClient("https://api.fortresstech.io/v1/agents/cora/state?propertyId=3a1e2b4c-5d6f-4a7b-8c9d-0e1f2a3b4c5d");
var request = new RestRequest(Method.GET);
request.AddHeader("x-api-key", "<apiKey>");
IRestResponse response = client.Execute(request);
```

```swift CORA with the baseline seed policy (the property never set one)
import Foundation

let headers = ["x-api-key": "<apiKey>"]

let request = NSMutableURLRequest(url: NSURL(string: "https://api.fortresstech.io/v1/agents/cora/state?propertyId=3a1e2b4c-5d6f-4a7b-8c9d-0e1f2a3b4c5d")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

### CORA with the Product faster cadence

**Response**

```json
{
  "propertyId": "3a1e2b4c-5d6f-4a7b-8c9d-0e1f2a3b4c5d",
  "agentKey": "cora",
  "isEnabled": true,
  "updatedAt": "2026-10-12T15:00:00.000Z",
  "cadencePolicy": {
    "touchpoints": [
      {
        "offset": null,
        "channels": [
          "SMS"
        ],
        "purpose": "INTRO"
      },
      {
        "offset": null,
        "channels": [
          "SMS"
        ],
        "purpose": "GET_STARTED"
      },
      {
        "offset": null,
        "channels": [
          "SMS"
        ],
        "purpose": "MOMENTUM"
      },
      {
        "offset": null,
        "channels": [
          "SMS"
        ],
        "purpose": "PROGRESS"
      },
      {
        "offset": null,
        "channels": [
          "SMS",
          "CALL"
        ],
        "purpose": "URGENCY"
      },
      {
        "offset": null,
        "channels": [
          "SITE_TASK"
        ],
        "purpose": "ESCALATION"
      }
    ],
    "expireAfterLastTouchpoint": {
      "days": 7
    },
    "isFallback": false,
    "quietHours": {
      "start": "20:00",
      "end": "08:00",
      "timezone": "America/Chicago"
    }
  }
}
```

**SDK Code**

```python CORA with the Product faster cadence
import requests

url = "https://api.fortresstech.io/v1/agents/cora/state"

querystring = {"propertyId":"3a1e2b4c-5d6f-4a7b-8c9d-0e1f2a3b4c5d"}

headers = {"x-api-key": "<apiKey>"}

response = requests.get(url, headers=headers, params=querystring)

print(response.json())
```

```javascript CORA with the Product faster cadence
const url = 'https://api.fortresstech.io/v1/agents/cora/state?propertyId=3a1e2b4c-5d6f-4a7b-8c9d-0e1f2a3b4c5d';
const options = {method: 'GET', headers: {'x-api-key': '<apiKey>'}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go CORA with the Product faster cadence
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://api.fortresstech.io/v1/agents/cora/state?propertyId=3a1e2b4c-5d6f-4a7b-8c9d-0e1f2a3b4c5d"

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("x-api-key", "<apiKey>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby CORA with the Product faster cadence
require 'uri'
require 'net/http'

url = URI("https://api.fortresstech.io/v1/agents/cora/state?propertyId=3a1e2b4c-5d6f-4a7b-8c9d-0e1f2a3b4c5d")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["x-api-key"] = '<apiKey>'

response = http.request(request)
puts response.read_body
```

```java CORA with the Product faster cadence
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.get("https://api.fortresstech.io/v1/agents/cora/state?propertyId=3a1e2b4c-5d6f-4a7b-8c9d-0e1f2a3b4c5d")
  .header("x-api-key", "<apiKey>")
  .asString();
```

```php CORA with the Product faster cadence
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('GET', 'https://api.fortresstech.io/v1/agents/cora/state?propertyId=3a1e2b4c-5d6f-4a7b-8c9d-0e1f2a3b4c5d', [
  'headers' => [
    'x-api-key' => '<apiKey>',
  ],
]);

echo $response->getBody();
```

```csharp CORA with the Product faster cadence
using RestSharp;

var client = new RestClient("https://api.fortresstech.io/v1/agents/cora/state?propertyId=3a1e2b4c-5d6f-4a7b-8c9d-0e1f2a3b4c5d");
var request = new RestRequest(Method.GET);
request.AddHeader("x-api-key", "<apiKey>");
IRestResponse response = client.Execute(request);
```

```swift CORA with the Product faster cadence
import Foundation

let headers = ["x-api-key": "<apiKey>"]

let request = NSMutableURLRequest(url: NSURL(string: "https://api.fortresstech.io/v1/agents/cora/state?propertyId=3a1e2b4c-5d6f-4a7b-8c9d-0e1f2a3b4c5d")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

### CORA, property never configured (default policy, isFallback true)

**Response**

```json
{
  "propertyId": "3a1e2b4c-5d6f-4a7b-8c9d-0e1f2a3b4c5d",
  "agentKey": "cora",
  "isEnabled": false,
  "updatedAt": null,
  "cadencePolicy": {
    "touchpoints": [
      {
        "offset": null,
        "channels": [
          "SMS"
        ]
      },
      {
        "offset": null,
        "channels": [
          "SMS"
        ]
      },
      {
        "offset": null,
        "channels": [
          "SMS",
          "CALL"
        ]
      },
      {
        "offset": null,
        "channels": [
          "SMS",
          "CALL"
        ]
      },
      {
        "offset": null,
        "channels": [
          "SITE_TASK"
        ]
      }
    ],
    "expireAfterLastTouchpoint": {
      "days": 7
    },
    "isFallback": true
  }
}
```

**SDK Code**

```python CORA, property never configured (default policy, isFallback true)
import requests

url = "https://api.fortresstech.io/v1/agents/cora/state"

querystring = {"propertyId":"3a1e2b4c-5d6f-4a7b-8c9d-0e1f2a3b4c5d"}

headers = {"x-api-key": "<apiKey>"}

response = requests.get(url, headers=headers, params=querystring)

print(response.json())
```

```javascript CORA, property never configured (default policy, isFallback true)
const url = 'https://api.fortresstech.io/v1/agents/cora/state?propertyId=3a1e2b4c-5d6f-4a7b-8c9d-0e1f2a3b4c5d';
const options = {method: 'GET', headers: {'x-api-key': '<apiKey>'}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go CORA, property never configured (default policy, isFallback true)
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://api.fortresstech.io/v1/agents/cora/state?propertyId=3a1e2b4c-5d6f-4a7b-8c9d-0e1f2a3b4c5d"

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("x-api-key", "<apiKey>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby CORA, property never configured (default policy, isFallback true)
require 'uri'
require 'net/http'

url = URI("https://api.fortresstech.io/v1/agents/cora/state?propertyId=3a1e2b4c-5d6f-4a7b-8c9d-0e1f2a3b4c5d")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["x-api-key"] = '<apiKey>'

response = http.request(request)
puts response.read_body
```

```java CORA, property never configured (default policy, isFallback true)
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.get("https://api.fortresstech.io/v1/agents/cora/state?propertyId=3a1e2b4c-5d6f-4a7b-8c9d-0e1f2a3b4c5d")
  .header("x-api-key", "<apiKey>")
  .asString();
```

```php CORA, property never configured (default policy, isFallback true)
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('GET', 'https://api.fortresstech.io/v1/agents/cora/state?propertyId=3a1e2b4c-5d6f-4a7b-8c9d-0e1f2a3b4c5d', [
  'headers' => [
    'x-api-key' => '<apiKey>',
  ],
]);

echo $response->getBody();
```

```csharp CORA, property never configured (default policy, isFallback true)
using RestSharp;

var client = new RestClient("https://api.fortresstech.io/v1/agents/cora/state?propertyId=3a1e2b4c-5d6f-4a7b-8c9d-0e1f2a3b4c5d");
var request = new RestRequest(Method.GET);
request.AddHeader("x-api-key", "<apiKey>");
IRestResponse response = client.Execute(request);
```

```swift CORA, property never configured (default policy, isFallback true)
import Foundation

let headers = ["x-api-key": "<apiKey>"]

let request = NSMutableURLRequest(url: NSURL(string: "https://api.fortresstech.io/v1/agents/cora/state?propertyId=3a1e2b4c-5d6f-4a7b-8c9d-0e1f2a3b4c5d")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```