Add a service to a proposal

  • Proposal service
  • Services
  • 4 routes
How do I add a service to an existing proposal?

This scenario shows how to identify the available additional services, review the services already attached to a proposal, and update the selected proposal service list.

Overview

The sequence POST/v0/proposals/search/best -> GET/v0/additional_services -> GET/v0/proposals/{proposal_id}/services -> PUT/v0/proposals/{proposal_id}/services helps identify the additional services that can be sold, review the services already attached, and then replace the final service list kept on the proposal.

Prerequisites

  • A usable proposal must be created or retrieved at the beginning of the journey.
  • The proposal_id returned by the first step is required to read and update services.
  • The final update follows a cancel & replace logic: each call must carry the complete list of services that should remain on the proposal.
  • The documented calls use accept-language and x-api-key.

Process workflow

Legend:
Mandatory
Optional
1

Create or retrieve the proposal

Mandatory

Use POST/v1/proposals/search/best to create or retrieve the working proposal that will later receive additional services.

Prerequisites

  • Send accept-language and x-api-key.
  • The body must carry the booking criteria used in your sales journey.
  • Keep the returned id so it can be reused as proposal_id in the next steps.

Calling CURL

curl -X 'POST' \
  'https://api.clubmed.com/v1/proposals/search/best' \
  -H 'accept: application/json' \
  -H 'accept-language: en-US' \
  -H 'x-api-key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "product_id": "MPAC",
    "package_id": "PAI",
    "resort_arrival_date": "20260415",
    "duration": 7,
    "number_attendees": 2
  }'

Example answer

{
  "id": "123456",
  "product_id": "MPAC",
  "package_id": "PAI",
  "resort_arrival_date": "20260415",
  "resort_departure_date": "20260422",
  "creation_date_time": "2026-04-03T10:15:00Z",
  "price": {
    "amount": 9815.4,
    "currency": "EUR"
  },
  "alternative_price": {
    "amount": 9650.4,
    "currency": "EUR"
  },
  "option_durability": {
    "expiration_date_time": "2026-04-03T18:00:00Z",
    "is_reliable": true
  }
}

info: search/best returns the best usable proposal for the submitted context, which is usually faster than browsing a full proposal list in a guided sales journey.


Response codes

  • OK Response (200): returns the created or retrieved proposal with the identifier to reuse downstream.
  • Error (400): invalid, incomplete, or inconsistent criteria.
  • Error (401): authentication or API key is missing, invalid, or expired.
  • Error (403): at least one customer carried by the criteria is not allowed to continue the journey.
  • Error (404): the requested product is unknown for the submitted context.
POST/v1/proposals/search/best
See more
2

List available additional services

Mandatory

This route returns the list of additional services available for a given proposal or booking. Use it to identify the services that can actually be sold before building the final list to send back on the proposal.

Prerequisites

  • accept-language and x-api-key are required.
  • Provide at least one business context such as proposal_id, booking_id, or customer_id depending on your journey.
  • types and filter can be used to narrow the search to the services you really need.

Calling CURL

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

Example answer

[
  {
    "id": "VMOSK1",
    "product_information_id": "ACT_AGAC_baby_club_med_bis",
    "stay_index": 1,
    "time_slot": "MORNING",
    "age_in_months": {
      "min": 12,
      "max": 48
    },
    "not_compatible_with": [
      "string"
    ],
    "sold_only_with": [
      "string"
    ],
    "type": "CHILDCARE",
    "currency": "EUR",
    "schedules": [
      {
        "start_date": "20100430",
        "end_date": "20100430",
        "initial_stock": 12,
        "remaining_stock": 2,
        "attendees": [
          {
            "id": "A",
            "price": 88,
            "resort_price": 100
          }
        ]
      }
    ]
  }
]

info: evaluate sold_only_with and not_compatible_with before any update, otherwise the final replacement call can be rejected.


Response codes

  • OK Response (200): the available additional services were returned successfully.
  • OK Response (206): the response is partial.
  • Error (400): the proposal or booking context is invalid, not validated, or inconsistent with the submitted customer.
  • Error (401): the request is unauthorized because the authentication data or API key is missing, invalid, or expired.
  • Error (403): access is forbidden for the targeted country or the expected authentication is missing for some uses with booking_id and customer_id.
  • Error (404): the proposal or booking was not found for the submitted context.
  • Error (409): the proposal criteria are no longer valid when the list is requested.
  • Error (416): the requested range or filter is not satisfiable.
GET/v0/additional_services
See more
3

Review current proposal services

Mandatory

This route reads back the services already present on the proposal. Use this response as your source of truth before the final update, because the modification call replaces the existing list.

Prerequisites

  • x-api-key is required.
  • The proposal_id path parameter is required.
  • The optional filter query parameter can narrow the read to specific services.

Calling CURL

curl -X 'GET' \
  'https://api.clubmed.com/v0/proposals/123456/services' \
  -H 'accept: application/json' \
  -H 'x-api-key: YOUR_API_KEY'

Example answer

[
  {
    "id": "AHSI01",
    "time_slot": "MORNING",
    "type": "RENTAL",
    "currency": "EUR",
    "schedules": [
      {
        "start_date": "20100430",
        "end_date": "20100430",
        "attendees": [
          {
            "id": "A",
            "price": 250,
            "price_without_discount": 300,
            "discounts": [
              {
                "amount": 50,
                "offer_id": "AVA",
                "code": "PARFIL"
              }
            ]
          }
        ]
      }
    ],
    "product_information_id": "ACT_AGAC_baby_club_med_bis",
    "sold_only_with": [
      "ATIG3A"
    ],
    "not_compatible_with": [
      "ATIG3A"
    ]
  }
]

info: build the full list to keep from this read-back, otherwise some services already present can be dropped when the PUT is submitted.


Response codes

  • OK Response (200): the services currently attached to the proposal were returned successfully.
  • Error (400): the request is invalid or the submitted filter cannot be processed.
  • Error (401): not documented in Swagger.
  • Error (404): not documented in Swagger.
GET/v0/proposals/{proposal_id}/services
See more
4

Replace the proposal service list

Mandatory

This route updates the list of services associated with a proposal. The behavior is cancel & replace: each call must contain the complete set of services that should remain attached to the proposal after the update.

Prerequisites

  • accept-language and x-api-key are required.
  • The proposal_id path parameter is required.
  • Build the final payload from both the available-service search and the read-back of the services already attached.
  • Validate incompatibility and dependency rules before submitting the update.

Calling CURL

curl -X 'PUT' \
  'https://api.clubmed.com/v0/proposals/123456/services' \
  -H 'accept: application/json' \
  -H 'accept-language: en-US' \
  -H 'x-api-key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '@proposal-services-body.json'

Example answer

No response body is documented on success. Swagger documents a 204 No Content success response.

info: after this PUT, call GET/v0/proposals/{proposal_id}/services again to verify the list actually kept on the proposal.


Response codes

  • OK Response (204): the service list was updated successfully with no response body.
  • Error (400): the payload is invalid, contains incompatible services, or omits a required service.
  • Error (401): the request is unauthorized because the authentication data or API key is missing, invalid, or expired.
  • Error (404): the targeted proposal cannot be found.
  • Error (409): the proposal economic control is no longer valid at update time.
PUT/v0/proposals/{proposal_id}/services
See more