> 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/bus-ticketing-routes-terminals-schedules-buses-purchase-etc/buy-bus-ticket-endpoint-v2.md).

# Buy Bus Ticket Endpoint (V2)

This endpoint is how your customers purchase bus tickets through your platform. Once a purchase goes through, their tickets are sent directly to their email and you can track everything from your MERPI dashboard.

One thing worth understanding before you integrate: bus schedules on this platform work in two different ways, and the request you send depends on which type you're dealing with.

### Understanding Schedule Types

**Timed Schedules**

These are routes with fixed departure times. Think long-distance trips where a bus leaves at 6:00 AM and that's that. Customers pick their seats ahead of time and pay a fixed price per seat.

**Random Schedules**

These work more like how most motor parks actually operate. There's no fixed departure time. Buses fill up as passengers arrive at the terminal, and once a bus is full, it goes. Your customer just specifies how many passengers are traveling and a preferred time window, and seats get assigned when they board. No seat selection needed on your end.

The request body structure is slightly different for each type, but it's the same endpoint for both. The `schedule_type` field in your request is what tells the API which flow to follow.

#### Key Features

* **Unified Endpoint**: Handles both Timed and Random schedule types
* **Hold-Based Purchase Flow**: Like all buy endpoints on MERPI, you must create holds first before purchasing
* **Dynamic Request Structure**: Request body adapts based on `schedule_type`
* **Flexible Seat Handling**: Timed schedules require seat selection; Random schedules assign seats automatically at boarding
* **Email Delivery**: Tickets are sent directly to the customer's email after a successful purchase
* **Next of Kin Information**: Can be included for safety and emergency contact purposes

**Endpoint**

```
POST /api/v2/merpi/transport/buy/tickets
```

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

**You must create holds before purchasing bus tickets.** This prevents situations where a customer completes payment but the seats are 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, seats could be taken by another customer between when your customer starts checkout and when payment completes. Holds guarantee 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 ticket 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.

#### Buy Bus Tickets (V2 - Supports both Timed and Random schedules)

> Endpoint to purchase bus tickets for a specific schedule. This V2 endpoint supports both Timed Schedules (fixed departure times with seat selection) and Random Schedules (flexible timing with automatic seat assignment). The request structure varies based on the schedule\_type field. You must create holds first via POST /api/v1/merpi/validate, then use the reservation\_ids to complete the purchase.

## Buy Bus Tickets (V2 - Supports both Timed and Random schedules)

> Endpoint to purchase bus tickets for a specific schedule. This V2 endpoint supports both Timed Schedules (fixed departure times with seat selection)  and Random Schedules (flexible timing with automatic seat assignment). The request structure varies based on the schedule\_type field. You must create holds first via POST /api/v1/merpi/validate, then use the reservation\_ids to complete the purchase. This guarantees inventory availability during checkout.<br>

