Claude Guide · OwnerRez

Connect OwnerRez to Claude

Ask Claude about your OwnerRez bookings, properties, and guests in plain English. No dashboards, no CSV exports, no copy-pasting into spreadsheets. Just ask.

10 minute setup Free & open source Claude Desktop or Claude Code

Last week Cole Rubin at Conduit (YC W24) published Claude MCP connector guides for 8 property management systems. OwnerRez wasn't one of them. This is ours. Shout out to Cole for lighting the match. This guide uses the same 4-step format and the same @modelcontextprotocol/sdk pattern so if you've followed one of his, this will feel familiar.

What you'll be able to ask Claude

Once connected, Claude can query your OwnerRez account directly. A few examples from real operator workflows:

"Show me every booking arriving this weekend, grouped by property."
"Which properties had the most bookings in the last 30 days?"
"List my active properties by bedroom count, highest to lowest."
"Look up the guest jane.doe@example.com and show their booking history."
"What bookings have been modified in the last 48 hours and which properties do they belong to?"

Prerequisites

1

Generate an OwnerRez Personal Access Token

OwnerRez uses HTTP Basic Auth for its v2 API. Your credentials are your account email plus a Personal Access Token (PAT) that starts with pt_.

  1. Log in to OwnerRez.
  2. Go to Settings → Advanced Tools → Developer / API Settings.
  3. Click Create Personal Access Token. Give it a name (e.g. "Claude MCP") and create it.
  4. Copy the token immediately. It looks like pt_abc123def456... and you won't be able to see it again after you close the dialog.

Note: Some endpoints are gated. The /v2/messages endpoints require a separate signed messaging agreement with OwnerRez (contact help@ownerrez.com), and /v2/listings and /v2/reviews require the "Integrated Websites" premium feature. The tools we expose in this guide (bookings, properties, guests) don't need either.

2

Set up the Node.js project

Create a new directory for the MCP server, initialize it, and install the two dependencies: the Model Context Protocol SDK and zod for schema validation.

Terminal
mkdir ownerrez-mcp
cd ownerrez-mcp
npm init -y
npm install @modelcontextprotocol/sdk zod

Then open package.json and add "type": "module" so Node uses ES modules:

package.json
{
  "name": "ownerrez-mcp",
  "version": "1.0.0",
  "type": "module",
  "main": "index.js",
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.0.0",
    "zod": "^3.22.0"
  }
}
3

Create the MCP server file

Create a file called index.js in your ownerrez-mcp directory and paste the following. This exposes four tools: list-bookings, list-properties, get-booking, and search-guests.

index.js
#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

const EMAIL = process.env.OWNERREZ_EMAIL;
const TOKEN = process.env.OWNERREZ_TOKEN;
const BASE_URL = "https://api.ownerrez.com/v2";

if (!EMAIL || !TOKEN) {
  console.error("Missing OWNERREZ_EMAIL or OWNERREZ_TOKEN environment variables");
  process.exit(1);
}

const authHeader =
  "Basic " + Buffer.from(`${EMAIL}:${TOKEN}`).toString("base64");

async function ownerrezGet(path, params = {}) {
  const url = new URL(`${BASE_URL}${path}`);
  for (const [key, value] of Object.entries(params)) {
    if (value !== undefined && value !== null && value !== "") {
      url.searchParams.append(key, String(value));
    }
  }

  const response = await fetch(url.toString(), {
    method: "GET",
    headers: {
      Authorization: authHeader,
      Accept: "application/json",
      "User-Agent": "rapideye-ownerrez-mcp/1.0",
    },
  });

  if (!response.ok) {
    const body = await response.text();
    throw new Error(`OwnerRez API ${response.status}: ${body}`);
  }

  return response.json();
}

const server = new McpServer({
  name: "ownerrez",
  version: "1.0.0",
});

// Tool 1: List bookings
server.tool(
  "list-bookings",
  "List OwnerRez bookings modified since a given date. Defaults to bookings modified in the last 30 days. Use for questions about upcoming arrivals, recent bookings, or activity on specific properties.",
  {
    since_utc: z
      .string()
      .optional()
      .describe(
        "ISO 8601 UTC timestamp (e.g. 2026-04-01T00:00:00Z). Returns bookings modified since this date. Defaults to 30 days ago."
      ),
    property_ids: z
      .string()
      .optional()
      .describe(
        "Comma-separated OwnerRez property IDs to filter by. Omit to query across all properties."
      ),
    include_charges: z
      .boolean()
      .optional()
      .describe("Include charge line items in the response."),
    include_guest: z
      .boolean()
      .optional()
      .describe("Include guest contact details."),
  },
  async ({ since_utc, property_ids, include_charges, include_guest }) => {
    const defaultSince = new Date(
      Date.now() - 30 * 24 * 60 * 60 * 1000
    ).toISOString();
    const data = await ownerrezGet("/bookings", {
      since_utc: since_utc || defaultSince,
      property_ids,
      include_charges,
      include_guest,
    });
    return {
      content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
    };
  }
);

