# DoorLoop API

> Every DoorLoop endpoint on Cotlet: inputs, JSON Schema, cURL, Python and Node.js examples and an example response for each call.

Source: https://cotlet.dev/docs/api/doorloop

Docs sections: [Get started](https://cotlet.dev/docs.md) · [REST API](https://cotlet.dev/docs/api.md) · [MCP](https://cotlet.dev/docs/mcp.md) · [SDKs & CLI](https://cotlet.dev/docs/sdks.md) · [Integrations](https://cotlet.dev/docs/integrations.md)

14 endpoints for DoorLoop. Each one is a POST with a JSON body, called with your API key.

- Base URL: `https://cotlet.dev/api/v1/doorloop`
- Connects with: Your own DoorLoop account (a user who can manage leases, payments and communications)
- Integration page: https://cotlet.dev/integrations/doorloop-api-mcp-server

## Endpoints

| Endpoint | Access | Description |
|---|---|---|
| `list_properties` | read | Your properties with address, type and unit count. |
| `list_units` | read | Units with property, rent and whether each is occupied or vacant. |
| `list_tenants` | read | Tenants with email, phone, unit and current lease. |
| `list_leases` | read | Leases with unit, tenants, rent, start and end dates and balance due. |
| `get_lease_transactions` | read | Charges, payments and credits on a lease, oldest first, with the running balance. |
| `list_tasks` | read | Tasks and work orders with status, due date, property and assignee. |
| `list_communications` | read | The Communications Log: announcements and messages sent, with channel and delivery status. |
| `create_tenant` | write | Add a tenant contact, ready to put on a lease. Sends nothing. |
| `create_task` | write | New task or work order for a property or unit. |
| `create_lease` | write | New lease on a unit with tenants, term, monthly rent and security deposit. |
| `post_lease_charge` | write | One-time charge on a lease, such as a late fee. It shows on the tenant's balance. |
| `record_lease_payment` | write | Record a payment received outside online payments and apply it to open charges. |
| `issue_lease_credit` | write | Credit a lease, such as a rent concession, lowering the tenant's balance. |
| `send_announcement` | write | Announcement to tenants by email, text or tenant portal. Texts use announcement credits. |

## List properties

`POST https://cotlet.dev/api/v1/doorloop/list_properties`

Access: read.

Returns the properties in your DoorLoop account, with address and number of units. Search matches the property name or address. Use the property_id of each item in the other tools.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `search` | string | no | Match on property name or address. |
| `cursor` | string | no | Next page cursor from the previous call. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/doorloop/list_properties \
  -H "Authorization: Bearer $COTLET_API_KEY"
```

**Python**

```python
result = client.call("doorloop", "list_properties")
```

**Node.js**

```js
const result = await cotlet.call('doorloop', 'list_properties');
```

**CLI**

```bash
cotlet call doorloop list_properties
```

### Example response

```json
{
  "properties": [
    {
      "property_id": "pr_3a91c7",
      "name": "Maple Court",
      "address": "120 Maple St, Springfield",
      "type": "Multi-family",
      "units": 12
    }
  ],
  "next_cursor": null
}
```

## List units

`POST https://cotlet.dev/api/v1/doorloop/list_units`

Access: read.

Returns units with the property they belong to and their occupancy. Filter by property or by occupied or vacant. Use unit_id from here in create_lease.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `property_id` | string | no | Only this property. Property ID from list_properties. |
| `status` | enum | no | all, occupied or vacant. Default: all. |
| `cursor` | string | no | Next page cursor from the previous call. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/doorloop/list_units \
  -H "Authorization: Bearer $COTLET_API_KEY"
```

**Python**

```python
result = client.call("doorloop", "list_units")
```

**Node.js**

```js
const result = await cotlet.call('doorloop', 'list_units');
```

**CLI**

```bash
cotlet call doorloop list_units
```

### Example response

```json
{
  "units": [
    {
      "unit_id": "un_5b20d4",
      "property": "Maple Court",
      "name": "Unit 4",
      "status": "vacant",
      "market_rent": "1600.00"
    }
  ],
  "next_cursor": null
}
```

## List tenants

`POST https://cotlet.dev/api/v1/doorloop/list_tenants`

Access: read.

Returns tenants with contact details and the lease they are on. Search matches name, email or phone. Use tenant_id from here in create_lease.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `search` | string | no | Match on name, email or phone. |
| `property_id` | string | no | Only this property. Property ID from list_properties. |
| `cursor` | string | no | Next page cursor from the previous call. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/doorloop/list_tenants \
  -H "Authorization: Bearer $COTLET_API_KEY"
```

**Python**

```python
result = client.call("doorloop", "list_tenants")
```

**Node.js**

```js
const result = await cotlet.call('doorloop', 'list_tenants');
```

**CLI**

```bash
cotlet call doorloop list_tenants
```

### Example response

```json
{
  "tenants": [
    {
      "tenant_id": "tn_91c0e7",
      "name": "Dana Reed",
      "email": "dana@example.com",
      "phone": "+15555550123",
      "unit": "Unit 4",
      "lease_id": null
    }
  ],
  "next_cursor": null
}
```

## List leases

`POST https://cotlet.dev/api/v1/doorloop/list_leases`

Access: read.

Returns leases with the unit, the tenants on them, monthly rent, dates and the balance due. Filter by status, by end date or by property. Use lease_id from here in the other lease tools. Search matches unit or tenant name.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `search` | string | no | Match on unit or tenant name. |
| `status` | enum | no | active or ended. Default: active. |
| `ends_before` | date | no | Only leases ending on or before this date. |
| `property_id` | string | no | Only this property. Property ID from list_properties. |
| `cursor` | string | no | Next page cursor from the previous call. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/doorloop/list_leases \
  -H "Authorization: Bearer $COTLET_API_KEY"
```

**Python**

```python
result = client.call("doorloop", "list_leases")
```

**Node.js**

```js
const result = await cotlet.call('doorloop', 'list_leases');
```

**CLI**

```bash
cotlet call doorloop list_leases
```

### Example response

```json
{
  "leases": [
    {
      "lease_id": "ls_64f1a9c2",
      "unit": "Unit 2B",
      "property": "Maple Court",
      "tenants": [
        "Marcus Hale"
      ],
      "rent": "1450.00",
      "start_date": "2026-01-01",
      "end_date": "2026-12-31",
      "balance_due": "1450.00"
    }
  ],
  "next_cursor": null
}
```

## Get lease transactions

`POST https://cotlet.dev/api/v1/doorloop/get_lease_transactions`

Access: read.

Returns the ledger of one lease, oldest first: charges, payments and credits, each with a running balance. Use it to see whether a rent charge is still open before you add a fee or record a payment.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `lease_id` | string | yes | Lease ID from list_leases. |
| `from` | date | no | Start date. Default: lease start. |
| `to` | date | no | End date. Default: today. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/doorloop/get_lease_transactions \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"lease_id":"lease_8c41d2"}'
```

**Python**

```python
result = client.call("doorloop", "get_lease_transactions", {"lease_id":"lease_8c41d2"})
```

**Node.js**

```js
const result = await cotlet.call('doorloop', 'get_lease_transactions', {"lease_id":"lease_8c41d2"});
```

**CLI**

```bash
cotlet call doorloop get_lease_transactions --data '{"lease_id":"lease_8c41d2"}'
```

### Example response

```json
{
  "lease_id": "ls_64f1a9c2",
  "transactions": [
    {
      "type": "charge",
      "date": "2026-10-01",
      "description": "Rent October 2026",
      "amount": "1450.00",
      "balance": "1450.00"
    }
  ],
  "balance_due": "1450.00"
}
```

## List tasks

`POST https://cotlet.dev/api/v1/doorloop/list_tasks`

