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 and the new amount toPUT /v1/account/brands/{brand_id}/pricing.
The brand rides the path and must be one of your own; the body names the offering and the amount —
nothing else. The body’s sku field takes the offering’s offering_ref (for example
SEMA-1M), never its sku_id. The response names the same offering by its sku_id.
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:
404. A sku_id sent in the sku field is an unknown offering too.
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.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: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:
A refused write stores nothing and does not consume your
Idempotency-Key — the same key
retries the corrected request cleanly.
The platform price binding
The floor is worked out from your account’s platform price binding for the offering: the record that says what the platform charges your account per transaction of that offering. There is one binding per offering your account sells. The platform creates it on your account. You never write it, and a price write never creates one. Until an offering’s binding exists, the floor for that offering cannot be worked out, and a price write for it is refused withPRICE_FLOOR_UNRESOLVABLE and the reason
no_platform_price_binding.
The reason member
A PRICE_FLOOR_UNRESOLVABLE refusal carries a machine-readable reason member next to code.
Branch on reason, never on the detail sentence, which is written for people and may change.
Each of these is fixed on the platform’s side, not by changing your request. Contact your
onboarding contact with the offering and the
reason. Other reason values can come back,
for example while the platform’s own price for the offering is being set or re-linked. Treat any
value not in this table like any other PRICE_FLOOR_UNRESOLVABLE.
Retry a 503
A price write that answers 503 with the code FLOOR_RESOLVER_UNAVAILABLE is not a refusal
of the price. It means the floor could not be
checked just now. For example, the offering was enabled moments ago and its binding is still
being written, or a read the check depends on did not answer in time. Nothing is stored and your
Idempotency-Key is not used up. Retry the same request after a short wait.
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
409with codeIDEMPOTENCY_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 opaque404 — 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. Your storefront hands the patient to the hosted checkout (see the checkout door), and the hosted checkout’s own server starts the checkout session. At that moment the server looks up your stored price for that offering and returns it in the session. Your server does not start the session itself. Every session create must prove that it holds the enrollment it names, by sending that enrollment’s capability on thex-held-capability header.
Only the hosted checkout sends it: on a questions-first offering your storefront hands it the
one-time checkout pass with a form POST (see handing the patient to checkout). A create from your server with a bare
journey_id is refused with 404 and the generic about:blank Not Found body, the same answer as
for an enrollment that does not exist. Nothing is created and your Idempotency-Key is not used up.
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, which is refused: promo
codes are not supported yet; the field is reserved. See
The checkout door for the full checkout flow and
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.