# Set a selling price

> Price a variant with a sale offer and read back the resolved price a buyer sees.

This guide sets a **selling price** for one of your product's [variants](/api/product-management/variants/) using a [sale offer](/api/product-management/sale-offers/), then reads back the **resolved price** a buyer would see.

A sale offer is the source of truth for a price: it ties a `taxExcludedPrice` to a [variant](/api/product-management/variants/), a [country](/api/product-management/countries/), a [tax](/api/product-management/taxes/) and one or more [channels](/api/product-management/channels/), over a validity period. From it Flowkiwi computes the tax-included `price` a buyer pays.

## Prerequisites

You need a **product with a variant** on an existing instance. If you don't have one yet, follow [Create your first product](/guides/create-your-first-product/) first - a new product comes with a single default variant.

As in that guide, you reuse:

- the organization `id` - shown as `<org_id>` in the commands below
- the instance `handle` (the tenant in the product service host) - shown as `<tenant>` below

Every request below goes to `https://<tenant>.product-management.flowkiwi.net` and carries the `X-Flowkiwi-Organization-Id` header.

## Before you start

You need an OAuth 2.0 **bearer token** for a partner account. Sign in below: your token is dropped into every command on this page, and you can pick your organization and instance to fill `<org_id>` and `<tenant>` too. See [Authentication](/authentication/) for more.

## Steps

A price is resolved from four things: a **country**, a **tax**, a **channel** and the **variant**. The first three are reference data you set up once; then you create the sale offer and read the price back.

<Steps>

1. **Create a country.**

   The market the price applies to. Capture the `@id` from the response.

   ```bash
   curl -X POST 'https://<tenant>.product-management.flowkiwi.net/rest/api/countries' \
     -H 'Authorization: Bearer {token}' \
     -H 'X-Flowkiwi-Organization-Id: <org_id>' \
     -H 'Content-Type: application/ld+json' \
     -d '{
       "name": "France",
       "code": "FR"
     }'
   ```

   ```json
   {
     "@id": "/rest/api/countries/019af8c2-3d4e-7f50-8a61-2b3c4d5e6f70",
     "code": "FR"
   }
   ```

2. **Create a tax for that country.**

   Pass the country `@id` you just got. The `rate` is a percentage; from it Flowkiwi turns a tax-excluded price into a tax-included one.

   ```bash
   curl -X POST 'https://<tenant>.product-management.flowkiwi.net/rest/api/taxes' \
     -H 'Authorization: Bearer {token}' \
     -H 'X-Flowkiwi-Organization-Id: <org_id>' \
     -H 'Content-Type: application/ld+json' \
     -d '{
       "name": "VAT",
       "rate": "20",
       "country": "/rest/api/countries/019af8c2-3d4e-7f50-8a61-2b3c4d5e6f70"
     }'
   ```

   ```json
   {
     "@id": "/rest/api/taxes/019af8c3-4e5f-7061-9b72-3c4d5e6f7081",
     "rate": "20.00"
   }
   ```

3. **Create a channel.**

   The sales surface the price is published to (your store, a marketplace...). Capture its `@id` too.

   ```bash
   curl -X POST 'https://<tenant>.product-management.flowkiwi.net/rest/api/channels' \
     -H 'Authorization: Bearer {token}' \
     -H 'X-Flowkiwi-Organization-Id: <org_id>' \
     -H 'Content-Type: application/ld+json' \
     -d '{
       "status": "/rest/api/statuses/ACTIVE",
       "name": "Online store"
     }'
   ```

   ```json
   {
     "@id": "/rest/api/channels/019af8c0-7a1b-7c2d-8e3f-1a2b3c4d5e6f",
     "name": "Online store"
   }
   ```

4. **Find the variant to price.**

   List your product's variants and copy the `@id` of the one you want to price.

   ```bash
   curl 'https://<tenant>.product-management.flowkiwi.net/rest/api/products/{productId}/variants' \
     -H 'Authorization: Bearer {token}' \
     -H 'X-Flowkiwi-Organization-Id: <org_id>' \
     -H 'Accept: application/ld+json'
   ```

5. **Create the sale offer.**

   Tie it all together: the `taxExcludedPrice`, the validity period, and the `country`, `tax`, `variant` and `channels` IRIs from the steps above.

   ```bash
   curl -X POST 'https://<tenant>.product-management.flowkiwi.net/rest/api/sale_offers' \
     -H 'Authorization: Bearer {token}' \
     -H 'X-Flowkiwi-Organization-Id: <org_id>' \
     -H 'Content-Type: application/ld+json' \
     -d '{
       "status": "/rest/api/statuses/ACTIVE",
       "taxExcludedPrice": {
         "amount": "9.80",
         "currency": "EUR"
       },
       "startsAt": "2026-03-01T00:00:00+00:00",
       "country": "/rest/api/countries/019af8c2-3d4e-7f50-8a61-2b3c4d5e6f70",
       "tax": "/rest/api/taxes/019af8c3-4e5f-7061-9b72-3c4d5e6f7081",
       "variant": "/rest/api/products/019af93e-420e-79f8-800b-6680f03dce20/variants/019af940-5a6b-7c8d-9e0f-1a2b3c4d5e6f",
       "channels": [
         "/rest/api/channels/019af8c0-7a1b-7c2d-8e3f-1a2b3c4d5e6f"
       ]
     }'
   ```

   The response carries both prices: the `taxExcludedPrice` you set, and the tax-included `price` computed from it and the tax rate (here `9.80` + 20% = `11.76`).

   ```json {4-7,8-11}
   {
     "@id": "/rest/api/sale_offers/019af8e0-1a2b-7c3d-8e4f-5a6b7c8d9e0f",
     "status": "/rest/api/statuses/ACTIVE",
     "taxExcludedPrice": {
       "amount": "9.80",
       "currency": "EUR"
     },
     "price": {
       "amount": "11.76",
       "currency": "EUR"
     }
   }
   ```

6. **Read the resolved price.**

   Ask the variant for its price in a given context. `resolvedPrice` is `null` unless all three of `resolveContext[country]`, `resolveContext[channel]` and `resolveContext[at]` are provided.

   ```bash
   curl -G 'https://<tenant>.product-management.flowkiwi.net/rest/api/products/{productId}/variants/{variantId}' \
     -H 'Authorization: Bearer {token}' \
     -H 'X-Flowkiwi-Organization-Id: <org_id>' \
     -H 'Accept: application/ld+json' \
     --data-urlencode 'resolveContext[country]=/rest/api/countries/019af8c2-3d4e-7f50-8a61-2b3c4d5e6f70' \
     --data-urlencode 'resolveContext[channel]=/rest/api/channels/019af8c0-7a1b-7c2d-8e3f-1a2b3c4d5e6f' \
     --data-urlencode 'resolveContext[at]=2026-03-15T00:00:00+00:00'
   ```

   The `at` instant falls inside the offer's validity period, so the variant resolves to the offer's price.

</Steps>

> **Why a context?**
>
> A variant can have several concurrent sale offers - different countries, channels or periods. The `resolveContext` is how you ask "what would a buyer in this country, on this channel, at this moment pay?" and get a single answer.

## Next steps

- **Run a promotion.** Add a `discountTaxExcludedPrice` to the offer - the resolved price then exposes `discountPrice` alongside the regular `price`.
- **Record your costs.** Mirror this on the buying side with a [Purchase offer](/api/product-management/purchase-offers/) to track margin.
- **Schedule a price change.** Close the current offer with an `endsAt` and create a new one that starts when it ends.