Access: read.

Returns tasks and work orders, filtered by status or property. Each item has its subject, the property or unit it concerns, the due date and who it is assigned to.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `status` | string | no | Only tasks with this status, as DoorLoop shows it. |
| `property_id` | string | no | Only this property. Property ID from list_properties. |
| `cursor` | string | no | Next page cursor from the previous call. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/doorloop/list_tasks \
  -H "Authorization: Bearer $COTLET_API_KEY"
```

**Python**

```python
result = client.call("doorloop", "list_tasks")
```

**Node.js**

```js
const result = await cotlet.call('doorloop', 'list_tasks');
```

**CLI**

```bash
cotlet call doorloop list_tasks
```

### Example response

```json
{
  "tasks": [
    {
      "task_id": "tk_20c8",
      "subject": "Fix hallway light",
      "property": "Maple Court",
      "unit": null,
      "status": "open",
      "due_date": "2026-10-15",
      "assignee": "Sam Ortiz"
    }
  ],
  "next_cursor": null
}
```

## List communications

`POST https://cotlet.dev/api/v1/doorloop/list_communications`

Access: read.

Returns entries from the Communications Log, newest first, with the channel, subject and delivery status. Use it after send_announcement to see who the announcement reached.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `since` | datetime | no | Only entries sent after this time. |
| `cursor` | string | no | Next page cursor from the previous call. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/doorloop/list_communications \
  -H "Authorization: Bearer $COTLET_API_KEY"
