# Promotions / Offers

To use this capability, add `octo/offers` to your `Octo-Capabilities` header.

This capability depends on `octo/pricing`.

Related capabilities:

* `octo/cart` is required for `offerCombinations` on order responses.
* `octo/gifts` is required for gift offer endpoints/fields.
* `octo/extras` is required for offer fields on `extraItems`.

All routes below are under the `/octo` prefix.

The offers capability lets you apply promotion codes to bookings/gifts, automatically apply eligible public offers, return upsell comparisons, and return discounted product combinations.

## Combination Product Discovery

This capability extends product routes documented in [Products](https://docs.ventrata.com/octo-core/products):

* `GET /products`
* `GET /products/{productId}`

When `offerCode` is a combination code (`combination_<...>`), product scope is expanded to include the combination target product, so you can fetch product/options/availability for that target using the same code.

## Offer Object

The same offer object is used in:

* `offers[]` and `offer` on availability responses
* `offer` and `offers[]` on booking/gift/extra-item responses
* `offer` in `offerCombinations[]`

{% openapi src="<https://221588849-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M7bgGIyO7QYNOfUMfxh%2Fuploads%2Fgit-blob-fa2d8cb1d7297d352c2639e6c4c6a990f2add6d7%2Fopenapi.yaml?alt=media>" path="/offers" method="get" %}
[openapi.yaml](https://221588849-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M7bgGIyO7QYNOfUMfxh%2Fuploads%2Fgit-blob-fa2d8cb1d7297d352c2639e6c4c6a990f2add6d7%2Fopenapi.yaml?alt=media)
{% endopenapi %}

## Pricing Field Added By Offers

Offers adds `offerDiscount` to pricing fragments emitted through pricing serializers, including:

* availability `pricing` / `pricingFrom`
* availability `unitPricing[]` / `unitPricingFrom[]`
* booking/gift/order `pricing`
* unit item / purchase item / extra item `pricing`

## Get Available Offers

This capability extends availability routes documented in [Availability](https://docs.ventrata.com/octo-core/availability):

* `POST /availability`
* `POST /availability/calendar`
* `POST /availability/batch`
* `POST /availability/calendar/batch`

### Response Additions Per Availability Object

If no `offerCode` is sent, new booking/gift flows auto-select the first eligible public offer.

## Discount Booking

Offer code can be sent on these booking write routes:

* `POST /bookings`
* `PATCH /bookings/{uuid}`
* `POST /bookings/{uuid}/confirm`

Base booking schema: [Bookings](https://docs.ventrata.com/octo-core/bookings#endpoints).

It can also be sent inside order payloads on:

* `bookings[].offerCode` in `POST /orders`, `PATCH /orders/:orderId`, `PATCH /orders/:orderId/preview`, `POST /orders/:orderId/confirm`

### Booking Response Additions

These fields are present on any serialized booking response, including:

* `GET /bookings/{uuid}`
* `GET /bookings`
* booking write responses
* bookings embedded in serialized order responses

## Product Comparisons

Comparisons are exposed on serialized booking objects from base booking routes (see [Bookings](https://docs.ventrata.com/octo-core/bookings#endpoints)).

`offerComparisons[]` is returned on booking objects (including booking responses embedded in order responses).

Comparisons are returned only for persisted, active, standalone, non-combination bookings; otherwise the field is `[]`.

## Product Combinations

Combinations are exposed on serialized order objects from base cart routes (see [Multi-Booking Cart](https://docs.ventrata.com/capabilities/cart)).

Combinations are returned on order objects as `offerCombinations[]` (requires `octo/cart`). They are also present on other order responses that serialize an order (`POST/PATCH /orders`, `GET /orders`, etc.).

To apply a combination, create/update a booking with the returned `offerCode` and matching `productId`/`optionId`.

## Gift Offers

Requires `octo/gifts`.

Offer code can be sent on:

* `POST /gifts`
* `PATCH /gifts/{uuid}`
* `PATCH /gifts/{uuid}/preview`
* `POST /gifts/{uuid}/confirm`
* `gifts[].offerCode` in order create/update payloads

These fields are present on:

* `GET /gifts/{uuid}`
* `GET /gifts`
* gift write responses
* gifts embedded in serialized order responses
* `offerCode`
* `offerTitle`
* `offerComparisons` (always `[]`)
* `offerIsCombination` (always `false`)
* `offer`
* `offers[]`
* `pricing.offerDiscount`

Base gift schema: [Gift Vouchers](https://docs.ventrata.com/gift-vouchers#endpoints).

## Extra Item Offers

Requires `octo/extras`.

Each serialized extra item (`booking.extraItems[]` and `booking.unitItems[].extraItems[]`) includes:

* `offerCode`
* `offerTitle`
* `offerComparisons` (always `[]`)
* `offerIsCombination` (always `false`)
* `offer`
* `offers[]`
* `pricing.offerDiscount`

{% openapi src="<https://221588849-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M7bgGIyO7QYNOfUMfxh%2Fuploads%2Fgit-blob-fa2d8cb1d7297d352c2639e6c4c6a990f2add6d7%2Fopenapi.yaml?alt=media>" path="/offers" method="get" %}
[openapi.yaml](https://221588849-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M7bgGIyO7QYNOfUMfxh%2Fuploads%2Fgit-blob-fa2d8cb1d7297d352c2639e6c4c6a990f2add6d7%2Fopenapi.yaml?alt=media)
{% endopenapi %}

Returns paginated supplier promotions serialized as the offer object above.

`GET /offers` returns supplier promotions sorted by position. Usability in this endpoint is not availability-specific; contextual usability is returned on availability/booking/gift/extra responses.

## Schema Additions (JSON)

These are additive fragments showing only fields introduced by this capability.

### `Availability`

```json
{
  "// ...rest of availability object": "...",
  "offerCode": "SUMMER25",
  "offerTitle": "Summer Promotion",
  "offerIsCombination": true,
  "offerComparisons": [
    {
      "offerCode": "SUMMER25",
      "offerTitle": "Summer Promotion",
      "pricing": {
        "original": 2300,
        "retail": 2300,
        "net": 1610,
        "currency": "EUR",
        "currencyPrecision": 2,
        "includedTaxes": [
          {
            "name": "10%",
            "shortDescription": null,
            "original": 209,
            "retail": 209,
            "net": 146
          }
        ]
      },
      "offer": {
        "id": "89fe0192-ddcd-430a-b285-e1396a4725d2",
        "title": "Summer Promotion",
        "code": "SUMMER25"
      }
    }
  ],
  "offer": {
    "id": "89fe0192-ddcd-430a-b285-e1396a4725d2",
    "title": "Summer Promotion",
    "code": "SUMMER25"
  },
  "offers": [
    {
      "id": "89fe0192-ddcd-430a-b285-e1396a4725d2",
      "title": "Summer Promotion",
      "code": "SUMMER25"
    }
  ]
}
```

### `AvailabilityBatchRequest`

```json
{
  "// ...rest of availability batch request object": "...",
  "offerCode": "SUMMER25"
}
```

### `AvailabilityBatchRow`

```json
{
  "// ...rest of availability batch row object": "...",
  "offerCode": "SUMMER25",
  "offerTitle": "Summer Promotion",
  "offerIsCombination": true,
  "offerComparisons": [
    {
      "offerCode": "SUMMER25",
      "offerTitle": "Summer Promotion",
      "pricing": {
        "original": 2300,
        "retail": 2300,
        "net": 1610,
        "currency": "EUR",
        "currencyPrecision": 2,
        "includedTaxes": [
          {
            "name": "10%",
            "shortDescription": null,
            "original": 209,
            "retail": 209,
            "net": 146
          }
        ]
      },
      "offer": {
        "id": "89fe0192-ddcd-430a-b285-e1396a4725d2",
        "title": "Summer Promotion",
        "code": "SUMMER25"
      }
    }
  ],
  "offer": {
    "id": "89fe0192-ddcd-430a-b285-e1396a4725d2",
    "title": "Summer Promotion",
    "code": "SUMMER25"
  },
  "offers": [
    {
      "id": "89fe0192-ddcd-430a-b285-e1396a4725d2",
      "title": "Summer Promotion",
      "code": "SUMMER25"
    }
  ]
}
```

### `AvailabilityCalendar`

```json
{
  "// ...rest of availability calendar object": "...",
  "offer": {
    "id": "89fe0192-ddcd-430a-b285-e1396a4725d2",
    "title": "Summer Promotion",
    "code": "SUMMER25"
  },
  "offers": [
    {
      "id": "89fe0192-ddcd-430a-b285-e1396a4725d2",
      "title": "Summer Promotion",
      "code": "SUMMER25"
    }
  ],
  "offerCode": "SUMMER25",
  "offerTitle": "Summer Promotion",
  "offerIsCombination": true,
  "offerComparisons": [
    {
      "offerCode": "SUMMER25",
      "offerTitle": "Summer Promotion",
      "pricing": {
        "original": 2300,
        "retail": 2300,
        "net": 1610,
        "currency": "EUR",
        "currencyPrecision": 2,
        "includedTaxes": [
          {
            "name": "10%",
            "shortDescription": null,
            "original": 209,
            "retail": 209,
            "net": 146
          }
        ]
      },
      "offer": {
        "id": "89fe0192-ddcd-430a-b285-e1396a4725d2",
        "title": "Summer Promotion",
        "code": "SUMMER25"
      }
    }
  ]
}
```

### `AvailabilityCalendarBatchRequest`

```json
{
  "// ...rest of availability calendar batch request object": "...",
  "offerCode": "SUMMER25"
}
```

### `AvailabilityCalendarBatchRow`

```json
{
  "// ...rest of availability calendar batch row object": "...",
  "offer": {
    "id": "89fe0192-ddcd-430a-b285-e1396a4725d2",
    "title": "Summer Promotion",
    "code": "SUMMER25"
  },
  "offers": [
    {
      "id": "89fe0192-ddcd-430a-b285-e1396a4725d2",
      "title": "Summer Promotion",
      "code": "SUMMER25"
    }
  ],
  "offerCode": "SUMMER25",
  "offerTitle": "Summer Promotion",
  "offerIsCombination": true,
  "offerComparisons": [
    {
      "offerCode": "SUMMER25",
      "offerTitle": "Summer Promotion",
      "pricing": {
        "original": 2300,
        "retail": 2300,
        "net": 1610,
        "currency": "EUR",
        "currencyPrecision": 2,
        "includedTaxes": [
          {
            "name": "10%",
            "shortDescription": null,
            "original": 209,
            "retail": 209,
            "net": 146
          }
        ]
      },
      "offer": {
        "id": "89fe0192-ddcd-430a-b285-e1396a4725d2",
        "title": "Summer Promotion",
        "code": "SUMMER25"
      }
    }
  ]
}
```

### `AvailabilityCalendarRequest`

```json
{
  "// ...rest of availability calendar request object": "...",
  "offerCode": "SUMMER25"
}
```

### `AvailabilityRequest`

```json
{
  "// ...rest of availability request object": "...",
  "offerCode": "SUMMER25"
}
```

### `Booking`

```json
{
  "// ...rest of booking object": "...",
  "offerCode": "SUMMER25",
  "offerTitle": "Summer Promotion",
  "offerIsCombination": true,
  "offerComparisons": [
    {
      "offerCode": "SUMMER25",
      "offerTitle": "Summer Promotion",
      "pricing": {
        "original": 22000,
        "retail": 20000,
        "net": 18000,
        "currency": "USD",
        "currencyPrecision": 2,
        "includedTaxes": [
          {
            "name": "Sales Tax",
            "shortDescription": "Local sales tax",
            "original": 2000,
            "retail": 1800,
            "net": 1600
          }
        ],
        "offerDiscount": {
          "retail": 2000,
          "net": 2000,
          "includedTaxes": [
            {
              "name": "Sales Tax",
              "shortDescription": "Local sales tax",
              "retail": 200,
              "net": 200
            }
          ]
        }
      },
      "offer": {
        "id": "89fe0192-ddcd-430a-b285-e1396a4725d2",
        "title": "Summer Promotion",
        "code": "SUMMER25"
      }
    }
  ],
  "offer": {
    "id": "89fe0192-ddcd-430a-b285-e1396a4725d2",
    "internalName": "Morning City Tour",
    "title": "Summer Promotion",
    "shortDescription": "Central departure with multilingual guide.",
    "code": "SUMMER25",
    "publicOfferId": "public_offer_id-example",
    "usable": true,
    "unusableReason": "Offer is not valid for the selected date.",
    "offerDiscount": {
      "retail": 2000,
      "net": 2000,
      "includedTaxes": [
        {
          "name": "Sales Tax",
          "shortDescription": "Local sales tax",
          "retail": 200,
          "net": 200
        }
      ]
    },
    "membershipBenefit": {
      "id": "94cdd032-3d32-416d-b0a4-abf8b7495b8b",
      "title": "VIP Member Discount",
      "description": "Reduced pricing for active members."
    },
    "tags": [
      "vip",
      "partner"
    ],
    "status": "CONFIRMED"
  },
  "offers": [
    {
      "id": "89fe0192-ddcd-430a-b285-e1396a4725d2",
      "internalName": "Morning City Tour",
      "title": "Summer Promotion",
      "shortDescription": "Central departure with multilingual guide.",
      "code": "SUMMER25",
      "publicOfferId": "public_offer_id-example",
      "usable": true,
      "unusableReason": "Offer is not valid for the selected date.",
      "offerDiscount": {
        "retail": 2000,
        "net": 2000,
        "includedTaxes": [
          {
            "name": "Sales Tax",
            "shortDescription": "Local sales tax",
            "retail": 200,
            "net": 200
          }
        ]
      },
      "membershipBenefit": {
        "id": "94cdd032-3d32-416d-b0a4-abf8b7495b8b",
        "title": "VIP Member Discount",
        "description": "Reduced pricing for active members."
      },
      "tags": [
        "vip",
        "partner"
      ],
      "status": "CONFIRMED"
    }
  ]
}
```

### `BookingUnitItem`

```json
{
  "// ...rest of booking unit item object": "...",
  "offerCode": "SUMMER25",
  "offerTitle": "Summer Promotion",
  "offerIsCombination": true,
  "offerComparisons": [
    {
      "offerCode": "SUMMER25",
      "offerTitle": "Summer Promotion",
      "pricing": {
        "original": 2300,
        "retail": 2300,
        "net": 1610,
        "currency": "EUR",
        "currencyPrecision": 2,
        "includedTaxes": [
          {
            "name": "10%",
            "shortDescription": null,
            "original": 209,
            "retail": 209,
            "net": 146
          }
        ]
      },
      "offer": {
        "id": "89fe0192-ddcd-430a-b285-e1396a4725d2",
        "title": "Summer Promotion",
        "code": "SUMMER25"
      }
    }
  ],
  "offer": {
    "id": "89fe0192-ddcd-430a-b285-e1396a4725d2",
    "title": "Summer Promotion",
    "code": "SUMMER25"
  },
  "offers": [
    {
      "id": "89fe0192-ddcd-430a-b285-e1396a4725d2",
      "title": "Summer Promotion",
      "code": "SUMMER25"
    }
  ]
}
```

### `BookingWriteRequest`

```json
{
  "// ...rest of booking write request object": "...",
  "offerCode": "SUMMER25"
}
```

### `ExtraPricing`

```json
{
  "// ...rest of extra pricing object": "...",
  "offerDiscount": {
    "retail": 2000,
    "net": 2000,
    "includedTaxes": [
      {
        "name": "Sales Tax",
        "shortDescription": "Local sales tax",
        "retail": 200,
        "net": 200
      }
    ]
  }
}
```

### `Gift`

```json
{
  "// ...rest of gift object": "...",
  "offerCode": "SUMMER25",
  "offerTitle": "Summer Promotion",
  "offerIsCombination": true,
  "offerComparisons": [
    {
      "offerCode": "SUMMER25",
      "offerTitle": "Summer Promotion",
      "pricing": {
        "original": 2300,
        "retail": 2300,
        "net": 1610,
        "currency": "EUR",
        "currencyPrecision": 2,
        "includedTaxes": [
          {
            "name": "10%",
            "shortDescription": null,
            "original": 209,
            "retail": 209,
            "net": 146
          }
        ]
      },
      "offer": {
        "id": "89fe0192-ddcd-430a-b285-e1396a4725d2",
        "title": "Summer Promotion",
        "code": "SUMMER25"
      }
    }
  ],
  "offer": {
    "id": "89fe0192-ddcd-430a-b285-e1396a4725d2",
    "title": "Summer Promotion",
    "code": "SUMMER25"
  },
  "offers": [
    {
      "id": "89fe0192-ddcd-430a-b285-e1396a4725d2",
      "title": "Summer Promotion",
      "code": "SUMMER25"
    }
  ]
}
```

### `GiftCreateRequest`

```json
{
  "// ...rest of gift create request object": "...",
  "offerCode": "SUMMER25"
}
```

### `Money`

```json
{
  "// ...rest of money object": "...",
  "offerDiscount": {
    "retail": 2000,
    "net": 2000,
    "includedTaxes": [
      {
        "name": "Sales Tax",
        "shortDescription": "Local sales tax",
        "retail": 200,
        "net": 200
      }
    ]
  }
}
```

### `Offer`

```json
{
  "// ...rest of offer object": "...",
  "id": "89fe0192-ddcd-430a-b285-e1396a4725d2",
  "tags": [
    "vip",
    "partner"
  ],
  "title": "Summer Promotion",
  "label": "Save 20%",
  "code": "SUMMER25",
  "description": "20% off selected departures this month.",
  "netDiscount": "FULL",
  "restrictions": {
    "minUnits": 1,
    "maxUnits": 10,
    "minTotal": 0,
    "maxTotal": 50000,
    "unitIds": [
      "3d6f0a3a-59d4-4b16-a0c5-11d2d8a4e6b7",
      "6b65fd83-4a1f-4744-8f4a-c24d8d770f29"
    ]
  },
  "usable": true,
  "unusableReason": null
}
```

### `Order`

```json
{
  "// ...rest of order object": "...",
  "offerCombinations": [
    {
      "productId": "e7cc8bb4-8d1c-4848-8824-5dbedb718681",
      "optionId": "94cdd032-3d32-416d-b0a4-abf8b7495b8b",
      "units": [
        {
          "unitId": "3d6f0a3a-59d4-4b16-a0c5-11d2d8a4e6b7",
          "quantity": 2
        }
      ],
      "offerCode": "SUMMER25"
    }
  ]
}
```

### `OrderUpdateRequest`

```json
{
  "// ...rest of order update request object": "...",
  "offers": [
    "offer-example"
  ]
}
```

### `PackageAvailability`

```json
{
  "// ...rest of package availability object": "...",
  "offerCode": "SUMMER25",
  "offerTitle": "Summer Promotion",
  "offerIsCombination": true,
  "offerComparisons": [
    {
      "offerCode": "SUMMER25",
      "offerTitle": "Summer Promotion",
      "pricing": {
        "original": 2300,
        "retail": 2300,
        "net": 1610,
        "currency": "EUR",
        "currencyPrecision": 2,
        "includedTaxes": [
          {
            "name": "10%",
            "shortDescription": null,
            "original": 209,
            "retail": 209,
            "net": 146
          }
        ]
      },
      "offer": {
        "id": "89fe0192-ddcd-430a-b285-e1396a4725d2",
        "title": "Summer Promotion",
        "code": "SUMMER25"
      }
    }
  ],
  "offer": {
    "id": "89fe0192-ddcd-430a-b285-e1396a4725d2",
    "title": "Summer Promotion",
    "code": "SUMMER25"
  },
  "offers": [
    {
      "id": "89fe0192-ddcd-430a-b285-e1396a4725d2",
      "title": "Summer Promotion",
      "code": "SUMMER25"
    }
  ]
}
```

### `Pricing`

```json
{
  "// ...rest of pricing object": "...",
  "offerDiscount": {
    "retail": 2000,
    "net": 2000,
    "includedTaxes": [
      {
        "name": "Sales Tax",
        "shortDescription": "Local sales tax",
        "retail": 200,
        "net": 200
      }
    ]
  }
}
```

### `UnitPricing`

```json
{
  "// ...rest of unit pricing object": "...",
  "offerDiscount": {
    "retail": 2000,
    "net": 2000,
    "includedTaxes": [
      {
        "name": "Sales Tax",
        "shortDescription": "Local sales tax",
        "retail": 200,
        "net": 200
      }
    ]
  }
}
```
