Back to Arky

Store Locations

The store's places, where pickups happen and deliveries leave from, and country reference data

A store location is one of the store’s places: a shop, a warehouse or a studio. Deliveries leave from its address, pickups happen there, and its timezone sets its business days. Every store starts with one default location.

import type { EpochMilliseconds } from "arky-sdk";

interface StoreLocation {
	id: string;
	store_id: string;
	key: string;
	address: PostalAddress;
	timezone: string;
	blocks: Block[];
	status: { type: "active" } | { type: "archived" } | { type: "deleting" };
	created_at: EpochMilliseconds;
	updated_at: EpochMilliseconds;
}

blocks hold its name, opening hours and pickup instructions per language.

Store Locations

POST /v1/stores/{store_id}/locations
SDK: sdk.store.location.create()
GET /v1/stores/{store_id}/locations
SDK: sdk.store.location.find()
GET /v1/stores/{store_id}/locations/{id}
SDK: sdk.store.location.get()
GET /v1/stores/{store_id}/locations/by-key/{key}
SDK: sdk.store.location.getByKey()
PUT /v1/stores/{store_id}/locations/{id}
SDK: sdk.store.location.update()
DELETE /v1/stores/{store_id}/locations/{id}?expected_updated_at=
SDK: sdk.store.location.delete()
const shop = await sdk.store.location.create({
	store_id: storeId,
	id: crypto.randomUUID(),
	key: "berlin-center",
	address: { street1: "Hauptstraße 1", city: "Berlin", postal_code: "10115", country: "DE" },
	timezone: "Europe/Berlin",
	blocks: [
		{ id: crypto.randomUUID(), key: "name", type: "localized_text", value: { en: "Berlin Center", de: "Berlin Mitte" } },
	],
});

Parameters

Name Type Description
id required string The location id the app picks (UUID v4)
key required string The location's name, unique in the store (409 STORE_LOCATION.KEY_TAKEN otherwise)
address optional PostalAddressInput Where deliveries leave from and pickups happen
timezone required string An IANA timezone, such as Europe/Berlin
blocks optional Block[] Name, hours and pickup instructions per language
  • Pickup points. A location offers pickup exactly when an active pickup shipping method names it. To stop pickup there, archive the method.
  • Archiving closes a location and keeps its history (stock movements, past shipments). It is refused while a role’s reach, a company location’s fulfillment, the routing record or an open job points at it.
  • Deleting marks the location deleting and cleans up after it; it is refused while anything points at it or any stock level there still counts on-hand, reserved or unavailable units.
  • Updates and deletes carry the expected_updated_at they read.

Storefront

const locations = await arky.store.location.find({ limit: 50 });
const pickupPoints = locations.items.filter((location) => location.pickup_point);

for (const location of pickupPoints) {
	console.log(location.key, location.address.city);
}

The storefront copy is { id, key, address, pickup_point }. The list takes key, sort_field (created_at or updated_at), sort_direction, limit and cursor; filter pickup points by their pickup_point flag.

Platform Reference Data

Read-only lists of countries and their states/provinces. Useful for populating address forms.

Get Countries

GET /v1/platform/countries
SDK: sdk.store.country.find()

Retrieve the paginated list of countries (each entry includes its states).

const result = await sdk.store.country.find();

result.items.forEach(country => {
console.log(country.code, country.name, country.states.length);
// US, United States, 50
// CA, Canada, 13
// ...
});

Response

type Countries = PaginatedResponse<LocationCountry>;

interface LocationCountry {
	code: string; // ISO 3166-1 alpha-2 code
	name: string; // Full country name
	states: LocationState[]; // States/provinces
}

interface LocationState {
	code: string; // State/province code
	name: string; // Full name
}

Get Country

GET /v1/platform/countries/{code}
SDK: sdk.store.country.get()

Get a single country along with its states/provinces.

const country = await sdk.store.country.get({ code: "US" });

country.states.forEach((state) => {
	console.log(state.code, state.name);
	// AL, Alabama
	// AK, Alaska
	// AZ, Arizona
	// ...
});

Parameters

Name Type Description
code required string ISO 3166-1 alpha-2 country code

Use Cases

Address Form

import { useState, useEffect } from 'react';
import type { LocationCountry, LocationState } from 'arky-sdk';

function AddressForm() {
  const [countries, setCountries] = useState<LocationCountry[]>([]);
  const [states, setStates] = useState<LocationState[]>([]);
  const [selectedCountry, setSelectedCountry] = useState('');

  // Load countries on mount
  useEffect(() => {
    sdk.store.country.find().then(res => setCountries(res.items));
  }, []);

  // Load states when country changes
  useEffect(() => {
    if (!selectedCountry) {
      setStates([]);
      return;
    }
    sdk.store.country.get({ code: selectedCountry }).then(country => {
      setStates(country.states);
    });
  }, [selectedCountry]);

  return (
    <form>
      <select
        value={selectedCountry}
        onChange={(e) => setSelectedCountry(e.target.value)}
      >
        <option value="">Select Country</option>
        {countries.map(country => (
          <option key={country.code} value={country.code}>
            {country.name}
          </option>
        ))}
      </select>

      <select disabled={!selectedCountry}>
        <option value="">Select State</option>
        {states.map(state => (
          <option key={state.code} value={state.code}>
            {state.name}
          </option>
        ))}
      </select>
    </form>
  );
}

Caching

Since country/state data rarely changes, cache it on the client:

import type { LocationCountry } from "arky-sdk";

let countriesCache: LocationCountry[] | null = null;
const countryCache = new Map<string, LocationCountry>();

async function getCountries(): Promise<LocationCountry[]> {
	if (countriesCache) return countriesCache;
	const res = await sdk.store.country.find();
	countriesCache = res.items;
	return countriesCache;
}

async function getCountry(countryCode: string): Promise<LocationCountry> {
	if (countryCache.has(countryCode)) {
		return countryCache.get(countryCode)!;
	}
	const country = await sdk.store.country.get({ code: countryCode });
	countryCache.set(countryCode, country);
	return country;
}
Tip

Zones, not country reference data, control tax and shipping. Each zone belongs to one market, and tax rules and shipping rates name zones; the quote matches the customer’s address against the market’s zones. See Markets and Channels.