> 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/release-a-hold-reservation-early.md).

# Release a Hold/Reservation Early

### Endpoint

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

When a customer creates a hold (reservation), it automatically expires after the configured time-to-live (default: 3 minutes). However, there are scenarios where you may want to release the hold earlier—such as when a customer explicitly cancels their selection, abandons the checkout flow, or navigates away from your booking page.

Releasing holds early improves inventory availability for other customers and provides better user experience by immediately freeing up seats, rooms, or tickets.

#### When to Use This Endpoint

**Common Use Cases:**

* **Cart Removal**: Customer removes items from their cart before checkout
* **Checkout Abandonment**: Customer closes the browser or navigates away from checkout
* **Explicit Cancellation**: Customer clicks "Cancel" or "Start Over" during the booking process
* **Selection Change**: Customer wants to select different seats/dates and needs to release current holds first
* **Session Timeout**: Your application detects session inactivity and proactively releases holds
* **Error Recovery**: Application error occurs during checkout and you need to clean up holds

**When NOT to Use:**

* Don't release holds right before completing a purchase—just proceed to the buy endpoint
* Don't release holds that have already been converted to bookings (status: "converted")
* Don't call this for holds that have already expired (status: "expired")—they're already released

***

### Request Parameters

#### Path Parameters

| Parameter        | Type          | Required | Description                                                                                                          |
| ---------------- | ------------- | -------- | -------------------------------------------------------------------------------------------------------------------- |
| `reservation_id` | string (UUID) | Yes      | The unique identifier of the hold to release. This is the `reservation_id` value returned when the hold was created. |

#### 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                                                                                               |
| ---------------------------- | ------ | ----------- | --------------------------------------------------------------------------------------------------------- |
| `customer_info`              | object | Yes         | Customer identification for hold ownership verification. Must match the info used when creating the hold. |
| `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**

* **Ownership Verification**: The `customer_info` values must **exactly match** what was used when creating the hold. This prevents one customer from releasing another customer's holds.
* **Customer Info Requirement**: At least one of `email` or `phone_number` must be provided. Both can be included.
* **Case Sensitivity**: Email addresses are case-insensitive, but ensure exact matches for phone numbers (including any formatting).

***

### Response Structure

#### Success Response (200 OK)

| Field     | Type    | Description                           |
| --------- | ------- | ------------------------------------- |
| `success` | boolean | Always `true` for successful release. |
| `message` | string  | Human-readable confirmation message.  |

**Example:**

```json
{
  "success": true,
  "message": "Hold released"
}
```

***

### Example Requests & Responses

#### Example 1: Release Experience Ticket Hold

**Request:**

```http
DELETE /api/v1/merpi/validate/d6043b00-dad5-4aed-823c-74608eaaaa63
Content-Type: application/json

{
  "customer_info": {
    "email": "customer@example.com"
  }
}
```

**Response (200 OK):**

```json
{
  "success": true,
  "message": "Hold released"
}
```

***

#### Example 2: Release Transport Ticket Hold

**Request:**

```http
DELETE /api/v1/merpi/validate/e6011a19-97ea-48cc-823f-a0fc909cec6c
Content-Type: application/json

{
  "customer_info": {
    "phone_number": "08085825362"
  }
}
```

**Response (200 OK):**

```json
{
  "success": true,
  "message": "Hold released"
}
```

***

#### Example 3: Release Hospitality Room Hold

**Request:**

```http
DELETE /api/v1/merpi/validate/a463ddd2-64df-4875-99bf-a2cbd11e1b6a
Content-Type: application/json

{
  "customer_info": {
    "email": "user@example.com",
    "phone_number": "08085825362"
  }
}
```

**Response (200 OK):**

```json
{
  "success": true,
  "message": "Hold released"
}
```

***

#### Example 4: Release Multiple Holds

When you need to release multiple holds (e.g., round-trip transport tickets or multi-room bookings), make separate DELETE requests for each `reservation_id`.

**Request 1:**

```http
DELETE /api/v1/merpi/validate/e6011a19-97ea-48cc-823f-a0fc909cec6c
Content-Type: application/json

