> 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/hold-reserve-ticket-inventory/hold-reserve-experience-inventory.md).

# Hold/Reserve Experience Inventory

### Endpoint

```
POST /api/v1/merpi/validate
```

### Description

The validation endpoint creates temporary reservations (holds) on experience tickets, preventing other customers from purchasing the same inventory while your customer completes their transaction. This is essential for maintaining inventory accuracy and providing a seamless checkout experience.

#### How It Works

1. **Reserve**: Customer selects tickets → Your system calls `/validate` → Inventory is held
2. **Checkout**: Customer completes payment details (3-minute window)
3. **Complete**: Submit hold to buy endpoint → Reservation converts to confirmed booking
4. **Auto-expire**: If no purchase is made, hold automatically releases after TTL(3 minutes)

#### Common Use Cases

* **Concert/Event Ticket Selection**: Reserve specific seats while customer enters payment information
* **Cinema Bookings**: Hold movie tickets during the checkout flow to prevent overbooking
* **Multi-Step Checkout**: Maintain inventory locks across multiple payment/form pages
* **Cart Management**: Allow customers to "add to cart" without immediate purchase commitment
* **Group Bookings**: Reserve multiple tickets simultaneously for coordinated purchases

***

### 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                                                                                                                               |
| ---------------------------- | ------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `tickets`                    | array         | Yes         | Array of ticket reservation requests. Minimum 1, maximum 10 items.                                                                        |
| `tickets[].ticket_type`      | string        | Yes         | Must be `"entertainment"` for experience reservations.                                                                                    |
| `tickets[].resource_id`      | string (UUID) | Yes         | Unique identifier of the experience ticket. References `tickets.id` from the experience details endpoint.                                 |
| `tickets[].quantity`         | integer       | Yes         | Number of tickets to reserve. Minimum value: 1.                                                                                           |
| `tickets[].metadata`         | object        | No          | Optional additional context. For entertainment tickets, no specific fields are required—include any data relevant to your tracking needs. |
| `customer_info`              | object        | Yes         | Customer identification for hold ownership verification.                                                                                  |
| `customer_info.email`        | string        | Conditional | Customer email address. Required if `phone_number` is not provided.                                                                       |
| `customer_info.phone_number` | string        | Conditional | Customer phone number (11-13 digits). Required if `email` is not provided.                                                                |

**Important Notes**

* **Customer Info**: At least one of `email` or `phone_number` must be provided. Both can be included.
* **Resource ID Format**: For entertainment tickets, `resource_id` must be a valid UUID matching a ticket ID from the experience listing.
* **Ownership Verification**: The `customer_info` values act as a session token—they must match exactly when using the hold in a buy request or when releasing the hold early.

***

### Response Structure

#### Success Response (201 Created)

| Field               | Type    | Description                            |
| ------------------- | ------- | -------------------------------------- |
| `success`           | boolean | Always `true` for successful requests. |
| `status`            | integer | HTTP status code (201).                |
| `message`           | string  | Human-readable success message.        |
| `data`              | object  | Contains the reservation details.      |
| `data.reservations` | array   | Array of created reservation objects.  |

**Reservation Object Fields**

| Field               | Type              | Description                                                                        |
| ------------------- | ----------------- | ---------------------------------------------------------------------------------- |
| `reservation_id`    | string (UUID)     | Unique identifier for this hold. **Use this value in buy requests.**               |
| `ticket_type`       | string            | Type of ticket reserved (will be `"entertainment"`).                               |
| `resource_id`       | string (UUID)     | The experience ticket ID that was reserved.                                        |
| `quantity`          | integer           | Number of tickets reserved.                                                        |
| `status`            | string            | Current hold status. Values: `"active"`, `"converted"`, `"released"`, `"expired"`. |
| `expires_at`        | string (datetime) | ISO 8601 timestamp when this hold will automatically expire (UTC).                 |
| `seconds_remaining` | integer           | Number of seconds until expiration (calculated at response time).                  |

