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

# Pricing

> Set the price your patients pay for each offering, what the price floor means, and how the hosted checkout resolves the price you set.

Every offering your brand sells has one price: the amount your patients pay. You set it yourself,
with your own API key, per brand and per offering — and the hosted checkout resolves whatever you
last stored, live, on every session. No price ever rides a request from a storefront or a link:
what a patient is charged always comes from this one stored row.

## The one price row

A price belongs to a **(brand, offering)** pair. One brand, one offering, one price — writing it
again replaces it. Prices are **integer minor units** (USD cents): `10900` means \$109.00. Floats
are never accepted, and USD is the only supported currency today.

Each supply length is its own offering with its own reference and its own price — a three-month
plan is priced by writing to its own offering code, not by multiplying anything.

## Set a price

Send the offering code and the new amount to
[`PUT /v1/account/brands/{brand_id}/pricing`](/api-reference/offering-configuration/set-your-brands-price-for-an-offering).
The brand rides the path and must be one of your own; the body names the offering and the amount —
nothing else.

```bash theme={null}
curl --request PUT "$BASE_URL/v1/account/brands/brd_acme/pricing" \
  --header "Authorization: Bearer $TEST_API_KEY" \
  --header "X-Brand-Id: brd_acme" \
  --header "Idempotency-Key: price-glp1-3mo-2026-08-24-a" \
  --header "Content-Type: application/json" \
  --data '{
    "sku": "glp1_3mo",
    "amount_minor": 10900,
    "currency": "USD"
  }'
```

`201` when this is the first price for the pair, `200` when it replaces an existing one. Either
way the response is the stored row:

```json theme={null}
{
  "data": {
    "id": "prc_8f2k1",
    "brand_id": "brd_acme",
    "sku_id": "sku_glp1_3mo",
    "amount_minor": 10900,
    "currency": "USD",
    "min_price": null
  }
}
```

The price takes effect immediately: the **next checkout session** for your brand resolves it. The
offering must already exist — a price write never creates an offering, and an unknown offering
answers `404`.

<Note>
  The self-serve price write is being switched on across accounts now. Until it is active for yours,
  a call to this endpoint answers `404`, and your onboarding contact sets and changes your prices
  for you — writing the same one price row, protected by the same floor. Everything else on this
  page — the floor, and the checkout resolving your stored price — is how pricing works today.
</Note>

## The price floor

Your account carries a **per-transaction cost floor** for each offering: the platform's own cost
of transacting it for you. A price below that floor would lose money on every order, so the write
is refused before anything is stored:

```json theme={null}
{
  "type": "about:blank",
  "title": "Unprocessable Entity",
  "status": 422,
  "detail": "retail 500 is below the floor 850 — the per-transaction platform cost; derivation: …",
  "instance": "/account/brands/brd_acme/pricing",
  "code": "PRICE_BELOW_FLOOR"
}
```

The refusal **names the floor** (here, `850` minor units) and how it was derived, so you can
correct the amount and retry — you never have to guess. Two codes can come back with `422`:

| Code                       | It means                                                                                                                                                                      |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `PRICE_BELOW_FLOOR`        | The amount is below your account's floor for this offering. The detail names the floor; send an amount at or above it.                                                        |
| `PRICE_FLOOR_UNRESOLVABLE` | The floor itself cannot be determined for this brand-and-offering pair — most often because the offering is not priceable by your account. The detail names the typed reason. |

A refused write stores **nothing** and does **not** consume your `Idempotency-Key` — the same key
retries the corrected request cleanly.

## Idempotency

`Idempotency-Key` is required on every price write. The semantics are the standard ones:

* **Replaying** the same key with the same body returns the original result — safe to retry on a
  timeout.
* **Reusing** the key with a materially different body answers `409` with code
  `IDEMPOTENCY_CONFLICT`.
* **Only performed writes consume a key.** A `422` (or any refusal) leaves the key unspent, so a
  corrected retry may reuse it.

## Only your own brands

You can price only brands that belong to your account. An unknown brand and another account's
brand answer the same opaque `404` — there is no way to probe whether a foreign brand exists. And
every accepted price change is recorded in your brand's audit trail, attributed to your account.

## Where the platform sets prices

The same price row can also be written from the platform's operator side — typically to seed your
starting prices during onboarding. Both paths write the **one** shared row, and the audit trail
records which side made each change. From your side there is simply one current price per
offering, whichever path last wrote it.

## How checkout shows your price

The hosted checkout never takes a price from anyone. When your storefront
[starts a checkout session](/api-reference/payments/start-a-checkout-session-and-get-a-token-to-collect-payment),
the server looks up your stored price for that offering **at that moment** and returns the result
in the session:

```bash theme={null}
curl --request POST "$BASE_URL/v1/payments/checkout-sessions" \
  --header "Authorization: Bearer $TEST_API_KEY" \
  --header "X-Brand-Id: brd_acme" \
  --header "Idempotency-Key: checkout-jny-31f8-1" \
  --header "Content-Type: application/json" \
  --data '{
    "offering_ref": "glp1_3mo",
    "journey_id": "jny_31f8k2"
  }'
```

The session's `amount` is the price the patient sees and pays — resolved from the row you wrote
above. A checkout request that tries to carry its own price, fee, or routing choice is rejected
outright (`422`, code `commerce/checkout/client-authored-charge-parameter`): the body accepts only
the offering reference, the enrollment id, and an optional promo code. See
[The checkout door](/pages/integrate/the-checkout-door) for the full checkout flow and
[Identifiers and error types](/pages/integrate/identifiers-and-error-types) for the payment error
vocabulary.

So the loop is closed by construction: you write the price with your key, the floor protects it
from going below cost, and checkout resolves the stored row live — three facts, one price.
