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

# Offerings

> Bundles of products shown to users in paywalls

An **Offering** is a named bundle of products that the SDK consumes to render a paywall or purchase flow. Offerings decouple the products a user sees from the product IDs in your app code — you can change the contents of an offering without shipping a new build. At most one offering per project is marked as the **main** offering and is returned to the SDK by default when no specific id is requested.

## Key concepts

* **Offering ID** — Stable identifier you choose (e.g., `onboarding`, `winback`). Used in the API path and in related resources (experiments, scheduled reports). Up to 64 characters; allowed characters are `a–z`, `A–Z`, `0–9`, `.`, `_`, `-`, `:`, and spaces. Immutable after create.
* **Products** — An ordered list of product UIDs attached to the offering. The SDK renders them in the order you define. Each UID must already exist in the same project; product UIDs are up to 255 characters from the same charset as offering ids.
* **Main offering** — At most one offering per project carries `tag: 1`. Use `POST /v4/offerings/{offering_id}/set-main` to switch the main — it atomically clears the previous main inside a single transaction. `tag=1` on `POST /offerings` and `PATCH /offerings/{id}` is rejected with `400` (typed code `cannot_set_main_directly`). To make an offering the project's main, create or patch it without `tag` and call `POST /offerings/{id}/set-main` afterward — that endpoint flips the main flag atomically. Patching the project's current main offering with `tag: 0` (or any non-main value) is rejected with `422` (typed code `cannot_demote_main`). To switch which offering is main, call `POST /offerings/{new-main-id}/set-main` — the previous main is cleared in the same transaction.
* **Tag** — On read: `1` = main offering, `0` = regular offering previously demoted from main, `null` = regular offering that has never been tagged. Treat `0` and `null` as equivalent on read. Tag is read-only via `PATCH`; use `set-main` to promote and `set-main` on a different offering to implicitly demote the current main. On `POST` only `0` and `null` are accepted (and `tag=1` is rejected as above). The very first offering created in an empty project is auto-promoted to `tag: 1` even if `tag` is omitted in the request.
* **Experiment-variant offerings** — Offerings owned by the experiments service are excluded from `GET /offerings` and return `404` from `GET /offerings/{offering_id}`. `PATCH`, `DELETE`, and `set-main` on an experiment-variant id return `422` with typed codes `cannot_patch_experiment_variant`, `cannot_delete_experiment_variant`, and `cannot_setmain_experiment_variant` respectively.

## Offering object

| Field         | Type            | Description                                |
| ------------- | --------------- | ------------------------------------------ |
| `object`      | string          | Always `offering`.                         |
| `id`          | string          | Offering identifier.                       |
| `url`         | string          | Canonical API path (`/v4/offerings/{id}`). |
| `tag`         | integer \| null | `1` = main, `0` or `null` = regular.       |
| `product_ids` | string\[]       | Product UIDs in display order.             |
| `created_at`  | string          | ISO 8601 UTC creation timestamp.           |
| `updated_at`  | string          | ISO 8601 UTC last-update timestamp.        |

## Available endpoints

| Method | Endpoint                            | Description                                       | Status |
| ------ | ----------------------------------- | ------------------------------------------------- | ------ |
| GET    | `/offerings`                        | List offerings                                    | New    |
| POST   | `/offerings`                        | Create an offering                                | New    |
| GET    | `/offerings/{offering_id}`          | Get an offering                                   | New    |
| PATCH  | `/offerings/{offering_id}`          | Update an offering (partial)                      | New    |
| DELETE | `/offerings/{offering_id}`          | Delete an offering                                | New    |
| POST   | `/offerings/{offering_id}/set-main` | Mark this offering as the project's main offering | New    |

`PATCH` replaces `product_ids` with the supplied list — pass `[]` to detach all products, or omit the field to leave the list unchanged.

## Authentication

All v4 endpoints require a **Secret Key**. See [Authentication](/reference/v4/authentication).

## Errors

The Offerings API follows the standard error envelope and codes documented on [Handling Errors](/reference/v4/handling-api-errors).

| Code                                | HTTP | Description                                                                                                                                                                  |
| ----------------------------------- | ---- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `not_found`                         | 404  | Offering does not exist in this project, or the id belongs to an experiment-variant offering filtered out by `GET`.                                                          |
| `offering_already_exists`           | 409  | `POST /offerings` was called with an `id` that already exists in this project.                                                                                               |
| `product_not_in_project`            | 400  | `product_ids` references a product UID that is not registered in the project.                                                                                                |
| `cannot_set_main_directly`          | 400  | `tag: 1` was sent on `POST /offerings` or `PATCH /offerings/{id}`. Call `POST /offerings/{id}/set-main` instead.                                                             |
| `cannot_demote_main`                | 422  | `PATCH /offerings/{id}` tried to set `tag: 0` (or any non-main value) on the project's current main offering. Promote a different offering via `set-main` to switch instead. |
| `cannot_patch_experiment_variant`   | 422  | `PATCH /offerings/{id}` was called against an offering owned by the experiments service.                                                                                     |
| `cannot_delete_experiment_variant`  | 422  | `DELETE /offerings/{id}` was called against an offering owned by the experiments service.                                                                                    |
| `cannot_setmain_experiment_variant` | 422  | `POST /offerings/{id}/set-main` was called against an offering owned by the experiments service.                                                                             |
