Enquiries Channel manager Bookings Your website Booking engine Pricing Log in Sign up
v2026-09

Create a property

Create a listing in Doorloom from your own system. The property belongs to the brand behind your API key, is covered by your integration the moment it exists, and comes back with the same representation GET /properties returns — so you can store the id mapping from the 201 and never ask Doorloom staff to onboard a property again.

POST https://doorloom.com/api/integrations/v1/properties Idempotency-Key required

When to use it

Use this when a listing exists in your PMS or channel manager and not yet in Doorloom: during onboarding, and whenever the host adds a property on your side first. If the property already exists in Doorloom — created by the host in the app — it is either already in your coverage (GET /properties lists it) or Doorloom staff map it for you. Do not create a second copy of it.

To load a portfolio, use POST /properties/bulk, which takes up to 25 of these bodies in one call.

Headers

Field Type Required Description
Authorization string Required Bearer <your API key>.
Content-Type string Required application/json.
Idempotency-Key string Required 8 to 128 characters, unique per create attempt. A UUID is ideal.

The idempotency contract

Retrying with the same key and the same body replays the original response byte-for-byte and never creates a second property. The same key with a different body is refused with IDEMPOTENCY_KEY_REUSED. Keys are remembered for 24 hours and are shared with POST /bookings, so never reuse a key across endpoints.

Refusals are stored too. If a create fails with 409 PROPERTY_EXTERNAL_ID_EXISTS, retrying the same key replays that refusal. To create after correcting the body, use a new key.

Request body

Body

Field Type Required Description
external_id string Optional Your id for this property. Max 128 characters of A-Z a-z 0-9 . _ : -. Unique within your integration. It becomes the external_id you may use anywhere the API takes a property reference.
name string Required The listing name guests see. Max 255 characters. Returned as name.
property_type string Optional One of villa, apartment, farm_stay, home_stay, hostel, bed_and_breakfast, resort, cottage, guest_house, room, suite, dorm, hotel.
property_category string Optional vacation_rental or resort_hotel.
description string Required The guest-facing description. Max 2000 characters.
address object Required Where the property is.
address.address_line_1 string Required Max 255 characters.
address.address_line_2 string Optional Max 255 characters.
address.landmark string Optional Max 255 characters.
address.city string Required Max 100 characters.
address.state string Required Max 100 characters.
address.country string Optional Max 100 characters. Defaults to India.
address.pin_code string Required Postal code, max 20 characters.
address.latitude number Optional -90 to 90.
address.longitude number Optional -180 to 180.
address.formatted_address string Optional Max 500 characters. Derived from the other address fields when omitted.
space object Required Room counts.
space.bedrooms integer Required 0 to 500.
space.washrooms integer Required 0 to 500.
occupancy object Required Guest limits for the property as a whole.
occupancy.min_guests integer Optional 1 or more. Defaults to 1.
occupancy.max_guests integer Required 1 to 500, not below min_guests.
pricing object Required The opening base rates. Doorloom owns pricing from here on: the host edits it in the app and you receive rates.changed. You may correct these base rates later with PUT.
pricing.weekday object Required Monday to Thursday nights.
pricing.weekday.base_price number Required INR per night, 0 or more.
pricing.weekday.per_night_occupancy integer Optional Guests included in the base price. Defaults to occupancy.max_guests.
pricing.weekday.extra_adult_price number Optional INR per extra adult per night. Defaults to 0.
pricing.weekday.extra_child_price number Optional INR per extra child per night. Defaults to 0.
pricing.weekend object Required Friday and Saturday nights. Same fields as weekday.
units array Optional The sellable units (rooms, cottages, tents). 1 to 50 entries. Omit it for a single-unit listing and Doorloom seeds one default unit.
units[].name string Required Max 255 characters. Unique within the request.
units[].external_id string Optional Your id for the unit — the external_unit_id you may use on bookings. Same character rules as external_id; unique within your integration.
units[].code string Optional A short code, max 64 characters.
units[].max_occupancy integer Optional Guests the unit sleeps. Not enforced by Doorloom.
features object Optional House rules and facts.
features.is_private boolean Optional The whole place, not shared. Defaults to false.
features.is_pet_friendly boolean Optional Defaults to false.
features.max_pets integer Optional 0 to 50.
features.pet_charges number Optional INR per pet.
features.is_veg boolean Optional Vegetarian-only kitchen. Defaults to false.
features.is_smoking_in_rooms boolean Optional Defaults to false.

Nothing else is accepted. The owner, the brand, the status, the coverage and the cluster position are decided by Doorloom from your API key, and any such field in the body is ignored. Photos, amenities, meal plans, cancellation policies and booking rules are set by the host in the Doorloom app after creation.