```json
{"openapi":"3.0.3","info":{"title":"Syticks API","version":"2.0.0"},"paths":{"/api/v2/merpi/transport/buy/tickets":{"post":{"summary":"Buy Bus Tickets (V2 - Supports both Timed and Random schedules)","description":"Endpoint to purchase bus tickets for a specific schedule. This V2 endpoint supports both Timed Schedules (fixed departure times with seat selection)  and Random Schedules (flexible timing with automatic seat assignment). The request structure varies based on the schedule_type field. You must create holds first via POST /api/v1/merpi/validate, then use the reservation_ids to complete the purchase. This guarantees inventory availability during checkout.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/TimedTicketRequest"},{"$ref":"#/components/schemas/RandomTicketRequest"}]}}}},"responses":{"201":{"description":"Successful purchase of bus tickets.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status":{"type":"integer"},"message":{"type":"string"},"data":{"oneOf":[{"$ref":"#/components/schemas/TimedTicketResponse"},{"$ref":"#/components/schemas/RandomTicketResponse"}]}}}}}},"400":{"description":"Bad Request - Invalid input or validation errors","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status":{"type":"integer"},"message":{"type":"string"},"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}}}}}},"404":{"description":"Not Found - Hold not found, or schedule, route, or bus not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status":{"type":"integer"},"message":{"type":"string"}}}}}},"409":{"description":"Conflict - Seats already booked (Timed schedules only)","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status":{"type":"integer"},"message":{"type":"string"}}}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status":{"type":"integer"},"message":{"type":"string"}}}}}}}}}},"components":{"schemas":{"TimedTicketRequest":{"type":"object","description":"Request body for purchasing tickets on a Timed Schedule (fixed departure times)","required":["schedule_type","schedule_id","reservation_ids","seats","departure_date","customer_info"],"properties":{"schedule_type":{"type":"string","enum":["timed"],"description":"Must be \"timed\" for timed schedules"},"schedule_id":{"type":"integer","description":"Unique identifier for the bus schedule being booked"},"reservation_ids":{"type":"array","description":"Array of reservation IDs from the hold creation response. Must contain at least one valid UUID. These are obtained by calling POST /api/v1/merpi/validate to create holds before purchasing.\n","items":{"type":"string","format":"uuid"}},"seats":{"type":"array","items":{"type":"integer"},"description":"List of seat IDs being booked (e.g., [9, 4, 3])"},"departure_date":{"type":"string","format":"date-time","description":"Date and time of the bus departure in YYYY-MM-DD HH:MM format"},"merchant_reference":{"type":"string","description":"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.\n","maxLength":255},"customer_info":{"$ref":"#/components/schemas/CustomerInfo"}}},"CustomerInfo":{"type":"object","required":["name","email","phone_number","dob","username","kin"],"properties":{"name":{"type":"string","description":"Customer's full name"},"email":{"type":"string","format":"email","description":"Customer's primary email address. Must match the email used when creating the holds.\n"},"phone_number":{"type":"string","description":"Customer's phone number. Must match the phone number used when creating the holds.\n"},"dob":{"type":"string","description":"Customer's date of birth in DD/MM/YYYY format"},"username":{"type":"string","description":"Customer's username for the booking system"},"kin":{"$ref":"#/components/schemas/NextOfKin"}}},"NextOfKin":{"type":"object","required":["first_name","last_name","phone_number","email","gender","relationship"],"properties":{"first_name":{"type":"string","description":"Emergency contact's first name"},"last_name":{"type":"string","description":"Emergency contact's last name"},"phone_number":{"type":"string","description":"Emergency contact's phone number"},"email":{"type":"string","format":"email","description":"Emergency contact's email address"},"gender":{"type":"string","description":"Emergency contact's gender"},"relationship":{"type":"string","description":"Relationship of the emergency contact to the customer"}}},"RandomTicketRequest":{"type":"object","description":"Request body for purchasing tickets on a Random Schedule (flexible timing)","required":["schedule_type","route_id","bus_id","reservation_ids","no_of_passengers","departure_date","departure_time","customer_info"],"properties":{"schedule_type":{"type":"string","enum":["random"],"description":"Must be \"random\" for random schedules"},"route_id":{"type":"integer","description":"ID of the route being booked"},"bus_id":{"type":"integer","description":"ID of the bus for this trip"},"reservation_ids":{"type":"array","description":"Array of reservation IDs from the hold creation response. Must contain at least one valid UUID. These are obtained by calling POST /api/v1/merpi/validate to create holds before purchasing.\n","items":{"type":"string","format":"uuid"}},"no_of_passengers":{"type":"integer","description":"Number of passengers traveling (seats assigned automatically)","minimum":1},"departure_date":{"type":"string","format":"date","description":"Date of departure in YYYY-MM-DD format"},"departure_time":{"type":"string","format":"time","description":"Preferred departure time in HH:MM format (24-hour)"},"merchant_reference":{"type":"string","description":"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.\n","maxLength":255},"customer_info":{"$ref":"#/components/schemas/CustomerInfo"}}},"TimedTicketResponse":{"type":"object","description":"Response for a successful Timed Schedule ticket purchase","properties":{"reference":{"type":"string","description":"Unique transaction reference"},"invoice_id":{"type":"integer","description":"Invoice ID for the purchase"},"name":{"type":"string","description":"Customer's name"},"email":{"type":"string","format":"email","description":"Customer's email"},"phone_number":{"type":"string","description":"Customer's phone number"},"merchant_reference":{"type":"string","nullable":true,"description":"Your reference for this transaction if provided, null otherwise."},"departure_date":{"type":"string","format":"date-time","description":"Confirmed departure date and time"},"seat":{"type":"array","description":"Array of booked seat objects with details","items":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"row":{"type":"integer"},"column":{"type":"integer"}}}},"bus":{"$ref":"#/components/schemas/BusInfo"},"schedule":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"image":{"type":"string"},"seats":{"type":"integer"},"price":{"type":"string"}}},"schedule_type":{"type":"string","enum":["timed"]}}},"BusInfo":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"image":{"type":"string"},"seats":{"type":"integer"},"price":{"type":"string"},"rows":{"type":"integer"},"columns":{"type":"integer"},"seat_grid":{"type":"array","description":"2D array showing all seats and their availability","items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"seat":{"type":"boolean"},"row":{"type":"integer"},"column":{"type":"integer"}}}}}}},"RandomTicketResponse":{"type":"object","description":"Response for a successful Random Schedule ticket purchase","properties":{"reference":{"type":"string","description":"Unique transaction reference"},"invoice_id":{"type":"integer","description":"Invoice ID for the purchase"},"name":{"type":"string","description":"Customer's name"},"email":{"type":"string","format":"email","description":"Customer's email"},"phone_number":{"type":"string","description":"Customer's phone number"},"merchant_reference":{"type":"string","nullable":true,"description":"Your reference for this transaction if provided, null otherwise."},"departure_date":{"type":"string","format":"date-time","description":"Confirmed departure date and time"},"seat":{"type":"array","description":"Empty array - seats assigned automatically upon boarding","items":{"type":"object"}},"bus":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"image":{"type":"string"},"seats":{"type":"integer"},"price":{"type":"integer"},"rows":{"type":"integer"},"columns":{"type":"integer"}}},"schedule":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"image":{"type":"string"},"seats":{"type":"integer"},"price":{"type":"integer","description":"Final price including commission"},"start_time":{"type":"string","format":"time","description":"Operating window start time"},"end_time":{"type":"string","format":"time","description":"Operating window end time"}}},"schedule_type":{"type":"string","enum":["random"]},"no_of_passengers":{"type":"integer","description":"Number of passengers booked"},"route_id":{"type":"integer","description":"Route ID for the trip"},"bus_id":{"type":"integer","description":"Bus ID for the trip"}}}}}}
```

