Turno, the cleaning marketplace and turnover management app formerly named TurnoverBnB, does not offer an official MCP server or a self-serve public API as of July 2026. It does run a documented External API, version 2, that Turno grants on request to partners. It uses OAuth 2.0 bearer tokens plus a partner ID header, and tokens last one year. Once access is granted, a small read-only MCP server lets Claude query your cleaning projects, checklists, cleaner photos, problem reports, cleaners, and reviews. Without API access, the practical route is downloading turnover photos from Turno and analyzing them with Claude or a dedicated photo inspection tool.
What you'd be able to ask Claude
Turno sits at the operational center of turnovers: which cleaning happens when, who took it, what the checklist proved, and what broke. Wired into Claude, that becomes a queryable ops layer:
What the Turno API v2 actually exposes
This table is compiled directly from Turno's own External API v2 documentation, which is published as a Postman collection at apidocs.turnoverbnb.com. These are the documented resources and the fields that matter for an AI workflow:
| Resource | Endpoints | What Claude gets from it |
|---|---|---|
| Projects | GET /v2/projects, GET /v2/projects/{id} |
Cleanings: schedule, status, property, assigned cleaner, payment status, project images. Filterable by property, cleaner, and date range. |
| Checklists | GET /v2/projects/{id}/checklist |
The interesting one. Per-item completion status, cleaner comments, an image_required flag, reference photo_url, and a cleaner_images array of the photos the cleaner actually uploaded. |
| Problems | GET /v2/problems, POST /v2/problems |
Issue reports with title, description, resolved/unresolved status, and attached images, tied to a property and project. |
| Cleaners | GET /v2/contractors |
Your cleaner roster, plus per-property cleaner assignment endpoints. |
| Reviews | GET /v2/reviews |
Host ratings and review text per cleaner and property, filterable by date range. |
| Properties & bookings | GET /v2/properties, GET /v2/bookings |
Property records (address, access codes, checklists) and the booking calendar driving auto-scheduling, plus blocked dates. |
| Webhooks | GET /v2/webhooks, POST /v2/webhooks |
Event callbacks to your own URL. The documented sample type is "Project Completed", the natural trigger for a photo review pipeline. |
Two details worth knowing before you write code: every request carries an Authorization: Bearer token and a TBNB-Partner-ID header, and the docs state that access and refresh tokens both have a one-year lifespan. The documented sandbox lives at sandbox.turnoverbnb.com/v2, on the old TurnoverBnB domain.
Two realistic paths
Because API access is granted, not self-serve, your route depends on whether Turno approves you. Both paths below are real; neither requires anything Turno hasn't documented.
You get API access: build the MCP server
Turno's API docs say it directly: "Ready to be our partner and use our API? Please contact us so we can grant you access to the API." Larger operators and integration partners are the audience.
Once granted, the walkthrough below gives Claude read-only tools over projects, checklists, problems, cleaners, and reviews.
No API access: work with the photos
Cleaner photos are still yours. Download them from the Turno dashboard and drop them into a Claude chat for one-off reviews, or send them to a purpose-built inspection pipeline.
Details in the "No API access?" section below.
Prerequisites (Path A)
- Turno API access granted by Turno, with your OAuth client credentials and partner ID. Request it through turno.com.
- Claude Desktop installed, or the Claude Code CLI. Download Claude Desktop.
- Node.js 18 or later. Check with
node --version.
Request access and complete the OAuth exchange
Unlike PriceLabs or OwnerRez, there is no "generate API key" button in Turno's settings. Access starts with a conversation: contact Turno, describe your use case, and they issue OAuth client credentials plus a TBNB-Partner-ID.
The documented flow is standard OAuth 2.0 authorization code:
- Send the user to
/v2/oauth/authorizewith yourclient_id,response_type=code, astatestring, and yourredirect_uri. - Exchange the returned code at
POST /v2/oauth/tokenfor an access token and refresh token. - Store both. Per the docs, "Both access and refresh tokens have a 1 year lifespan," so for a personal integration you can treat the access token like a long-lived key and refresh it annually.
Turno will point you at the sandbox environment (sandbox.turnoverbnb.com/v2) first and confirm the production base URL when they approve you. The server below reads the base URL from an environment variable so you can switch without touching code.
Set up the Node.js project
Two dependencies: the MCP SDK and zod. Turno has no official Node SDK, so we call the REST API directly.
mkdir turno-mcp
cd turno-mcp
npm init -y
npm install @modelcontextprotocol/sdk zod
Add "type": "module" to package.json:
{
"name": "turno-mcp",
"version": "1.0.0",
"type": "module",
"main": "index.js",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.0.0",
"zod": "^3.22.0"
}
}
Create the MCP server file
Save the following as index.js. It ships five read-only tools: list-projects, get-project-checklist, list-problems, list-cleaners, and list-reviews. Endpoint paths and headers match the Turno External API v2 documentation; if Turno gives you a different production base URL, set it in TURNO_BASE_URL.
#!/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 TOKEN = process.env.TURNO_ACCESS_TOKEN;
const PARTNER_ID = process.env.TURNO_PARTNER_ID;
const BASE_URL =
process.env.TURNO_BASE_URL || "https://sandbox.turnoverbnb.com/v2";
if (!TOKEN || !PARTNER_ID) {
console.error("Missing TURNO_ACCESS_TOKEN or TURNO_PARTNER_ID");
process.exit(1);
}
async function turnoGet(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: `Bearer ${TOKEN}`,
"TBNB-Partner-ID": PARTNER_ID,
Accept: "application/json",
},
});
if (!response.ok) {
const body = await response.text();
throw new Error(`Turno API ${response.status}: ${body}`);
}
return response.json();
}
const server = new McpServer({
name: "turno",
version: "1.0.0",
});
// Tool 1: List cleaning projects
server.tool(
"list-projects",
"List cleaning projects (turnovers) from Turno. Returns project id, status, schedule, property, assigned cleaner, and payment status. Use this first to find project ids.",
{
start: z.string().optional().describe("Start date, YYYY-MM-DD"),
end: z.string().optional().describe("End date, YYYY-MM-DD"),
properties: z
.string()
.optional()
.describe("Comma-separated property ids to filter by"),
cleaners: z
.string()
.optional()
.describe("Comma-separated cleaner ids to filter by"),
page: z.number().optional(),
limit: z.number().optional(),
},
async (args) => {
const data = await turnoGet("/projects", args);
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
};
}
);
// Tool 2: Get a project's checklist, including cleaner photos
server.tool(
"get-project-checklist",
"Fetch the checklist for a specific cleaning project. Each item includes its completion status, cleaner comment, whether a photo was required (image_required), the host's reference photo_url, and cleaner_images: the photo URLs the cleaner uploaded during the turnover.",
{
project_id: z
.string()
.describe("Turno project id (get from list-projects)"),
},
async ({ project_id }) => {
const data = await turnoGet(`/projects/${project_id}/checklist`);
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
};
}
);
// Tool 3: List problem reports
server.tool(
"list-problems",
"List problem reports (damage, missing inventory, maintenance issues) raised through Turno, with title, description, resolved/unresolved status, attached image URLs, property, and reporting cleaner.",
{
property_id: z
.string()
.optional()
.describe("Optional property id to filter by"),
status: z
.enum(["resolved", "unresolved"])
.optional()
.describe("Filter by resolution status"),
page: z.number().optional(),
limit: z.number().optional(),
},
async ({ property_id, status, page, limit }) => {
const data = await turnoGet("/problems", {
"property-id": property_id,
status,
page,
limit,
});
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
};
}
);
// Tool 4: List cleaners
server.tool(
"list-cleaners",
"List the cleaners (contractors) connected to this Turno account.",
{},
async () => {
const data = await turnoGet("/contractors");
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
};
}
);
// Tool 5: List cleaner reviews
server.tool(
"list-reviews",
"List host reviews of completed cleanings: star rating, review text, cleaner, property, and project. Useful for cleaner performance analysis.",
{
start: z.string().optional().describe("Start date, YYYY-MM-DD"),
end: z.string().optional().describe("End date, YYYY-MM-DD"),
properties: z
.string()
.optional()
.describe("Comma-separated property ids to filter by"),
},
async (args) => {
const data = await turnoGet("/reviews", args);
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
};
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
Keeping the server read-only is deliberate. The v2 API also documents write operations (creating projects, assignments, webhooks, problems), but an exploratory Claude session shouldn't be able to cancel a cleaning by accident. Add write tools later, in a separate server, if you need them.
Register the server with Claude Desktop
Open your Claude Desktop config file:
~/Library/Application Support/Claude/claude_desktop_config.json
%APPDATA%\Claude\claude_desktop_config.json
Add the Turno entry (merge into your existing mcpServers block if you've set up other guides from this series):
{
"mcpServers": {
"turno": {
"command": "node",
"args": ["/absolute/path/to/turno-mcp/index.js"],
"env": {
"TURNO_ACCESS_TOKEN": "your_oauth_access_token",
"TURNO_PARTNER_ID": "your_tbnb_partner_id",
"TURNO_BASE_URL": "https://sandbox.turnoverbnb.com/v2"
}
}
}
}
Replace the path and credentials with your real values, then fully quit and reopen Claude Desktop. Ask something like: "List my Turno cleaning projects from the last two weeks as a table with property, cleaner, and status."
No API access? The photo route still works
If Turno doesn't grant you API access, don't force it. The highest-value data in Turno for an AI workflow is the photos, and those reach Claude two ways without any API:
Manual review in Claude. Open a completed project in the Turno dashboard, save the cleaner photos and any problem-report photos, and upload them into a Claude chat. Claude reads images natively, so prompts like "Compare these turnover photos against this reference photo and list anything missing, damaged, or out of place" work with zero integration. This is fine for spot-checks and disputes; it does not scale to every turnover.
A dedicated inspection pipeline. Reviewing every photo from every turnover by hand is exactly the problem RapidEye exists to remove: it runs AI damage and issue detection across all of your turnover photos automatically and flags what cleaners and inspectors miss. If you'd rather wire photo analysis into your systems programmatically, the RapidEye API accepts turnover photos and returns structured damage findings.
And if you want event-driven automation later, remember the API's documented "Project Completed" webhook: that's the hook a granted partner uses to trigger photo analysis the moment a cleaner finishes.
Troubleshooting
401 Unauthorized on every request
Check three things: the Authorization: Bearer token is your OAuth access token (not the authorization code, not the refresh token), the TBNB-Partner-ID header is present with the ID Turno issued you, and you're calling the environment your credentials were issued for. Sandbox credentials don't work against production and vice versa.
If the token is over a year old, it has expired. Use the refresh token at POST /v2/oauth/token to mint a new pair.
I can't find an API key anywhere in my Turno settings
That's expected. There isn't one. Turno's API is partner-gated: the docs say to contact Turno so they can grant you access. If you're a host or property manager with real volume, that conversation is worth having; if you're a single-property host, plan on the manual photo route instead.
Claude says it doesn't see any Turno tools
Claude Desktop only reads the config file at startup. Fully quit Claude (Cmd+Q on macOS, right-click tray icon then Quit on Windows) and reopen it. Closing the window doesn't reload the config.
Still broken? Check Help then Open Logs Folder for a startup error. Common causes: invalid JSON in the config file, wrong absolute path to index.js, or node not on your PATH.
Checklist items come back without cleaner_images
The cleaner_images array lives inside each checklist item's status object, and it's only populated when the cleaner actually uploaded a photo for that item. Items with image_required: 0 often have none. If a photo-required item has an empty array, that itself is the finding: the proof photo is missing.
My endpoint paths return 404 in production
The paths in this guide match the documented sandbox (sandbox.turnoverbnb.com/v2). When Turno grants production access they will confirm the production base URL; set it in TURNO_BASE_URL rather than editing the code. If a specific resource 404s, re-check the current docs at apidocs.turnoverbnb.com, since gated APIs can change between partner agreements.
FAQ
Does Turno have an official MCP server?
No. As of July 2026, Turno publishes no MCP server and none appears in the MCP community registries. Connecting Turno to Claude means building a small MCP server against Turno's External API v2, or working with exported photos manually.
Does Turno have a public API?
Yes, but not self-serve. The External API v2 is publicly documented at apidocs.turnoverbnb.com, and the docs state you must contact Turno to be granted access. Auth is OAuth 2.0 bearer tokens plus a TBNB-Partner-ID header; access and refresh tokens last one year.
What data does the API expose?
Projects (cleanings), project checklists with per-item cleaner photos and comments, problems (issue reports with images), properties, bookings, blocked dates, cleaners, assignments, reviews, and webhooks. The documented sample webhook type is "Project Completed".
Does Turno integrate with Zapier?
No. Turno has no listed app in Zapier's public directory as of July 2026, so there's no no-code bridge to Claude. The API's own webhooks are the closest event-driven equivalent, and they require granted API access.
Can Claude see the photos cleaners take in Turno?
Through the API, yes: the checklist endpoint returns a cleaner_images array of photo URLs per completed item, which an MCP server can hand to Claude. Without the API, download photos from the dashboard and upload them to Claude, or run them through a photo inspection pipeline like RapidEye.
What happened to the TurnoverBnB API v1?
Deprecated. The current docs cover v2 and link to v1 only for legacy integrations. Turno was formerly named TurnoverBnB, which is why the API docs and sandbox still live on the turnoverbnb.com domain.
Sources & references
- Turno External API v2 documentation (Postman) https://apidocs.turnoverbnb.com/
- Turno: Integrations & Partners https://turno.com/integrations/
- Hospitable: Connect Hospitable with Turno to automate your cleaning operations https://help.hospitable.com/en/articles/7178662-connect-hospitable-with-turno-to-automate-your-cleaning-operations
- Guesty Help Center: Turno (formerly TurnoverBnB) https://help.guesty.com/hc/en-gb/articles/9371607465501-Turno-formerly-TurnoverBnB
- Model Context Protocol TypeScript SDK https://github.com/modelcontextprotocol/typescript-sdk
Other guides in this series
We're building the definitive set of guides for connecting the STR stack to Claude. Browse all Claude guides.
Connect PriceLabs to Claude
A read-only MCP server over the self-serve Customer API. Listings and dynamic prices.
Connect Hostaway to Claude
Reservations, listings, and tasks from one of the biggest PMSes in the space.
Connect OwnerRez to Claude
Bookings, properties, guests, and payments via Personal Access Token.