***

### Example Requests & Responses

#### Example 1: Reserve Single Experience Ticket

**Request:**

```json
POST /api/v1/merpi/validate
Content-Type: application/json

{
  "tickets": [
    {
      "ticket_type": "entertainment",
      "resource_id": "3f15c9b7-c0cc-4260-99ef-dec620c97ae8",
      "quantity": 1,
      "metadata": {
        "seat_preference": "front_row",
        "session_id": "checkout_abc123"
      }
    }
  ],
  "customer_info": {
    "email": "customer@example.com",
    "phone_number": "08085825362"
  }
}
```

**Response (201 Created):**

```json
{
  "success": true,
  "status": 201,
  "message": "Holds created",
  "data": {
    "reservations": [
      {
        "reservation_id": "d6043b00-dad5-4aed-823c-74608eaaaa63",
        "ticket_type": "entertainment",
        "resource_id": "3f15c9b7-c0cc-4260-99ef-dec620c97ae8",
        "quantity": 1,
        "status": "active",
        "expires_at": "2026-03-29T18:15:11.000000Z",
        "seconds_remaining": 180
      }
    ]
  }
}
```

***

#### Example 2: Reserve Multiple Experience Tickets (Batch)

**Request:**

```json
POST /api/v1/merpi/validate
Content-Type: application/json

{
  "tickets": [
    {
      "ticket_type": "entertainment",
      "resource_id": "3f15c9b7-c0cc-4260-99ef-dec620c97ae8",
      "quantity": 1
    },
    {
      "ticket_type": "entertainment",
      "resource_id": "55bcf436-5f62-4ab0-b062-41fe9960b39f",
      "quantity": 1
    }
  ],
  "customer_info": {
    "email": "customer@example.com"
  }
}
```

**Response (201 Created):**

```json
{
  "success": true,
  "status": 201,
  "message": "Holds created",
  "data": {
    "reservations": [
      {
        "reservation_id": "d6043b00-dad5-4aed-823c-74608eaaaa63",
        "ticket_type": "entertainment",
        "resource_id": "3f15c9b7-c0cc-4260-99ef-dec620c97ae8",
        "quantity": 1,
        "status": "active",
        "expires_at": "2026-03-29T18:15:11.000000Z",
        "seconds_remaining": 180
      },
      {
        "reservation_id": "59e90de9-b6d8-4126-95d3-eebfb32d697a",
        "ticket_type": "entertainment",
        "resource_id": "55bcf436-5f62-4ab0-b062-41fe9960b39f",
        "quantity": 1,
        "status": "active",
        "expires_at": "2026-03-29T18:15:11.000000Z",
        "seconds_remaining": 180
      }
    ]
  }
}
```

***

### Error Responses

#### 409 Conflict - Insufficient Inventory

Occurs when the requested tickets are no longer available or already reserved by another customer.

**Response:**

```json
{
  "success": false,
  "status": 409,
  "message": "The requested ticket (3f15c9b7-c0cc-4260-99ef-dec620c97ae8) does not have sufficient quantity available.",
  "errors": {
    "tickets": [
      "The requested ticket (3f15c9b7-c0cc-4260-99ef-dec620c97ae8) does not have sufficient quantity available."
    ]
  }
}
```

**Common Causes:**

* Tickets sold out between listing view and hold attempt
* Another customer's active hold is consuming the inventory
* Requested quantity exceeds available tickets

***

#### 422 Unprocessable Entity - Validation Error

Occurs when the request body contains invalid or missing required fields.

**Response:**

```json
{
  "success": false,
  "status": 422,
  "message": "The resource_id field must be a valid UUID.",
  "errors": {
    "tickets.0.resource_id": [
      "The resource_id field must be a valid UUID."
    ],
    "customer_info.email": [
      "The email field is required when phone_number is not present."
    ]
  }
}
```

**Common Causes:**