#### Request Body - Timed Schedule

| Parameter                        | Type             | Required | Description                                                                                                                                                                                                                                                                                                     |
| -------------------------------- | ---------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `schedule_type`                  | String           | Yes      | Must be `"timed"` for timed schedules                                                                                                                                                                                                                                                                           |
| `schedule_id`                    | Integer          | Yes      | Unique identifier for the bus schedule being booked (from Get Schedules endpoint)                                                                                                                                                                                                                               |
| `reservation_ids`                | Array of strings | Yes      | Array of reservation IDs from the hold creation response. Must contain at least one valid UUID.                                                                                                                                                                                                                 |
| `seats`                          | Array\[Integer]  | Yes      | List of seat IDs being booked (e.g., \[9, 4, 3] indicates seats with ID 9, 4, and 3)                                                                                                                                                                                                                            |
| `departure_date`                 | String           | Yes      | Date and time of departure in format: `YYYY-MM-DD HH:MM` (e.g., "2024-07-23 20:21")                                                                                                                                                                                                                             |
| `customer_info`                  | Object           | Yes      | Customer information object                                                                                                                                                                                                                                                                                     |
| `customer_info.name`             | String           | No       | Full name of the customer                                                                                                                                                                                                                                                                                       |
| `customer_info.email`            | String           | Yes      | Email address (tickets will be sent here). **Must match the email used when creating the holds.**                                                                                                                                                                                                               |
| `customer_info.phone_number`     | String           | Yes      | Customer's phone number. **Must match the phone number used when creating the holds.**                                                                                                                                                                                                                          |
| `customer_info.dob`              | String           | No       | Date of birth in format: `DD/MM/YYYY`                                                                                                                                                                                                                                                                           |
| `customer_info.username`         | String           | No       | Username for the customer                                                                                                                                                                                                                                                                                       |
| `customer_info.kin`              | Object           | No       | Next of kin information                                                                                                                                                                                                                                                                                         |
| `customer_info.kin.first_name`   | String           | No       | First name of next of kin                                                                                                                                                                                                                                                                                       |
| `customer_info.kin.last_name`    | String           | No       | Last name of next of kin                                                                                                                                                                                                                                                                                        |
| `customer_info.kin.phone_number` | String           | No       | Phone number of next of kin                                                                                                                                                                                                                                                                                     |
| `customer_info.kin.email`        | String           | No       | Email of next of kin                                                                                                                                                                                                                                                                                            |
| `customer_info.kin.gender`       | String           | No       | Gender of next of kin (e.g., "male", "female")                                                                                                                                                                                                                                                                  |
| `customer_info.kin.relationship` | String           | No       | Relationship to customer (e.g., "son", "daughter", "spouse")                                                                                                                                                                                                                                                    |
| `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. |

**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

#### Request Body - Random Schedule

| Parameter                        | Type             | Required | Description                                                                                                                                                                                                                                                                                                     |
| -------------------------------- | ---------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `schedule_type`                  | String           | Yes      | Must be `"random"` for random schedules                                                                                                                                                                                                                                                                         |
| `route_id`                       | Integer          | Yes      | ID of the route being booked                                                                                                                                                                                                                                                                                    |
| `bus_id`                         | Integer          | Yes      | ID of the bus for this trip (from buses array in Get Schedules response)                                                                                                                                                                                                                                        |
| `reservation_ids`                | Array of strings | Yes      | Array of reservation IDs from the hold creation response. Must contain at least one valid UUID.                                                                                                                                                                                                                 |
| `no_of_passengers`               | Integer          | Yes      | Number of passengers traveling                                                                                                                                                                                                                                                                                  |
| `departure_time`                 | String           | Yes      | Preferred departure time in format: `HH:MM` (24-hour format)                                                                                                                                                                                                                                                    |
| `departure_date`                 | String           | Yes      | Date and time of departure in format: `YYYY-MM-DD HH:MM` (e.g., "2024-07-23 20:21")                                                                                                                                                                                                                             |
| `customer_info`                  | Object           | Yes      | Customer information object                                                                                                                                                                                                                                                                                     |
| `customer_info.name`             | String           | No       | Full name of the customer                                                                                                                                                                                                                                                                                       |
| `customer_info.email`            | String           | Yes      | Email address (tickets will be sent here). **Must match the email used when creating the holds.**                                                                                                                                                                                                               |
| `customer_info.phone_number`     | String           | Yes      | Customer's phone number. **Must match the phone number used when creating the holds.**                                                                                                                                                                                                                          |
| `customer_info.dob`              | String           | No       | Date of birth in format: `DD/MM/YYYY`                                                                                                                                                                                                                                                                           |
| `customer_info.username`         | String           | No       | Username for the customer                                                                                                                                                                                                                                                                                       |
| `customer_info.kin`              | Object           | No       | Next of kin information                                                                                                                                                                                                                                                                                         |
| `customer_info.kin.first_name`   | String           | No       | First name of next of kin                                                                                                                                                                                                                                                                                       |
| `customer_info.kin.last_name`    | String           | No       | Last name of next of kin                                                                                                                                                                                                                                                                                        |
| `customer_info.kin.phone_number` | String           | No       | Phone number of next of kin                                                                                                                                                                                                                                                                                     |
| `customer_info.kin.email`        | String           | No       | Email of next of kin                                                                                                                                                                                                                                                                                            |
| `customer_info.kin.gender`       | String           | No       | Gender of next of kin (e.g., "male", "female")                                                                                                                                                                                                                                                                  |
| `customer_info.kin.relationship` | String           | No       | Relationship to customer (e.g., "son", "daughter", "spouse")                                                                                                                                                                                                                                                    |
| `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. |

