Documentation complète de l'API de gestion des réservations. Créer, modifier, consulter, check-in et gérer les paiements des bookings.
Toutes les requêtes aux endpoints /bookings et /booking-payments nécessitent une clé API valide.
curl -H "x-api-key: YOUR_API_KEY" https://listings.4u-realestate.org/bookings
Les calculs utilisent la configuration de la collection BookingSource pour chaque plateforme.
En plus des réservations, un lot peut être rendu indisponible manuellement sur Lodgify pour ne pas recevoir de bookings. Il existe deux types de blocages, exposés différemment dans l'API :
| Type de blocage | Où le trouver | Comment le reconnaître |
|---|---|---|
| Réservation manuelle "Owner blocked" | GET /bookings, GET /lots/:id/availability (tableau bookings) |
bookingSource: "Manual" et totalAmount: 0 (flag isManualBlock: true dans /lots/:id/availability). Nom client de type "Owner 203", "LONG TERM - BLOCKED PERIOD"… |
| Blocage pur du calendrier (closed period) | GET /bookings?includeBlockedPeriods=true, GET /lots/:id/availability (tableau blockedPeriods) |
Objet { start, end, type, lodgifyPeriodId }. Ce n'est pas un booking : aucune fiche client, aucun montant. |
blockedPeriodsSyncedAt indique la date de la dernière synchro.
Un blocage retiré sur Lodgify disparaît de l'API à la synchro suivante.
start et end sont au format YYYY-MM-DD et représentent la
première et la dernière nuit bloquées (inclusives). Ex: start: "2026-10-11", end: "2026-10-12" = nuits du 11 et du 12 octobre bloquées, le lot est de nouveau disponible à partir du 13.
À la différence des bookings où departure est le jour du départ.
| Champ | Type | Description |
|---|---|---|
| start | YYYY-MM-DD | Première nuit bloquée (inclusif) |
| end | YYYY-MM-DD | Dernière nuit bloquée (inclusif) |
| type | string | closed_period = blocage manuel du calendrier Lodgify · unavailable = indisponible sans booking ni closed period (ex: calendrier externe/iCal) |
| lodgifyPeriodId | number | null | ID de la closed period côté Lodgify (null pour unavailable) |
| syncedAt | ISO date | Dernière fois que la période a été vue sur Lodgify |
| lotId, lotName, lotNo, lodgifyId | — | Lot concerné (uniquement dans GET /bookings, où plusieurs lots sont mélangés) |
const res = await fetch(`${BASE}/lots/${lotId}/availability?start=2026-10-01&end=2026-12-31`, { headers });
const { bookings, blockedPeriods } = await res.json();
// Nuits occupées par des réservations (arrival inclus, departure exclu)
const busy = bookings.map(b => ({ from: b.arrival.slice(0, 10), to: b.departure.slice(0, 10), reason: b.isManualBlock ? 'owner-block' : 'booking' }));
// Nuits bloquées manuellement (start et end inclus)
const blocked = blockedPeriods.map(p => ({ from: p.start, to: p.end, reason: p.type }));
Liste paginée des bookings avec filtres, tri et résumé des paiements.
| Param | Type | Description |
|---|---|---|
| page | number | Page (défaut: 1) |
| limit | number | Résultats/page (défaut: 20, max: 100) |
| lotId | ObjectId | Filtrer par lot |
| lotIds | string | IDs séparés par virgules |
| status | string | Ex: "Booked,Checked" (séparés par virgules) |
| bookingSource | string | Ex: "AirbnbIntegration" |
| bookingNo | number | Recherche exacte |
| tenantName | string | Recherche regex insensible casse |
| startDate | ISO date | arrival ≥ startDate |
| endDate | ISO date | arrival ≤ endDate |
| sort | string | arrival_asc, arrival_desc, newest, oldest |
| includeBlockedPeriods | boolean | Ajoute les dates bloquées manuellement sur Lodgify (synchronisées chaque heure). Lots = lotId/lotIds si fournis, sinon tous. Plage = startDate/endDate (défaut: aujourd'hui → +12 mois, max 24 mois) |
{
"success": true,
"bookings": [{
"_id": "...", "bookingNo": 1234567,
"lotId": { "_id": "...", "name": "Villa Ocean", "lotNo": "A-101" },
"status": "Booked", "arrival": "2025-06-01", "departure": "2025-06-07",
"totalAmount": 1500, "cleaningFees": 100, "taxes": 71.43,
"tenantInformations": { "name": "John Doe", "email": "john@example.com" },
"paymentsSummary": { "totalPaid": 750, "count": 1, "remaining": 750, "isFullyPaid": false }
}],
"total": 42, "page": 1, "limit": 20, "totalPages": 3,
"blockedPeriodsRange": { "start": "2026-09-26", "end": "2027-09-26" }, // si includeBlockedPeriods=true
"blockedPeriodsSyncedAt": "2026-09-26T10:05:12.000Z", // dernière synchro Lodgify
"blockedPeriods": [{
"lotId": "...", "lotName": "B-203", "lotNo": "A-101", "lodgifyId": "675031",
"start": "2026-11-03", "end": "2026-11-09", // nuits bloquées, dates inclusives
"type": "closed_period", "lodgifyPeriodId": 4917894, "syncedAt": "2026-09-26T10:05:12.000Z"
}]
}
Note : les blocages faits via une réservation manuelle "Owner blocked" sur Lodgify
remontent déjà comme bookings (bookingSource: "Manual", totalAmount: 0).
blockedPeriods ne contient que les blocages purs du calendrier Lodgify (closed_period),
synchronisés toutes les heures depuis Lodgify (plage aujourd'hui → +18 mois).
Bookings actifs (Booked, Checked, Paid, Stand by) chevauchant la plage + périodes bloquées manuellement sur Lodgify (synchronisées chaque heure).
| Param | Type | Description |
|---|---|---|
| start | YYYY-MM-DD | Début de plage (défaut: aujourd'hui) |
| end | YYYY-MM-DD | Fin de plage (défaut: start + 12 mois, max 24 mois) |
{
"success": true,
"lot": { "id": "...", "name": "B-203", "lotNo": "SXM CB 097/2022 A-55", "lodgifyId": "675031" },
"range": { "start": "2026-09-26", "end": "2027-09-26" },
"blockedPeriodsSyncedAt": "2026-09-26T10:05:12.000Z",
"bookings": [{
"_id": "...", "bookingNo": 23108984, "arrival": "2026-10-01T15:00:00.000Z", "departure": "2026-10-03T11:00:00.000Z",
"status": "Booked", "bookingSource": "Manual", "isManualBlock": true, "guestName": "Owner 203"
}],
"blockedPeriods": [{
"start": "2026-11-03", "end": "2026-11-09", "type": "closed_period", "lodgifyPeriodId": 4917894,
"syncedAt": "2026-09-26T10:05:12.000Z"
}]
}
Bookings dont le départ est ≥ maintenant. Exclut Cancelled, Declined, Open, Tentative.
| Param | Type | Description |
|---|---|---|
| lotIds | string | IDs séparés par virgules |
| page | number | Défaut: 1 |
| limit | number | Défaut: 20 |
| sortBy | string | Champ de tri (défaut: arrival) |
| sortOrder | string | asc ou desc |
Vue comptable avec agrégation des totaux financiers et liste des sources.
| Param | Type | Description |
|---|---|---|
| lotIds | string | IDs séparés par virgules |
| status | string | Filtrer par statut(s) |
| bookingSource | string | Regex insensible casse |
| bookingNo | number | Numéro exact |
| startDate | ISO date | arrival ≥ |
| endDate | ISO date | arrival ≤ |
| page, limit | number | Pagination (limit défaut: 50) |
| sortBy, sortOrder | string | Tri (défaut: arrival desc) |
{
"success": true,
"bookings": [...],
"totalCount": 150, "totalPages": 3, "currentPage": 1,
"totals": {
"totalAmount": 125000, "totalSubtotal": 110000,
"totalPrepaidAmount": 100000, "totalCleaningFees": 8000,
"totalTaxes": 5952, "totalPlatformFees": 3750,
"totalBankFees": 5000, "totalRentalIncome": 95000,
"bookingCount": 150
},
"bookingSources": ["AirbnbIntegration", "BookingCom", "Manual"]
}
Liste des services add-on actifs proposés au check-in.
{
"success": true, "total": 5,
"services": [{
"_id": "...", "title": "Early Check-in",
"description": "Check-in before 3 PM",
"price": 50, "originalPrice": 75,
"image": "...", "displayOrder": 1
}]
}
Liste des sources de booking avec leurs pourcentages de frais.
{
"success": true, "total": 6,
"sources": [{
"source": "AirbnbIntegration",
"platformFees": 3, "bankFees": 0,
"tax": 5, "guestInsurance": 28.99
}]
}
| Param | Type | Description |
|---|---|---|
| bookingNo | numberREQUIS | Numéro de réservation |
Retourne le booking avec les financials calculés.
Recalcule les données financières à partir de la source du booking.
{
"success": true,
"financials": {
"totalAmount": 1500, "cleaningFees": 100,
"guestInsurance": 28.99, "taxes": 71.43,
"platformFees": 45, "subtotal": 1328.57,
"rentalIncome": 1194.58, "bankFees": 60,
"managementFees": 298.65, "netIncome": 895.94
}
}
Détail complet d'un booking avec financials calculés et résumé des paiements (confirmedPaid, pendingPaid).
| Param | Type | Description |
|---|---|---|
| id | ObjectIdREQUIS | ID MongoDB du booking |
passport, signature et authId ne sont jamais retournés.
| Champ | Type | Description |
|---|---|---|
| booking.bookingNo | numberREQUIS | Numéro unique (par lot) |
| booking.lotId | ObjectIdREQUIS | ID du lot |
| booking.arrival | ISO dateREQUIS | Date d'arrivée |
| booking.departure | ISO dateREQUIS | Date de départ |
| booking.totalAmount | numberREQUIS | Montant total TTC |
| booking.prepaidAmount | numberREQUIS | Montant pré-payé |
| booking.bedrooms | numberREQUIS | Nombre de chambres |
| booking.tenantInformations.name | stringREQUIS | Nom du locataire |
| booking.tenantInformations.email | stringREQUIS | Email du locataire |
| booking.status | string | Défaut: "Booked" |
| booking.bookingSource | string | Source de la réservation |
| booking.cleaningFees | number | Frais de ménage |
| booking.paymentMethod | string | Méthode de paiement |
{
"booking": {
"bookingNo": 9999999,
"lotId": "664f1a2b3c4d5e6f7a8b9c0d",
"arrival": "2025-06-01",
"departure": "2025-06-07",
"totalAmount": 1500,
"prepaidAmount": 1500,
"bedrooms": 2,
"bookingSource": "Manual",
"tenantInformations": {
"name": "John Doe",
"email": "john@example.com"
}
}
}
| Champ | Type | Description |
|---|---|---|
| bookingData.status | string | Nouveau statut |
| bookingData.arrival | ISO date | Nouvelle date d'arrivée |
| bookingData.departure | ISO date | Nouvelle date de départ |
| bookingData.totalAmount | number | Nouveau montant |
| bookingData.lotId | ObjectId ou {_id} | Nouveau lot |
| bookingData.tenantInformations | object | name, email, phone, address... |
| bookingData.isExtension | boolean | Marquer/annuler une extension (pas de frais de nettoyage / assurance) |
| bookingData.* | any | cleaningFees, guestInsurance, services, noShow fields... |
passport, signature, authId dans tenantInformations sont ignorés.Passe le booking en status "Checked", calcule les financials et met à jour les infos tenant.
| Champ | Type | Description |
|---|---|---|
| tenantInformations | object | { name, email, phone, address, companyName } |
| bookingSource | string | Source pour le calcul financier |
| cleaningFees | number | Frais de ménage |
| totalAmount | number | Montant total |
| paymentMethod | string | Méthode de paiement |
| guestInsurance | number | Optionnel (défaut: config source) |
| services | array | [{ serviceId, title, price, quantity, total }] |
| servicesTotal | number | Total services add-on |
| isSupervisorPresent | boolean | Superviseur présent au check-in |
| checkInEmployee | string | Nom de l'employé |
{
"success": true,
"message": "Check-in successful",
"booking": { ... },
"calculatedFinancials": {
"totalAmount": 1500, "taxes": 71.43,
"platformFees": 45, "bankFees": 60,
"rentalIncome": 1194.58, ...
},
"note": "PDF generation and email sending are handled by the main API only."
}
| Champ | Type | Description |
|---|---|---|
| isNoShow | boolean | true pour marquer, false pour annuler |
| noShowEmployee | stringREQUIS | Nom de l'employé |
| noShowNote | string | Note optionnelle |
| noShowValidated | boolean | Validé par un superviseur |
| noShowConfirmedSteps | boolean | Étapes confirmées |
| Champ | Type | Description |
|---|---|---|
| isExtension | boolean | true pour marquer, false pour annuler (pas de frais de nettoyage / assurance) |
Change le status en "Paid".
Marque isDeleted=true. Le booking n'apparaît plus dans les listes.
Remet isDeleted=false.
Retourne tous les paiements non supprimés pour un booking donné.
{
"success": true,
"data": {
"booking": { "_id": "...", "bookingNo": 1234, "totalAmount": 1500, "prepaidAmount": 1500 },
"payments": {
"count": 2, "totalPaid": 1200,
"confirmedPaid": 800, "pendingPaid": 400,
"remaining": 300, "isFullyPaid": false
}
}
}
| Param | Type | Description |
|---|---|---|
| page, limit | number | Pagination |
| search | string | Recherche par numéro ou nom tenant |
| lotId | ObjectId | Filtrer par lot |
| bookingSource | string | Source |
| paymentMethod | string | Méthode de paiement |
| paymentStatus | string | "pending" ou "confirmed" |
| paymentDateStart | ISO date | Date début |
| paymentDateEnd | ISO date | Date fin |
| amountMin | number | Montant minimum |
| amountMax | number | Montant maximum |
| Param | Type | Description |
|---|---|---|
| search | string | Numéro ou nom |
| lotId, status, bookingSource | string | Filtres booking |
| startDate, endDate | ISO date | Période |
| hasPayments | boolean | Avec paiements |
| hasPendingPayments | boolean | Avec paiements en attente |
| paymentStatus | string | "pending" ou "confirmed" |
| paymentDateStart, paymentDateEnd | ISO date | Période paiement |
| Champ | Type | Description |
|---|---|---|
| bookingId | ObjectIdREQUIS | ID du booking |
| amount | numberREQUIS | Montant |
| paymentMethod | string | Airbnb, Expedia, Payo, VRBO, Credit Card, Cash, Bank Transfer, Check, Other |
| paymentDate | ISO date | Défaut: maintenant |
| description | string | Description |
| reference | string | Référence |
Champs modifiables : amount, paymentMethod, paymentDate, description, reference.
Marque le paiement comme confirmé avec la date de confirmation.
| Champ | Type | Description |
|---|---|---|
| paymentIds | string[]REQUIS | Liste des IDs de paiements |
| isConfirmed | boolean | true = confirmer, false = infirmer |
Soft delete (isDeleted=true).
const API = 'https://listings.4u-realestate.org';
const KEY = 'YOUR_API_KEY';
const headers = { 'x-api-key': KEY, 'Content-Type': 'application/json' };
// Lister les bookings
const res = await fetch(`${API}/bookings?status=Booked&limit=10`, { headers });
const data = await res.json();
console.log(`Total: ${data.total} bookings`);
// Créer un booking
const created = await fetch(`${API}/bookings`, {
method: 'POST', headers,
body: JSON.stringify({
booking: {
bookingNo: 9999999, lotId: 'LOT_ID',
arrival: '2025-06-01', departure: '2025-06-07',
totalAmount: 1500, prepaidAmount: 1500, bedrooms: 2,
bookingSource: 'Manual',
tenantInformations: { name: 'John Doe', email: 'john@ex.com' }
}
})
});
// Check-in
await fetch(`${API}/bookings/${bookingId}/check-in`, {
method: 'PATCH', headers,
body: JSON.stringify({
tenantInformations: { name: 'John Doe', email: 'john@ex.com', phone: '+1234' },
bookingSource: 'Manual', cleaningFees: 100,
totalAmount: 1500, paymentMethod: 'Credit Card',
services: [], servicesTotal: 0, checkInEmployee: 'Agent'
})
});
// Ajouter un paiement
await fetch(`${API}/booking-payments`, {
method: 'POST', headers,
body: JSON.stringify({
bookingId: 'BOOKING_ID', amount: 750,
paymentMethod: 'Credit Card', description: 'First payment'
})
});
import requests
API = 'https://listings.4u-realestate.org'
HEADERS = {'x-api-key': 'YOUR_KEY', 'Content-Type': 'application/json'}
# Lister
r = requests.get(f'{API}/bookings', params={'status': 'Booked', 'limit': 10}, headers=HEADERS)
print(f"Total: {r.json()['total']} bookings")
# Créer
r = requests.post(f'{API}/bookings', json={
'booking': {
'bookingNo': 9999999, 'lotId': 'LOT_ID',
'arrival': '2025-06-01', 'departure': '2025-06-07',
'totalAmount': 1500, 'prepaidAmount': 1500, 'bedrooms': 2,
'tenantInformations': {'name': 'John Doe', 'email': 'john@ex.com'}
}
}, headers=HEADERS)
# Check-in
r = requests.patch(f'{API}/bookings/{booking_id}/check-in', json={
'tenantInformations': {'name': 'John Doe', 'email': 'john@ex.com'},
'bookingSource': 'Manual', 'cleaningFees': 100,
'totalAmount': 1500, 'paymentMethod': 'Credit Card',
'services': [], 'servicesTotal': 0
}, headers=HEADERS)
# Paiement
r = requests.post(f'{API}/booking-payments', json={
'bookingId': 'ID', 'amount': 750, 'paymentMethod': 'Credit Card'
}, headers=HEADERS)
# Lister les bookings
curl -H "x-api-key: KEY" "https://listings.4u-realestate.org/bookings?status=Booked&limit=10"
# Détail
curl -H "x-api-key: KEY" "https://listings.4u-realestate.org/bookings/BOOKING_ID"
# Vue comptable
curl -H "x-api-key: KEY" "https://listings.4u-realestate.org/bookings/accounting?status=Paid"
# Créer
curl -X POST -H "x-api-key: KEY" -H "Content-Type: application/json" \
-d '{"booking":{"bookingNo":9999999,"lotId":"LOT_ID","arrival":"2025-06-01","departure":"2025-06-07","totalAmount":1500,"prepaidAmount":1500,"bedrooms":2,"tenantInformations":{"name":"John Doe","email":"john@ex.com"}}}' \
"https://listings.4u-realestate.org/bookings"
# Check-in
curl -X PATCH -H "x-api-key: KEY" -H "Content-Type: application/json" \
-d '{"tenantInformations":{"name":"John","email":"john@ex.com"},"bookingSource":"Manual","cleaningFees":100,"totalAmount":1500,"paymentMethod":"Credit Card","services":[],"servicesTotal":0}' \
"https://listings.4u-realestate.org/bookings/BOOKING_ID/check-in"
# Paiement
curl -X POST -H "x-api-key: KEY" -H "Content-Type: application/json" \
-d '{"bookingId":"BOOKING_ID","amount":750,"paymentMethod":"Credit Card"}' \
"https://listings.4u-realestate.org/booking-payments"
# Confirmer paiement
curl -X PATCH -H "x-api-key: KEY" \
"https://listings.4u-realestate.org/booking-payments/PAYMENT_ID/confirm"
# Valider → Paid
curl -X PATCH -H "x-api-key: KEY" \
"https://listings.4u-realestate.org/bookings/BOOKING_ID/validate-payments"
| Code | Signification | Solution |
|---|---|---|
| 400 | Données invalides ou manquantes | Vérifier les champs requis |
| 401 | Clé API manquante ou invalide | Ajouter un header x-api-key valide |
| 403 | Clé API désactivée | Contacter l'admin |
| 404 | Booking/paiement non trouvé | Vérifier l'ID |
| 409 | Doublon (bookingNo + lotId) | Utiliser un numéro unique |
| 429 | Rate limit dépassé | Attendre et réessayer |
| 500 | Erreur serveur | Contacter contact@4u-realestate.org |