> 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/experience-ticketing-events-parties-conference-comedy-and-more/buy-experience-tickets.md).

# Buy Experience Tickets

**Endpoint**

```
POST /api/v1/merpi/experience/buy/tickets
```

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

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

**You must create holds before purchasing tickets.** This prevents situations where a customer completes payment but the tickets 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, tickets could be sold to another customer between when your customer starts checkout and when payment completes. Holds guarantee inventory availability during the checkout process.

#### Purchase Experience/Event Tickets

> Allows the purchase of tickets using reserved inventory. You must create holds first via POST /api/v1/merpi/validate to reserve tickets, then use the reservation\_ids to complete the purchase. This guarantees inventory availability during checkout.

## Purchase Experience/Event Tickets

> Allows the purchase of tickets using reserved inventory. You must create holds first  via POST /api/v1/merpi/validate to reserve tickets, then use the reservation\_ids  to complete the purchase. This guarantees inventory availability during checkout.<br>

```json
{"openapi":"3.0.0","info":{"title":"Buy Experience/Event Tickets API","version":"1.0.0"},"paths":{"/api/v1/merpi/experience/buy/tickets":{"post":{"summary":"Purchase Experience/Event Tickets","description":"Allows the purchase of tickets using reserved inventory. You must create holds first  via POST /api/v1/merpi/validate to reserve tickets, then use the reservation_ids  to complete the purchase. This guarantees inventory availability during checkout.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["reservation_ids","tickets","experience_id","customer_info"],"properties":{"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"}},"tickets":{"type":"array","description":"Array of ticket objects specifying which tickets to purchase.","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier of the ticket category."},"count":{"type":"integer","description":"Number of tickets to purchase for this category.","minimum":1}},"required":["id","count"]}},"experience_id":{"type":"string","format":"uuid","description":"Unique identifier of the experience."},"customer_info":{"type":"object","description":"Customer information for the booking. The email or phone_number must exactly  match what was used when creating the holds via POST /api/v1/merpi/validate.\n","properties":{"name":{"type":"string","description":"Full name of the customer."},"email":{"type":"string","format":"email","description":"Customer's 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","pattern":"^\\d{11,13}$"},"dob":{"type":"string","format":"date","description":"Customer's date of birth (in YYYY-MM-DD format)."},"username":{"type":"string","description":"Customer's username."}},"required":["name","email","phone_number"]},"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}}}}}},"responses":{"201":{"description":"Ticket purchased successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status":{"type":"integer"},"message":{"type":"string"},"data":{"type":"object","properties":{"reference":{"type":"string"},"invoice_id":{"type":"integer"},"name":{"type":"string"},"email":{"type":"string"},"phone_number":{"type":"string"},"merchant_reference":{"type":"string","nullable":true,"description":"Your reference for this transaction if provided, null otherwise."},"tickets":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"price":{"type":"number"}}}}}}}}}}},"400":{"description":"Validation error – some fields are required or invalid","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"reason":{"type":"string"},"errors":{"type":"object","properties":{"experience_id":{"type":"array","items":{"type":"string"}},"tickets":{"type":"array","items":{"type":"string"}}}},"status":{"type":"integer"}}}}}},"404":{"description":"Hold not found - reservation IDs are invalid or don't belong to the customer","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status":{"type":"integer"},"message":{"type":"string"}}}}}},"409":{"description":"Conflict - reserved inventory no longer available (holds expired)","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status":{"type":"integer"},"message":{"type":"string"}}}}}}},"tags":["Experience/Event Tickets"]}}}}
```

### **Request Parameters**

**Headers**

| Parameter       | Type   | Required | Description                   |
| --------------- | ------ | -------- | ----------------------------- |
| `Content-Type`  | string | Yes      | Must be `application/json`    |
| `Authorization` | string | Yes      | Your API authentication token |

**Request Body**

