PUT
Set a variant's price in one currency

Authorizations

Authorization
string
header
required

A secret API key. Publishable keys cannot reach this API. A key may carry an expiry, and an expired key is refused exactly like an unknown one, with a 401 that names no reason; check the key's expires_at in the dashboard rather than inferring it from a response. When a merchant rolls a key's secret they choose a grace window of up to 3 days, and for its duration BOTH the new secret and the one it replaced authenticate, so an integration moves over on its own deploy schedule instead of at the instant the button is pressed. Move before the window closes: after it, the old secret is refused. Nothing else about this contract moves with a roll. The key keeps its id and its scopes, so the only thing an integration updates is the credential itself.

Headers

Idempotency-Key
string
required

A unique key per logical write. Replaying a request with the same key returns the first response byte for byte instead of applying the write twice.

Path Parameters

variantId
string
required
currencyCode
string
required

Body

application/json

WHAT A SHOPPER IS CHARGED. An explicit price on a variant WINS over the variant's base price outright, with no active flag, no schedule and no review step, so this is the write that changes the number at checkout. A MEMBER PUT, with the currency as the second half of the address. The service lowercases it, so /prices/USD and /prices/usd name the same row, and the set and the DELETE beside it address a price identically. It answers 200 rather than 201 because the upsert cannot say whether the row was created or replaced, and there is no Location the request did not already name. Replaying the same body converges. OMITTING compare_at_amount_minor CLEARS IT, which is what PUT means and is worth stating because PATCH-shaped clients get it wrong: a body naming only amount_minor removes an existing struck-through price. There is deliberately no way to say "leave the compare-at alone" on this address. THE TRAP, and it is the opposite of the price-list one: a price list applies only when it is strictly BELOW the base price, while a variant price applies whether it is higher or lower. This is the surface where a price INCREASE actually takes effect. Resolution happens at READ time and is never snapshotted, so carts already open re-price. Nothing is emitted: this family publishes no webhook topics, so a client mirroring prices must poll. A currency this store does not sell in is a 409 rather than a 400: the code is well formed and the store's own configuration is what refuses it, so enabling the currency is the merchant action that makes the same request succeed. A path segment that is not three letters is a 400 naming the address, so an integrator is not sent hunting for a variant that is fine. id, variant_id, created_at and updated_at are refused BY NAME rather than ignored: the server mints the id and owns the timestamps, and variant_id is in the path. currency_code and currency are refused for the same reason, they are in the path. amount, price and price_minor are refused because the admin route takes {"amount": 1299} and a client copying that body would leave amount_minor at its zero value, which prices the variant free. compare_at_amount, compare_at and on_sale are refused by name, on_sale because it is derived from the two amounts and is not stored. min_quantity and max_quantity are refused too: quantity breaks are POST /api/v1/variants/{variantId}/price-tiers.

amount_minor
integer
required

Required, in INTEGER MINOR UNITS of the currency in the path (1299 is 12.99 in a two-decimal currency). It may not be null and it is not defaulted: an explicit price of ZERO still beats the base price, so a missing amount read as 0 would make the variant free in this currency and a zero-total order takes the free checkout path with no provider call.

Required range: 0 <= x <= 1000000000000
compare_at_amount_minor
integer | null

The struck-through "was" price, in the same currency and the same minor units. It must be STRICTLY GREATER than amount_minor and no larger than 1000000000000, or the write is a 400 and nothing is stored. Explicit null clears it, and so does omitting it. There is no on_sale field to send or read: it would be true exactly when this one is non-null.

Response

Success

data
object