```

**Python**

```python
result = client.call("doorloop", "list_communications")
```

**Node.js**

```js
const result = await cotlet.call('doorloop', 'list_communications');
```

**CLI**

```bash
cotlet call doorloop list_communications
```

### Example response

```json
{
  "communications": [
    {
      "communication_id": "cm_77f0",
      "subject": "Water shutoff Thursday",
      "channel": "email",
      "sent_at": "2026-10-11T09:02:00Z",
      "delivery": "delivered",
      "recipients": 12
    }
  ],
  "next_cursor": null
}
```

## Create tenant

`POST https://cotlet.dev/api/v1/doorloop/create_tenant`

Access: write.

Adds one tenant contact. It sends nothing to the tenant and attaches no charge. It runs without asking first, because it reaches no one and the contact can be deleted in DoorLoop.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `first_name` | string | yes | First name. |
| `last_name` | string | yes | Last name. |
| `email` | string | no | Email address. |
| `phone` | string | no | Phone number, E.164. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/doorloop/create_tenant \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"first_name":"first name","last_name":"last name"}'
```

**Python**

```python
result = client.call("doorloop", "create_tenant", {"first_name":"first name","last_name":"last name"})
```

**Node.js**

```js
const result = await cotlet.call('doorloop', 'create_tenant', {"first_name":"first name","last_name":"last name"});
```

**CLI**

```bash
cotlet call doorloop create_tenant --data '{"first_name":"first name","last_name":"last name"}'
```

### Example response

```json
{
  "tenant_id": "tn_91c0e7",
  "name": "Dana Reed",
  "email": "dana@example.com",
  "status": "created"
}
```

## Create task

`POST https://cotlet.dev/api/v1/doorloop/create_task`

Access: write.

Creates a task or work order with a subject, optional property or unit and a due date. It does not message anyone by itself. It runs without asking first, because it reaches no tenant and the task can be removed in DoorLoop.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `subject` | string | yes | Short title. |
| `description` | string | no | Details for whoever does the work. |
| `property_id` | string | no | Only this property. Property ID from list_properties. |
| `unit_id` | string | no | Unit it concerns. |
| `due_date` | date | no | Due date. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/doorloop/create_task \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"subject":"subject"}'
```

**Python**

```python
result = client.call("doorloop", "create_task", {"subject":"subject"})
```

**Node.js**

```js
const result = await cotlet.call('doorloop', 'create_task', {"subject":"subject"});
```

**CLI**

```bash
cotlet call doorloop create_task --data '{"subject":"subject"}'
```

### Example response

```json
{
  "task_id": "tk_20c9",
  "subject": "Replace smoke detector, Unit 2B",
  "property": "Maple Court",
  "due_date": "2026-10-18",
  "status": "created"
}
```

## Create lease

`POST https://cotlet.dev/api/v1/doorloop/create_lease`

Access: write. Confirm: waits for the user's OK before it runs.