| Field                        | Type             | Required | Description                                                                                                                                                                                                                                                                                                     |
| ---------------------------- | ---------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `reservation_ids`            | array of strings | Yes      | Array of reservation IDs from the hold creation response. Must contain at least one valid UUID.                                                                                                                                                                                                                 |
| `tickets`                    | array            | Yes      | Array of ticket objects specifying which tickets to purchase.                                                                                                                                                                                                                                                   |
| `tickets[].id`               | string (UUID)    | Yes      | Unique identifier of the ticket category.                                                                                                                                                                                                                                                                       |
| `tickets[].count`            | integer          | Yes      | Number of tickets to purchase for this category. Must be positive.                                                                                                                                                                                                                                              |
| `experience_id`              | string (UUID)    | Yes      | Unique identifier of the experience.                                                                                                                                                                                                                                                                            |
| `customer_info`              | object           | Yes      | Customer information for the booking.                                                                                                                                                                                                                                                                           |
| `customer_info.name`         | string           | Yes      | Full name of the customer.                                                                                                                                                                                                                                                                                      |
| `customer_info.email`        | string           | Yes      | Customer's email address. **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       | Customer's date of birth in `YYYY-MM-DD` format.                                                                                                                                                                                                                                                                |
| `customer_info.username`     | string           | No       | Customer's username.                                                                                                                                                                                                                                                                                            |
| `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:**

| Field                     | Type    | Description                                                                                                       |
| ------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------- |
| `success`                 | boolean | Always `true` for successful purchases.                                                                           |
| `status`                  | integer | HTTP status code (201).                                                                                           |
| `message`                 | string  | Human-readable success message.                                                                                   |
| `data.reference`          | string  | Unique booking reference for this purchase.                                                                       |
| `data.invoice_id`         | integer | Invoice ID for this transaction.                                                                                  |
| `data.name`               | string  | Customer name from the request.                                                                                   |
| `data.email`              | string  | Customer email from the request.                                                                                  |
| `data.phone_number`       | string  | Customer phone number from the request.                                                                           |
| `data.merchant_reference` | string  | Your reference for this transaction, if you provided one. `null` if not provided.                                 |
| `data.tickets`            | array   | Array of purchased ticket objects with details. Call the requery endpoint after purchase to get the full details. |

### **Error Responses**

**400 Bad Request - Validation Error**

Occurs when required fields are missing or invalid.

json

```json
{
  "success": false,
  "message": "The provided experience ID is invalid.",
  "reason": "The provided experience ID is invalid.",
  "errors": {
    "experience_id": [
      "The provided experience ID is invalid."
    ],
    "tickets": [
      "Ticket not found (ID: 51cbea98-85df-4ac8-8fa8-a9e669a16f0f)"
    ]
  },
  "status": 400
}
```

**Common Causes:**

* Invalid `experience_id` or `tickets[].id` (UUID not found)
* Missing required fields
* Invalid data formats

**404 Not Found - Hold Not Found**

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

json

```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")

**409 Conflict - Inventory No Longer Available**

Occurs when holds have expired and inventory was taken by another customer.

json

```json
{
  "success": false,
  "status": 409,
  "message": "Reserved inventory no longer available"
}
```

**Common Causes:**

* Hold TTL expired before purchase completed
* Tickets sold to another customer after expiration

### **Integration Checklist**

**Before calling this endpoint:**

1. Created holds via `POST /api/v1/merpi/validate`
2. Stored `reservation_ids` from hold response
3. Stored `customer_info` (email/phone) used during hold creation
4. Verified holds are still active (not expired)
5. Customer completed payment/checkout details

**In your request:**

1. Include all `reservation_ids` from the hold
2. Use the **exact same** `customer_info.email` and `customer_info.phone_number` as during hold creation
3. Include complete ticket details (`tickets` array with `id` and `count`)
4. Include `experience_id`
5. 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 customer
4. Send confirmation email/SMS with `reference`

### **Quick Tips**

**Customer Info Must Match**: The most common error is mismatched customer info between hold and buy. Store the email/phone from hold creation and reuse them exactly.

**Don't Release Before Buying**: Never call `DELETE /api/v1/merpi/validate/{id}` before purchasing. Just submit the buy request — successful purchases automatically convert holds.

**Handle Expired Holds**: If holds expire before payment completes, redirect customers to start over with new holds. Don't retry the buy request — the inventory is gone.

**Multiple Tickets**: When purchasing multiple ticket types for the same experience, create separate holds for each in the same request, then include all `reservation_ids` in a single buy request.
