# ZenMaid MCP tools

> Every ZenMaid MCP tool on the Cotlet server: tool names, input schemas, a tools/call example and an example result for each tool.

Source: https://cotlet.dev/docs/mcp/zenmaid

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)

13 tools for ZenMaid on the Cotlet MCP server. Your agent picks them from tools/list.

- MCP server: `https://cotlet.dev/mcp/v1`
- Connects with: Your ZenMaid owner or office manager account
- Integration page: https://cotlet.dev/integrations/zenmaid-api-mcp-server

## Endpoints

| Endpoint | Access | Description |
|---|---|---|
| `zenmaid_list_appointments` | read | Appointments in a date range with customer, time, cleaners, price and paid flag. |
| `zenmaid_get_appointment` | read | Full appointment: address, service type, frequency, work items, billing, notes and cleaners. |
| `zenmaid_list_contacts` | read | Contacts with email, phone, address and status. |
| `zenmaid_get_contact` | read | One contact with notes, custom fields, and past and upcoming appointments. |
| `zenmaid_list_cleaners` | read | Active cleaners and teams with their IDs. |
| `zenmaid_create_contact` | write | Add a new contact. ZenMaid starts it as a lead. |
| `zenmaid_create_appointment` | write | Schedule a one-time or recurring appointment for a contact. |
| `zenmaid_reschedule_appointment` | write | Move one appointment to a new date or time. |
| `zenmaid_assign_cleaners` | write | Set the cleaners or team on one appointment, replacing the current ones. |
| `zenmaid_add_note` | write | Add a note to an appointment or to a contact. |
| `zenmaid_cancel_appointment` | write | Set an appointment to Cancelled, optionally notifying the client and cleaner. |
| `zenmaid_mark_appointment_paid` | write | Record an offline payment such as cash, Venmo or PayPal. Does not charge a card. |
| `zenmaid_send_message` | write | Send a customer an email, text or both from one of your communication templates. |

## List appointments

MCP tool: `zenmaid_list_appointments`

Access: read.

Returns appointments between two dates, in date and time order. An appointment with nobody assigned has an empty cleaners list. Filter by payment status to find unpaid jobs, or search by customer name, cleaner name, street address or a custom field, as ZenMaid's List View does. Cancelled appointments are included only when you ask for them.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `from` | date | yes | First day of the range. |
| `to` | date | yes | Last day of the range. |
| `query` | string | no | Customer name, cleaner name, street address or custom field value. |
| `paid` | enum | no | all, paid or unpaid. Default: all. |
| `include_cancelled` | boolean | no | Also return cancelled appointments. Default: false. |
| `limit` | integer | no | 1 to 100. Default: 50. |
| `cursor` | string | no | Next page cursor from the previous call. |

### tools/call

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "zenmaid_list_appointments",
    "arguments": {
      "from": "2026-10-01",
      "to": "2026-10-01"
    }
  }
}
```

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "appointments": [
      {
        "appointment_id": "a_993102",
        "contact": "Sample Customer",
        "date": "2026-10-16",
        "start_time": "09:00",
        "end_time": "12:00",
        "cleaners": [],
        "price": "180.00",
        "paid": false
      },
      {
        "appointment_id": "a_993140",
        "contact": "Sample Household",
        "date": "2026-10-16",
        "start_time": "13:00",
        "end_time": "15:00",
        "cleaners": [
          "Cleaner A"
        ],
        "price": "120.00",
        "paid": false
      }
    ],
    "next_cursor": null
  },
  "isError": false
}
```

## Get appointment

MCP tool: `zenmaid_get_appointment`

Access: read.

Returns one appointment with the service address, service type, whether it belongs to a recurring service, work items, billing (price before tax, tip, paid flag) and its appointment and customer notes. Use it before changing or cancelling an appointment.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `appointment_id` | string | yes | Appointment ID from list_appointments. |

### tools/call

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

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "appointment_id": "a_993102",
    "contact": {
      "contact_id": "c_48213",
      "name": "Sample Customer"
    },
    "address": "12 Sample Street, Springfield",
    "date": "2026-10-16",
    "start_time": "09:00",
    "end_time": "12:00",
    "service_type": "Deep Clean",
    "recurring": false,
    "cleaners": [],
    "work_items": [
      "Kitchen",
      "Bathrooms"
    ],
    "billing": {
      "price": "180.00",
      "tip": "0.00",
      "paid": false
    },
    "notes": [
      {
        "visibility": "public",
        "body": "Gate code is on the contact."
      }
    ]
  },
  "isError": false
}
```

## List contacts

MCP tool: `zenmaid_list_contacts`

Access: read.

Searches contacts by name, email, phone or address. Status is one of ZenMaid's four: lead, one_time_customer, recurring_customer or former_customer. ZenMaid sets it from the appointments on the calendar, so a new contact stays a lead until someone books them.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `query` | string | no | Name, email, phone or address to search. |
| `status` | enum | no | lead, one_time_customer, recurring_customer or former_customer. |
| `limit` | integer | no | 1 to 100. Default: 50. |
| `cursor` | string | no | Next page cursor from the previous call. |

### tools/call

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

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "contacts": [
      {
        "contact_id": "c_48213",
        "name": "Sample Customer",
        "email": "customer@example.com",
        "phone": "+1 555 0100",
        "address": "12 Sample Street, Springfield",
        "status": "lead"
      }
    ],
    "next_cursor": null
  },
  "isError": false
}
```