{
  "customer_info": {
    "email": "user@example.com"
  }
}
```

**Request 2:**

```http
DELETE /api/v1/merpi/validate/f7122b2a-08fb-59dd-934e-b1gd010ffd7d
Content-Type: application/json

{
  "customer_info": {
    "email": "user@example.com"
  }
}
```

**Note**: There is currently no batch release endpoint. Each hold must be released individually.

***

### Error Responses

#### 404 Not Found - Hold Not Found or Unauthorized

Occurs when the hold cannot be found, has already been released/expired, or the customer info doesn't match the hold owner.

**Response:**

```json
{
  "success": false,
  "status": 404,
  "message": "Hold not found or already released"
}
```

**Common Causes:**

| Scenario                 | Explanation                                                                    |
| ------------------------ | ------------------------------------------------------------------------------ |
| Invalid `reservation_id` | The UUID doesn't exist in the system                                           |
| Customer info mismatch   | The email/phone in the request doesn't match the hold owner                    |
| Already released         | The hold was manually released earlier                                         |
| Already expired          | The TTL expired and the hold auto-released                                     |
| Already converted        | The hold was successfully used in a buy request and is now a confirmed booking |

**Troubleshooting Tips:**

1. **Verify the `reservation_id`**: Ensure you're using the exact UUID returned when the hold was created
2. **Check customer info**: Confirm the email/phone matches exactly what was used during hold creation
3. **Check hold status**: Before releasing, verify the hold is still active (status: "active")
4. **Don't retry**: If you get a 404, the hold is already gone—retrying won't help

***

#### 422 Unprocessable Entity - Validation Error

Occurs when the request body is malformed or missing required fields.

**Response:**

```json
{
  "success": false,
  "status": 422,
  "message": "Validation failed",
}
```

**Common Causes:**

* Missing both `email` and `phone_number` in `customer_info`
* Invalid `phone_number` format (must be 11-13 digits)
* Malformed request body (invalid JSON)

***

### Hold Status After Release

Once a hold is successfully released, its status changes to `"released"`. The inventory that was held becomes immediately available for other customers to book.

**Status Lifecycle:**

```
status: "active"  →  DELETE /validate/{id}  →  status: "released"
                                                      ↓
                                              Inventory freed
                                              Available to others
```

**Note**: Released holds cannot be reactivated. If the customer wants to book the same inventory again, they must create a new hold via `POST /api/v1/merpi/validate`.

***

### Best Practices

#### 1. Track Hold Status Client-Side

Maintain hold status in your application state. Before calling the release endpoint, check if the hold is still active to avoid unnecessary API calls.

```javascript
// Example tracking
const holdStatus = {
  reservation_id: "d6043b00-dad5-4aed-823c-74608eaaaa63",
  status: "active",
  expires_at: "2026-03-29T18:15:11Z"
};

if (holdStatus.status === "active") {
  // Safe to release
  releaseHold(holdStatus.reservation_id);
}
```

#### 2. Release on Navigation Away

Use browser events to detect when users leave your booking flow:

```javascript
// Example: Release holds on page unload
window.addEventListener('beforeunload', (event) => {
  if (activeHolds.length > 0) {
    // Release holds before user leaves
    activeHolds.forEach(holdId => releaseHold(holdId));
  }
});
```

**Caution**: `beforeunload` events have limited time to execute. Consider using the [Beacon API](https://developer.mozilla.org/en-US/docs/Web/API/Beacon_API) for more reliable delivery.

#### 3. Store Customer Info Consistently

Store the `customer_info` values (email/phone) used during hold creation in your session/state. Use these exact values for all subsequent operations (release, buy) to ensure ownership verification passes.

```javascript
// Store during hold creation
sessionStorage.setItem('hold_customer_email', customerEmail);

