Inventory
GET /api/v1/inventory/levels requires inventory.read and a granted location_id. Levels have no synthetic ID; use (product_id, location_id). Up to 100 repeated product_id filters are accepted.
GET /api/v1/inventory/movements and its member route return immutable imv_ movements. POST /api/v1/inventory/adjustments requires inventory.write, a granted location, and an idempotency key. It accepts an absolute decimal-string target_quantity and an allowlisted reason: stock_count, external_adjustment, or correction. A no-op target is rejected.