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ường | Kiểu | Mô tả |
|---|---|---|
id | int | ID duy nhất của listing |
team_id | int | ID của team sở hữu listing |
parent_listing_id | int? | ID listing cha (nếu là listing con) |
name | string | Tên listing (max 255 ký tự) |
slug | string | URL-friendly identifier (duy nhất trong team) |
description | string? | Mô tả chi tiết |
listing_type | string | Loại hình: ENTIRE_PLACE, PRIVATE_ROOM, SHARED_ROOM |
property_type | string | Loại tài sản: VILLA, APARTMENT, HOUSE, CONDO, ... |
Vị trí
| Trường | Kiểu | Mô tả |
|---|---|---|
address | string | Địa chỉ đầy đủ |
country_code | string | Mã quốc gia (mặc định: VN) |
province_code | string | Mã tỉnh/thành |
province_name | string | Tên tỉnh/thành (response only) |
latitude | decimal? | Vĩ độ |
longitude | decimal? | Kinh độ |
place_id | string? | Google Place ID |
Sức chứa
| Trường | Kiểu | Mô tả |
|---|---|---|
capacity | int | Số khách tối đa |
standard_capacity | int | Số khách tính trong giá cơ bản |
bedrooms | int? | Số phòng ngủ |
bathrooms | int? | Số phòng tắm |
beds | int? | Số giường |
square_meters | int? | Diện tích (m²) |
shared_bathrooms | int | Số phòng tắm chung |
Giá (Pricing)
| Trường | Kiểu | Mô tả |
|---|---|---|
full_rate | decimal | Giá đầy đủ (giá gốc) |
minimum_rate | decimal | Giá tối thiểu |
extra_adult_rate | decimal | Phí thêm người lớn |
extra_child_rate | decimal | Phí thêm trẻ em |
guest_included_in_rate | int | Số khách được tính trong giá |
maximum_guest | int | Số khách tối đa |
currency | string | Đơn vị tiền tệ (VND) |
security_deposit | decimal | Tiền đặt cọc |
cleaning_fee | decimal | Phí vệ sinh (một lần) |
Hourly Booking
| Trường | Kiểu | Mô tả |
|---|---|---|
allow_hourly_booking | bool | Cho phép đặt theo giờ |
min_hours | int | Số giờ tối thiểu (mặc định: 2) |
price_first_hours | decimal | Giá cho N giờ đầu |
extra_hour_price | decimal | Giá mỗi giờ tiếp theo |
price_rate_in_day | decimal | Hệ số giá cho đặt theo ngày (1-10) |
checkin_night_short_hour | time? | Giờ check-in cho đặt qua đêm |
checkout_night_short_hour | time? | Giờ check-out cho đặt qua đêm |
night_short_discount | decimal | % giảm giá cho đặt qua đêm |
Check-in
| Trường | Kiểu | Mô tả |
|---|---|---|
standard_check_in_time | time? | Giờ check-in tiêu chuẩn |
standard_check_out_time | time? | Giờ check-out tiêu chuẩn |
flexible_check_in_time | time? | Giờ check-in linh hoạt |
flexible_check_out_time | time? | Giờ check-out linh hoạt |
checkin_method | enum | Phương thức: SELF_CHECKIN, HOST_CHECKIN, BOTH |
checkin_guide_url | string? | URL hướng dẫn check-in |
no_checkin_after_hour | time? | Giờ chặn check-in |
Trạng thái và đánh giá
| Trường | Kiểu | Mô tả |
|---|---|---|
state | enum | Trạng thái: DRAFT, ACTIVE, INACTIVE, ARCHIVED |
rating_average | decimal? | Điểm đánh giá trung bình |
review_count | int | Số lượng đánh giá |
view_count | int | Số lượt xem |
instant_book_enabled | bool | Cho phép đặt ngay |
commission_rate | decimal | Tỷ lệ hoa hồng (0.15 = 15%) |
Khác
| Trường | Kiểu | Mô tả |
|---|---|---|
refund_rule | string | Chính sách hoàn tiền |
view_types | string[]? | Loại view: city, beach, garden, ... |
additional_areas | string[]? | Khu vực thêm: garden, balcony, pool, ... |
wifi_name | string? | Tên WiFi |
wifi_password | string? | Mật khẩu WiFi |
door_code | object? | 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:
| Parameter | Kiểu | Mô tả |
|---|---|---|
page | int | Số trang (mặc định: 1) |
limit | int | Số items/trang (mặc đ ịnh: 20) |
search | string? | Tìm kiếm theo tên |
status | string? | Lọc theo trạng thái: DRAFT, ACTIVE, INACTIVE |
province_code | string? | 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:
| Type | Mô tả |
|---|---|
overnight | Combo qua đêm |
day_use | Combo day use (thuê theo ngày) |
hourly_block | Combo theo khối giờ |
other | Loạ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ường | Kiểu | Mô tả |
|---|---|---|
service_name | string | Tên dịch vụ |
description | string? | Mô tả chi tiết |
price | decimal | Giá dịch vụ |
price_unit | string | Đơn vị tính: per_use, per_night, per_person |
is_required | bool | Dịch vụ bắt buộc |
is_active | bool | Dị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:
| Type | Mô tả |
|---|---|
motorbike | Xe máy |
car | Ô tô |
ParkingLocationTypeEnum:
| Type | Mô tả |
|---|---|
on_site | Tại chỗ |
nearby | Gần đó |
street | Đường phố |
none | Khô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:
| Type | Mô tả |
|---|---|
ALWAYS | Áp dụng luôn |
WEEKDAY | Theo ngày trong tuần (0=Chủ nhật, 6=Thứ 7) |
DATE_RANGE | Trong khoảng ngày |
SPECIFIC_DATE | Ngày cụ thể |
ChargeUnitEnum:
| Type | Mô tả |
|---|---|
PER_BOOKING | Theo booking |
PER_NIGHT | Theo đêm |
PER_HOUR | Theo giờ |
PACKAGE | Theo gói |
PER_MONTH | Theo tháng |
StayTypeEnum:
| Type | Mô tả |
|---|---|
DAY | Day use |
OVERNIGHT | Qua đêm |
ANY | Cả 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ường | Mô tả |
|---|---|
outside_food_allowed | Cho phép mang đồ ăn vào |
party_allowed | Cho phép tổ chức tiệc |
smoking_allowed | Cho phép hút thuốc |
pet_allowed | Cho phép mang thú cưng |
id_card_required | Yêu cầu CCCD/CMND |
quiet_time_start | Giờ bắt đầu giữ yên lặng |
quiet_time_end | Giờ kết thúc giữ yên lặng |
refund_rule | Chí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:
| Parameter | Kiểu | Mô tả |
|---|---|---|
year | int | Năm (mặc định: năm hiện tại) |
month | int | Thá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ái | Mô tả |
|---|---|
AVAILABLE | Có sẵn, có thể đặt |
BOOKED | Đã có reservation |
BLOCKED | Bị khóa (host chủ động khóa) |
SOFT_LOCK | Khó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:
| Parameter | Kiểu | Mô tả |
|---|---|---|
start_date | date? | Ngày bắt đầu (YYYY-MM-DD) |
end_date | date? | Ngày kết thúc (YYYY-MM-DD) |
state | string? | 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:
| Parameter | Kiểu | Mô tả |
|---|---|---|
start_time | string | Thời gian bắt đầu (format: YYYY-MM-DDTHHmm, ví dụ: 2026-01-01T0000) |
end_time | string | Thờ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ường | Kiểu | Mô tả |
|---|---|---|
block_date | date | Ngày cần block (alias: date) |
start_at | datetime | Thời gian bắt đầu |
end_at | datetime | Thời gian kết thúc |
note | string? | 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_typeprovince_name,country_namefull_rate,minimum_ratestate,rating_average,review_countimages(array vớiid,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
}
]
}
]
}