Skip to content

Store Locator ​

What It Is ​

A listed hub is a customer-facing store profile: the record the store locator reads. It is a different entity from a hub, which is the physical inventory location that fulfils orders.

A listed hub may point at a hub (hubId), and then carries that hub's room and POS integration into the storefront response. It does not have to. Every brand location the shopper is meant to see is a listed hub, whether or not anything ships from it.

Manage them in the admin under Profile → Locations.

What You Can Do With It ​

Put a location on the map ​

Create the profile, give it an address, and it becomes findable.

Config: Profile → Locations → Create Location. name is required; so is a complete address — address1, city, county, state, zip, country. Pick the address through the Google autocomplete field so the coordinates come back with it. addressString is assembled from the parts you enter.

A new location is hidden from "nearest" until show_on_nearest is set

show_on_nearest defaults to false, and the Create/Edit Location form never sends it — nor does CSV import, whose payload does not accept the field at all. POST /nearest and /nearest/zipcode return only listed hubs where it is true, so a location added through the admin will not appear in either until someone flips the flag through the API. See Locations API.

Load a whole chain at once ​

Import a CSV of locations, let Google resolve each address, and review the failures before anything is written.

Config: Profile → Locations → Import. Download the sample CSV from the modal. name and addressString are the only required columns. The admin always runs the file through validation first and shows you the rows that could not be resolved; the import commits only after you confirm. See CSV import below.

Take a location off the storefront ​

Config: the trash icon on the row in Profile → Locations. The delete is soft — the row is retained with a deletedAt and disappears from every read.

Pull the current list out ​

Config: Profile → Locations → Export downloads locations.csv.

Export is thinner than import

Export builds its rows from GET /listed/hubs, which returns only listedHubId, name, email, phoneNumber, phone_prefix, currency, timings, license and addressString. The exporter also looks for coords and flat address columns that this response does not contain, so the latitude, longitude and per-part address fields come out empty. An exported file is not a re-importable round trip.

Attach the location to the hub that fulfils there ​

Set hubId so the storefront gets the fulfilment picture along with the profile.

Config: hubId on POST/PUT /listed/hub. Once set, GET /stores merges that hub's vault room and its embed_pos integration into the listed hub entry.

Not settable on update

_updateListedHub branches on payload.hubId but never imports the Hub model, so sending hubId to PUT /listed/hub/:listedHubId throws a ReferenceError. Set it at creation.

Change how shoppers search the list ​

Text-only, driving distance, or straight-line distance — the brand's search configuration decides, not the location record.

Config: the brand's siteConfiguration.listedHub master data, which Batch sets. See Search configuration.

Reference ​

Listed hub fields ​

FieldTypeDefaultNotes
namestring—Required. The list sorts by it, ascending.
addressStringstring—Required. Copied from address.addressString on write. Text search matches against it.
coords{ lat, lng }{}Copied from address.latLng on write. Straight-line search skips any hub without it.
emailstring—
phoneNumber / phone_prefixstring—phone_prefix is required whenever phoneNumber is sent.
currencystringUSD
timingsobject{}Free-form. The admin form writes only timezone and preOrder.
licensestring—
imagesstring—A single string, not a list. Not exposed in the admin form.
show_on_nearestbooleanfalseThe only field that gates visibility. API-only — see the warning above.
typestring—Free-form; no enum, no branch reads it.
placeId / locationIdstring—Stored for external reference.
hubIdFK—The physical hub this profile stands for.
org_idFK—Set by association. Used only when org-wide listing is switched on.

The address is a separate row (Address, one per listed hub, FK listed_hub_id), created alongside the hub.

Fields that are stored but never read ​

These are accepted, saved and returned to the storefront, but no backend branch reads them. Delivery and pickup eligibility is decided entirely by the physical hub — its hasPickUp, hasStandardDelivery, hasRapidDelivery, hasShipment, deliveryRadius and pickup_radius.

FieldDefault
has_pickuptrue
has_standard_deliverytrue
has_shipmenttrue
has_rapid_deliveryfalse
delivery_radius10
pickup_radius15
taxjson{}
onboardingDatenow

A storefront theme can still render them, since every read returns them. Do not expect changing one to change what a shopper can order.

CSV import ​

Columns the importer reads: name, addressString, phoneNumber, phone_prefix, email, license, images, address1, city, state, county, zip, country, latitude, longitude.

The header check is case-sensitive — the file must carry literal name and addressString headers — while the values are read case-insensitively.

What happens on upload:

  1. The browser parses the CSV and rejects the file outright if name or addressString is missing from any row.
  2. The rows are posted to /listed/hub/import?isValidation=true. Nothing is written.
  3. Each row without address.latLng is forward-geocoded through the Google Geocoding API using "<name>, <addressString>". The response fills address1, zip, city, state, county, country and latLng — a geocode result overwrites what the CSV supplied for those fields.
  4. If the geocode returned a place_id, Place Details is called and formatted_phone_number replaces phoneNumber. Nothing else is taken from Place Details — opening hours are not fetched.
  5. Rows still missing name, addressString, address1, city, state, country or zip are reported as errors.
  6. You confirm, and the same payload is posted again without isValidation.

Latitude and longitude in the CSV do not skip geocoding

The admin maps the CSV's latitude/longitude to coords, but the importer tests address.latLng when deciding whether to geocode. A row with coordinates in the file is geocoded anyway, and the geocoded coordinates win.

Search configuration ​

The storefront list endpoint reads siteConfiguration.listedHub from the brand's master data. There is no admin screen for it.

KeyEffect
limitPage size when the caller sends no limit. Falls back to 10.
searchMethodsearch_within, gmap or distance — see below
searchRadiusMiles. Proximity results beyond it are dropped.
searchCountryAppended to an all-digits search term so a bare zip geocodes.
mapBoundsPassed to the Geocoding API as bounds to bias distance lookups.
  • search_within — text match only. Returns immediately; no proximity pass runs and related comes back empty.
  • gmap — Google Distance Matrix, driving distance in miles, from the search address to every hub's addressString.
  • distance — haversine from the search coordinates to each hub's stored coords, floored to whole miles. Uses the caller's lat/lng when sent, otherwise geocodes the search term.

A separate brand_listed_hub / query_type / org_wide master data row set to "true" switches the listing from brandId to org_id, so one brand's locator returns every location in its organisation.

Batch platform, Shopify app and API documentation