REST API for property management software (PMS) and partner integrations. Push listings, pull leads, exchange messages, and receive real-time webhook events.
If you use Claude Code, Codex, Cursor or another MCP client, you can point it at your BookMyColiving account and ask about your listings and enquiries in plain language, instead of writing requests by hand. It uses the same API key as the REST API above.
This section is the technical reference. If you just want to connect an assistant to your own account, the walkthrough at Connect an AI assistant is written for that and does not assume a terminal.
It is read-only. The tools can list and explain your listings, enquiries and conversations. They cannot publish, edit, unpublish or reply. Writing comes later, and will ask before it acts.
https://bookmycoliving.com/api/mcp
claude mcp add --transport http bookmycoliving \ https://bookmycoliving.com/api/mcp \ --header "Authorization: Bearer bmc_your_api_key_here"
Add it as a streamable HTTP server with one header:
{
"mcpServers": {
"bookmycoliving": {
"type": "http",
"url": "https://bookmycoliving.com/api/mcp",
"headers": { "Authorization": "Bearer bmc_your_api_key_here" }
}
}
}Enquiry text is written by members of the public, so the server fences it before an agent reads it and tells the agent not to follow instructions found inside. Treat anything an agent reports back from an enquiry as a quote, not as a fact it has verified.
Your API key is equivalent to a password for your operator account. A client you paste it into can read everything the tools expose. You can rotate it any time in Dashboard Settings; the old key keeps working for seven days.
There is a second, open server for searching published listings. It needs no API key and no account, and it is what to point an agent at when the question is “find me somewhere to live” rather than “how is my listing doing”.
claude mcp add --transport http bookmycoliving-search \ https://bookmycoliving.com/api/mcp/public
Search by city, budget, community type and amenities; ask in US dollars and prices come back in the operator’s own currency with a dollar equivalent. Every result links to the listing page, which is where an enquiry is made. Rate limited per caller; nothing there can contact an operator or change anything.
Add it as a custom connector with the same URL. There is no API key to paste: Claude registers itself, sends you here to sign in, and shows you exactly what it is asking for before anything is shared.
https://bookmycoliving.com/api/mcp
You approve the connection once, as yourself. The token it receives is read-only, expires every hour and refreshes quietly in the background, and is tied to your account rather than to a key that can be copied out of a config file.
To disconnect it later, open Dashboard Settings and choose Disconnect under Connected AI apps. It stops working immediately.
All requests require an x-api-key header. Keys are scoped to a single operator account.
curl -H "x-api-key: bmc_your_api_key_here" \ https://bookmycoliving.com/api/v1/leads
https://bookmycoliving.com/api/v1
List endpoints return a { data, pagination } envelope. Single-resource endpoints return the resource directly.
{
"data": [ ... ],
"pagination": {
"page": 1,
"limit": 20,
"total": 42,
"totalPages": 3
}
}Every authenticated response carries:
X-RateLimit-Limit, your hourly budget for this endpointX-RateLimit-Remaining, calls left in the current windowX-RateLimit-Reset, epoch seconds when the window resetsPass Idempotency-Key on writes. We cache the first response for 24h and replay it on retries, even after a 5xx or timeout you can safely retry without creating duplicates. Replayed responses include Idempotent-Replayed: true.
curl -X POST https://bookmycoliving.com/api/v1/properties \
-H "x-api-key: bmc_..." \
-H "Idempotency-Key: 8b1f...-uuid-..." \
-H "Content-Type: application/json" \
-d '{ ... }'Pass an externalId in the property body and we'll record it alongside our own id. On POST /properties, if the same externalId already exists for your operator account, we return the existing record (HTTP 200 + { reused: true }) instead of creating a duplicate. Filter list endpoints with ?externalId=.
/propertiesList all properties owned by the authenticated operator.
Query: page, limit (max 100)
/propertiesCreate a new property. Auto-submits for review when the photo requirements below are met, otherwise it stays in DRAFT.
curl -X POST https://bookmycoliving.com/api/v1/properties \
-H "x-api-key: bmc_..." \
-H "Content-Type: application/json" \
-d '{
"name": "Sunny Coliving",
"description": "...",
"cityId": "<from /api/v1/cities>",
"address": "123 Main St",
"amenities": ["wifi", "gym"],
"contactEmail": "host@example.com",
"colivingTypeIds": ["<from /api/v1/coliving-types>"],
"roomOptions": [
{ "type": "PRIVATE", "priceMonthly": 80000, "currency": "EUR", "billsIncluded": true },
{ "type": "SHARED", "priceMonthly": 45000, "billsIncluded": false, "billsEstimate": 6000 }
],
"photos": [
{ "url": "https://cdn.example.com/building.jpg" },
{ "url": "https://cdn.example.com/kitchen.jpg" },
{ "url": "https://cdn.example.com/lounge.jpg" }
],
"roomPhotos": {
"0": [{ "url": "https://cdn.example.com/private-room.jpg" }],
"1": [{ "url": "https://cdn.example.com/shared-room.jpg" }]
}
}'Photos decide whether your listing can go live.
A listing is only submitted for review once it has at least 3 property photos and at least one photo on every room option. Miss either and the call still returns 201, but the listing stays in DRAFT and the submission field tells you what is missing.
Room photos go in roomPhotos, keyed by the room's position in your roomOptions array as a string — "0" is the first room option, "1" the second. A photos array inside a room option is not read.
Note: priceMonthly, deposit and billsEstimate are in the minor units of the room's currency — 80000 is €800.00 when currency is "EUR", $800.00 when it is "USD" (the default). Price in the money your hosts actually charge; visitors see it converted to whichever currency they have selected. billsIncluded defaults to false; billsEstimate is your monthly utilities estimate and is ignored when bills are included. Listings that state this rank better against ones that leave guests guessing.
/properties/:idGet a single property by ID.
/properties/:idReplace a property's details. APPROVED listings stay live but get flagged for re-review.
PUT replaces the whole listing. Room options are recreated, so their ids change; send roomPhotos again in the same request to keep room photos attached.
/properties/:idPermanently delete a property and all its data.
/properties/:id/submitSubmit a DRAFT or REJECTED property for admin review.
/properties/:id/unpublishMove an APPROVED property back to DRAFT (off the public site).
/properties/:id/photosAppend property-level photos to an existing property.
{ "photos": [{ "url": "https://..." }, { "url": "https://..." }] }Property-level only. To add or change room photos, send roomPhotos on POST /properties or PUT /properties/:id.
/leadsList all leads for properties owned by the authenticated operator.
Query: page, limit, propertyId, since (ISO 8601)
curl -H "x-api-key: bmc_..." \ "https://bookmycoliving.com/api/v1/leads?since=2026-04-01T00:00:00Z"
/leads/:idGet a single lead. Operator must own the linked property.
/messagesList all conversations the authenticated operator is part of.
Query: propertyId, dateFrom, dateTo
/messages/:threadIdGet all messages in a single conversation. threadId is the partner user ID.
/messages/:threadIdReply to a conversation.
{ "content": "Thanks for your interest!", "propertyId": "..." }Capped at 5,000 characters per message and 60 messages per hour per API key.
These endpoints return canonical IDs and slugs. Cache responses for at least 1 hour.
/citiesAll active cities. Use the returned `id` as `cityId` when creating a property.
/citiesAdd a city we do not have yet.
curl -X POST https://bookmycoliving.com/api/v1/cities \
-H "x-api-key: bmc_..." \
-H "Content-Type: application/json" \
-d '{ "name": "Ljubljana", "country": "Slovenia" }'cityId has to come from our list, so this is how a city gets onto it. The name is checked against OpenStreetMap before it is created. Use the English country name — "Spain", not "España". A city that already exists comes back with created: false and a 200 rather than an error, so you can call this for every city in a portfolio without checking first.
/coliving-typesAll community types. Use returned `id` values in `colivingTypeIds`.
/amenitiesCanonical amenity slugs (e.g. `wifi`, `gym`, `coworking`). Use these in the `amenities` array.
Set a webhook URL in Dashboard Settings to receive real-time events. Events are POSTed as JSON.
lead.created, new tenant enquiry on one of your propertiesmessage.received, a tenant or admin sent you a messageproperty.created, property created (typically by your own integration)property.updated, property details changedproperty.approved, admin approved your listing, it's now liveproperty.rejected, admin rejected your listing (check adminNotes)property.deleted, property removedPOST <your-webhook-url>
Content-Type: application/json
X-BMC-Event: lead.created
X-BMC-Timestamp: 2026-04-25T10:00:00.000Z
X-BMC-Signature: <hmac-sha256(`${timestamp}.${body}`, your-webhook-secret)>
User-Agent: BookMyColiving-Webhook/1.0
{
"event": "lead.created",
"timestamp": "2026-04-25T10:00:00.000Z",
"data": {
"id": "lead_abc",
"propertyId": "prop_xyz",
"guestName": "Jane Smith",
"guestEmail": "jane@example.com",
...
}
}Each delivery is signed with HMAC-SHA256 over `${timestamp}.${rawBody}` using your webhook secret (separate from your API key, generate it in dashboard settings). Including the timestamp in the signed material defeats replay attacks. Verify on your end:
// Node.js example
const crypto = require("crypto");
function verifyWebhook(rawBody, headers, webhookSecret) {
const sig = headers["x-bmc-signature"];
const ts = headers["x-bmc-timestamp"];
// Reject events older than 5 minutes, protects against replay
if (Math.abs(Date.now() - Date.parse(ts)) > 5 * 60_000) return false;
const expected = crypto
.createHmac("sha256", webhookSecret)
.update(`${ts}.${rawBody}`)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(sig),
);
}We deliver fire-and-forget, failed deliveries are not retried. Use POST /webhooks/test below to validate your receiver before going live.
/webhooks/testFires a synthetic event at your configured webhook URL. Body: { event?: 'lead.created' | ... }. Useful for verifying signature handling.
curl -X POST https://bookmycoliving.com/api/v1/webhooks/test \
-H "x-api-key: bmc_..." \
-H "Content-Type: application/json" \
-d '{ "event": "lead.created" }'Errors return a JSON body: { "error": "Human-readable message" }
| Status | Meaning |
|---|---|
400 | Bad request, body validation failed |
401 | Missing or invalid API key |
403 | API key doesn't own this resource |
404 | Resource not found |
409 | Conflict (e.g. duplicate slug) |
429 | Rate limit exceeded |
500 | Server error, safe to retry |
/cities, /coliving-types, /amenities), 5,000 per hourPOST /messages/:threadId), 60 per hour per API keyRead your remaining budget from the X-RateLimit-* response headers. Exceeded limits return 429. Cache reference data to avoid unnecessary calls.
The API is versioned via the URL path (/api/v1/). We commit to at least 6 months notice before deprecating any endpoint, and breaking changes only happen in new major versions.
Email us at mayank@everythingcoliving.com with your operator account email and a description of your integration. Or schedule a call.