// Reuse during release
const releasePayload = {
  customer_info: {
    email: sessionStorage.getItem('hold_customer_email')
  }
};
```

#### 4. Handle 404 Gracefully

A 404 response means the hold is already gone. Don't treat this as a critical error—simply update your UI to reflect that the inventory is no longer reserved.

```javascript
try {
  await releaseHold(reservationId);
} catch (error) {
  if (error.status === 404) {
    // Hold already gone - not a problem
    console.log('Hold already released or expired');
    updateUIToUnreserved();
  } else {
    // Actual error - handle appropriately
    console.error('Failed to release hold', error);
  }
}
```

#### 5. Don't Release Right Before Purchase

If a customer is proceeding to payment, don't release their holds. Just submit the `reservation_ids` to the buy endpoint. The system will automatically convert holds to confirmed bookings.

```javascript
// BAD: Releasing then buying
await releaseHolds(reservationIds); // ❌ Don't do this
await buyTickets({ reservation_ids: reservationIds }); // This will fail

// GOOD: Just buy directly
await buyTickets({ reservation_ids: reservationIds }); // ✅ Correct
```

#### 6. Batch Release for Multi-Hold Flows

When releasing multiple holds (e.g., round-trip tickets, multi-room bookings), loop through and release each individually. Consider using `Promise.all()` for parallel execution:

```javascript
// Release multiple holds in parallel
const releasePromises = reservationIds.map(id => 
  releaseHold(id, customerInfo)
);

await Promise.all(releasePromises);
```

#### 7. Provide User Feedback

When a customer explicitly cancels their selection, confirm the release with clear UI feedback:

```javascript
// Example user feedback
const confirmRelease = await showConfirmDialog(
  "Cancel your reservation?",
  "Your selected seats will be released and become available to others."
);

if (confirmRelease) {
  await releaseHold(reservationId);
  showNotification("Reservation cancelled successfully");
}
```

#### 8. Monitor Hold Expiration

Instead of only relying on manual release, display countdown timers showing when holds will automatically expire. This reduces the need for explicit release calls and improves UX.

```javascript
// Show countdown to user
const secondsRemaining = calculateSecondsRemaining(expiresAt);
displayCountdown(secondsRemaining); // "2:45 remaining"
```

#### 9. Idempotent Release Calls

Releasing an already-released hold returns a 404, which is safe to ignore. You can call the release endpoint multiple times without harm—the first successful call releases it, subsequent calls get 404.

***

### FAQ

**Q: What happens to inventory when I release a hold?**\
A: The seats/tickets/rooms immediately become available for other customers to book.

**Q: Can I reactivate a released hold?**\
A: No. Once released, holds cannot be reactivated. Create a new hold if needed.

**Q: Do I need to release holds that have already expired?**\
A: No. Expired holds are automatically released by the system. Calling release on an expired hold will return a 404.

**Q: What if I try to release someone else's hold?**\
A: The system verifies ownership via `customer_info`. Mismatched customer info returns a 404 (not unauthorized/403, to prevent information leakage).

**Q: Can I release multiple holds in a single request?**\
A: Not currently. Each hold must be released with a separate DELETE request. Consider parallel execution with `Promise.all()` for better performance.

**Q: Should I release holds before calling the buy endpoint?**\
A: **No.** Just pass `reservation_ids` to the buy endpoint. The system automatically converts holds to bookings—releasing first will cause the purchase to fail.

**Q: How do I check if a hold is still active before releasing?**\
A: Maintain hold status client-side based on creation time and TTL. Alternatively, just call release—a 404 means it's already gone.

**Q: What's the difference between status "released" and "expired"?**\
A: Both mean the hold is gone. "Released" = manual release via this endpoint. "Expired" = automatic TTL expiration. Both free up inventory.


---

# 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/release-a-hold-reservation-early.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.
