Stop a price list applying to one customer group
Authorizations
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
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.
Body
Takes no parameters, and an empty body is expected. The pair being removed is entirely in the path, because price_list_customer_group has no id of its own and its primary key is (price_list_id, customer_group_id). The body is still READ AND VALIDATED so a client that assumes otherwise is told: customer_group_id is refused BY NAME because it is in the path, and customer_group_ids because an array here would read as a REPLACEMENT SET whose empty value means "applies to every customer", so a request meaning "these are the only groups left" would remove one group and report success. group_id, price_list_id, id and created_at are refused for the usual reasons. REMOVING THE LAST TARGET PUBLISHES THE LIST TO EVERY SHOPPER. This is the trap, and it is the opposite of what the word untarget suggests: a price list with NO targeting rows applies store-wide, so taking away the final group does not retire a wholesale tier, it hands its negotiated prices to the whole store. Nothing fails, nothing is emitted, and the first evidence is an order at a trade price from a shopper who never negotiated one. The reversible way to stop a list applying is PATCH /api/v1/price-lists/{priceListId} with status disabled, which removes it from resolution entirely and destroys nothing. NO MONEY MOVES HERE, but what shoppers are CHARGED changes at the next resolution: the applicable list price is worked out when a cart is priced rather than stamped onto it, so carts already open re-price at their next read. This family registers no webhook topics, so a client mirroring targeting must poll. IT TAKES write_pricing:delete ON TOP OF write_pricing, unlike the POST beside it, for exactly that reason: a nightly sync that only adds targets needs the parent grant alone. 404 WHEN THE PAIR IS NOT THERE, which is where this route diverges from the price DELETE. That one answers 204 either way so a lost response cannot make a success look like a failure; here the ledger already covers that, because replaying the Idempotency-Key returns the stored 204 without re-executing, and only a FRESH key reaches the statement a second time. What 204-always would cost is real: targeting is subtractive and unreadable from the answer alone, so a mistyped customer_group_id answered 204 would leave that tier still receiving these prices while the caller recorded a successful removal. A nonexistent price list is a 404 as well. THE GROUP ITSELF IS NOT CHECKED, deliberately: a targeting row whose customer group has since been deleted would otherwise be unremovable through this API forever.
Response
Success. The response has no body.
