Appearance
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
| Field | Type | Default | Notes |
|---|---|---|---|
name | string | — | Required. The list sorts by it, ascending. |
addressString | string | — | 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. |
email | string | — | |
phoneNumber / phone_prefix | string | — | phone_prefix is required whenever phoneNumber is sent. |
currency | string | USD | |
timings | object | {} | Free-form. The admin form writes only timezone and preOrder. |
license | string | — | |
images | string | — | A single string, not a list. Not exposed in the admin form. |
show_on_nearest | boolean | false | The only field that gates visibility. API-only — see the warning above. |
type | string | — | Free-form; no enum, no branch reads it. |
placeId / locationId | string | — | Stored for external reference. |
hubId | FK | — | The physical hub this profile stands for. |
org_id | FK | — | 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.
| Field | Default |
|---|---|
has_pickup | true |
has_standard_delivery | true |
has_shipment | true |
has_rapid_delivery | false |
delivery_radius | 10 |
pickup_radius | 15 |
taxjson | {} |
onboardingDate | now |
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:
- The browser parses the CSV and rejects the file outright if
nameoraddressStringis missing from any row. - The rows are posted to
/listed/hub/import?isValidation=true. Nothing is written. - Each row without
address.latLngis forward-geocoded through the Google Geocoding API using"<name>, <addressString>". The response fillsaddress1,zip,city,state,county,countryandlatLng— a geocode result overwrites what the CSV supplied for those fields. - If the geocode returned a
place_id, Place Details is called andformatted_phone_numberreplacesphoneNumber. Nothing else is taken from Place Details — opening hours are not fetched. - Rows still missing
name,addressString,address1,city,state,countryorzipare reported as errors. - 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.
| Key | Effect |
|---|---|
limit | Page size when the caller sends no limit. Falls back to 10. |
searchMethod | search_within, gmap or distance — see below |
searchRadius | Miles. Proximity results beyond it are dropped. |
searchCountry | Appended to an all-digits search term so a bare zip geocodes. |
mapBounds | Passed to the Geocoding API as bounds to bias distance lookups. |
search_within— text match only. Returns immediately; no proximity pass runs andrelatedcomes back empty.gmap— Google Distance Matrix, driving distance in miles, from the search address to every hub'saddressString.distance— haversine from the search coordinates to each hub's storedcoords, floored to whole miles. Uses the caller'slat/lngwhen 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.
Related pages
- Store locator setup
- Taxes & shipping
- Finding a store — how customers pick a location, and why it changes the catalogue