> ## Documentation Index
> Fetch the complete documentation index at: https://docs.voyantcloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Get product extensions

> GET /v1/products/:id/extensions - Retrieve available add-ons and extensions

## Endpoint

```
GET https://api.voyantcloud.com/v1/products/:id/extensions
```

Retrieve available add-ons and extensions for a product. Extensions are optional or required add-ons that enhance the base product (e.g., airport transfers, travel insurance, meal upgrades).

## Authentication

<ParamField header="Authorization" type="string" required>
  Bearer token (e.g. <code>Authorization: Bearer YOUR\_API\_KEY</code>)
</ParamField>

## Path parameters

<ParamField path="id" type="string" required>
  Product ID (UUID format)
</ParamField>

## Request example

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.voyantcloud.com/v1/products/prod_123abc/extensions \
    -H "Authorization: Bearer YOUR_API_KEY"
  ```

  ```javascript Node.js theme={null}
  const productId = "prod_123abc"

  const response = await fetch(`https://api.voyantcloud.com/v1/products/${productId}/extensions`, {
    headers: { Authorization: `Bearer ${process.env.VOYANT_API_KEY}` },
  })

  const data = await response.json()
  console.log(data)
  ```

  ```python Python theme={null}
  import requests
  import os

  product_id = 'prod_123abc'
  response = requests.get(
    f'https://api.voyantcloud.com/v1/products/{product_id}/extensions',
    headers={'Authorization': f"Bearer {os.environ['VOYANT_API_KEY']}"}
  )

  data = response.json()
  print(data)
  ```
</CodeGroup>

## Response

<ResponseField name="extensions" type="array" required>
  Array of available extensions

  <Expandable title="Extension properties">
    <ResponseField name="id" type="string">
      Extension ID (UUID)
    </ResponseField>

    <ResponseField name="name" type="string">
      Extension name/title
    </ResponseField>

    <ResponseField name="required" type="boolean">
      Whether this extension is required
    </ResponseField>

    <ResponseField name="selectable" type="boolean">
      Whether customers can select this extension
    </ResponseField>

    <ResponseField name="refProductId" type="string | null">
      Referenced product ID if extension links to another product
    </ResponseField>

    <ResponseField name="refSource" type="string">
      Source of extension: `own` (internal) or external provider
    </ResponseField>

    <ResponseField name="hasOptions" type="boolean">
      Whether extension has selectable options (e.g., room types)
    </ResponseField>

    <ResponseField name="thumb" type="string | null">
      Thumbnail image URL
    </ResponseField>

    <ResponseField name="pricePerPerson" type="number | null">
      Minimum price per person
    </ResponseField>

    <ResponseField name="currency" type="string | null">
      3-letter ISO currency code
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="details" type="object">
  Map of extension details by product ID

  <Expandable title="Detail properties">
    <ResponseField name="description" type="string">
      Extension description
    </ResponseField>

    <ResponseField name="media" type="array">
      Array of media items (images/videos)
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseExample>
  ```json 200 Success theme={null}
  {
    "extensions": [
      {
        "id": "ext_123abc",
        "name": "Airport Transfer",
        "required": false,
        "selectable": true,
        "refProductId": "prod_transfer_456",
        "refSource": "own",
        "hasOptions": false,
        "thumb": "https://cdn.voyantcloud.com/transfers/airport.jpg",
        "pricePerPerson": 25.00,
        "currency": "EUR"
      },
      {
        "id": "ext_789def",
        "name": "Travel Insurance",
        "required": true,
        "selectable": true,
        "refProductId": "prod_insurance_012",
        "refSource": "own",
        "hasOptions": true,
        "thumb": "https://cdn.voyantcloud.com/insurance/standard.jpg",
        "pricePerPerson": 15.00,
        "currency": "EUR"
      }
    ],
    "details": {
      "prod_transfer_456": {
        "description": "Private airport transfer with professional driver",
        "media": [
          {
            "url": "https://cdn.voyantcloud.com/transfers/airport.jpg",
            "alt": null
          }
        ]
      },
      "prod_insurance_012": {
        "description": "Comprehensive travel insurance coverage",
        "media": [
          {
            "url": "https://cdn.voyantcloud.com/insurance/standard.jpg",
            "alt": null
          }
        ]
      }
    }
  }
  ```

  ```json 200 No extensions theme={null}
  {
    "extensions": [],
    "details": {}
  }
  ```

  ```json 500 Server Error theme={null}
  {
    "error": "Failed to fetch extensions"
  }
  ```
</ResponseExample>

## Extension types

### Required extensions

Extensions marked as `required: true` must be included in bookings. These are typically:

* Travel insurance
* Visa assistance
* Mandatory transfers

### Optional extensions

Extensions marked as `required: false` are optional add-ons:

* Meal upgrades
* Excursions
* Room upgrades
* Equipment rental

### Extensions with options

When `hasOptions: true`, the extension references a product with multiple pricing options (e.g., different insurance tiers, room types).

## Use cases

### Display add-ons in booking flow

Show available extensions with pricing:

```javascript theme={null}
async function loadProductExtensions(productId) {
  const response = await fetch(`https://api.voyantcloud.com/v1/products/${productId}/extensions`, {
    headers: { Authorization: `Bearer ${process.env.VOYANT_API_KEY}` },
  })

  const { extensions, details } = await response.json()

  return extensions.map((ext) => ({
    id: ext.id,
    name: ext.name,
    required: ext.required,
    price: ext.pricePerPerson
      ? `${ext.currency} ${ext.pricePerPerson.toFixed(2)}/person`
      : "Price varies",
    description: details[ext.refProductId]?.description || "",
    image: ext.thumb,
    hasOptions: ext.hasOptions,
  }))
}
```

### Calculate total with addons

Include selected extensions in price calculation:

```javascript theme={null}
async function calculateWithAddons(productId, departureId, pax, selectedExtensions) {
  const response = await fetch(
    `https://api.voyantcloud.com/v1/departures/${departureId}/price`,
    {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.VOYANT_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      productId,
      pax,
      addons: selectedExtensions.map((ext) => ({
        id: ext.id,
        quantity: ext.quantity,
      })),
    }),
    },
  )

  return response.json()
}
```

### Validate required extensions

Ensure required extensions are selected:

```javascript theme={null}
function validateExtensions(extensions, selectedIds) {
  const required = extensions.filter((ext) => ext.required)
  const missing = required.filter((ext) => !selectedIds.includes(ext.id))

  if (missing.length > 0) {
    throw new Error(`Missing required extensions: ${missing.map((e) => e.name).join(", ")}`)
  }

  return true
}
```

<Tip>Pre-select required extensions in your booking UI to improve user experience.</Tip>

## Related endpoints

<CardGroup cols={2}>
  <Card title="Calculate price" icon="calculator" href="/api-reference/pricing/calculate-price">
    Calculate total price including extensions
  </Card>

  <Card title="Get product" icon="box" href="/api-reference/products/get-product">
    Get base product details
  </Card>

  <Card title="Pricing options" icon="tag" href="/api-reference/products/pricing-options">
    Get room options if extension has choices
  </Card>
</CardGroup>