## Get contact

MCP tool: `zenmaid_get_contact`

Access: read.

Returns one contact profile: addresses, customer notes (public and private), contact custom fields, revenue to date, unpaid balance and a list of their appointments, newest first. Private notes are returned only to owner and office manager accounts.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `contact_id` | string | yes | Contact ID from list_contacts. |

### tools/call

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

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "contact_id": "c_48213",
    "name": "Sample Customer",
    "email": "customer@example.com",
    "phone": "+1 555 0100",
    "status": "one_time_customer",
    "revenue_to_date": "180.00",
    "unpaid_balance": "0.00",
    "notes": [
      {
        "visibility": "private",
        "body": "Prefers morning visits."
      }
    ],
    "appointments": [
      {
        "appointment_id": "a_993102",
        "date": "2026-10-16",
        "paid": false
      }
    ]
  },
  "isError": false
}
```

## List cleaners

MCP tool: `zenmaid_list_cleaners`

Access: read.

Returns the cleaners and teams in your account with their IDs, which assign_cleaners and create_appointment need. Inactive staff are left out unless you ask for them.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `include_inactive` | boolean | no | Also list inactive staff. Default: false. |

### tools/call

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

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "cleaners": [
      {
        "cleaner_id": "cl_1207",
        "name": "Cleaner A",
        "type": "cleaner",
        "active": true
      },
      {
        "cleaner_id": "cl_1344",
        "name": "Cleaner B",
        "type": "cleaner",
        "active": true
      },
      {
        "cleaner_id": "tm_21",
        "name": "Team One",
        "type": "team",
        "active": true
      }
    ]
  },
  "isError": false
}
```

## Create contact

MCP tool: `zenmaid_create_contact`

Access: write.

Adds a contact profile. ZenMaid starts a new contact as a Lead until an appointment is booked. It reaches no one and is undone in one call, so it does not ask for your OK.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `first_name` | string | yes | First name. |
| `last_name` | string | no | Last name. |
| `email` | string | no | Email address. |
| `phone` | string | no | Phone number. |
| `address` | string | no | Service address. |
| `notes` | string | no | Customer note, public by default. |

