> For the complete documentation index, see [llms.txt](https://syticks.gitbook.io/merpi-by-syticks/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://syticks.gitbook.io/merpi-by-syticks/api-reference/hospitality-ticketing-hotels-resorts-apartments/book-hotel-apartment-room-tickets.md).

# Book Hotel/Apartment Room Tickets

**Important:** MERPI only creates transactions for successful bookings. If the booking fails for any reason (unavailable room, payment issue, etc.), no transaction record is created and your wallet is not debited.

#### Endpoint

```
POST /v2/merpi/hotels/buy
```

### **Important: Hold First, Then Buy**

**You must create holds before purchasing hotel rooms.** This prevents situations where a customer completes payment but the room is no longer available.

**Required Flow:**

1. Create holds via `POST /api/v1/merpi/validate` (holds inventory for 3 minutes)
2. Customer completes payment details
3. Call this buy endpoint with `reservation_ids` (converts holds to confirmed bookings)

**Why this matters:** Without holds, a room could be booked by another customer between when your customer starts checkout and when payment completes. Holds guarantee room availability during the checkout process.

#### **Before you buy: your own transaction reference**

You can pass your own reference for a transaction using the optional `merchant_reference` field in the request body. This lets you tie a MERPI purchase directly to an order or record in your own system.

Once a purchase is complete, you can use that reference to look up the transaction at:

```
GET /api/v1/merpi/transaction?merchant_reference=YOUR_REFERENCE
```

Keep in mind that the purchase response gives you a summary only and does not include full booking details. You should always call the requery endpoint after every successful purchase to get the complete record.

Also worth knowing: only successful purchases create a transaction on the platform. If you query by `merchant_reference` and get a 404, it means nothing was recorded under that reference and the purchase did not go through.

#### Request Body

```json
{
    "room_id": 10,
    "reservation_ids": [
        "d6043b00-dad5-4aed-823c-74608eaaaa63"
    ],
    "number_of_guests": 1,
    "number_of_rooms": 1,
    "checkin_date": "2026-02-03",
    "checkout_date": "2026-02-04",
    "merchant_reference": "your-order-id-123",
    "customer_info": {
        "name": "John Doe",
        "email": "john@example.com",
        "phone_number": "+2348012345678",
        "dob": "1990-01-01"
    }
}
```

#### Request Fields

| Field                | Type             | Required | Description                                                                                                                                                                                                                                                                                                     |
| -------------------- | ---------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `room_id`            | number           | Yes      | Room identifier from hotel rooms endpoint                                                                                                                                                                                                                                                                       |
| `reservation_ids`    | array of strings | Yes      | Array of reservation IDs from the hold creation response. Must contain at least one valid UUID.                                                                                                                                                                                                                 |
| `number_of_guests`   | number           | Yes      | Total number of guests (must not exceed `max_occupancy`)                                                                                                                                                                                                                                                        |
| `number_of_rooms`    | number           | Yes      | Number of rooms to book (of the same type)                                                                                                                                                                                                                                                                      |
| `checkin_date`       | string           | Yes      | Check-in date (YYYY-MM-DD format)                                                                                                                                                                                                                                                                               |
| `checkout_date`      | string           | Yes      | Check-out date (YYYY-MM-DD format)                                                                                                                                                                                                                                                                              |
| `merchant_reference` | string           | No       | Your own reference for this transaction. Max 255 characters. Must be unique across all your purchases. If you pass a reference that was already used in a previous successful purchase, the request will be rejected and the response will include a pointer to the existing transaction so you can requery it. |
| `customer_info`      | object           | Yes      | Guest information                                                                                                                                                                                                                                                                                               |

**Customer Info Object**

| Field          | Type   | Required | Description                                                                                                |
| -------------- | ------ | -------- | ---------------------------------------------------------------------------------------------------------- |
| `name`         | string | No       | Full name of primary guest                                                                                 |
| `email`        | string | Yes      | Guest email address. **Must match the email used when creating the holds.**                                |
| `phone_number` | string | Yes      | Guest phone number (format: +234XXXXXXXXXX). **Must match the phone number used when creating the holds.** |
| `dob`          | string | No       | Date of birth (YYYY-MM-DD format)                                                                          |

### **Critical Requirements:**

* The `customer_info.email` or `customer_info.phone_number` must **exactly match** what was used when creating the holds
* This matching verifies hold ownership and prevents unauthorized use of someone else's reservations
* All `reservation_ids` must belong to the same customer and be in `"active"` status
* Holds convert to bookings upon successful purchase and cannot be reused

#### Header Parameters

| Parameter           | Type   | Required | Description                                   |
| ------------------- | ------ | -------- | --------------------------------------------- |
| `X-API-KEY`         | string | Yes      | Your API key to access this endpoint          |
| `TransactionMedium` | string | Yes      | Transaction medium: `Web`, `Mobile`, or `POS` |
| `Content-Type`      | string | Yes      | Must be `application/json`                    |

### Code Examples

**JavaScript - Fetch**

```javascript
const bookingData = {
  room_id: 10,
  reservation_ids: [
    "d6043b00-dad5-4aed-823c-74608eaaaa63"
  ],
  number_of_guests: 2,
  number_of_rooms: 1,
  checkin_date: "2026-03-15",
  checkout_date: "2026-03-17",
  merchant_reference: "your-order-id-123",
  customer_info: {
    name: "Chidinma Okafor",
    email: "chidinma@example.com",
    phone_number: "+2348012345678",
    dob: "1988-05-20"
  }
};

const response = await fetch('{{url}}/v2/merpi/hotels/book', {
  method: 'POST',
  headers: {
    'X-API-KEY': 'your_api_key_here',
    'TransactionMedium': 'Web',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(bookingData)
});

const result = await response.json();
console.log(result);
```

#### Response

**Success Response (201)**

```json
{
    "success": true,
    "status": 201,
    "message": "Hotel Booking Purchased Successfully",
    "data": {
        "reference": "TR182685730",
        "invoice_id": 20210003,
        "booking_number": "HYAVOSPRRC",
        "name": "John Doe",
        "email": "john@example.com",
        "phone_number": "+2348012345678",
        "merchant_reference": "your-order-id-123",
        "hotel": {
            "id": 16,
            "name": "Grand Paradise Hotel",
            "location": "Downtown City"
        },
        "room": {
            "id": 10,
            "room_name": "Basic",
            "number_of_rooms": "22"
        },
        "checkin_date": "2026-02-03",
        "checkout_date": "2026-02-04",
        "number_of_nights": 1,
        "number_of_guests": 1,
        "number_of_rooms": 1,
        "amount": 25650
    }
}
```

**Error Response (400/422)**

```json
{
    "success": false,
    "status": 422,
    "message": "Room is no longer available for selected dates",
    "errors": {
        "room_id": ["The selected room is not available for the specified dates"]
    }
}
```

#### Response Fields

**Success Response (201)**

| Field     | Type    | Description                                    |
| --------- | ------- | ---------------------------------------------- |
| `success` | boolean | Indicates if the booking was successful        |
| `status`  | number  | HTTP status code (201 for successful creation) |
| `message` | string  | Success message                                |
| `data`    | object  | Booking details                                |

**Error Response (400/422)**

| Field     | Type    | Description                                                      |
| --------- | ------- | ---------------------------------------------------------------- |
| `success` | boolean | Always `false` for errors                                        |
| `status`  | number  | HTTP error code (400 for bad request, 422 for validation errors) |
| `message` | string  | Error message describing what went wrong                         |
| `errors`  | object  | Detailed validation errors (when applicable)                     |

**Data Object (Success Only)**

| Field                | Type   | Description                                                                          |
| -------------------- | ------ | ------------------------------------------------------------------------------------ |
| `reference`          | string | Unique transaction reference number                                                  |
| `invoice_id`         | number | Invoice identifier for this booking                                                  |
| `booking_number`     | string | Unique booking confirmation number (use for check-in)                                |
| `name`               | string | Guest name                                                                           |
| `email`              | string | Guest email                                                                          |
| `phone_number`       | string | Guest phone number                                                                   |
| `merchant_reference` | string | Your reference for this transaction, if you provided one. `null` if not provided.    |
| `hotel`              | object | Hotel information. Call the requery endpoint after purchase to get the full details. |
| `room`               | object | Room details. Call the requery endpoint after purchase to get the full details.      |
| `checkin_date`       | string | Check-in date (YYYY-MM-DD)                                                           |
| `checkout_date`      | string | Check-out date (YYYY-MM-DD)                                                          |
| `number_of_nights`   | number | Total number of nights                                                               |
| `number_of_guests`   | number | Total number of guests                                                               |
| `number_of_rooms`    | number | Number of rooms booked                                                               |
| `amount`             | number | Total booking amount in Naira                                                        |

**Hotel Object**

| Field      | Type   | Description            |
| ---------- | ------ | ---------------------- |
| `id`       | number | Hotel identifier       |
| `name`     | string | Hotel name             |
| `location` | string | Hotel location/address |

**Room Object**

| Field         | Type   | Description                                |
| ------------- | ------ | ------------------------------------------ |
| `id`          | number | Room type identifier                       |
| `room_name`   | string | Room category name (e.g., Basic, Deluxe)   |
| `room_number` | string | Number of rooms available in this category |

**Example:**

* Room price: ₦25,650/night
* Number of nights: 1 (Feb 3 - Feb 4)
* Number of rooms: 1
* **Total: ₦25,650**

**For multi-night stays:**

* Room price: ₦25,650/night
* Number of nights: 3 (Feb 3 - Feb 6)
* Number of rooms: 2
* **Total: ₦153,900**

### Important Notes

**Transaction Creation:** Transactions are only created for successful bookings. Failed bookings do not create transaction records in the system and your wallet is not debited.

**Date Format:** Always use YYYY-MM-DD format for dates (e.g., "2026-02-03").

**Number of Nights Calculation:** The system automatically calculates nights based on check-in and check-out dates. The check-out date is exclusive.

**Booking Number:** The `booking_number` field is what guests should present at hotel check-in. Make sure to display this prominently in confirmation emails and screens.

**Reference Number:** The `reference` field is the unique transaction identifier. Use this for tracking, customer support, and transaction queries.

**Availability Check:** Always verify room availability immediately before calling this endpoint by checking `is_available` in the rooms response.

**Occupancy Validation:** Ensure `number_of_guests` does not exceed the room's `max_occupancy`.

**Phone Number Format:** Use international format with country code: +234XXXXXXXXXX

### Common Errors

| Error Code | Error Message               | Cause                                                                  | Solution                                                      |
| ---------- | --------------------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------- |
| 422        | Room is no longer available | Room already booked or unavailable                                     | Check availability before booking                             |
| 422        | Invalid dates               | Check-out before check-in or past dates                                | Ensure checkout\_date > checkin\_date and dates are in future |
| 422        | Exceeds occupancy           | Too many guests for room type                                          | Select larger room or book multiple rooms                     |
| 400        | Invalid phone format        | Wrong phone number format                                              | Use +234XXXXXXXXXX format                                     |
| 400        | Invalid email               | Malformed email address                                                | Verify email format is correct                                |
| 404        | Reservations not found      | reservation\_ids are invalid, expired, or don't belong to the customer | Create new holds and retry                                    |
| 401        | Unauthorized                | Invalid or missing API key                                             | Check X-API-KEY header                                        |

#### Complete Booking Flow Example

```javascript
// 1. Get hotels in Lagos
const hotelsResponse = await fetch('{{url}}/v2/merpi/hotels?city=Lagos');
const hotelsData = await hotelsResponse.json();
const selectedHotel = hotelsData.data.hotels[0];

console.log(`Selected: ${selectedHotel.name}`);

// 2. Get rooms for selected hotel
const roomsResponse = await fetch(`{{url}}/v2/merpi/hotels/${selectedHotel.id}/rooms`, {
  headers: {
    'X-API-KEY': 'sk_test_...',
    'TransactionMedium': 'Web'
  }
});
const roomsData = await roomsResponse.json();

// 3. User selects "Basic" room
const selectedRoom = roomsData.data.rooms.find(r => r.room_name === 'Basic');

console.log(`Room: ${selectedRoom.room_name}, Price: ₦${selectedRoom.price}/night`);

// 4. Verify room is available
if (!selectedRoom.is_available) {
  alert('Sorry, this room is no longer available');
  return;
}

// 5. Check occupancy
const guestCount = 2;
if (guestCount > selectedRoom.max_occupancy) {
  alert(`This room can only accommodate ${selectedRoom.max_occupancy} guests`);
  return;
}

// 6. Create holds
const holdsResponse = await fetch('{{url}}/api/v1/merpi/validate', {
  method: 'POST',
  headers: {
    'X-API-KEY': 'sk_test_...',
    'TransactionMedium': 'Web',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    room_id: selectedRoom.id,
    customer_info: {
      email: 'ada@example.com',
      phone_number: '+2348012345678'
    }
  })
});
const holdsData = await holdsResponse.json();
const reservationIds = holdsData.data.reservation_ids;

// 7. Calculate and display total price
const checkinDate = '2026-03-15';
const checkoutDate = '2026-03-17';
const numberOfNights = 2;
const numberOfRooms = 1;
const totalPrice = selectedRoom.price * numberOfNights * numberOfRooms;

console.log(`Total: ₦${totalPrice.toLocaleString()} for ${numberOfNights} nights`);

// 8. Complete booking
const bookingResponse = await fetch('{{url}}/v2/merpi/hotels/book', {
  method: 'POST',
  headers: {
    'X-API-KEY': 'sk_test_...',
    'TransactionMedium': 'Web',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    room_id: selectedRoom.id,
    reservation_ids: reservationIds,
    number_of_guests: guestCount,
    number_of_rooms: numberOfRooms,
    checkin_date: checkinDate,
    checkout_date: checkoutDate,
    merchant_reference: 'your-internal-order-id',
    customer_info: {
      name: 'Ada Nwosu',
      email: 'ada@example.com',
      phone_number: '+2348012345678',
      dob: '1992-06-15'
    }
  })
});

const bookingResult = await bookingResponse.json();

if (bookingResult.success) {
  console.log('Booking confirmed!');
  console.log('Booking Number:', bookingResult.data.booking_number);
  console.log('Reference:', bookingResult.data.reference);
  console.log('Hotel:', bookingResult.data.hotel.name);
  console.log('Room:', bookingResult.data.room.room_name);
  console.log('Amount Paid: ₦' + bookingResult.data.amount.toLocaleString());

  showConfirmation(bookingResult.data);
} else {
  console.error('Booking failed:', bookingResult.message);
  alert(bookingResult.message);
}
```

#### Verifying Booking Status

After a successful booking, you can verify the transaction using the `reference` number:

javascript

```javascript
const reference = bookingResult.data.reference;
const verificationResponse = await fetch(`{{url}}/v2/merpi/transactions/${reference}`, {
  headers: {
    'X-API-KEY': 'your_api_key_here',
    'TransactionMedium': 'Web'
  }
});

const transactionData = await verificationResponse.json();
console.log('Transaction Status:', transactionData);
```

See [Transaction Query](/merpi-by-syticks/api-reference/general-endpoints-get-all-businesses-transaction-requery-etc/transaction-query-endpoint.md) documentation for complete details on verifying transactions.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://syticks.gitbook.io/merpi-by-syticks/api-reference/hospitality-ticketing-hotels-resorts-apartments/book-hotel-apartment-room-tickets.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