### **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

#### Response Fields Comparison

**Common Fields (Both Schedule Types)**

| Field                     | Type    | Description                                                                       |
| ------------------------- | ------- | --------------------------------------------------------------------------------- |
| `success`                 | Boolean | Indicates if the request was successful                                           |
| `status`                  | Integer | HTTP status code (201 for successful creation)                                    |
| `message`                 | String  | Human-readable message about the result                                           |
| `data.reference`          | String  | Unique transaction reference (e.g., "TR197016656")                                |
| `data.invoice_id`         | Integer | Invoice ID for the purchase (customer can present this at the transport terminal) |
| `data.name`               | String  | Customer's name                                                                   |
| `data.email`              | String  | Customer's email (where ticket was sent)                                          |
| `data.phone_number`       | String  | Customer's phone number                                                           |
| `data.merchant_reference` | String  | Your reference for this transaction, if you provided one. `null` if not provided. |
| `data.departure_date`     | String  | Confirmed departure date and time                                                 |
| `data.bus`                | Object  | Complete bus information                                                          |
| `data.schedule`           | Object  | Schedule information for the booked trip                                          |
| `data.schedule_type`      | String  | Type of schedule: "timed" or "random"                                             |

**Timed Schedule Specific Fields**

| Field                | Type           | Description                                                                                                       |
| -------------------- | -------------- | ----------------------------------------------------------------------------------------------------------------- |
| `data.seat`          | Array\[Object] | Array of booked seat objects with full details. Call the requery endpoint after purchase to get the full details. |
| `data.seat[].id`     | Integer        | Seat ID                                                                                                           |
| `data.seat[].name`   | String         | Seat position (e.g., "1-1" for row 1, column 1)                                                                   |
| `data.seat[].row`    | Integer        | Row number                                                                                                        |
| `data.seat[].column` | Integer        | Column number                                                                                                     |
| `data.bus.seat_grid` | Array          | 2D array showing all seats and availability                                                                       |

