This page exists in one language only. Some pages here are English, some Swedish.
Integrations & Embed
This guide is for the developer who puts the Stockisto widget on a website. It covers the script tag, the attributes that configure it, the eight widgets, the public locator endpoints, and analytics consent. For getting retailer data into Stockisto, see the Data Import guide (CSV/XLSX), the Public imports API (NDJSON bundles) or the Public intake API (source-bound batches).
The widget script
The widget is a single, dependency-free script, widget.js, served from the Stockisto CDN. The
widget.js address always serves the current deployed build, so your script tag never needs a
version bump.
Open a saved embed in your admin and copy its Install snippet. The embed id identifies the widget you saved. This example uses a placeholder id; keep the id and origins your admin generates.
<script
src="https://cdn.test.stockisto.com/widget.js"
data-api-base="https://api.test.stockisto.com"
data-embed="we_0123456789abcdef0123456789abcdef"
async></script>
Save its type, view, language and settings in Supplier Admin, Retailer Admin or Installer Admin.
The installed snippet reads those changes on the next page load. Add only the page keys the page supplies,
such as data-sku="YOUR-PRODUCT-CODE". The script inserts its own host; no slot div is needed.
Snippets installed today keep working
Existing attribute snippets still work. Omitted settings come from the owner's default embed; a failed default lookup leaves the existing attribute behaviour intact.
<script
src="https://cdn.test.stockisto.com/widget.js"
data-api-base="https://api.test.stockisto.com"
data-stockisto-slug="YOUR-BRAND-SLUG"
data-widget-type="where-to-buy"
async></script>
What the script does
- Finds its own
<script>tag. Async loads are fine: it looks fordocument.currentScript, thenscript[data-stockisto-widget="true"], then any script whosesrccontainswidget.js. - Resolves
data-embedthroughGET /api/v1/locator/widget-embeds/{publicId}before choosing language, slot, shape or data source. The request is anonymous and sends no cookies. - Explicit page attributes override saved values. An empty
data-skusuppresses product-dependent rendering. - Caches only the loading shape (
widgetType,view,settings) understockisto-embed:<publicId>. A repeat visit can show that skeleton during resolve; cached settings never replace live results. - Renders into
<div id="stockisto-<data-view>">when that div exists, else into<div id="stockisto-<data-widget-type>">. With neither, it injects its own host element right after the script tag, so the div is optional. - Resolves the embed immediately. Store-result fetching waits until the widget scrolls into view. The asynchronous script does not block page loading.
- Fails closed. A missing brand, a refused search or an empty result renders an honest empty state
or nothing at all. It never throws into your page; boot failures are stamped as
data-stockisto-erroron the script tag. - Adds one
<script type="application/ld+json">block describing the visible store list as a schema.orgItemListofStorenodes. It never emitsProductorOffermarkup. Turn it off withdata-jsonld="off".
Set data-api-base on the TEST environment
Without data-api-base the widget calls https://api.stockisto.com, which
is the production origin. Every snippet the dashboard generates carries the
right origin for its environment. Keep that line when you copy it.
Configuration attributes
Identity and placement
For new installs use data-embed, the we_ id copied from your saved widget, plus data-api-base.
The following attributes describe page overrides and legacy installs. Saved settings fill omitted values;
an explicit attribute takes precedence. A named embed does not need a brand slug on the page.
| Attribute | Required | Default | What it does |
|---|---|---|---|
data-stockisto-slug | no | Your public brand slug for a legacy install; a named embed supplies it. The widget resolves it through GET /api/v1/locator/brands/{slug}. data-supplier is an accepted alias. | |
data-supplier-id | no | Your supplier tenant GUID, as an alternative to the slug. A non-GUID value is ignored. A GUID embed skips the brand fetch, so it gets no saved theme. | |
data-api-base | no | prod API | The API origin. Only https://*.stockisto.com origins are accepted; anything else falls back to production. |
data-widget-type | no | where-to-buy | One of the eight widgets below. An explicit empty, unknown or retired value renders nothing, so check your spelling. |
data-widget-id | no | random | A stable id when one page carries several widgets. |
Products
| Attribute | What it does |
|---|---|
data-sku | One public product code, up to 100 characters. Scopes Where to buy, and required by store-availability and stock-alert. data-product is an alias. An unknown code renders a not-found state, never brand-wide rows. |
data-sku-type | Declares the scheme of data-sku: rsk, gtin (ean accepted), sku, mpn, vvs, nrf, lvi, nobb, finfo, enummer, efo, elnummer_dk, sahkonumero, tun or retailer_sku. |
data-skus | A comma-separated list of up to 6 codes, forwarded into the store locator's framed search. |
data-skus-type | The scheme for every code in data-skus. One entry that fails the scheme refuses the whole list. |
Without a declared scheme the code is tried as rsk, then sku, then gtin. A declared scheme is
only ever looked up as that scheme. Classification and certificate codes such as etim, bk04 or
va are refused: they are not product keys.
Location and list
| Attribute | Default | What it does |
|---|---|---|
data-lat / data-lng | A fixed search centre in decimal degrees. When set, the widget never asks the browser for location. Results are then always centred on that point, not on the visitor. | |
data-postcode | A label shown with an explicit centre. | |
data-radius | 50 | Search radius in km. The API accepts 1 to 500. |
data-filter | Where to buy: confirmed shows only rows with a confirmed in-stock signal; showroom only showroom rows. Absent shows every row the search returned. | |
data-rank | server | distance or price. Absent means the server's own ranking (stock first). Price ranking refuses when prices are hidden. |
data-per-view | 3 | Rows shown before the "see all" footer, 1 to 12 (Where to buy's inline view). |
data-max-height | Caps the scrolling store list, 240 to 1200 px. | |
data-channel | online opens the webshop view first. | |
data-prices | retailer | off hides published retailer prices; online shows them on webshop rows only. msrp is refused with a console warning, because no suggested retail price exists on the wire. |
data-guided | false | Where to buy: ask two guided questions before showing rows (requires a resolvable SKU). Excluded by a locator-target pill. |
data-sold-out | false | Where to buy: a "sold out online" banner when nothing in the response is attested in stock. |
data-reserve | false | Where to buy: a per-row Reserve action that posts a reservation. |
data-pill-target | locator | Where to buy pill view: locator links to the hosted brand page, overlay opens the store list overlay. |
data-corner | bottom-right | Where to buy corner view: bottom-right, bottom-left, top-right or top-left. |
data-show-hours | true | Show opening hours (store card visit part; the shared store-list expansion). |
data-show-services | false | Show the showroom or service chip when known. |
data-directions | true | Show the Directions link/button. |
data-fields | Store card only: a comma list of hours, phone, directions. The address always shows. | |
data-exceptions | on | Store card only: off hides dated holiday hours. |
data-view | Where to buy: full, inline, button, pill or corner. Store locator: list opens the directory instead of the map. | |
data-map-height | 480 | Frame height for store-locator / installer-finder, 320 to 800 px. Defaults: 480 for the map leg, 560 for the list leg, 640 for installer-finder. |
data-service | installer-finder: preselect a service type the brand publishes. | |
data-audience | Where to buy: trade sends authorizedOnly=true server-side and keeps wholesalers. | |
data-quantity | Where to buy: a positive integer quantity filter (requires a resolvable SKU). | |
data-brands | Store card: up to 12 brand slugs to narrow the brands shown. It can never add a brand the store does not carry. | |
data-lang | page | UI language: sv, nb, da or fi. Falls back to <html lang>, then the browser, then English. data-locale is an alias. |
data-analytics-consent | false | Only the literal "true" sends analytics. See below. |
data-jsonld | on | off disables the structured-data block. |
Keys for retailer and installer widgets
| Attribute | Used by |
|---|---|
data-store-id | store-card, stock-alert: a store location GUID |
data-retailer | The retailer's public slug. Required by the three retailer widgets. |
data-installer | installer-card: the installer company GUID |
A non-GUID id is dropped and the widget hides itself rather than looping on a 404.
Styling
Every styling attribute is optional. Absent means the Heritage default look.
| Attribute | Values |
|---|---|
data-theme | A hex accent (#0057A8), a preset name, or the id of a theme you saved in Supplier Admin |
data-theme-preset | heritage, light, dark, minimal, dense |
data-theme-accent2 | Secondary accent, hex |
data-theme-surface | Card surface colour, hex |
data-theme-ink | Text colour, hex |
data-radius-scale | sm, md, lg |
data-density | cozy, compact |
data-shadow | soft, flat, none |
data-font | grotesk, sans, serif, mono, system. The widget never loads a web font. |
data-width | full, or a pixel value from 200 to 2000 |
Anything off these lists is dropped. A hex accent with poor contrast against the card is replaced by the default.
Consider a default search centre
Without data-lat/data-lng the widget asks the visitor before it uses
browser location, and offers a postcode field instead. If most of your
shoppers are in one market, a fixed centre (Stockholm: data-lat="59.3293" data-lng="18.0686") with a generous data-radius shows them stores at
once. The trade-off: a fixed centre disables the location prompt entirely.
Widget list
Exactly eight widgets ship. Select one with data-widget-type; an omitted value defaults to
where-to-buy, and an explicit empty, unknown or retired value renders nothing. The Supplier
Admin Install → Advanced builder generates snippets for the four supplier/brand widgets;
Retailer Admin Widgets generates the three retailer widgets; an installer's detail page in
Supplier Admin generates the installer widget.
On a supplier's product pages
where-to-buy(default): nearby authorized retailers, confirmed stock ranked first. Runs brand-wide, or scoped to one product withdata-sku. Five views (data-view):full(the whole list, default),inline(a compact panel in the page's own flow),button(opens an overlay),pill(a compact count pill, "Stocked by N stores near you", degrading to a count-less "Find where to buy" CTA with zero nearby stores, never "0 stores"),corner(a pinned corner pill that opens the list over the page).data-guided,data-sold-outanddata-reservelayer optional guided questions, a sold-out-online banner and a reserve action over any view.guided-handoff: a three-step flow. Pick a retailer, optionally pick an approved installer, then give explicit consent before an installer lead is sent.
On a supplier's brand pages
store-locator: the whole network as a map, or as a server-rendered directory withdata-view="list". Visitors can switch inside the frame.installer-finder: your approved installers with coverage areas, service filters and an in-frame callback request. It never asks for browser location.
On a retailer's own site
All three need data-retailer.
store-availability: the retailer's own branches near the shopper with the stock signal fordata-sku, and the retailer's published price where the server releases one. Needsdata-stockisto-slug,data-retaileranddata-sku.store-card: one store's address, opening hours, the brands it carries and its aggregate rating, stacked in that order. Each part hides on its own (data-show-visit/-brands/-rating); with every part off, empty or failed the card hides. Needsdata-store-idanddata-retailer.stock-alert: a back-in-stock email form for one product at one store. Needsdata-stockisto-slug,data-store-idanddata-sku. Sign-up is double opt-in.
On an installer's own site
installer-card: the installer's registry credential and service-area coverage check, stacked in that order; each section hides on its own (data-show-credential/-coverage). Needsdata-installerand the supplier'sdata-stockisto-slug. The public wire carries a bare certified flag with no supplier, category or date, so it cannot say which brand approved what.
Display signals are server-computed
The In stock / May carry / Contact retailer label comes from the API's
displaySignal field and is never re-derived in the browser. The freshness
line ("confirmed 3 days ago") is likewise the server's verdict. When the API
marks a row isSponsored, the widget always shows a "Sponsored" label.
Content Security Policy
Allow https://cdn.test.stockisto.com in script-src and https://api.test.stockisto.com in connect-src on TEST.
For a production snippet, allow https://cdn.stockisto.com and https://api.stockisto.com respectively.
Use the origins from your generated snippet. The API origin must be allowed for the embed resolve.
The map and installer-finder frames also need frame-src https://find.test.stockisto.com on TEST.
A refused connect-src prevents a named embed from resolving. It shows a load-failed state with Retry;
it never renders cached results. A deleted or unknown id renders nothing.
Public locator endpoints
The widget reads from anonymous endpoints under https://api.test.stockisto.com/api/v1/locator. No
token or cookie is needed, and any origin may call them. You can call them yourself for a custom
integration.
GET /api/v1/locator/search
The widget's main read. It needs the supplier GUID and a search centre:
GET /api/v1/locator/search?supplierId=SUPPLIER_GUID&latitude=59.33&longitude=18.06&radiusKm=50&pageSize=12
| Parameter | Rule |
|---|---|
supplierId | Required. Resolve a brand slug to it with GET /api/v1/locator/brands/{slug} first. |
latitude / longitude | Required unless browse=true. Dot-decimal only. lat and lng are accepted aliases. |
radiusKm | 1 to 500, default 25. radius is an alias. |
browse=true | Returns the whole active network without a centre. |
skuCode | Scope to one public product code. skuCodes takes up to 50 codes for one batched search. |
inStockOnly, showroomOnly, authorizedOnly | Boolean filters. authorizedOnly keeps only tiered-relationship stockists. |
page, pageSize | Zero-based page; pageSize defaults to 50. |
Each retailer row carries id, retailerId, slug, name, address, city, postalCode,
country, latitude, longitude, distanceKm, isShowroom, phone, email, website,
openingHours, displaySignal, rankingScore, isSponsored, isSample and a stockStatus
object (freshness, lastVerifiedAt, confidenceTier). The envelope adds totalCount and
hasMore. displaySignal is InStock, MayCarry or ContactRetailer. Results are cached for
five minutes per supplier.
Coordinates are always dot-decimal
Write latitude=59.33, never 59,33. A search without a usable centre
returns HTTP 400 rather than silently searching at (0,0).
Other routes the widget uses
| Route | Purpose |
|---|---|
GET /api/v1/locator/brands/{slug} | Brand theme and supplier id for a slug. Cached ten minutes. |
GET /api/v1/locator/retailers/{slug}/stores | A retailer's own stores. A centre is optional here. |
GET /api/v1/locator/find/stores/{id} | One store's detail: address, structured hours, brands carried. |
GET /api/v1/locator/find/stores/{id}/reviews | The store's rating aggregate. |
GET /api/v1/locator/find/installers/{id} | One installer's coverage areas and registry certification. |
GET /api/v1/locator/{supplierSlug}/installers?take=5 | Approved installers for the guided handoff. |
POST /api/v1/installers/companies/{id}/leads | Sends the guided-handoff lead once the visitor consents. |
POST /api/v1/locator/stock-alerts | Back-in-stock sign-up. 202 means a confirmation email was sent, not a live alert. |
POST /api/v1/locator/reservations | A reservation request from data-reserve. |
GET /api/v1/locator/geocode?q=… | Postcode and address geocoding, Nordic countries only. 20 requests per minute per IP. |
GET /api/v1/locator/resolve-host?host=… | Maps a verified custom domain to { supplierId, slug }. No DNS lookup happens here. |
Status codes to handle
| Status | Meaning |
|---|---|
400 | Missing or invalid parameters, for example no latitude. |
403 | The locator is set to Private, or the widget type is above the supplier's plan. |
429 | Rate limit hit. Honor Retry-After. |
503 | The supplier tenant is suspended. |
Rate limits
/locator/search allows 100 requests per minute per tenant and 20 per
minute per anonymous IP. /locator/brands/{slug} allows 500 per minute.
Store and installer detail reads allow 80 per minute per anonymous IP.
Analytics events
With data-analytics-consent="true" the widget posts events to
POST https://api.test.stockisto.com/api/v1/analytics/events. It uses fetch with keepalive
and no credentials, so no cookie ever travels with an event.
The body carries schemaVersion: 1, sessionId, correlationId, eventType and a payload.
The server resolves your tenant from the brand slug and ignores any tenant id in the body. It
answers 202 Accepted without waiting for the write. Anonymous callers may only send known event
types; an unknown type returns 400.
Events are credited to your account only when the page's origin is on your Allowed origins list
(Supplier Admin → Install → Embed script). Add every domain that hosts the widget, one per line,
such as https://www.yourbrand.com. The same list gates lead attribution for the guided handoff.
Consent gating
With the default data-analytics-consent="false" the widget sends no
analytics at all and writes nothing to session storage. Set it to "true"
only after your consent tool has recorded a lawful basis.
Installing through a tag manager or a CMS
- GTM install guide: paste the snippet as a Custom HTML tag.
- Shopify install guide: a Custom Liquid section, or the Stockisto app block.
- WordPress & WooCommerce install guide: the plugin shortcode or a Custom HTML block.
Content-Security-Policy
If your site sends a CSP header, add https://cdn.test.stockisto.com to
script-src and https://api.test.stockisto.com to connect-src. The
framed widgets (store-locator, installer-finder) also need the locator
host in frame-src.
What's next?
- Data Import guide: the CSV template, geocoding and the review queue
- Getting Started: trial, onboarding and publishing your locator
- Searches, clicks and reports guide: what the events show in your dashboard
- MCP assistant guide: connect an assistant for read-only access first, then authorize writes separately.