---
uuid: 08f41e86-9750-458b-a6aa-3f88d9f8b78a
date_created: 2025-07-10T16:55:24.634Z
date_updated: 2026-04-07T09:48:40.789Z
seo_url: booking-journey
category: Booking
tags: 
  - Booking
routes: 
  - POST /v3/proposals/search
  - GET /v0/additional_services
  - PUT /v0/proposals/{proposal_id}/services
  - PUT /v3/proposals/{proposal_id}/attendees
  - GET /v0/additional_services
  - PUT /v0/proposals/{proposal_id}/services
  - PUT /v0/proposals/{proposal_id}/services
  - POST /v3/bookings
---

# Bookings: Booking journey

This guide explains how to transform a booking intent into a confirmed option with the Club Med API.

It covers proposal search, optional services and insurance handling, attendee qualification, and booking creation.

## Flow

```mermaid
flowchart LR
    step0["Search matching proposals"]
    step1["List available additional services"]
    step2["Add selected additional services"]
    step3["Qualify proposal attendees"]
    step4["List available insurance options"]
    step5["Add the selected insurance"]
    step6["Remove an insurance from the proposal"]
    step7["Create the booking option"]
    step0 --> step1
    step1 --> step2
    step2 --> step3
    step3 --> step4
    step4 --> step5
    step5 --> step6
    step6 --> step7
    classDef optional fill:transparent,stroke:#ffffff,color:#ffffff,stroke-width:1px,stroke-dasharray: 6\,4
    class step1 optional
    class step2 optional
    class step4 optional
    class step5 optional
    class step6 optional
```

## Overview

This scenario documents a booking-preparation flow that starts from proposal search and ends with booking creation.

## Prerequisites

* A valid `x-api-key` and `accept-language`.
* The booking criteria required to search a proposal.
* A `proposal_id` returned by the search step before adding services, insurance, or attendee data.

## Expected result

The application can create or retrieve a proposal, enrich it with optional services or insurance, qualify attendees, and create the booking option.

## 1 - Search matching proposals

Use `POST /v3/proposals/search` to create the proposal that will drive the booking journey. This first step calculates price, stock, and optionability from the submitted booking criteria and returns the `proposal_id` required by all later steps.

#### Prerequisites

* Send `accept-language` and `x-api-key`.
* Add `authorization` when the seller or partner context requires it.
* Prepare a booking payload with product, dates, number of attendees, and package context.

#### Calling CURL

```bash
curl -X 'POST' \
  'https://api.clubmed.com/v3/proposals/search' \
  -H 'accept: application/json' \
  -H 'accept-language: en-US' \
  -H 'x-api-key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{ ... }'
```

#### Example answer

```json
{
  "id": "123456",
  "product_id": "MPAC",
  "price": {
    "total": 9815.4,
    "currency": "EUR"
  },
  "remaining_stock": 2,
  "option_available": true
}
```

> **info:** The returned `proposal_id` becomes the working file for services, attendees, insurance, and booking creation.

***

**Response codes**

* `200 OK`: returns the proposal matching the submitted criteria.
* `400 Bad Request`: the payload is invalid, incomplete, or inconsistent.
* `401 Unauthorized`: authentication is missing, invalid, or expired.
* `403 Forbidden`: the current context cannot create the proposal.
* `404 Not Found`: the product cannot be found.
* `409 Conflict`: the proposal criteria are no longer valid.