Creates a lease on one unit in a single step: tenants, term, recurring rent with its due day and an optional security deposit. Leave end_date empty for a month-to-month lease. The lease creates charges on the ledger. By default you confirm each call; you can switch that off for this tool.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `unit_id` | string | yes | Unit ID from list_units. |
| `tenant_ids` | string[] | yes | Tenant IDs from list_tenants or create_tenant. |
| `start_date` | date | yes | First day of the lease. |
| `end_date` | date | no | Last day. Leave empty for month-to-month. |
| `rent` | object | yes | amount and due_day of the recurring rent charge. |
| `security_deposit` | object | no | amount and due_date. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/doorloop/create_lease \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"unit_id":"un_5b20d4","tenant_ids":["tn_91c0e7"],"start_date":"2026-11-01","end_date":"2027-10-31","rent":{"amount":"1600.00","due_day":1},"security_deposit":{"amount":"1600.00","due_date":"2026-11-01"}}'
```

**Python**

```python
result = client.call("doorloop", "create_lease", {"unit_id":"un_5b20d4","tenant_ids":["tn_91c0e7"],"start_date":"2026-11-01","end_date":"2027-10-31","rent":{"amount":"1600.00","due_day":1},"security_deposit":{"amount":"1600.00","due_date":"2026-11-01"}})
```

**Node.js**

```js
const result = await cotlet.call('doorloop', 'create_lease', {"unit_id":"un_5b20d4","tenant_ids":["tn_91c0e7"],"start_date":"2026-11-01","end_date":"2027-10-31","rent":{"amount":"1600.00","due_day":1},"security_deposit":{"amount":"1600.00","due_date":"2026-11-01"}});
```

**CLI**

```bash
cotlet call doorloop create_lease --data '{"unit_id":"un_5b20d4","tenant_ids":["tn_91c0e7"],"start_date":"2026-11-01","end_date":"2027-10-31","rent":{"amount":"1600.00","due_day":1},"security_deposit":{"amount":"1600.00","due_date":"2026-11-01"}}'
```

### Example response

```json
{
  "lease_id": "ls_9e13b5",
  "unit": "Unit 4",
  "tenants": [
    "Dana Reed"
  ],
  "start_date": "2026-11-01",
  "end_date": "2027-10-31",
  "rent": {
    "amount": "1600.00",
    "due_day": 1
  },
  "security_deposit": {
    "amount": "1600.00",
    "due_date": "2026-11-01"
  },
  "status": "created"
}
```

## Post lease charge

`POST https://cotlet.dev/api/v1/doorloop/post_lease_charge`

Access: write. Confirm: waits for the user's OK before it runs.

Adds one charge to a lease ledger, for example a late fee or a utility bill. It raises the balance due. It does not change your late fee policy. By default you confirm each call; you can switch that off for this tool.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `lease_id` | string | yes | Lease ID from list_leases. |
| `amount` | string | yes | Decimal amount, e.g. "50.00". |
| `date` | date | yes | Charge date. |
| `account` | string | yes | Income account, as named in your DoorLoop chart of accounts. |
| `memo` | string | no | Shown on the ledger. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/doorloop/post_lease_charge \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"lease_id":"lease_8c41d2","amount":"50.00","date":"2026-10-01","account":"account"}'
```

**Python**

```python
result = client.call("doorloop", "post_lease_charge", {"lease_id":"lease_8c41d2","amount":"50.00","date":"2026-10-01","account":"account"})
```

**Node.js**

```js
const result = await cotlet.call('doorloop', 'post_lease_charge', {"lease_id":"lease_8c41d2","amount":"50.00","date":"2026-10-01","account":"account"});
```

**CLI**

```bash
cotlet call doorloop post_lease_charge --data '{"lease_id":"lease_8c41d2","amount":"50.00","date":"2026-10-01","account":"account"}'
```

### Example response

```json
{
  "charge_id": "ch_41b7",
  "lease_id": "ls_64f1a9c2",
  "amount": "50.00",
  "date": "2026-10-11",
  "account": "Late Fees",
  "balance_due": "1500.00",
  "status": "posted"
}
```

## Record lease payment

`POST https://cotlet.dev/api/v1/doorloop/record_lease_payment`

Access: write. Confirm: waits for the user's OK before it runs.