**Random Schedule Specific Fields**

| Field                      | Type           | Description                                                                                                                 |
| -------------------------- | -------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `data.seat`                | Array          | Empty array - seats assigned automatically upon boarding. Call the requery endpoint after purchase to get the full details. |
| `data.no_of_passengers`    | Integer        | Number of passengers booked                                                                                                 |
| `data.route_id`            | Integer        | Route ID for the trip                                                                                                       |
| `data.bus_id`              | Integer        | Bus ID for the trip                                                                                                         |
| `data.schedule.start_time` | String         | Operating window start time                                                                                                 |
| `data.schedule.end_time`   | String         | Operating window end time                                                                                                   |
| `data.schedule.price`      | Integer/Number | Price for the schedule                                                                                                      |

### Key Differences Summary

| Key Aspect         | Timed Schedule                                                                  | Random Schedule                                                |
| ------------------ | ------------------------------------------------------------------------------- | -------------------------------------------------------------- |
| **Request Fields** | `schedule_id` + `reservation_ids` + `seats`                                     | `route_id` + `bus_id` + `reservation_ids` + `no_of_passengers` |
| **Seat Selection** | Required - customer chooses seats. If not, available random seats are assigned. | Not required - auto-assigned upon boarding                     |
| **Departure Time** | Fixed time from schedule                                                        | Customer's preferred time within window                        |
| **Response Seats** | Array of seat objects with details                                              | Empty array `[]`                                               |
| **Use Case**       | Long-distance, fixed schedule                                                   | Short-distance, flexible timing                                |

### Error Responses

#### **400 Bad Request - Validation Error**

Occurs when required fields are missing or invalid.

```json
{
  "success": false,
  "status": 400,
  "message": "The provided schedule ID is invalid.",
  "errors": {
    "schedule_id": [
      "The selected schedule does not exist."
    ]
  }
}
```

