{
  "openapi": "3.0.3",
  "info": {
    "title": "ABP API \u2014 Multi-Provider Hotel Booking",
    "version": "1.0.0",
    "description": "Provider-agnostic hotel booking API. Multiple suppliers (TunisiaBeds, debdab) sit behind one\nunified API: the client never sees a provider name or a raw rate token \u2014 everything routes\nthrough opaque, HMAC-signed `offerId`s and a booking state machine\n(`quoted \u2192 committed \u2192 settled \u2192 cancelled \u2192 refunded`).\n\n**Auth** is delegated to Wasata. `POST /users/login` proxies Wasata and returns a bearer token;\nsend it as `Authorization: Bearer <token>` on every protected route. Permissions:\n- `use-booking` \u2014 search + booking (customer).\n- `create-transaction` \u2014 the owner/superadmin gate (`/admin/*`, provider sync, `/health/catalog`);\n  also lets a user act on any booking.\n- `is_owner` \u2014 agency owner (`/agency/*`).\n\nEvery response is enveloped as `{ success, message, data }`; errors as `{ success:false, message, error }`.\nAll customer routes are also mounted under `/v2` (identical).\n\n\u26a0\ufe0f While provider integration is under test, `INTEGRATION_TEST_HOTEL_ID` is set: search returns the\nreal catalogue but every TunisiaBeds booking is silently re-bound to the approved test hotel."
  },
  "servers": [
    {
      "url": "https://api.wasata.160.19.97.6.sslip.io",
      "description": "Deployed (Coolify)"
    },
    {
      "url": "https://api.wasata.160.19.97.6.sslip.io/v2",
      "description": "Deployed \u2014 /v2 alias (identical customer routes)"
    },
    {
      "url": "http://localhost:3000",
      "description": "Local"
    }
  ],
  "tags": [
    {
      "name": "Auth",
      "description": "Login (Wasata proxy) + session."
    },
    {
      "name": "Catalog",
      "description": "Unified city catalogue + hotel detail (public reads)."
    },
    {
      "name": "Search & Booking",
      "description": "Customer flow \u2014 requires use-booking."
    },
    {
      "name": "Agency",
      "description": "Agency-owner dashboard."
    },
    {
      "name": "Admin",
      "description": "Owner console \u2014 requires create-transaction."
    },
    {
      "name": "Provider Sync",
      "description": "Superadmin-only catalogue/booking sync triggers."
    },
    {
      "name": "Health",
      "description": "Liveness + catalogue diagnostics."
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/health": {
      "get": {
        "tags": [
          "Health"
        ],
        "summary": "Liveness + build marker",
        "security": [],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "ok": {
                          "type": "boolean"
                        },
                        "build": {
                          "type": "string"
                        },
                        "uptimeSeconds": {
                          "type": "integer"
                        }
                      }
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/health/catalog": {
      "get": {
        "tags": [
          "Health"
        ],
        "summary": "Cross-provider catalogue diagnostic (superadmin)",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "unifiedTotal": {
                          "type": "integer"
                        },
                        "unifiedMultiProvider": {
                          "type": "integer"
                        },
                        "mappingHistogram": {
                          "type": "array",
                          "items": {}
                        },
                        "cityByProvider": {
                          "type": "array",
                          "items": {}
                        },
                        "pendingMerges": {
                          "type": "integer",
                          "nullable": true
                        }
                      }
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/users/login": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Login (Wasata proxy)",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "user_name",
                  "password"
                ],
                "properties": {
                  "user_name": {
                    "type": "string"
                  },
                  "password": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Bearer token + user.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "token": {
                          "type": "string"
                        },
                        "user": {
                          "$ref": "#/components/schemas/WasataUser"
                        }
                      }
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/users/me": {
      "get": {
        "tags": [
          "Auth"
        ],
        "summary": "Authenticated session (user, agency, wallets)",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "$ref": "#/components/schemas/Profile"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/users/logout": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Invalidate cached session",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "nullable": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/users/me/currencies": {
      "get": {
        "tags": [
          "Auth"
        ],
        "summary": "Currencies the caller can pay in",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "type": "array",
                      "items": {}
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/users/me/bookings": {
      "get": {
        "tags": [
          "Auth"
        ],
        "summary": "My bookings (alias of GET /bookings)",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Booking"
                          }
                        },
                        "total": {
                          "type": "integer"
                        },
                        "page": {
                          "type": "integer"
                        },
                        "totalPages": {
                          "type": "integer"
                        }
                      }
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/cities": {
      "get": {
        "tags": [
          "Catalog"
        ],
        "summary": "Typeahead city lookup",
        "security": [],
        "parameters": [
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "anchored prefix match; capped page without it"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/City"
                      }
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/hotels/{id}": {
      "get": {
        "tags": [
          "Catalog"
        ],
        "summary": "Hotel detail (lazy-loads on first request)",
        "description": "Accepts EITHER our Mongo `_id` OR the provider hotel id \u2014 the latter is what search returns in `offer.hotel.id`, so it is usually what you have. Unknown ids return 404 (never 500). Detail is lazy-loaded from the provider on first request; if that provider call fails the cached record is still returned.",
        "security": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "provider",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Disambiguates when two providers share a hotel id."
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "type": "object"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/search": {
      "post": {
        "tags": [
          "Search & Booking"
        ],
        "summary": "Availability search (fans out to every mapped provider)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SearchBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "$ref": "#/components/schemas/SearchResult"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/bookings": {
      "get": {
        "tags": [
          "Search & Booking"
        ],
        "summary": "List my bookings",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "state",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Booking"
                          }
                        },
                        "total": {
                          "type": "integer"
                        },
                        "page": {
                          "type": "integer"
                        },
                        "totalPages": {
                          "type": "integer"
                        }
                      }
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "post": {
        "tags": [
          "Search & Booking"
        ],
        "summary": "Create a booking (runs the state machine)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateBookingBody"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created (and settled, unless integrationError is set).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "$ref": "#/components/schemas/CreateBookingResult"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "410": {
            "description": "Offer expired \u2014 search again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Currency gate: no wallet can pay the offer currency.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/bookings/{id}": {
      "get": {
        "tags": [
          "Search & Booking"
        ],
        "summary": "Get a booking",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "$ref": "#/components/schemas/Booking"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/bookings/{id}/settle": {
      "post": {
        "tags": [
          "Search & Booking"
        ],
        "summary": "Retry settlement (mirror + wallet charge)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "type": "object"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "502": {
            "description": "Wasata settle failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/bookings/{id}/cancel": {
      "post": {
        "tags": [
          "Search & Booking"
        ],
        "summary": "Cancel at provider, then void the wallet charge",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "cancellationFeeOverride": {
                    "type": "number"
                  },
                  "notifyCustomer": {
                    "type": "boolean",
                    "default": false,
                    "description": "Email the guest a cancellation confirmation"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Ends at refunded / refundPending (settled) or cancelled (unsettled).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "$ref": "#/components/schemas/Booking"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/bookings/{id}/refund": {
      "post": {
        "tags": [
          "Search & Booking"
        ],
        "summary": "Release the wallet void for a refundPending booking (no re-cancel)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "penalty": {
                    "type": "number",
                    "description": "override withheld fee"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "$ref": "#/components/schemas/Booking"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "502": {
            "description": "Void failed \u2014 parked in refundPending.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/agency/bookings": {
      "get": {
        "tags": [
          "Agency"
        ],
        "summary": "Agency bookings",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "agencyId",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "description": "admin override"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Booking"
                          }
                        },
                        "total": {
                          "type": "integer"
                        },
                        "page": {
                          "type": "integer"
                        },
                        "totalPages": {
                          "type": "integer"
                        }
                      }
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/agency/bookings/{id}": {
      "get": {
        "tags": [
          "Agency"
        ],
        "summary": "Agency booking detail",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "$ref": "#/components/schemas/Booking"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/agency/users/{wasataUserId}/bookings": {
      "get": {
        "tags": [
          "Agency"
        ],
        "summary": "Bookings for one agency user",
        "parameters": [
          {
            "name": "wasataUserId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Booking"
                          }
                        },
                        "total": {
                          "type": "integer"
                        },
                        "page": {
                          "type": "integer"
                        },
                        "totalPages": {
                          "type": "integer"
                        }
                      }
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/agency/stats": {
      "get": {
        "tags": [
          "Agency"
        ],
        "summary": "Agency booking stats",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "$ref": "#/components/schemas/AdminStats"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/admin/stats": {
      "get": {
        "tags": [
          "Admin"
        ],
        "summary": "Dashboard stats",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "$ref": "#/components/schemas/AdminStats"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/admin/bookings": {
      "get": {
        "tags": [
          "Admin"
        ],
        "summary": "All bookings (filter + sort)",
        "parameters": [
          {
            "name": "provider",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "state",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "settlementStatus",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "settled",
                "failed"
              ]
            }
          },
          {
            "name": "lifecycleState",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "agencyId",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "wasataUserId",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "fromDate",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "toDate",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "sortBy",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "createdAt",
                "totalPrice",
                "settlementAttemptedAt",
                "checkIn",
                "provider"
              ]
            }
          },
          {
            "name": "sortDir",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Booking"
                          }
                        },
                        "total": {
                          "type": "integer"
                        },
                        "page": {
                          "type": "integer"
                        },
                        "totalPages": {
                          "type": "integer"
                        }
                      }
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/admin/bookings/{id}": {
      "get": {
        "tags": [
          "Admin"
        ],
        "summary": "Booking detail",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "$ref": "#/components/schemas/Booking"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/admin/bookings/{id}/sync": {
      "post": {
        "tags": [
          "Admin"
        ],
        "summary": "Refresh one booking from its provider",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "$ref": "#/components/schemas/Booking"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/admin/bookings/{id}/settle": {
      "post": {
        "tags": [
          "Admin"
        ],
        "summary": "Settle a booking (owner)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "type": "object"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "502": {
            "description": "Wasata settle failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/admin/bookings/{id}/cancel": {
      "post": {
        "tags": [
          "Admin"
        ],
        "summary": "Cancel + void (owner)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "cancellationFeeOverride": {
                    "type": "number"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "$ref": "#/components/schemas/Booking"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/admin/bookings/{id}/refund": {
      "post": {
        "tags": [
          "Admin"
        ],
        "summary": "Release refund void (owner)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "penalty": {
                    "type": "number"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "$ref": "#/components/schemas/Booking"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "502": {
            "description": "Void failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/admin/providers": {
      "get": {
        "tags": [
          "Admin"
        ],
        "summary": "Registered providers",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/admin/providers/{name}/sync": {
      "post": {
        "tags": [
          "Admin"
        ],
        "summary": "Force static sync",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Sync started (background).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "nullable": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/admin/providers/{name}/sync/bookings": {
      "post": {
        "tags": [
          "Admin"
        ],
        "summary": "Force booking sync",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "nullable": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/admin/city-merges": {
      "get": {
        "tags": [
          "Admin"
        ],
        "summary": "City-merge review queue",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "approved",
                "rejected"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CityMerge"
                      }
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/admin/city-merges/{id}/approve": {
      "post": {
        "tags": [
          "Admin"
        ],
        "summary": "Approve a merge (rebuilds catalogue)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "merge": {
                          "$ref": "#/components/schemas/CityMerge"
                        },
                        "rebuild": {}
                      }
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/admin/city-merges/{id}/reject": {
      "post": {
        "tags": [
          "Admin"
        ],
        "summary": "Reject a merge",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "type": "object"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/providers/{providerName}/sync": {
      "post": {
        "tags": [
          "Provider Sync"
        ],
        "summary": "Full static sync + unified-city rebuild",
        "parameters": [
          {
            "name": "providerName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "tunisiabeds",
                "debdab",
                "mockbeds"
              ]
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "nullable": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/providers/{providerName}/sync/bookings": {
      "post": {
        "tags": [
          "Provider Sync"
        ],
        "summary": "Refresh booking states",
        "parameters": [
          {
            "name": "providerName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "tunisiabeds",
                "debdab",
                "mockbeds"
              ]
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "nullable": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/providers/{providerName}/sync/hotels": {
      "post": {
        "tags": [
          "Provider Sync"
        ],
        "summary": "Bulk hotel sync (501 for lazy providers)",
        "parameters": [
          {
            "name": "providerName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "tunisiabeds",
                "debdab",
                "mockbeds"
              ]
            }
          },
          {
            "name": "cityId",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "nullable": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "501": {
            "description": "Provider is lazy-catalog.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/providers/{providerName}/sync/hotels/{id}": {
      "post": {
        "tags": [
          "Provider Sync"
        ],
        "summary": "Sync one hotel's detail",
        "parameters": [
          {
            "name": "providerName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "tunisiabeds",
                "debdab",
                "mockbeds"
              ]
            }
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "nullable": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/bookings/{id}/voucher.pdf": {
      "get": {
        "summary": "Download / print the hotel voucher",
        "description": "Streams the same document the guest is emailed. Inline by default so a browser can print it; ?disposition=attachment forces a download. A cancelled booking returns the cancellation confirmation. Contains no pricing.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "disposition",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "inline",
                "attachment"
              ],
              "default": "inline"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "application/pdf",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "403": {
            "description": "Not your booking"
          }
        }
      }
    },
    "/bookings/{id}/send-voucher": {
      "post": {
        "summary": "Email the voucher PDF to the guest",
        "description": "Explicit action \u2014 sends regardless of notifyCustomer, to the stored customer.email unless overridden.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sent"
          },
          "400": {
            "description": "No guest email, or the send failed"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Wasata bearer token from POST /users/login."
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing/invalid bearer token.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "success": false,
              "message": "Authentication token is required"
            }
          }
        }
      },
      "Forbidden": {
        "description": "Authenticated but missing the required Wasata permission.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "success": false,
              "message": "Booking not found"
            }
          }
        }
      },
      "BadRequest": {
        "description": "Validation error.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "example": false
          },
          "message": {
            "type": "string"
          },
          "error": {
            "nullable": true,
            "description": "Upstream/provider detail when present."
          }
        },
        "required": [
          "success",
          "message"
        ]
      },
      "WasataUser": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "agency_id": {
            "type": "integer"
          },
          "is_owner": {
            "type": "boolean"
          },
          "permissions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "WasataAccount": {
        "type": "object",
        "description": "A wallet.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "account id \u2014 used as void currency_id"
          },
          "currency_id": {
            "type": "integer"
          },
          "currency_code": {
            "type": "string",
            "example": "USD"
          },
          "currency_symbol": {
            "type": "string"
          },
          "balance": {
            "type": "number"
          }
        }
      },
      "Profile": {
        "type": "object",
        "properties": {
          "user": {
            "$ref": "#/components/schemas/WasataUser"
          },
          "agency": {
            "type": "object",
            "nullable": true,
            "properties": {
              "id": {
                "type": "integer"
              },
              "name": {
                "type": "string"
              }
            }
          },
          "accounts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WasataAccount"
            }
          }
        }
      },
      "City": {
        "type": "object",
        "description": "A unified city \u2014 the id the client sends to /search.",
        "properties": {
          "_id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "country": {
            "type": "string",
            "nullable": true,
            "description": "ISO alpha-2 (or raw name fallback)"
          }
        }
      },
      "CancelPolicy": {
        "type": "object",
        "properties": {
          "fees": {
            "type": "number"
          },
          "type": {
            "type": "string",
            "nullable": true,
            "enum": [
              "PRICE",
              "PERCENT",
              "NIGHT",
              null
            ]
          },
          "nature": {
            "type": "string",
            "nullable": true
          },
          "fromDate": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "Supplement": {
        "type": "object",
        "properties": {
          "index": {
            "type": "integer",
            "nullable": true
          },
          "type": {
            "type": "string",
            "nullable": true
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "price": {
            "type": "number"
          },
          "included": {
            "type": "boolean"
          }
        }
      },
      "OfferBoarding": {
        "type": "object",
        "description": "Heterogeneous per provider: TunisiaBeds nests pax/rooms; debdab is flat with price/mealType.",
        "properties": {
          "id": {
            "type": "integer"
          },
          "code": {
            "type": "string"
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "price": {
            "type": "number",
            "nullable": true
          },
          "tax": {
            "type": "number"
          },
          "currency": {
            "type": "string",
            "nullable": true
          },
          "mealType": {
            "type": "string",
            "nullable": true
          },
          "isRefundable": {
            "type": "boolean",
            "nullable": true
          },
          "inclusion": {
            "type": "string",
            "nullable": true
          },
          "recommendedSellingRate": {
            "type": "string",
            "nullable": true
          },
          "cancellationPolicy": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CancelPolicy"
            }
          },
          "supplements": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Supplement"
            }
          },
          "pax": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "adults": {
                  "type": "integer"
                },
                "children": {
                  "type": "array",
                  "items": {
                    "type": "integer"
                  }
                },
                "rooms": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "integer"
                      },
                      "name": {
                        "type": "string"
                      },
                      "price": {
                        "type": "number",
                        "nullable": true
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "Offer": {
        "type": "object",
        "properties": {
          "offerId": {
            "type": "string",
            "description": "Opaque, HMAC-signed. Hides provider + rate token + nationality + dates. Send back verbatim to book."
          },
          "currency": {
            "type": "string",
            "nullable": true
          },
          "hotel": {
            "type": "object",
            "properties": {
              "id": {
                "oneOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "integer"
                  }
                ]
              },
              "name": {
                "type": "string",
                "nullable": true
              },
              "category": {
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string",
                    "nullable": true
                  },
                  "star": {
                    "type": "string",
                    "nullable": true
                  }
                }
              },
              "city": {
                "type": "object",
                "properties": {
                  "id": {
                    "oneOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "integer"
                      }
                    ]
                  },
                  "name": {
                    "type": "string"
                  }
                }
              },
              "address": {
                "type": "string",
                "nullable": true
              },
              "image": {
                "type": "string",
                "nullable": true
              },
              "description": {
                "type": "string",
                "nullable": true
              },
              "coords": {
                "type": "object",
                "nullable": true,
                "properties": {
                  "lat": {
                    "type": "number"
                  },
                  "lng": {
                    "type": "number"
                  }
                }
              }
            }
          },
          "boardings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OfferBoarding"
            }
          },
          "bookable": {
            "type": "boolean",
            "description": "false when the offer currency is not in the caller's wallets."
          },
          "bookableReason": {
            "type": "string",
            "nullable": true,
            "example": "currencyMismatch"
          }
        }
      },
      "SearchResult": {
        "type": "object",
        "properties": {
          "available": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Offer"
            }
          },
          "partial": {
            "type": "boolean",
            "description": "true if a provider timed out/failed."
          },
          "unavailableProviders": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "AdultPax": {
        "type": "object",
        "properties": {
          "civility": {
            "type": "string",
            "example": "Mr"
          },
          "name": {
            "type": "string"
          },
          "surname": {
            "type": "string"
          },
          "holder": {
            "type": "boolean"
          }
        },
        "required": [
          "name",
          "surname"
        ]
      },
      "ChildPax": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "surname": {
            "type": "string"
          },
          "age": {
            "type": "integer"
          }
        },
        "required": [
          "name",
          "surname",
          "age"
        ]
      },
      "BookRoom": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "TunisiaBeds room id (omit for debdab \u2014 bound in offerId)"
          },
          "boarding": {
            "type": "integer"
          },
          "views": {
            "type": "array",
            "items": {
              "type": "integer"
            }
          },
          "supplements": {
            "type": "array",
            "items": {
              "type": "integer"
            }
          },
          "pax": {
            "type": "object",
            "properties": {
              "adults": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/AdultPax"
                }
              },
              "children": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ChildPax"
                }
              }
            },
            "required": [
              "adults"
            ]
          }
        },
        "required": [
          "pax"
        ]
      },
      "SearchBody": {
        "type": "object",
        "required": [
          "cityId",
          "checkIn",
          "checkOut",
          "rooms",
          "guestNationality"
        ],
        "properties": {
          "cityId": {
            "type": "string",
            "description": "UnifiedCity._id"
          },
          "checkIn": {
            "type": "string",
            "format": "date"
          },
          "checkOut": {
            "type": "string",
            "format": "date"
          },
          "rooms": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "adult": {
                  "type": "integer",
                  "minimum": 1
                },
                "children": {
                  "type": "array",
                  "items": {
                    "type": "integer",
                    "description": "ages"
                  }
                }
              },
              "required": [
                "adult"
              ]
            }
          },
          "guestNationality": {
            "type": "string",
            "description": "ISO country code \u2014 REQUIRED (baked into every offer)."
          },
          "currency": {
            "type": "string"
          }
        }
      },
      "CreateBookingBody": {
        "type": "object",
        "required": [
          "offerId",
          "rooms"
        ],
        "properties": {
          "offerId": {
            "type": "string"
          },
          "rooms": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BookRoom"
            }
          },
          "customer": {
            "type": "object",
            "properties": {
              "email": {
                "type": "string"
              },
              "phone": {
                "type": "string"
              }
            },
            "description": "debdab Book fields."
          },
          "idempotencyNonce": {
            "type": "string",
            "description": "Client nonce for double-submit protection."
          },
          "notifyCustomer": {
            "type": "boolean",
            "default": false,
            "description": "Email the guest their voucher (PDF, no prices) once the booking is confirmed. Defaults to false \u2014 nothing is sent unless requested."
          }
        }
      },
      "BookingRoom": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "price": {
            "type": "number",
            "nullable": true
          },
          "boarding": {
            "type": "object",
            "properties": {
              "id": {
                "type": "integer"
              },
              "code": {
                "type": "string"
              },
              "name": {
                "type": "string"
              }
            }
          },
          "adults": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AdultPax"
            }
          },
          "children": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ChildPax"
            }
          },
          "notRefundable": {
            "type": "boolean"
          },
          "cancellationPolicy": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CancelPolicy"
            }
          }
        }
      },
      "Booking": {
        "type": "object",
        "properties": {
          "_id": {
            "type": "string"
          },
          "provider": {
            "type": "string"
          },
          "providerBookingId": {
            "type": "string"
          },
          "providerRef": {
            "type": "string",
            "nullable": true
          },
          "state": {
            "type": "string",
            "enum": [
              "OnRequest",
              "Validated",
              "Cancelled"
            ]
          },
          "lifecycleState": {
            "type": "string",
            "nullable": true,
            "enum": [
              "quoted",
              "committed",
              "settled",
              "cancelled",
              "refundPending",
              "refunded",
              null
            ]
          },
          "hotel": {
            "type": "object",
            "properties": {
              "id": {
                "oneOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "integer"
                  }
                ]
              },
              "name": {
                "type": "string"
              },
              "image": {
                "type": "string",
                "nullable": true
              },
              "address": {
                "type": "string",
                "nullable": true
              },
              "category": {
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string",
                    "nullable": true
                  },
                  "star": {
                    "type": "string",
                    "nullable": true
                  }
                }
              },
              "city": {
                "type": "object",
                "properties": {
                  "id": {
                    "oneOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "integer"
                      }
                    ]
                  },
                  "name": {
                    "type": "string"
                  }
                }
              }
            }
          },
          "checkIn": {
            "type": "string",
            "format": "date-time"
          },
          "checkOut": {
            "type": "string",
            "format": "date-time"
          },
          "currency": {
            "type": "string"
          },
          "totalPrice": {
            "type": "number"
          },
          "rooms": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BookingRoom"
            }
          },
          "cancellationFee": {
            "type": "number",
            "nullable": true
          },
          "settlementStatus": {
            "type": "string",
            "enum": [
              "pending",
              "settled",
              "failed"
            ]
          },
          "settlementError": {
            "type": "string",
            "nullable": true
          },
          "wasataBookingId": {
            "type": "integer",
            "nullable": true
          },
          "wasataTransactionId": {
            "type": "integer",
            "nullable": true
          },
          "wasataUserName": {
            "type": "string",
            "nullable": true
          },
          "wasataAgencyName": {
            "type": "string",
            "nullable": true
          },
          "refundAmount": {
            "type": "number",
            "nullable": true
          },
          "refundPenalty": {
            "type": "number",
            "nullable": true
          },
          "refundReference": {
            "type": "string",
            "nullable": true
          },
          "refundedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "refundError": {
            "type": "string",
            "nullable": true
          },
          "stateTimeline": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "state": {
                  "type": "string"
                },
                "at": {
                  "type": "string",
                  "format": "date-time"
                },
                "idempotencyKey": {
                  "type": "string",
                  "nullable": true
                }
              }
            }
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "customer": {
            "type": "object",
            "properties": {
              "email": {
                "type": "string"
              },
              "phone": {
                "type": "string"
              },
              "name": {
                "type": "string"
              }
            }
          },
          "notifyCustomer": {
            "type": "boolean"
          }
        }
      },
      "CreateBookingResult": {
        "type": "object",
        "properties": {
          "booking": {
            "$ref": "#/components/schemas/Booking"
          },
          "lifecycleState": {
            "type": "string"
          },
          "wasataMirror": {},
          "wasataSettle": {},
          "integrationError": {
            "type": "object",
            "nullable": true,
            "properties": {
              "stage": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      },
      "StatGroup": {
        "type": "object",
        "properties": {
          "_id": {
            "type": "string",
            "nullable": true
          },
          "count": {
            "type": "integer"
          },
          "totalRevenue": {
            "type": "number"
          }
        }
      },
      "AdminStats": {
        "type": "object",
        "properties": {
          "bookings": {
            "type": "object",
            "properties": {
              "total": {
                "type": "integer"
              },
              "confirmedRevenue": {
                "type": "number"
              },
              "byState": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/StatGroup"
                }
              },
              "byProvider": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/StatGroup"
                }
              },
              "bySettlement": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/StatGroup"
                }
              }
            }
          },
          "providers": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "CityMerge": {
        "type": "object",
        "description": "A cross-provider \"same city?\" pair awaiting owner review.",
        "properties": {
          "_id": {
            "type": "string"
          },
          "isoCountry": {
            "type": "string"
          },
          "nameA": {
            "type": "string"
          },
          "nameB": {
            "type": "string"
          },
          "providersA": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "providersB": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "score": {
            "type": "number",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "approved",
              "rejected"
            ]
          }
        }
      }
    }
  }
}