Each level sets onHand (available + allocated + on hold) at a location (a warehouseId addresses its floor location) for one lot (lotNumber or lotId; neither: the stock without a lot). The whole request is ONE stock move in one transaction. A level equal to the stock on hand is unchanged (no ledger row, no message); a product excluded from inventory tracking, or a tenant with tracking off, answers every level skipped. At most 100 levels. 404 unknown product; 422 unresolved_reference (a location, warehouse or lot that does not resolve, a queue location, a group container, a lot the product's lot policy refuses) or inventory_unavailable (onHand below what allocated or on hold keeps); 409 state_conflict, retryable, when another write moved the stock meanwhile.
Scope: products:write, channel:write or admin.
Idempotency: send Idempotency-Key; a retry with the same key replays the first answer.
Rate limit: write class, 1,200 requests per minute per tenant by default (read class 6,000). Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset; a refusal is 429 rate_limited with Retry-After.
