POST
Create a stock location

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.

Body

application/json

Creates the warehouse a picker is told to go to. PUT /api/v1/inventory-levels has required a location_id since it shipped and nothing on this API could mint one, so a 3PL onboarding a new site had to stop, open the dashboard, type the address by hand and copy the id back into its own config. NO STOCK MOVES AND NO MONEY MOVES. A fresh location holds nothing at all: every quantity is a level, written afterwards with PUT /api/v1/inventory-levels, and until then this row is a name and an address. THERE IS NO DELETE ON THIS FAMILY, and it is refused rather than forgotten. inventory_level.location_id is ON DELETE CASCADE, so removing a location would silently destroy every stock record held there, with no soft delete and no restore; the existing guard is a COUNT, so a location whose levels were just zeroed would pass it. The reversible operation is published instead: PATCH with is_active false retires a warehouse, keeps every level resolvable, and is undone with one more patch. apiscope publishes no delete qualifier for this family to gate a DELETE with either. IT DOES NOT CONVERGE PAST THE IDEMPOTENCY WINDOW. stock_location has no unique index beyond its primary key, and that is correct rather than an oversight: two warehouses may legitimately share a name and even an address, since a mezzanine and a floor in one building are two locations to a picker. So a retry with a FRESH key creates a second location. That failure is recoverable in the safe direction, because a fresh location holds no levels and no stock: list, spot the duplicate, and have the merchant remove it from the dashboard with nothing having moved. Nothing is emitted: this family registers no webhook topics. THE ADDRESS IS FLAT, and a nested "address" object is refused BY NAME because the internal payload the dashboard consumes nests it and a client copying that shape would have every address key ignored and get a 201 for it. state, region and country are refused too: other commerce APIs spell them that way, and a warehouse address typed against the wrong vocabulary fails at the carrier rather than here.

name
string
required

Required, and it has no defensible default. Blank or whitespace is a 400 naming the key.

is_active
boolean

Optional, defaulting to true. false creates a location that exists and is not somewhere new stock is placed, which is the same retired state PATCH produces. It may not be null.

address_line_1
string

Optional. Every address field may be sent as null, which means this warehouse has no recorded value for it, and an empty string is accepted as the SAME request rather than refused, because a form with a cleared input sends "". Both store SQL NULL, so the next read answers null rather than "".

address_line_2
string
city
string
province
string

The sub-national division. This resource spells it province; state and region are refused by name.

postal_code
string
country_code
string

The ISO 3166-1 alpha-2 code. NOT case-normalised on the way in, unlike currency codes elsewhere on this API: the read publishes it exactly as stored, so send it in the case you want to read back and be consistent across your own writes.

phone
string

Response

Created

data
object