### tools/call

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

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "contact_id": "c_48290",
    "name": "Sample Lead",
    "email": "lead@example.com",
    "phone": "+1 555 0111",
    "status": "lead"
  },
  "isError": false
}
```

## Create appointment

MCP tool: `zenmaid_create_appointment`

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

Books an appointment for an existing contact with a start and end time. Leave cleaner_ids empty to keep it unassigned, and price empty to let ZenMaid calculate it if your account is set up for that. A recurring frequency creates a recurring service. The contact becomes a customer once the appointment is on the calendar. By default you confirm each call; you can switch that off for this tool.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `contact_id` | string | yes | Contact ID from list_contacts. |
| `date` | date | yes | Day of the appointment. |
| `start_time` | string | yes | Start time, HH:MM. |
| `end_time` | string | yes | End time, HH:MM. |
| `service_type` | string | no | Service type as set up in your account. |
| `frequency` | string | no | one_time or a recurring frequency from your account. Default: one_time. |
| `cleaner_ids` | string[] | no | Cleaner or team IDs from list_cleaners. Empty leaves it unassigned. |
| `price` | string | no | Price before tax. Empty lets ZenMaid calculate it if configured. |
| `notes` | string | no | Appointment note. |

### tools/call

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "zenmaid_create_appointment",
    "arguments": {
      "contact_id": "c_48213",
      "date": "2026-10-16",
      "start_time": "09:00",
      "end_time": "12:00",
      "service_type": "Deep Clean",
      "frequency": "one_time",
      "cleaner_ids": [
        "cl_1207"
      ]
    }
  }
}
```

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "appointment_id": "a_993102",
    "contact": "Sample Customer",
    "date": "2026-10-16",
    "start_time": "09:00",
    "end_time": "12:00",
    "service_type": "Deep Clean",
    "cleaners": [
      "Cleaner A"
    ],
    "price": "180.00",
    "paid": false
  },
  "isError": false
}
```

## Reschedule appointment

MCP tool: `zenmaid_reschedule_appointment`

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

Moves one appointment, as the Reschedule window does in ZenMaid. It keeps work items, price and status. The length stays the same unless you pass end_time. One-time appointment notes do not carry over, so add them again with add_note. For a recurring appointment, apply_to chooses this one or all future appointments. By default you confirm each call; you can switch that off for this tool.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `appointment_id` | string | yes | Appointment ID from list_appointments. |
| `date` | date | yes | New day. |
| `start_time` | string | yes | New start time, HH:MM. |
| `end_time` | string | no | New end time, HH:MM. Default: keeps the length. |
| `recalculate_price` | boolean | no | Recalculate the amount owed for the new length. Default: false. |
| `apply_to` | enum | no | this_appointment or all_future, for recurring appointments. Default: this_appointment. |

### tools/call

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "zenmaid_reschedule_appointment",
    "arguments": {
      "appointment_id": "a_993102",
      "date": "2026-10-20",
      "start_time": "09:00",
      "apply_to": "this_appointment"
    }
  }
}
```

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "appointment_id": "a_993102",
    "date": "2026-10-19",
    "start_time": "10:00",
    "end_time": "13:00",
    "price": "180.00",
    "price_recalculated": false
  },
  "isError": false
}
```

## Assign cleaners

MCP tool: `zenmaid_assign_cleaners`

Access: write.

Replaces the cleaners on one appointment. It does not deactivate a cleaner and does not touch the default cleaner of a recurring service. One more call undoes it, so it does not ask for your OK.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `appointment_id` | string | yes | Appointment ID from list_appointments. |
| `cleaner_ids` | string[] | yes | Cleaner or team IDs from list_cleaners. |

### tools/call

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "zenmaid_assign_cleaners",
    "arguments": {
      "appointment_id": "a_993102",
      "cleaner_ids": [
        "cl_1344"
      ]
    }
  }
}
```

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "appointment_id": "a_993102",
    "cleaners": [
      "Cleaner B"
    ]
  },
  "isError": false
}
```

## Add note

MCP tool: `zenmaid_add_note`

Access: write.

Adds an Appointment Note or a Customer Note, as in ZenMaid. A public note goes on the work orders sent to cleaners; a private note is visible only to owners and office managers. It reaches no one and is undone by editing the note, so it does not ask for your OK.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `appointment_id` | string | no | Appointment to note. Give this or contact_id. |
| `contact_id` | string | no | Contact to note. |
| `body` | string | yes | Note text. |
| `visibility` | enum | no | public or private. Default: public. |

### tools/call

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

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "note_id": "n_5521",
    "appointment_id": "a_993102",
    "visibility": "public",
    "body": "Moved one day because of rain."
  },
  "isError": false
}
```

## Cancel appointment

MCP tool: `zenmaid_cancel_appointment`

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

Sets the status to Cancelled and keeps the record; it never deletes. notify takes none, client, cleaner or both, the same choices ZenMaid offers when you cancel. A cancelled appointment may be hidden by the calendar filters. By default you confirm each call; you can switch that off for this tool.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `appointment_id` | string | yes | Appointment ID from list_appointments. |
| `notify` | enum | no | none, client, cleaner or both. Default: none. |

### tools/call

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

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "appointment_id": "a_993102",
    "status": "Cancelled",
    "notified": [
      "client",
      "cleaner"
    ]
  },
  "isError": false
}
```

## Mark appointment paid

MCP tool: `zenmaid_mark_appointment_paid`

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

Marks one appointment as paid, like the Mark as paid button in its Billing section. It never charges the card on file. Appointments paid by card cannot be changed this way. The customer's Revenue To Date and Unpaid Balance update. By default you confirm each call; you can switch that off for this tool.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `appointment_id` | string | yes | Appointment ID from list_appointments. |

### tools/call

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

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "appointment_id": "a_991870",
    "paid": true,
    "total": "120.00"
  },
  "isError": false
}
```

## Send message

MCP tool: `zenmaid_send_message`

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

Sends from a template under Type of communication, as the Send Text/Email action does. You can replace the template text with your own. ZenMaid only sends if the contact has an email address or phone number on file, and manual texts count toward your SMS usage. By default you confirm each call; you can switch that off for this tool.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `appointment_id` | string | no | Appointment the message is about. Give this or contact_id. |
| `contact_id` | string | no | Contact to message. |
| `template` | string | yes | Communication template name. |
| `channels` | enum | no | email, sms or both. Default: email. |
| `body` | string | no | Replace the template text with your own. |

### tools/call

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

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "appointment_id": "a_993102",
    "template": "Appointment Reminder",
    "channels": [
      "email",
      "sms"
    ],
    "sent": true
  },
  "isError": false
}
```