// Tool 2: List properties
server.tool(
  "list-properties",
  "List all OwnerRez properties (rental listings) on the account. Returns property ID, name, address, bedroom/bathroom count, and active status.",
  {},
  async () => {
    const data = await ownerrezGet("/properties");
    return {
      content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
    };
  }
);

// Tool 3: Get a single booking
server.tool(
  "get-booking",
  "Fetch a single OwnerRez booking by ID. Returns full booking details including guest, charges, dates, and notes.",
  {
    id: z.number().describe("Booking ID"),
  },
  async ({ id }) => {
    const data = await ownerrezGet(`/bookings/${id}`);
    return {
      content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
    };
  }
);

// Tool 4: Search guests
server.tool(
  "search-guests",
  "Search OwnerRez guests by name or email. Returns matching guest records with contact details.",
  {
    q: z.string().describe("Search query (name or email substring)"),
  },
  async ({ q }) => {
    const data = await ownerrezGet("/guests", { q });
    return {
      content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
    };
  }
);

const transport = new StdioServerTransport();
await server.connect(transport);

That's the entire server. About 120 lines. You can extend it with more tools by copying the server.tool(...) pattern and pointing at any of the 60+ v2 endpoints OwnerRez publishes.

4

Register the server with Claude Desktop

Open (or create) your Claude Desktop config file. On macOS, it lives at:

Path (macOS)
~/Library/Application Support/Claude/claude_desktop_config.json

On Windows:

Path (Windows)
%APPDATA%\Claude\claude_desktop_config.json

Add the following (if the file already has an mcpServers block, just merge the ownerrez entry into it):

claude_desktop_config.json
{
  "mcpServers": {
    "ownerrez": {
      "command": "node",
      "args": ["/absolute/path/to/ownerrez-mcp/index.js"],
      "env": {
        "OWNERREZ_EMAIL": "your-email@example.com",
        "OWNERREZ_TOKEN": "pt_your_personal_access_token_here"
      }
    }
  }
}

Replace /absolute/path/to/ownerrez-mcp/index.js with the actual absolute path on your machine (run pwd inside the ownerrez-mcp directory to get it). Replace the email and token values with your real credentials.

Restart Claude Desktop fully. Quit the app (don't just close the window) and reopen it. The OwnerRez tools should appear in the tools menu.

Try your first query

Open Claude Desktop and ask it something that requires your OwnerRez data. A good first test:

"List my OwnerRez properties and tell me how many are active."

Claude will call list-properties, read the response, and answer in plain English. If it works, you're connected. Try a harder query next: "What bookings do I have arriving in the next 7 days, grouped by property?"

Troubleshooting

401 Unauthorized when Claude calls a tool

Your email or Personal Access Token is wrong or the token was revoked. Double-check the values in claude_desktop_config.json. The token must start with pt_. Regenerate it in OwnerRez Settings → Advanced Tools → Developer / API Settings if in doubt.

Also verify the email matches the OwnerRez account that owns the token — the Basic Auth uses both.

Claude says it doesn't see any OwnerRez tools

Claude Desktop only reads the config file at startup. Fully quit Claude (Cmd+Q on macOS, right-click tray icon → Quit on Windows) and reopen it. A window close is not enough.

If it still doesn't show up, check the Claude Desktop logs (Help → Open Logs Folder) for a startup error. Common causes: invalid JSON in the config file, wrong path to index.js, or node not being in your PATH.

"Cannot find module '@modelcontextprotocol/sdk/server/mcp.js'"

The dependency wasn't installed or you ran node index.js from outside the project directory. Make sure you ran npm install inside ownerrez-mcp and that "type": "module" is set in package.json.

OwnerRez returns an error about required parameters on /v2/bookings

The /v2/bookings endpoint requires either property_ids or since_utc. The MCP server passes a default since_utc of 30 days ago if you don't specify one, so this shouldn't happen with the code above — but if it does, pass an explicit since_utc in your prompt (e.g. "list bookings since 2026-03-01").

I want to expose more endpoints

The OwnerRez v2 API has 60+ endpoints covering payments, refunds, quotes, fees, field definitions, owners, inquiries, tag definitions, and webhooks. Add more tools by copying the existing server.tool(...) pattern and pointing at any path in the v2 reference. The auth header and fetch helper stay the same.

Remember: /v2/listings, /v2/reviews require the WordPress Plugin + Integrated Websites premium feature, and /v2/messages requires a signed messaging agreement.

What's next

This is guide 1 in a series for STR operators. Cole covered 8 PMSes. We're extending the format to categories of the stack he skipped: smart locks, guest screening, dynamic pricing, and more.