Hydra is now in beta|Get started free|Follow our journey on X.com

Locations API
On this page

Locations

A location is a physical or logical place where a merchant holds inventory — a retail store, warehouse, popup, or the default online location. Every project gets a default location on creation. Inventory is tracked per variant per location via inventory_levels, with a denormalized total on the variant for backward compatibility.

The locations extension must be enabled to create or delete locations. The default location is always accessible regardless of extension state.

Base URL: https://api.hydrajs.dev

Endpoints

Method Path Auth Description
GET /v1/locations Secret List locations
POST /v1/locations Secret Create a location
GET /v1/locations/{id} Secret Get a location
PATCH /v1/locations/{id} Secret Update a location
DELETE /v1/locations/{id} Secret Soft-delete a location
POST /v1/locations/{id}/default Secret Set as default location
POST /v1/locations/{id}/transfer Secret Transfer all inventory to another location

List locations

GET /v1/locations

Returns a paginated list of locations. Soft-deleted locations are excluded.

Query parameters

Parameter Type Default Description
limit integer 25 Results per page (1-100)
cursor string - Pagination cursor from a previous response
sort string created_at Sort field: created_at, updated_at, name
order string desc Sort direction: asc, desc
type string - Filter by type: retail, warehouse, popup, online
active string - Filter by active status: true, false

Request

curl "https://api.hydrajs.dev/v1/locations" \
  -H "Authorization: Bearer sk_live_YOUR_KEY"

Response 200

{
	"data": [
		{
			"id": "loc_abc123def456ghi789",
			"name": "Default",
			"handle": "default",
			"type": "online",
			"is_default": true,
			"is_active": true,
			"metadata": null,
			"address_line1": null,
			"address_line2": null,
			"city": null,
			"state": null,
			"postal_code": null,
			"country": null,
			"phone": null,
			"email": null,
			"created_at": "2026-08-28T12:00:00.000Z",
			"updated_at": "2026-08-28T12:00:00.000Z"
		}
	],
	"pagination": {
		"cursor": null,
		"has_more": false,
		"total": 1
	}
}

Pagination

All list endpoints use cursor-based pagination. Pass the cursor value from the response to fetch the next page.


Create a location

POST /v1/locations

Creates a new location. Requires the locations extension to be enabled. New locations are always created with is_default: false.

Request body

Field Type Required Description
name string Yes Location name (1-255 chars)
handle string No URL-friendly slug. Auto-generated from name if omitted
type string No Location type: retail, warehouse, popup, online (default retail)
address_line1 string No Street address (max 500 chars)
address_line2 string No Apartment, suite, etc. (max 500 chars)
city string No City (max 100 chars)
state string No State or province (max 100 chars)
postal_code string No Postal or ZIP code (max 20 chars)
country string No ISO 3166-1 alpha-2 country code (2 letters)
phone string No Contact phone (max 30 chars)
email string No Contact email (max 255 chars)
metadata object No Arbitrary key-value data

Request

curl -X POST https://api.hydrajs.dev/v1/locations \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Birmingham Store",
    "type": "retail",
    "address_line1": "123 High St",
    "city": "Birmingham",
    "state": "West Midlands",
    "postal_code": "B1 1AA",
    "country": "GB",
    "phone": "+441234567890",
    "metadata": { "store_hours": "Mon-Sat 9-5" }
  }'

Response 201

{
	"data": {
		"id": "loc_xyz789abc456def123",
		"name": "Birmingham Store",
		"handle": "birmingham-store",
		"type": "retail",
		"is_default": false,
		"is_active": true,
		"metadata": { "store_hours": "Mon-Sat 9-5" },
		"address_line1": "123 High St",
		"address_line2": null,
		"city": "Birmingham",
		"state": "West Midlands",
		"postal_code": "B1 1AA",
		"country": "GB",
		"phone": "+441234567890",
		"email": null,
		"created_at": "2026-08-28T12:00:00.000Z",
		"updated_at": "2026-08-28T12:00:00.000Z"
	}
}

Extension required

Creating a location requires the locations extension to be enabled. Returns 400 if the extension is not active.


Get a location

GET /v1/locations/{id}

Returns a single location by ID.

Request

curl "https://api.hydrajs.dev/v1/locations/loc_xyz789abc456def123" \
  -H "Authorization: Bearer sk_live_YOUR_KEY"

Response 200

{
	"data": {
		"id": "loc_xyz789abc456def123",
		"name": "Birmingham Store",
		"handle": "birmingham-store",
		"type": "retail",
		"is_default": false,
		"is_active": true,
		"metadata": { "store_hours": "Mon-Sat 9-5" },
		"address_line1": "123 High St",
		"address_line2": null,
		"city": "Birmingham",
		"state": "West Midlands",
		"postal_code": "B1 1AA",
		"country": "GB",
		"phone": "+441234567890",
		"email": null,
		"variant_count": 12,
		"total_units": 350,
		"created_at": "2026-08-28T12:00:00.000Z",
		"updated_at": "2026-08-28T12:00:00.000Z"
	}
}

