OS-Travel compatible gateway (Octasoft)
This gateway reproduces the Octasoft OS-Travel API (V1 and V2): same methods, same field names, same formats. If your system already talks to MyGo, Tunisia Beds or Mouradi, it talks to the gateway unmodified.
Transport
- URL —
POST {base}/api/hotel/<Method>(e.g.{base}/api/hotel/HotelSearch). - Content-Type —
application/json(V2) andapplication/x-www-form-urlencoded(V1 sends raw JSON without an explicit header: both are accepted). - Response — always HTTP 200, even on business errors: V1/V2 clients only parse 200.
- Authentication —
Credential { Login, Password }in every request (exceptBookingDetail).
Error envelope — still HTTP 200
{
"ErrorMessage": {
"Code": 401,
"Description": "Invalid credentials."
}
}
On V2 success, ErrorMessage is always present with Code: null and
Description: null — never test for the absence of ErrorMessage, test for
an empty or null Code.
Methods
| Method | Brave gateway equivalent | Description |
|---|---|---|
ListCountry | GET /v1/{code}/reference/countries | Countries |
ListCity | GET /v1/{code}/reference/cities | Cities |
ListBoarding | GET /v1/{code}/reference/boardings | Board types |
ListCategorie | GET /v1/{code}/reference/categories | Hotel categories |
ListCurrency | GET /v1/{code}/reference/currencies | Currencies |
ListHotel | GET /v1/{code}/hotels | Exposed hotel catalogue |
HotelDetail | GET /v1/{code}/hotels/{id} | Editorial sheet |
HotelSearch | POST /v1/{code}/availability | Search by city or hotel list |
BookingCreation (PreBooking) | POST /v1/{code}/checkrate + POST /v1/{code}/bookings | Pre-booking then confirmation |
BookingList | GET /v1/{code}/bookings | Booking list |
BookingDetail | GET /v1/{code}/bookings/{ref} | Detail and status |
BookingCancellation (PreCancelled) | POST /v1/{code}/bookings/{ref}/cancel | Simulation (PreCancelled) then cancellation |
Full examples
HotelSearch
Dates in Y-m-d format. City or Hotels (id list). Rooms exists in two dialects, both accepted:
- V1 — strings:
{ "Adult": "2", "Child": ["3", "8"] }(Childkey absent when no children) - V2 — integers:
{ "Adult": 2, "Child": [3, 8] }
Request — POST /api/hotel/HotelSearch
{
"Credential": { "Login": "demo_os_login", "Password": "demo_os_password" },
"SearchDetails": {
"BookingDetails": {
"CheckIn": "2027-01-15",
"CheckOut": "2027-01-20",
"City": 146070
},
"Filters": {
"Category": [],
"OnlyAvailable": true
},
"Rooms": [{ "Adult": 2, "Child": [8] }]
}
}
The response is an array HotelSearch, one entry per source (direct contract), with the Token (= our rateKey) to keep:
Response — 200
{
"HotelSearch": [
{
"Hotel": { "Id": 1803258, "Image": "https://…/thumb.jpg" },
"Source": "12",
"Token": "rk_x9F2kQ8example",
"Price": {
"Boarding": [
{
"Id": 16,
"Name": "Petit-déjeuner",
"Pax": [
{
"Adult": 2,
"Child": [8],
"Rooms": [
{
"Id": 18,
"Name": "Chambre Deluxe Vue Mer",
"Price": 500,
"Quantity": 3,
"NotRefundable": false,
"OnRequest": false,
"StopReservation": false,
"CancellationPolicy": [
{ "Fees": 0, "Type": "PERCENT", "FromDate": "2026-12-15" }
]
}
]
}
]
}
]
}
}
],
"ErrorMessage": { "Code": null, "Description": null }
}
BookingCreation (pre-booking)
PreBooking: true = pre-booking only. The client then replays the request without PreBooking to confirm — the gateway accepts both.
Request — POST /api/hotel/BookingCreation
{
"Credential": { "Login": "demo_os_login", "Password": "demo_os_password" },
"HotelBooking": {
"PreBooking": true,
"City": 0,
"Hotel": 1803258,
"CheckIn": "2027-01-15",
"CheckOut": "2027-01-20",
"Source": "12",
"Token": "rk_x9F2kQ8example",
"Rooms": [
{
"Id": 18,
"Boarding": "16",
"View": [],
"Supplement": [],
"Pax": {
"Adult": [
{ "Civility": "Mr", "Name": "TEST-A", "Surname": "TEST-B", "Holder": true }
],
"Child": [{ "Name": "TEST-C", "Surname": "TEST-B", "Age": 8 }]
}
}
]
}
}
Success = non-empty BookingCreation (Id, TotalPrice, Rooms, CancellationPolicy with FromDate in d/m/Y). Failure = absence of BookingCreation + ErrorMessage { Code ≠ 200 }.
BookingDetail
Body without Credential:
Request — POST /api/hotel/BookingDetail
{
"BookingReferenceId": "BPG-2026-000123",
"PaymentMode": "Limit"
}
Success when Status.Code == 200; read BookingDetail { BookingStatus, VoucherStatus, ConfirmationNumber, InvoiceNumber }. Cancelled statuses: Cancelled, CancellationInProgress, CancelPending, CancelledAndRefundAwaited.
BookingCancellation (simulation then cancellation)
Request — POST /api/hotel/BookingCancellation (simulation)
{
"Credential": { "Login": "demo_os_login", "Password": "demo_os_password" },
"Booking": "BPG-2026-000123",
"PreCancelled": true
}
PreCancelled: true = fee simulation, without really cancelling. Replay without PreCancelled to cancel. Error = BookingCancellation.ErrorMessage { Code ≠ 200 }.
Pitfalls
- HTTP 200 everywhere — always read
ErrorMessage.Code(andBookingCancellation.ErrorMessagefor cancellations), never the HTTP code alone. - Replay the
Tokenas-is — theTokenfrom theHotelSearchresponse must come back inBookingCreation.Token, unmodified and undecoded. - Quota — when exceeded,
Descriptioncontains"Too many requests ... Retry after YYYY-MM-DD HH:MM:SS.": parse it for your backoff. - Client-side availability — a room is sellable when
OnRequest != 1,StopSalesempty andStopReservation != 1. Boardingas a string — inBookingCreation,Boardingis a string ("16"), not an integer.- Dates —
Y-m-dinHotelSearch/BookingCreation, butd/m/Yin responseCancellationPolicy.FromDate. Source— Brave direct contract code, replayed as-is inBookingCreation.Source.