* Missing required fields (`ticket_type`, `resource_id`, `quantity`)
* Invalid `resource_id` format (must be UUID for entertainment)
* Missing both `email` and `phone_number` in `customer_info`
* Invalid `phone_number` format (must be 11-13 digits)
* `quantity` less than 1
* More than 10 items in `tickets` array

***

### Using Holds in Purchase Requests

Once you've successfully created a hold, use the `reservation_id` values to complete the purchase via the experience buy endpoint.

**Critical Requirements:**

* The `customer_info.email` or `customer_info.phone_number` in the buy request **must exactly match** what was used when creating the hold
* This matching is how the system verifies hold ownership
* Mismatched customer info will result in a 404 error (hold not found)

***

### Hold Lifecycle & Behavior

#### Status Transitions

```
POST /validate  →  status: "active"  (TTL timer starts)
                         │
          ┌──────────────┼──────────────────┐
          │              │                  │
    buy request    DELETE /validate    TTL expires
          │              │                  │
   status: converted  status: released  status: expired
```

#### Key Behaviors

**Idempotency**: If you submit the exact same hold parameters while an active hold already exists for that customer, the API returns the existing hold instead of creating a duplicate.

**Time-to-Live (TTL)**: Holds automatically expire after 3 minutes. The `expires_at` and `seconds_remaining` fields in the response help you display countdown timers to customers.

**Batch Rollback**: When creating multiple holds in a single request, if any individual hold fails (e.g., insufficient inventory), **all previously created holds in that same request are automatically released**. This ensures transactional consistency—either all holds succeed or none do.

***

### Early Release (Optional)

If a customer abandons their checkout or you need to release holds programmatically, use the hold release endpoint:

```
DELETE /api/v1/merpi/validate/{reservation_id}
```

**See Documentation**: [Release Hold Endpoint](/merpi-by-syticks/api-reference/hold-reserve-ticket-inventory/release-a-hold-reservation-early.md) for detailed usage instructions.

**Note**: Holds automatically release after TTL expiration, so manual release is only necessary for early cleanup or improved user experience.

***

### Best Practices

1. **Display Timer**: Show the `seconds_remaining` or `expires_at` to customers so they know how much time they have to complete checkout.
2. **Handle 409 Gracefully**: If inventory is insufficient, immediately notify the customer and refresh the experience listing to show current availability.
3. **Retry Logic**: Implement exponential backoff for transient failures, but **do not retry** on 409 errors (insufficient inventory).
4. **Customer Info Consistency**: Store the `customer_info` values (email/phone) in your session and use them consistently across hold creation, buy requests, and release calls.
5. **Batch Carefully**: When reserving multiple tickets, consider that batch rollback means an "all or nothing" outcome. If you need partial success handling, create holds individually.
6. **Monitor TTL**: The default 3-minute TTL is suitable for most checkouts, but consider your average checkout duration. If customers frequently lose holds, you may need to optimize your checkout flow.

***

### Related Endpoints

* [**Get Experience Details**](/merpi-by-syticks/api-reference/experience-ticketing-events-parties-conference-comedy-and-more/get-experience-details.md): Retrieve ticket information and availability before creating holds
* [**Buy Experience Tickets**](/merpi-by-syticks/api-reference/experience-ticketing-events-parties-conference-comedy-and-more/buy-experience-tickets.md): Complete purchase using `reservation_ids` from successful holds
* [**Release Hold/Reservation**:](/merpi-by-syticks/api-reference/hold-reserve-ticket-inventory/release-a-hold-reservation-early.md) Manually release holds before TTL expiration
* [**Get Experience Listing**](/merpi-by-syticks/api-reference/experience-ticketing-events-parties-conference-comedy-and-more/get-list-of-experiences.md): Browse available experiences to present to customers


---

# 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, and the optional `goal` query parameter:

```
GET https://syticks.gitbook.io/merpi-by-syticks/api-reference/hold-reserve-ticket-inventory/hold-reserve-experience-inventory.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