Update a location

PATCH /v1/locations/{id}

Partially updates a location. All fields are optional — send only what changed. Cannot change is_default via PATCH; use the set default endpoint instead.

Request body

Field Type Description
name string Location name (1-255 chars)
handle string URL-friendly slug
type string retail, warehouse, popup, online
is_active boolean Active state. Cannot deactivate the default location
address_line1 string | null Street address
address_line2 string | null Apartment, suite, etc.
city string | null City
state string | null State or province
postal_code string | null Postal or ZIP code
country string | null ISO 3166-1 alpha-2 code
phone string | null Contact phone
email string | null Contact email
metadata object | null Arbitrary key-value data. Set to null to clear

Request

curl -X PATCH https://api.hydrajs.dev/v1/locations/loc_xyz789abc456def123 \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Birmingham Flagship",
    "phone": "+449876543210"
  }'

Response 200

Returns the full updated location object (same shape as Get a location).


Delete a location

DELETE /v1/locations/{id}

Soft-deletes a location. Requires the locations extension to be enabled. The handle is mutated to free the unique constraint.

Three conditions must be met:

  1. The location is not the default location
  2. The location is not the last active location
  3. The location has no remaining inventory — transfer inventory first via the transfer endpoint

Request

curl -X DELETE https://api.hydrajs.dev/v1/locations/loc_xyz789abc456def123 \
  -H "Authorization: Bearer sk_live_YOUR_KEY"

Response 204

Empty body.

Transfer inventory first

If the location still has inventory levels, the request returns 400 with a message directing you to transfer inventory first. Use POST /v1/locations/{id}/transfer before deleting.


Set as default location

POST /v1/locations/{id}/default

Sets the specified location as the default. The previous default is unset in the same transaction. Requires the locations extension to be enabled.

The target location must be active. If it is already the default, the response returns the location unchanged.

Request

curl -X POST https://api.hydrajs.dev/v1/locations/loc_xyz789abc456def123/default \
  -H "Authorization: Bearer sk_live_YOUR_KEY"

Response 200

{
	"data": {
		"id": "loc_xyz789abc456def123",
		"name": "Birmingham Store",
		"handle": "birmingham-store",
		"type": "retail",
		"is_default": true,
		"is_active": true,
		"metadata": { "store_hours": "Mon-Sat 9-5" },
		"address_line1": "123 High St",
		"address_line2": null,
		"city": "Birmingham",
		"state": "West Midlands",
		"postal_code": "B1 1AA",
		"country": "GB",
		"phone": "+441234567890",
		"email": null,
		"variant_count": 12,
		"total_units": 350,
		"created_at": "2026-08-28T12:00:00.000Z",
		"updated_at": "2026-08-28T14:30:00.000Z"
	}
}

Transfer inventory

POST /v1/locations/{id}/transfer

Transfers all inventory from this location to a destination location. Used before decommissioning a location. Requires the locations extension to be enabled.

The transfer runs in a single transaction: upserts destination inventory levels, deletes source levels, creates adjustment records, and syncs variant totals.

Request body

Field Type Required Description
destination_location_id string Yes ID of the location to receive the inventory

Request

curl -X POST https://api.hydrajs.dev/v1/locations/loc_xyz789abc456def123/transfer \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "destination_location_id": "loc_abc123def456ghi789"
  }'

Response 200

{
	"data": {
		"transferred": 5
	}
}

The transferred field indicates the number of inventory level rows moved.

Existing destination inventory

If the destination already has inventory for a variant, the transferred quantity is added to the existing amount. No inventory is lost.


Webhooks

Location changes fire the following webhook events:

Event Trigger
location.created A location is created
location.updated A location is updated or set as default
location.deleted A location is soft-deleted

See Webhooks for subscription setup.


The location object

Field Type Description
id string Unique ID (prefix: loc_)
name string Human-readable name
handle string URL-friendly slug, unique per project
type string retail, warehouse, popup, or online
is_default boolean Whether this is the default location
is_active boolean Whether the location can receive/deduct stock
metadata object | null Arbitrary merchant data
address_line1 string | null Street address
address_line2 string | null Apartment, suite, etc.
city string | null City
state string | null State or province
postal_code string | null Postal or ZIP code
country string | null ISO 3166-1 alpha-2 country code
phone string | null Contact phone
email string | null Contact email
variant_count integer Number of variants with inventory at this location
total_units integer Total inventory units across all variants at this location
created_at string ISO 8601 timestamp
updated_at string ISO 8601 timestamp