Chuyển tới nội dung chính

Listings

Tài liệu này mô tả cách làm việc với Listing API trong Cohost:

  • Cấu trúc dữ liệu và các trường.
  • Cách lấy danh sách và chi tiết listing.
  • Cách tính giá.
  • Cách lấy calendar và availability.

Lưu ý: Tài liệu này tập trung vào Internal API (/api/v1/listings). Đối với External/Partner API, xem thêm Partner Integration.


1. Tổng quan về Listing

Listing là đơn vị cơ bản đại diện cho một chỗ ở (phòng, căn hộ, villa, ...) cho thuê.

Mỗi listing bao gồm:

  • Thông tin cơ bản: loại hình, vị trí, sức chứa.
  • Mô tả: tên, mô tả, hình ảnh.
  • Giá: daily/weekly/hourly, combo, extra guest, cleaning fee.
  • Chính sách: refund rule, quiet time, nội quy.
  • Availability: lịch trống/khỏa.

2. Cấu trúc dữ liệu Listing

2.1. ListingResponse - Các trường chính

{
"id": 1,
"team_id": 123,
"name": "Villa Đà Lạt",
"slug": "villa-da-lat",
"description": "Mô tả chi tiết...",
"listing_type": "ENTIRE_PLACE",
"property_type": "VILLA",
"address": "123 Đường XYZ, Phường XXX, Đà Lạt",
"country_code": "VN",
"province_code": "STT",
"province_name": "Lâm Đồng",
"latitude": 11.939,
"longitude": 108.458,
"capacity": 10,
"standard_capacity": 6,
"bedrooms": 4,
"bathrooms": 3,
"beds": 5,
"square_meters": 200,
"shared_bathrooms": 0,
"state": "ACTIVE",
"full_rate": 3000000,
"minimum_rate": 2500000,
"extra_adult_rate": 200000,
"extra_child_rate": 100000,
"guest_included_in_rate": 6,
"maximum_guest": 10,
"currency": "VND",
"security_deposit": 5000000,
"cleaning_fee": 200000,
"allow_hourly_booking": true,
"allow_individual_rental": false,
"instant_book_enabled": true,
"commission_rate": 0.15,
"rating_average": 4.5,
"review_count": 25,
"view_count": 1200,
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-02-20T14:00:00Z"
}

2.2. Mô tả chi tiết các trường

Thông tin cơ bản

TrườngKiểuMô tả
idintID duy nhất của listing
team_idintID của team sở hữu listing
parent_listing_idint?ID listing cha (nếu là listing con)
namestringTên listing (max 255 ký tự)
slugstringURL-friendly identifier (duy nhất trong team)
descriptionstring?Mô tả chi tiết
listing_typestringLoại hình: ENTIRE_PLACE, PRIVATE_ROOM, SHARED_ROOM
property_typestringLoại tài sản: VILLA, APARTMENT, HOUSE, CONDO, ...

Vị trí

TrườngKiểuMô tả
addressstringĐịa chỉ đầy đủ
country_codestringMã quốc gia (mặc định: VN)
province_codestringMã tỉnh/thành
province_namestringTên tỉnh/thành (response only)
latitudedecimal?Vĩ độ
longitudedecimal?Kinh độ
place_idstring?Google Place ID

Sức chứa

TrườngKiểuMô tả
capacityintSố khách tối đa
standard_capacityintSố khách tính trong giá cơ bản
bedroomsint?Số phòng ngủ
bathroomsint?Số phòng tắm
bedsint?Số giường
square_metersint?Diện tích (m²)
shared_bathroomsintSố phòng tắm chung

Giá (Pricing)

TrườngKiểuMô tả
full_ratedecimalGiá đầy đủ (giá gốc)
minimum_ratedecimalGiá tối thiểu
extra_adult_ratedecimalPhí thêm người lớn
extra_child_ratedecimalPhí thêm trẻ em
guest_included_in_rateintSố khách được tính trong giá
maximum_guestintSố khách tối đa
currencystringĐơn vị tiền tệ (VND)
security_depositdecimalTiền đặt cọc
cleaning_feedecimalPhí vệ sinh (một lần)

Hourly Booking

TrườngKiểuMô tả
allow_hourly_bookingboolCho phép đặt theo giờ
min_hoursintSố giờ tối thiểu (mặc định: 2)
price_first_hoursdecimalGiá cho N giờ đầu
extra_hour_pricedecimalGiá mỗi giờ tiếp theo
price_rate_in_daydecimalHệ số giá cho đặt theo ngày (1-10)
checkin_night_short_hourtime?Giờ check-in cho đặt qua đêm
checkout_night_short_hourtime?Giờ check-out cho đặt qua đêm
night_short_discountdecimal% giảm giá cho đặt qua đêm