Minimal request

Request bash
curl -X POST https://doorloom.com/api/integrations/v1/properties \
  -H "Authorization: Bearer $DOORLOOM_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2c5d0b7e-1f7a-4c1b-9b3e-3e7d9a4f6c21" \
  -d '{
    "external_id": "VILLA-001",
    "name": "Amber Villa, Alibaug",
    "property_type": "villa",
    "description": "Sea-facing villa with a private pool and staff.",
    "address": { "address_line_1": "12 Kihim Beach Road", "city": "Alibaug", "state": "Maharashtra", "pin_code": "402201" },
    "space": { "bedrooms": 3, "washrooms": 3 },
    "occupancy": { "max_guests": 8 },
    "pricing": { "weekday": { "base_price": 12000 }, "weekend": { "base_price": 15000 } }
  }'

That creates a single-unit villa with one default unit, priced from the day it exists.

Complete request

Request json
{
    "external_id": "RESORT-77",
    "name": "Kihim Sands Resort",
    "property_type": "resort",
    "property_category": "resort_hotel",
    "description": "Twelve garden cottages and a pool on the beach road.",
    "address": {
        "address_line_1": "4 Beach Road",
        "address_line_2": "Near Kihim jetty",
        "landmark": "Opposite the lighthouse",
        "city": "Alibaug",
        "state": "Maharashtra",
        "country": "India",
        "pin_code": "402201",
        "latitude": 18.7302,
        "longitude": 72.8711
    },
    "space": {
        "bedrooms": 12,
        "washrooms": 12
    },
    "occupancy": {
        "min_guests": 1,
        "max_guests": 30
    },
    "pricing": {
        "weekday": {
            "base_price": 6500,
            "per_night_occupancy": 2,
            "extra_adult_price": 1200,
            "extra_child_price": 600
        },
        "weekend": {
            "base_price": 8500,
            "per_night_occupancy": 2,
            "extra_adult_price": 1500,
            "extra_child_price": 800
        }
    },
    "units": [
        {
            "name": "Garden Cottage 1",
            "external_id": "GC-1",
            "code": "GC1",
            "max_occupancy": 3
        },
        {
            "name": "Garden Cottage 2",
            "external_id": "GC-2",
            "code": "GC2",
            "max_occupancy": 3
        },
        {
            "name": "Pool Suite",
            "external_id": "PS-1",
            "code": "PS1",
            "max_occupancy": 4
        }
    ],
    "features": {
        "is_private": false,
        "is_pet_friendly": true,
        "max_pets": 1,
        "pet_charges": 500,
        "is_veg": false,
        "is_smoking_in_rooms": false
    }
}

Response

Field Type Description
201 created data is the property block, exactly as GET /properties returns it.
Response 201 json
{
    "success": true,
    "message": "Property created",
    "data": {
        "doorloom_id": 902,
        "external_id": "RESORT-77",
        "coverage": "active",
        "name": "Kihim Sands Resort",
        "property_type": "resort",
        "status": "active",
        "is_master": true,
        "master_property_id": 902,
        "units": [
            {
                "inventory_id": 3101,
                "external_unit_id": "GC-1",
                "name": "Garden Cottage 1",
                "code": "GC1",
                "is_active": true,
                "is_default": true,
                "max_occupancy": 3,
                "deleted": false
            },
            {
                "inventory_id": 3102,
                "external_unit_id": "GC-2",
                "name": "Garden Cottage 2",
                "code": "GC2",
                "is_active": true,
                "is_default": false,
                "max_occupancy": 3,
                "deleted": false
            },
            {
                "inventory_id": 3103,
                "external_unit_id": "PS-1",
                "name": "Pool Suite",
                "code": "PS1",
                "is_active": true,
                "is_default": false,
                "max_occupancy": 4,
                "deleted": false
            }
        ]
    }
}

Response fields

Field Type Description
doorloom_id integer Doorloom's permanent id for the property. Store it.
external_id string|null The id you sent, now mapped. Null if you sent none.
coverage string Always active on a create: the property is in your scope at once.
name string The listing name you sent.
property_type string|null As sent.
status string active on a create. See the note on plans below.
is_master boolean Always true on a create: a new property is standalone.
master_property_id integer Equals doorloom_id on a create.
units[] array The units, in the order you sent them, each with its permanent inventory_id and the external_unit_id you mapped. The first is the default unit.

Doorloom ids and your ids

Every property has a permanent doorloom_id. Your external_id is a mapping on top of it, scoped to your integration: another integration may use the same string for a different property, and a property may have no external id at all. The mapping is created with the property, in the same transaction, so there is never a moment where the property exists and the id does not.