**Common Causes:**

* Invalid `schedule_id`, `route_id`, or `bus_id`
* Missing required fields for the specified `schedule_type`
* Invalid `departure_date` format
* `seats` array is empty for a timed schedule request
* Missing or invalid `reservation_ids`

#### **404 Not Found - Hold Not Found**

Occurs when reservation IDs are invalid or don't belong to the customer.

```json
{
  "success": false,
  "status": 404,
  "message": "One or more reservations not found or already used"
}
```

**Common Causes:**

* `reservation_ids` don't exist
* Customer info doesn't match the hold owner (email/phone mismatch)
* Holds already expired (TTL passed)
* Holds already used in a previous purchase (status: "converted")
* Holds manually released (status: "released")
* Schedule, route, or bus cannot be found

#### **409 Conflict - Seats Already Booked**

Occurs on timed schedules when the selected seats have already been taken.

json

```json
{
  "success": false,
  "status": 409,
  "message": "One or more selected seats are no longer available"
}
```

**Common Causes:**

* Another customer booked the same seats between your availability check and purchase attempt
* Always fetch fresh seat availability before submitting a purchase

#### **500 Internal Server Error**

```json
{
  "success": false,
  "status": 500,
  "message": "An error occurred while processing your request"
}
```

**Action**: Retry the request. If the error persists, contact API support.

### Implementation Guide

**Determine Schedule Type**

After calling the Get Schedules endpoint, check the `schedule_type` field:

```javascript
const schedule = schedulesResponse.data.schedules[0];

if (schedule.schedule_type === 'timed') {
  // Show seat selection UI - if you want to.
  // Require: schedule_id, reservation_ids, seats, departure_date
} else if (schedule.schedule_type === 'random') {
  // Show passenger count selector
  // Show time selector within operating hours
  // Require: route_id, bus_id, reservation_ids, no_of_passengers, departure_date, departure_time
}
```

**Build Request Based on Schedule Type**

**For Timed Schedules:**

```javascript
const timedRequest = {
  schedule_type: 'timed',
  schedule_id: schedule.id,
  reservation_ids: reservationIds, // From hold creation response
  seats: selectedSeatIds, // From user selection
  departure_date: `${date} ${schedule.time.departure}`,
  merchant_reference: 'your-internal-order-id', // Optional
  customer_info: customerData
};
```

**For Random Schedules:**

```javascript
const randomRequest = {
  schedule_type: 'random',
  route_id: schedule.route.id,
  bus_id: selectedBus.bus_id, // From buses array
  reservation_ids: reservationIds, // From hold creation response
  no_of_passengers: passengerCount,
  departure_date: selectedDate,
  departure_time: selectedTime, // Within operating_hours
  merchant_reference: 'your-internal-order-id', // Optional
  customer_info: customerData
};
```

### Integration Checklist

#### **Before calling this endpoint:**

1. Called the Get Schedules endpoint and identified the `schedule_type`
2. Created holds via `POST /api/v1/merpi/validate`
3. Stored `reservation_ids` from the hold response
4. Stored `customer_info` (email/phone) used during hold creation
5. For timed schedules: fetched available seats and customer has made a selection
6. For random schedules: customer has specified passenger count and preferred departure time
7. Verified holds are still active (not expired)
8. Customer completed payment/checkout details

#### **In your request:**

1. Set `schedule_type` to either `"timed"` or `"random"` to match the schedule
2. Include all `reservation_ids` from the hold
3. Use the **exact same** `customer_info.email` and `customer_info.phone_number` as during hold creation
4. For timed: include `schedule_id`, `seats`, and `departure_date`
5. For random: include `route_id`, `bus_id`, `no_of_passengers`, `departure_date`, and `departure_time`
6. Pass `merchant_reference` if you want to tie this transaction to your own internal reference

#### **After successful purchase:**

1. Store `reference` and `invoice_id` for customer records
2. Call the requery endpoint to retrieve the full ticket details
3. Display booking confirmation to the customer
4. Send confirmation email/SMS with `reference` and trip details