Check-in

TrườngKiểuMô tả
standard_check_in_timetime?Giờ check-in tiêu chuẩn
standard_check_out_timetime?Giờ check-out tiêu chuẩn
flexible_check_in_timetime?Giờ check-in linh hoạt
flexible_check_out_timetime?Giờ check-out linh hoạt
checkin_methodenumPhương thức: SELF_CHECKIN, HOST_CHECKIN, BOTH
checkin_guide_urlstring?URL hướng dẫn check-in
no_checkin_after_hourtime?Giờ chặn check-in

Trạng thái và đánh giá

TrườngKiểuMô tả
stateenumTrạng thái: DRAFT, ACTIVE, INACTIVE, ARCHIVED
rating_averagedecimal?Điểm đánh giá trung bình
review_countintSố lượng đánh giá
view_countintSố lượt xem
instant_book_enabledboolCho phép đặt ngay
commission_ratedecimalTỷ lệ hoa hồng (0.15 = 15%)

Khác

TrườngKiểuMô tả
refund_rulestringChính sách hoàn tiền
view_typesstring[]?Loại view: city, beach, garden, ...
additional_areasstring[]?Khu vực thêm: garden, balcony, pool, ...
wifi_namestring?Tên WiFi
wifi_passwordstring?Mật khẩu WiFi
door_codeobject?Thông tin mở cửa

3. Các API Endpoints

3.1. Lấy danh sách Listing

GET /api/v1/listings

Query Parameters:

ParameterKiểuMô tả
pageintSố trang (mặc định: 1)
limitintSố items/trang (mặc định: 20)
searchstring?Tìm kiếm theo tên
statusstring?Lọc theo trạng thái: DRAFT, ACTIVE, INACTIVE
province_codestring?Lọc theo mã tỉnh

Response:

{
"items": [
{
"id": 1,
"name": "Villa Đà Lạt",
"slug": "villa-da-lat",
"listing_type": "ENTIRE_PLACE",
"province_name": "Lâm Đồng",
"full_rate": 3000000,
"minimum_rate": 2500000,
"state": "ACTIVE",
"rating_average": 4.5,
"review_count": 25,
"images": [
{ "id": 1, "url": "https://...", "order": 0 }
]
}
],
"total": 100,
"page": 1,
"limit": 20,
"total_pages": 5
}

3.2. Lấy chi tiết Listing

GET /api/v1/listings/{listing_id}

Response: Trả về ListingResponse với đầy đủ các trường như mô tả ở phần 2.

3.3. Tạo Listing mới

POST /api/v1/listings

Request Body: Xem ListingCreate schema với các trường chính:

{
"name": "Villa Đà Lạt",
"slug": "villa-da-lat",
"listing_type": "ENTIRE_PLACE",
"property_type": "VILLA",
"address": "123 Đường XYZ, Đà Lạt",
"country_code": "VN",
"province_code": "STT",
"latitude": 11.939,
"longitude": 108.458,
"capacity": 10,
"standard_capacity": 6,
"bedrooms": 4,
"bathrooms": 3,
"beds": 5,
"full_rate": 3000000,
"minimum_rate": 2500000,
"extra_adult_rate": 200000,
"extra_child_rate": 100000,
"guest_included_in_rate": 6,
"maximum_guest": 10,
"currency": "VND",
"cleaning_fee": 200000,
"allow_hourly_booking": true,
"min_hours": 2,
"instant_book_enabled": true,
"commission_rate": 0.15,
"refund_rule": "Hủy miễn phí trước 3 ngày"
}

3.4. Cập nhật Listing

PATCH /api/v1/listings/{listing_id}

Request Body: Các trường cần cập nhật (partial update).

3.5. Xóa Listing (Soft Delete)

DELETE /api/v1/listings/{listing_id}

4. Cách tính giá (Pricing)

4.1. Giá cơ bản (Daily Rate)

Giá cơ bản được xác định bởi:

  • full_rate: Giá đầy đủ (giá gốc)
  • minimum_rate: Giá tối thiểu (áp dụng cho một số ngày đặc biệt)

Công thức tính giá ngày thường:

daily_price = full_rate

4.2. Giá theo ngày trong tuần (Weekly Pricing)

Có thể cấu hình giá khác nhau cho từng ngày trong tuần:

PATCH /api/v1/listings/{id}/pricing/weekly
{
"base_price_by_weekday": {
"0": 3000000, // Chủ nhật
"1": 2500000, // Thứ 2
"2": 2500000, // Thứ 3
"3": 2500000, // Thứ 4
"4": 2500000, // Thứ 5
"5": 2800000, // Thứ 6
"6": 3000000 // Thứ 7
}
}

4.3. Giá theo giờ (Hourly Pricing)

Khi allow_hourly_booking = true:

base_price = price_first_hours  (giá cho min_hours đầu)
extra_price = (total_hours - min_hours) * extra_hour_price
total_price = base_price + extra_price

Ví dụ: min_hours = 4, price_first_hours = 800000, extra_hour_price = 150000

  • Đặt 4 giờ: 800,000 VND
  • Đặt 6 giờ: 800,000 + (6-4) * 150,000 = 1,100,000 VND

4.4. Giá người thêm (Extra Guest)

guests = max(0, total_guests - guest_included_in_rate)
extra_adult = guests * extra_adult_rate
extra_child = child_guests * extra_child_rate
total_extra = extra_adult + extra_child

4.5. Giá tổng cộng

total_price = daily_price * nights + extra_guests + cleaning_fee

4.6. Combo Pricing (Gói giá)

Combo pricing cho phép tạo các gói giá đặc biệt với giờ check-in/check-out linh hoạt.

GET /api/v1/listing-combo-prices/listings/{listing_id}

Response:

[
{
"id": 1,
"listing_id": 1,
"name": "Combo qua đêm 21h-9h",
"start_time": "21:00:00",
"end_time": "09:00:00",
"original_price": 2500000,
"discounted_price": 1800000,
"combo_type": "overnight",
"priority": 1,
"is_active": true
},
{
"id": 2,
"listing_id": 1,
"name": "Combo day use 8h-18h",
"start_time": "08:00:00",
"end_time": "18:00:00",
"original_price": 1500000,
"discounted_price": 1000000,
"combo_type": "day_use",
"priority": 2,
"is_active": true
}
]

ComboTypeEnum:

TypeMô tả
overnightCombo qua đêm
day_useCombo day use (thuê theo ngày)
hourly_blockCombo theo khối giờ
otherLoại khác

Tạo/Cập nhật Combo:

POST /api/v1/listing-combo-prices
PATCH /api/v1/listing-combo-prices/{combo_id}
DELETE /api/v1/listing-combo-prices/{combo_id}

Request:

{
"name": "Combo qua đêm 21h-9h",
"start_time": "21:00:00",
"end_time": "09:00:00",
"original_price": 2500000,
"discounted_price": 1800000,
"combo_type": "overnight",
"priority": 1,
"is_active": true
}

4.7. Extra Services (Dịch vụ thêm)

Dịch vụ bổ sung có thể tính phí ( VD: xe đạp, BBQ, check-in muộn...).

GET /api/v1/listings/{listing_id}/extra-services

Response:

[
{
"id": 1,
"listing_id": 1,
"service_name": "Xe đạp cho thuê",
"description": "Xe đạp leo núi",
"price": 50000,
"price_unit": "per_use",
"is_required": false,
"is_active": true,
"created_at": "2024-01-15T10:30:00Z"
},
{
"id": 2,
"listing_id": 1,
"service_name": "BBQ",
"description": "Dụng cụ BBQ + than",
"price": 150000,
"price_unit": "per_use",
"is_required": false,
"is_active": true
}
]

Trường:

TrườngKiểuMô tả
service_namestringTên dịch vụ
descriptionstring?Mô tả chi tiết
pricedecimalGiá dịch vụ
price_unitstringĐơn vị tính: per_use, per_night, per_person
is_requiredboolDịch vụ bắt buộc
is_activeboolDịch vụ đang hoạt động

API:

POST /api/v1/listings/{listing_id}/extra-services
PATCH /api/v1/listings/{listing_id}/extra-services/{service_id}
DELETE /api/v1/listings/{listing_id}/extra-services/{service_id}

4.8. Parking Rules (Chỗ để xe)

Quản lý thông tin đỗ xe cho xe máy và ô tô.

GET /api/v1/listings/{listing_id}/parking-rules

Response:

{
"motorbike": {
"id": 1,
"listing_id": 1,
"vehicle_type": "motorbike",
"is_free": true,
"price": 0,
"currency": "VND",
"location_type": "on_site",
"distance_in_meters": null,
"description": "Chỗ để xe máy miễn phí tại chỗ"
},
"car": {
"id": 2,
"listing_id": 1,
"vehicle_type": "car",
"is_free": false,
"price": 50000,
"currency": "VND",
"location_type": "nearby",
"distance_in_meters": 200,
"description": "Bãi đỗ xe gần đó, cách 200m"
}
}

VehicleTypeEnum:

TypeMô tả
motorbikeXe máy
carÔ tô

ParkingLocationTypeEnum:

TypeMô tả
on_siteTại chỗ
nearbyGần đó
streetĐường phố
noneKhông có

API:

POST /api/v1/listings/{listing_id}/parking-rules
PATCH /api/v1/listings/{listing_id}/parking-rules/{rule_id}
DELETE /api/v1/listings/{listing_id}/parking-rules/{rule_id}

Request:

{
"vehicle_type": "car",
"is_free": false,
"price": 50000,
"currency": "VND",
"location_type": "nearby",
"distance_in_meters": 200,
"description": "Bãi đỗ xe gần đó, cách 200m"
}

4.9. Pricing Rules (Nâng cao)

Pricing Rules cho phép cấu hình giá phức tạp hơn: theo ngày cụ thể, theo khoảng ngày, theo giờ...

GET /api/v1/listing-pricing-rules/listings/{listing_id}

Response:

[
{
"id": 1,
"listing_id": 1,
"date_type": "WEEKDAY",
"day_of_week": 5,
"charge_unit": "PER_NIGHT",
"stay_type": "ANY",
"price": 2800000,
"priority": 1,
"is_active": true
},
{
"id": 2,
"listing_id": 1,
"date_type": "DATE_RANGE",
"date_from": "2024-12-20",
"date_to": "2024-12-31",
"charge_unit": "PER_NIGHT",
"price": 4000000,
"priority": 2,
"is_active": true
}
]

DateTypeEnum:

TypeMô tả
ALWAYSÁp dụng luôn
WEEKDAYTheo ngày trong tuần (0=Chủ nhật, 6=Thứ 7)
DATE_RANGETrong khoảng ngày
SPECIFIC_DATENgày cụ thể

ChargeUnitEnum:

TypeMô tả
PER_BOOKINGTheo booking
PER_NIGHTTheo đêm
PER_HOURTheo giờ
PACKAGETheo gói
PER_MONTHTheo tháng

StayTypeEnum:

TypeMô tả
DAYDay use
OVERNIGHTQua đêm
ANYCả hai

API:

POST /api/v1/listing-pricing-rules/listings/{listing_id}
PATCH /api/v1/listing-pricing-rules/{rule_id}
DELETE /api/v1/listing-pricing-rules/{rule_id}

4.10. Preview Pricing

Xem trước giá:

POST /api/v1/listing-pricing-rules/preview

Request:

{
"listing_id": 1,
"check_in_date": "2024-12-25",
"check_out_date": "2024-12-28",
"check_in_time": "14:00:00",
"check_out_time": "12:00:00",
"guest_number": 4
}

Response:

{
"base_price": 12000000,
"total_price": 14500000,
"currency": "VND",
"breakdown": {
"nights": 3,
"nightly_rate": 4000000,
"extra_guests": 2,
"extra_guest_fee": 500000,
"cleaning_fee": 200000
}
}

4.11. Policies (Chính sách)

Policies bao gồm các quy định về nội quy và hoàn tiền.

GET /api/v1/listings/{listing_id}/policies

Response:

{
"id": 1,
"listing_id": 1,
"outside_food_allowed": true,
"party_allowed": true,
"smoking_allowed": false,
"photography_allowed": false,
"pet_allowed": true,
"id_card_required": true,
"quiet_time_start": "23:00:00",
"quiet_time_end": "08:00:00",
"other_rules": "Không mang vật nuôi vào phòng",
"refund_rule": "Hủy miễn phí trước 3 ngày",
"created_at": "2024-01-15T10:30:00Z"
}

Trường Policies:

TrườngMô tả
outside_food_allowedCho phép mang đồ ăn vào
party_allowedCho phép tổ chức tiệc
smoking_allowedCho phép hút thuốc
pet_allowedCho phép mang thú cưng
id_card_requiredYêu cầu CCCD/CMND
quiet_time_startGiờ bắt đầu giữ yên lặng
quiet_time_endGiờ kết thúc giữ yên lặng
refund_ruleChính sách hoàn tiền

5. Calendar và Availability

5.1. Lấy Calendar theo tháng

GET /api/v1/listing-availability/listings/{listing_id}/calendar?year=2024&month=3

