# 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](/octo-core/products.md):

* `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="/files/9aBZzqykFPaTU7bfbNrr" 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](/octo-core/availability.md):

* `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](/octo-core/bookings.md#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](/octo-core/bookings.md#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](/capabilities/cart.md)).

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](/capabilities/gift-vouchers.md#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="/files/9aBZzqykFPaTU7bfbNrr" 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
      }
    ]
  }
}
```


---

# Agent Instructions: 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:

```
GET https://docs.ventrata.com/capabilities/offers.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