**Related route**: [POST https://api.clubmed.com//v3/proposals/search](https://api.clubmed.com/doc?search=POST%20%2Fv3%2Fproposals%2Fsearch)

## 2 - List available additional services

Use `GET /v0/additional_services` with `proposal_id` to list the additional services available for the current proposal. This step is typically used to surface childcare, transfer, rental, or activity upsells before the proposal is qualified.

#### Prerequisites

* Reuse a valid `proposal_id`.
* Send `accept-language` and `x-api-key`.
* Optionally add `types` when you want to focus on a specific family of services.

#### Calling CURL

```bash
curl -X 'GET' \
  'https://api.clubmed.com/v0/additional_services?proposal_id=123456' \
  -H 'accept: application/json' \
  -H 'accept-language: en-US' \
  -H 'x-api-key: YOUR_API_KEY'
```

#### Example answer

```json
[
  {
    "id": "VMOSK1",
    "type": "CHILDCARE",
    "currency": "EUR",
    "schedules": [
      {
        "start_date": "20100430",
        "end_date": "20100430",
        "remaining_stock": 2
      }
    ]
  }
]
```

> **info:** Only attach services that come from this service-search response. It is the reference list for compatible upsells.

***

**Response codes**

* `200 OK`: returns the services available for the proposal.
* `206 Partial Content`: the result is partial and more pages may exist.
* `400 Bad Request`: the proposal or filters are invalid.
* `401 Unauthorized`: authentication is missing, invalid, or expired.
* `403 Forbidden`: the current market or authentication does not allow this lookup.
* `404 Not Found`: the proposal cannot be found.
* `409 Conflict`: the proposal criteria are no longer valid.
* `416 Requested Range Not Satisfiable`: the requested range is invalid.

**Related route**: [GET https://api.clubmed.com//v0/additional\_services](https://api.clubmed.com/doc?search=GET%20%2Fv0%2Fadditional_services)

## 3 - Add selected additional services

Use `PUT /v0/proposals/{proposal_id}/services` to apply the selected additional services to the proposal. This route works in cancel-and-replace mode, so each call must send the full service selection you want to keep on the proposal.

#### Prerequisites

* Provide `proposal_id`.
* Send `accept-language`, `authorization` when required, and `x-api-key`.
* Build the payload from the identifiers returned by the additional-services search route.

#### Calling CURL

```bash
curl -X 'PUT' \
  'https://api.clubmed.com/v0/proposals/123456/services' \
  -H 'accept: application/json' \
  -H 'accept-language: en-US' \
  -H 'authorization: Bearer YOUR_TOKEN' \
  -H 'x-api-key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{ ... }'
```

#### Example answer

```http
HTTP/1.1 204 No Content
```

> **info:** Because the route is cancel-and-replace, forgetting a previously selected service will remove it from the proposal.

***

**Response codes**

* `204 No Content`: the proposal services were updated successfully.
* `400 Bad Request`: services are incompatible, missing, or the payload is invalid.
* `401 Unauthorized`: authentication is missing, invalid, or expired.
* `404 Not Found`: the proposal cannot be found.
* `409 Conflict`: the proposal is no longer economically valid.

**Related route**: [PUT https://api.clubmed.com//v0/proposals/{proposal\_id}/services](https://api.clubmed.com/doc?search=PUT%20%2Fv0%2Fproposals%2F%7Bproposal_id%7D%2Fservices)

## 4 - Qualify proposal attendees

Use `PUT /v3/proposals/{proposal_id}/attendees` to attach or update attendees on the proposal before turning it into a booking. This is where identity, household composition, and customer linkage are validated.

#### Prerequisites

* Provide `proposal_id`.
* Send `accept-language`, `authorization` when required, and `x-api-key`.
* Prepare an attendee payload that respects the rules on birthdates, documents, and household consistency.

#### Calling CURL

```bash
curl -X 'PUT' \
  'https://api.clubmed.com/v3/proposals/123456/attendees' \
  -H 'accept: application/json' \
  -H 'accept-language: en-US' \
  -H 'authorization: Bearer YOUR_TOKEN' \
  -H 'x-api-key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{ ... }'
```

#### Example answer

```json
[
  {
    "attendees": [
      {
        "id": "A",
        "customer_id": "123456789",
        "customer_status": "NEW_CUSTOMER",
        "loyalty_status": "GOLD"
      }
    ]
  }
]
```

> **info:** Most blocking data-quality issues appear here, so the UI should surface the validation feedback clearly.

***

**Response codes**

* `200 OK`: returns the updated attendee structure.
* `400 Bad Request`: attendee data is invalid, incomplete, duplicated, or inconsistent.
* `401 Unauthorized`: authentication is missing, invalid, or expired.
* `403 Forbidden`: at least one customer is not allowed to continue.
* `409 Conflict`: the proposal is no longer economically valid.

**Related route**: [PUT https://api.clubmed.com//v3/proposals/{proposal\_id}/attendees](https://api.clubmed.com/doc?search=PUT%20%2Fv3%2Fproposals%2F%7Bproposal_id%7D%2Fattendees)

## 5 - List available insurance options

Use `GET /v0/additional_services` with `types=INSURANCE` to list the insurance products available for the proposal. This step isolates the insurance subset so the user can review guarantees before adding or replacing them on the proposal.

#### Prerequisites

* Reuse a valid `proposal_id`.
* Send `accept-language` and `x-api-key`.
* Add `types=INSURANCE` to focus on insurance services only.

#### Calling CURL

```bash
curl -X 'GET' \
  'https://api.clubmed.com/v0/additional_services?proposal_id=123456&types=INSURANCE' \
  -H 'accept: application/json' \
  -H 'accept-language: en-US' \
  -H 'x-api-key: YOUR_API_KEY'
```

#### Example answer

```json
[
  {
    "id": "TRAVEL_INSURANCE",
    "type": "INSURANCE",
    "currency": "EUR",
    "schedules": [
      {
        "start_date": "20100430",
        "end_date": "20100430",
        "remaining_stock": 99
      }
    ]
  }
]
```

> **info:** Filtering the service search on `INSURANCE` keeps the UI focused on insurance choices instead of the full additional-service catalog.

***

**Response codes**

* `200 OK`: returns the insurance services available for the proposal.
* `206 Partial Content`: the result is partial and more pages may exist.
* `400 Bad Request`: the proposal or filters are invalid.
* `401 Unauthorized`: authentication is missing, invalid, or expired.
* `403 Forbidden`: the current market or authentication does not allow this lookup.
* `404 Not Found`: the proposal cannot be found.
* `409 Conflict`: the proposal criteria are no longer valid.
* `416 Requested Range Not Satisfiable`: the requested range is invalid.

**Related route**: [GET https://api.clubmed.com//v0/additional\_services](https://api.clubmed.com/doc?search=GET%20%2Fv0%2Fadditional_services)

## 6 - Add the selected insurance

Use `PUT /v0/proposals/{proposal_id}/services` to apply the selected insurance to the proposal. Because the route behaves as cancel-and-replace, the insurance payload must be merged with the rest of the services you want to keep.

#### Prerequisites

* Provide `proposal_id`.
* Send `accept-language`, `authorization` when required, and `x-api-key`.
* Build the insurance payload from identifiers returned by the insurance-service search step.

#### Calling CURL

```bash
curl -X 'PUT' \
  'https://api.clubmed.com/v0/proposals/123456/services' \
  -H 'accept: application/json' \
  -H 'accept-language: en-US' \
  -H 'authorization: Bearer YOUR_TOKEN' \
  -H 'x-api-key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{ ... }'
```

#### Example answer

```http
HTTP/1.1 204 No Content
```

> **info:** Never send only the new insurance if other services must remain on the proposal. The route replaces the whole service list.

***

**Response codes**

* `204 No Content`: the proposal services were updated successfully.
* `400 Bad Request`: services are incompatible, missing, or the payload is invalid.
* `401 Unauthorized`: authentication is missing, invalid, or expired.
* `404 Not Found`: the proposal cannot be found.
* `409 Conflict`: the proposal is no longer economically valid.

**Related route**: [PUT https://api.clubmed.com//v0/proposals/{proposal\_id}/services](https://api.clubmed.com/doc?search=PUT%20%2Fv0%2Fproposals%2F%7Bproposal_id%7D%2Fservices)

## 7 - Remove an insurance from the proposal

Use `PUT /v0/proposals/{proposal_id}/services` to remove an insurance from the proposal by sending an updated service list without that insurance. Because the route replaces the full list, removal is done by omission in the new payload.

#### Prerequisites

* Provide `proposal_id`.
* Send `accept-language`, `authorization` when required, and `x-api-key`.
* Start from the current proposal service state so you only remove the targeted insurance.

#### Calling CURL

```bash
curl -X 'PUT' \
  'https://api.clubmed.com/v0/proposals/123456/services' \
  -H 'accept: application/json' \
  -H 'accept-language: en-US' \
  -H 'authorization: Bearer YOUR_TOKEN' \
  -H 'x-api-key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{ ... }'
```

#### Example answer

```http
HTTP/1.1 204 No Content
```

> **info:** To remove only one insurance safely, rebuild the payload from the latest service state instead of composing it from memory.

***

**Response codes**

* `204 No Content`: the service list was updated successfully.
* `400 Bad Request`: services are incompatible, missing, or the payload is invalid.
* `401 Unauthorized`: authentication is missing, invalid, or expired.
* `404 Not Found`: the proposal cannot be found.
* `409 Conflict`: the proposal is no longer economically valid.

**Related route**: [PUT https://api.clubmed.com//v0/proposals/{proposal\_id}/services](https://api.clubmed.com/doc?search=PUT%20%2Fv0%2Fproposals%2F%7Bproposal_id%7D%2Fservices)

## 8 - Create the booking option

Use `POST /v3/bookings` to create the booking option from the qualified proposal. This is the step that turns the proposal into a booking file and consumes Club Med stock.

#### Prerequisites

* Send `accept-language`, `authorization` when required, and `x-api-key`.
* Make sure attendee data has already been qualified on the proposal.
* Keep the same locale that was used for proposal creation and qualification.

#### Calling CURL

```bash
curl -X 'POST' \
  'https://api.clubmed.com/v3/bookings' \
  -H 'accept: application/json' \
  -H 'accept-language: en-US' \
  -H 'authorization: Bearer YOUR_TOKEN' \
  -H 'x-api-key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{ ... }'
```

#### Example answer

```json
{
  "booking_id": "654321",
  "households": [
    {
      "attendees": [
        {
          "customer_id": 987654,
          "type": "MAIN"
        }
      ]
    }
  ],
  "option_durability": {
    "is_reliable": true,
    "expiration_date_time": "20160415T10:23:00.234Z"
  }
}
```

> **info:** Booking creation is the committing step of the journey. If it fails, the error usually points to stale proposal data, missing services, or attendee inconsistencies.

***

**Response codes**

* `200 OK`: the booking was created and confirmation data is returned.
* `201 Created`: the booking was created successfully.
* `400 Bad Request`: the payload is invalid or one required data element is missing.
* `401 Unauthorized`: authentication is missing, invalid, or expired.
* `403 Forbidden`: the current context is not allowed to create the booking.
* `409 Conflict`: price, stock, promotion, or transport conditions are no longer valid.
* `419`: the related flight is no longer available.

**Related route**: [POST https://api.clubmed.com//v3/bookings](https://api.clubmed.com/doc?search=POST%20%2Fv3%2Fbookings)