Records a payment on a lease, the same as Receive Payment in DoorLoop, and applies it to open charges. The response lists the charges it was applied to and the balance after. By default you confirm each call; you can switch that off for this tool.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `lease_id` | string | yes | Lease ID from list_leases. |
| `amount` | string | yes | Decimal amount received. |
| `date` | date | yes | Date received. |
| `method` | string | yes | Payment method as DoorLoop lists it, e.g. check or cash. |
| `reference` | string | no | Check number or other reference. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/doorloop/record_lease_payment \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"lease_id":"ls_64f1a9c2","amount":"1450.00","date":"2026-10-11","method":"check","reference":"1042"}'
```

**Python**

```python
result = client.call("doorloop", "record_lease_payment", {"lease_id":"ls_64f1a9c2","amount":"1450.00","date":"2026-10-11","method":"check","reference":"1042"})
```

**Node.js**

```js
const result = await cotlet.call('doorloop', 'record_lease_payment', {"lease_id":"ls_64f1a9c2","amount":"1450.00","date":"2026-10-11","method":"check","reference":"1042"});
```

**CLI**

```bash
cotlet call doorloop record_lease_payment --data '{"lease_id":"ls_64f1a9c2","amount":"1450.00","date":"2026-10-11","method":"check","reference":"1042"}'
```

### Example response

```json
{
  "payment_id": "pm_8d2e71b0",
  "lease_id": "ls_64f1a9c2",
  "amount": "1450.00",
  "method": "check",
  "reference": "1042",
  "applied_to": [
    {
      "charge": "Rent October 2026",
      "amount": "1450.00"
    }
  ],
  "balance_due": "0.00"
}
```

## Issue lease credit

`POST https://cotlet.dev/api/v1/doorloop/issue_lease_credit`

Access: write. Confirm: waits for the user's OK before it runs.

Adds a credit to a lease ledger, which lowers the balance due. Use the memo to record the reason. By default you confirm each call; you can switch that off for this tool.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `lease_id` | string | yes | Lease ID from list_leases. |
| `amount` | string | yes | Decimal amount. |
| `date` | date | yes | Credit date. |
| `memo` | string | no | Reason, shown on the ledger. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/doorloop/issue_lease_credit \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"lease_id":"lease_8c41d2","amount":"amount","date":"2026-10-01"}'
```

**Python**

```python
result = client.call("doorloop", "issue_lease_credit", {"lease_id":"lease_8c41d2","amount":"amount","date":"2026-10-01"})
```

**Node.js**

```js
const result = await cotlet.call('doorloop', 'issue_lease_credit', {"lease_id":"lease_8c41d2","amount":"amount","date":"2026-10-01"});
```

**CLI**

```bash
cotlet call doorloop issue_lease_credit --data '{"lease_id":"lease_8c41d2","amount":"amount","date":"2026-10-01"}'
```

### Example response

```json
{
  "credit_id": "cr_19a4",
  "lease_id": "ls_64f1a9c2",
  "amount": "100.00",
  "date": "2026-10-11",
  "balance_due": "1350.00",
  "status": "posted"
}
```

## Send announcement

`POST https://cotlet.dev/api/v1/doorloop/send_announcement`

Access: write. Confirm: waits for the user's OK before it runs.

Sends an announcement to tenants of the full portfolio or of chosen properties. Email and portal posts are free. Text messages use announcement credits (1 per 160 characters) and are capped at 800 characters. Announcements need DoorLoop's Pro or Premium plan. By default you confirm each call; you can switch that off for this tool.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `subject` | string | yes | Announcement title. |
| `body` | string | yes | Message text. |
| `property_ids` | string[] | no | Only tenants in these properties. Default: all. |
| `channels` | string[] | yes | Any of email, text, portal. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/doorloop/send_announcement \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"subject":"Water shutoff Thursday","body":"Water will be off Thursday from 9:00 to 12:00 for pipe repairs.","property_ids":["pr_3a91c7"],"channels":["email","text"]}'
```

**Python**

```python
result = client.call("doorloop", "send_announcement", {"subject":"Water shutoff Thursday","body":"Water will be off Thursday from 9:00 to 12:00 for pipe repairs.","property_ids":["pr_3a91c7"],"channels":["email","text"]})
```

**Node.js**

```js
const result = await cotlet.call('doorloop', 'send_announcement', {"subject":"Water shutoff Thursday","body":"Water will be off Thursday from 9:00 to 12:00 for pipe repairs.","property_ids":["pr_3a91c7"],"channels":["email","text"]});
```

**CLI**

```bash
cotlet call doorloop send_announcement --data '{"subject":"Water shutoff Thursday","body":"Water will be off Thursday from 9:00 to 12:00 for pipe repairs.","property_ids":["pr_3a91c7"],"channels":["email","text"]}'
```

### Example response

```json
{
  "communication_id": "cm_77f0",
  "subject": "Water shutoff Thursday",
  "channels": [
    "email",
    "text"
  ],
  "recipients": 12,
  "status": "sent"
}
```