Query Parameters:

ParameterKiểuMô tả
yearintNăm (mặc định: năm hiện tại)
monthintTháng (1-12, mặc định: tháng hiện tại)

Response:

[
{
"id": 1,
"listing_id": 1,
"reservation_id": 100,
"date": "2024-03-01",
"start_at": "2024-03-01T14:00:00",
"end_at": "2024-03-02T12:00:00",
"state": "BOOKED",
"locked_until": null,
"note": null,
"created_at": "2024-02-15T10:00:00"
},
{
"id": 2,
"listing_id": 1,
"reservation_id": null,
"date": "2024-03-05",
"start_at": "2024-03-05T00:00:00",
"end_at": "2024-03-06T00:00:00",
"state": "AVAILABLE",
"locked_until": null,
"note": null,
"created_at": "2024-01-01T00:00:00"
},
{
"id": 3,
"listing_id": 1,
"reservation_id": null,
"date": "2024-03-10",
"start_at": "2024-03-10T00:00:00",
"end_at": "2024-03-11T00:00:00",
"state": "BLOCKED",
"locked_until": null,
"note": "Bảo trì",
"created_at": "2024-03-01T08:00:00"
}
]

5.2. Trạng thái Availability

Trạng tháiMô tả
AVAILABLECó sẵn, có thể đặt
BOOKEDĐã có reservation
BLOCKEDBị khóa (host chủ động khóa)
SOFT_LOCKKhóa mềm (hết hạn sau locked_until)

5.3. Lấy Availability với filter

GET /api/v1/listing-availability/listings/{listing_id}?start_date=2024-03-01&end_date=2024-03-07&state=AVAILABLE

Query Parameters:

ParameterKiểuMô tả
start_datedate?Ngày bắt đầu (YYYY-MM-DD)
end_datedate?Ngày kết thúc (YYYY-MM-DD)
statestring?Lọc theo trạng thái

5.4. Lấy unavailable time slots (theo giờ)

API này trả về danh sách các slot (theo giờ) không thể đặt.

GET /api/v1/listing-availability/listings/{listing_id}/calendars/unavailable?start_time=2026-01-01T0000&end_time=2026-01-05T0000

Query Parameters:

ParameterKiểuMô tả
start_timestringThời gian bắt đầu (format: YYYY-MM-DDTHHmm, ví dụ: 2026-01-01T0000)
end_timestringThời gian kết thúc (format: YYYY-MM-DDTHHmm)

Response:

[
"2026-01-02T1400",
"2026-01-02T1500",
"2026-01-02T1600",
"2026-01-03T0000",
"2026-01-03T0100"
]

Ý nghĩa: FE sẽ tạo lưới hourly grid và đánh dấu các slot trong response này là unavailable.

5.5. Block ngày/giờ cụ thể

POST /api/v1/listing-availability/listings/{listing_id}/blocks

Request:

{
"block_date": "2024-03-10",
"start_at": "2024-03-10T00:00:00",
"end_at": "2024-03-11T00:00:00",
"note": "Bảo trì"
}

Trường:

TrườngKiểuMô tả
block_datedateNgày cần block (alias: date)
start_atdatetimeThời gian bắt đầu
end_atdatetimeThời gian kết thúc
notestring?Ghi chú

5.6. Unblock

DELETE /api/v1/listing-availability/listings/{listing_id}/blocks/{availability_id}

Response:

{
"message": "Availability unblocked",
"availability_id": 123
}

5.7. iCal Sync

Đồng bộ calendar với bên ngoài:

POST /api/v1/listings/{listing_id}/calendar/ical

Request:

{
"url": "https://calendar.google.com/..."
}

Xem thêm: iCal Sync


6. Response Models

6.1. ListingResponse

Response đầy đủ khi lấy chi tiết listing.

6.2. ListingSummaryResponse

Response ngắn gọn cho danh sách, bao gồm:

  • id, name, slug, listing_type
  • province_name, country_name
  • full_rate, minimum_rate
  • state, rating_average, review_count
  • images (array với id, url, order)

6.3. Listing với Amenities

Khi cần amenities chi tiết:

GET /api/v1/listings/{id}/amenities

Response:

{
"categories": [
{
"id": 1,
"name": "Tiện nghi phòng ngủ",
"code": "bedroom",
"amenities": [
{
"id": 1,
"amenity_id": 10,
"amenity_name": "Giường đôi",
"amenity_code": "double_bed",
"is_chargeable": false,
"price": 0
}
]
}
]
}