Sending an external_id that is already mapped to one of your properties is a 409 PROPERTY_EXTERNAL_ID_EXISTS. Nothing is created, and errors.doorloom_id tells you which property already carries it — if that is the same listing, store the mapping and carry on. To change a property's external id later, use PUT /properties/{id}.

The same rules apply to units[].external_id, which becomes the unit's external_unit_id. Unit aliases are unique within your integration; one already mapped to another unit is refused with 422 VALIDATION_FAILED on units.N.external_id.

Units

Every property in Doorloom has at least one unit. Omit units[] and Doorloom seeds one default unit; send units[] and it creates exactly those, the first one being the default. Creation is transactional: the property, its units, the address and the prices either all exist or none do. Add, rename or deactivate units afterwards through PUT /properties/{id}; there is no separate unit endpoint.

Coverage

The property is added to your integration's coverage in the same transaction, with coverage: "active", whether or not your integration auto-enrols new listings. It is listed by GET /properties and accepted by POST /bookings from the moment the 201 arrives. Other integrations of the same brand that auto-enrol new listings also gain it, as they would for a property the host created in the app.

What happens afterwards

Within seconds you receive the property's full initial state through the normal webhook path, the same set POST /sync produces for one property: property.changed, inventory.changed, and availability.changed, rates.changed and restrictions.changed in month-sized windows across your horizon. That is around forty deliveries per property, all subject to your event subscription and the sequence rule. The 201 already carries the property block, so none of them tells you anything you do not know; they exist so your calendar starts from Doorloom's state rather than your assumptions.

Properties need a plan

A property created by API starts active. If the brand has no active Doorloom plan covering it, Doorloom deactivates it after seven days and it reads status: "inactive" on GET /properties — without a property.changed event, since that check runs as a bulk job. The brand keeps its plan current in the Doorloom app.

Validation

  • Required: name, description, address (line 1, city, state, pin code), space (bedrooms, washrooms), occupancy.max_guests, and both pricing groups with a base_price.
  • external_id and units[].external_id: letters, digits and . _ : - only, up to 128 characters.
  • property_type and property_category must be one of the listed values; anything else is a 422, never coerced.
  • units[]: 1 to 50 entries, unique names, unique external ids.
  • Every failure is a 422 VALIDATION_FAILED whose errors keys are the dotted field paths, e.g. address.pin_code or units.2.external_id.

Errors

Errors

Status Code Meaning
422 VALIDATION_FAILED The request body failed validation. errors is Laravel's field bag: an object keyed by field path, each holding an array of messages.
422 IDEMPOTENCY_KEY_REQUIRED A create — POST /bookings, POST /properties or POST /properties/bulk — was sent without a usable Idempotency-Key header.
422 IDEMPOTENCY_KEY_REUSED The key has already been used, with a different request body. Doorloom fingerprints the body (SHA-256 over key-sorted JSON), so key order does not matter but any value change does. Keys are shared across every create endpoint, so a booking key resent on a property create is a reuse too.
409 IN_PROGRESS An identical request is still being processed. Two requests raced; the first one is still inside its transaction.
409 PROPERTY_EXTERNAL_ID_EXISTS The external_id you sent is already mapped to a property of your integration. On a create, that property was not created; on an update, the mapping was not changed. errors.doorloom_id tells you which property holds the id, so you can store the mapping or pick a different id.
429 RATE_LIMITED You exceeded one of the rate limits. The standard Retry-After and X-RateLimit-* headers are set on the response.
401 UNAUTHENTICATED No API key, or a key that is not valid. Rotating your API key invalidates the previous one immediately.
403 NOT_INTEGRATION_TOKEN The bearer token is valid but is not an integration API key — it belongs to another Doorloom token surface. Integration keys are the only credential this API accepts.
403 INTEGRATION_DISABLED Doorloom staff disabled this integration. No events are recorded and no API call succeeds. This is distinct from paused: a paused integration keeps full API access and only stops receiving webhooks.

Retrying

Field Type Description
Timeout or 5xx retry, same key Resend the identical body with the same Idempotency-Key. If the first attempt actually succeeded you get its 201 back; if it did not, nothing was left behind and the create runs afresh.
409 IN_PROGRESS retry, same key Your own earlier attempt is still inside its transaction. Retry in a few seconds.
429 retry after Retry-After The write tier is 30 creates a minute; use the bulk endpoint for a portfolio.
409 PROPERTY_EXTERNAL_ID_EXISTS do not retry Store errors.doorloom_id if it is the same listing, or correct the id and send with a new key.
422 do not retry unchanged Fix the body and send with a new key.

Something unclear or wrong on this page? Write to [email protected] and tell us which page — we would rather fix the doc than answer the ticket twice.