Delivery Promises

Unify Delivery Promises Widget

Two elements working together: <unify-delivery-promises-widget> is a headless container that fetches delivery promises once for an item at a given location, and <unify-delivery-offer-widget> is a lightweight offer block placed inside it, one per target, that renders either a single resolved offer (click & go, in-stock at a store, or nearby stores) or, for all-delivery-promises, every offer the API returned. There is no "standard"/"express" delivery-speed tiering — that distinction doesn't exist in the API for home or relay offers, so those are only ever surfaced through all-delivery-promises.

Integrators place one offer block per spot on the page — a product page might show an all-delivery-promises block near the price listing every option, and a location-click-and-go block near a store locator — all inside the same container, so the technical/request attributes (API key, item, amount, location...) are declared once, not once per block.

For installation instructions, see Getting Started with Unify Widgets.

Basic Implementation

<unify-delivery-promises-widget
  api-key="your-api-key"
  item-id="your-item-id"
  source="web"
  amount="49.99"
  latitude="48.8566"
  longitude="2.3522"
>
  <unify-delivery-offer-widget target="all-delivery-promises"></unify-delivery-offer-widget>
</unify-delivery-promises-widget>

<unify-delivery-promises-widget> (container)

Fetches once and pushes the result to every <unify-delivery-offer-widget> placed inside it (light DOM children — plain nesting, no slot name needed). Renders nothing itself.

Attributes

AttributeTypeRequiredDescription
api-keyStringYesAPI key for Unify requests
item-idStringYesProduct item identifier to check delivery promises for
sourceStringYesOrder source, matching the values accepted by the API's source field (e.g. web)
amountStringYesItem amount/price. Sent to the API formatted to exactly two decimals
quantityNumberNoQuantity for item-id. Defaults to 1
latitudeStringYes*Latitude of the delivery location. Required unless address is set
longitudeStringYes*Longitude of the delivery location. Required unless address is set
demoBooleanNoEnable demo mode for testing

Properties

PropertyTypeRequiredDescription
addressObjectNo{ zip_code, city, country_code } — used instead of latitude/longitude when coordinates aren't available
configObjectNo{ cacheTtl, requestTimeout } — see Config Options

<unify-delivery-offer-widget> (offer block)

Must be placed inside a <unify-delivery-promises-widget>. Renders one resolved offer for its target.

Attributes

AttributeTypeRequiredDescription
targetStringNoWhich offer(s) to render: all-delivery-promises (default), location-click-and-go, location-click-and-collect, or locations-near
location-idStringNoA location from promises.store to match against. Used by location-click-and-go, location-click-and-collect (to pick that exact store), locations-near (to exclude that store from the nearby count — only when it matches an actual location in the response; an unset or non-matching location-id excludes nothing, so locations-near lists every store) and all-delivery-promises (to scope its store items to that exact store instead of every store location)
typeStringNoOnly used by target="all-delivery-promises": restricts its list to a single node - home, relay, or store. Unset (default) lists every node. Ignored by every other target

Properties

PropertyTypeRequiredDescription
configObjectNoConfiguration object (see Config Options)

Targets

targetResolved fromDescription
location-click-and-gopromises.storeThe store location (matching location-id if set) whose reception_type.click_go flag is set — a prepared order ready for pickup
location-click-and-collectpromises.storeThe store location (matching location-id if set) whose reception_type.click_collect flag is set — item in stock at that location, reserve-then-collect
locations-nearpromises.storeCount and list of store locations other than the one matching location-id (all of them if location-id is unset or doesn't match any store)
all-delivery-promisespromises.home + promises.relay + promises.storeEvery offer the API returned, unfiltered — no "best offer" curation. Each home/relay offer and each store location (matching location-id if set) is listed as its own item, tagged home/relay/store. Set type="home"/"relay"/"store" to list only that one node

Note: location-click-and-go, location-click-and-collect, locations-near and the store items of all-delivery-promises are all derived from the store branch of the API response, which does not currently include per-location stock quantity — only which reception types (click & go / click & collect / e-reservation) are available. Each location's %estimate%/%date_from%/%date_to% come from that location's own delivery_delays when the API provides one, falling back to the carrier-wide delivery_delays otherwise.

Events

delivery-offer-loaded

Fired by <unify-delivery-offer-widget> once its resolved offer is available (or determined unavailable). Bubbles, so listening on an ancestor also catches it:

offerBlock.addEventListener('delivery-offer-loaded', (event) => {
  const { target, offer } = event.detail;
  console.log(`${target} offer`, offer);
});

Detail shape:

{
  target: string,    // The offer block's `target` attribute
  offer: object|null // Resolved offer, or null when unavailable
}

delivery-promises-loaded

Fired by <unify-delivery-promises-widget> once per fetch resolution (idle, error or missing-params), with the raw, unresolved API payload rather than a single offer - useful to react once per request instead of once per offer block. Bubbles, and is distinct from the offer block's own delivery-offer-loaded event above, so a listener on the container never has to tell the two apart:

container.addEventListener('delivery-promises-loaded', (event) => {
  const { dataState, promises } = event.detail;
  console.log(dataState, promises);
});

Detail shape:

{
  dataState: string,        // 'idle' | 'error' | 'missing-params'
  promises: object|null     // Raw `delivery_promises` from the API, or null
}

Config Options

Customize widget text and behavior. cacheTtl and requestTimeout go on the container's config; everything else (labels, locale, currency) goes on each offer block's config, since those are display concerns specific to that block.

document.querySelector('unify-delivery-promises-widget').config = {
  cacheTtl: 300000,
  requestTimeout: 10000,
};

document.querySelector('unify-delivery-offer-widget[target="all-delivery-promises"]').config = {
  allDeliveryPromisesHomeLabel: 'Home delivery (%carrier%) - %date_from% to %date_to% (%fees%)',
};

Labels (offer block)

OptionDefaultDescription
locationClickAndGoLabel"Click & Collect available at %store_name%"Label for target="location-click-and-go". Supports %store_name%, %city%, %address%, %prep_delay%, %estimate%, %date_from% and %date_to%
locationClickAndCollectLabel"In stock at %store_name%"Label for target="location-click-and-collect". Same placeholders as locationClickAndGoLabel
locationsNearLabel"Available in %count% nearby stores"Label for target="locations-near". Supports %count%. Also accepts { one, other } (each with its own %count% template) to pick a grammatically correct singular/plural form — one is used when the count is exactly 1, other otherwise
allDeliveryPromisesHomeLabel"Home delivery (%carrier%) - in %estimate%"Label for each home item of target="all-delivery-promises". Supports %estimate%, %date_from%, %date_to%, %fees%, %limit_fees% and %carrier%
allDeliveryPromisesRelayLabel"Pickup point delivery (%carrier%) - in %estimate%"Label for each relay item of target="all-delivery-promises". Same placeholders as allDeliveryPromisesHomeLabel
allDeliveryPromisesStoreLabel"%store_name% - in %estimate%"Label for each store item of target="all-delivery-promises". Supports %store_name%, %city%, %address%, %prep_delay%, %estimate%, %date_from% and %date_to%
carrierLabelsnone (raw API code)Object mapping a raw carrier code (as returned by the API, e.g. chronopost_express) to the display name to use wherever %carrier% is rendered. A code with no entry falls back to itself as-is

Example of carrierLabels, mapping raw carrier codes to display names for %carrier%:

document.querySelector('unify-delivery-offer-widget[target="all-delivery-promises"]').config = {
  carrierLabels: {
    chronopost_express: 'Express',
    chronopost: 'Chronopost',
  },
};

Per-target defaults for the "unavailable" state (no placeholders — override with ${target}UnavailableLabel, e.g. locationClickAndGoUnavailableLabel):

targetDefault unavailable text
location-click-and-go"Click & Go - not available"
location-click-and-collect"No stock available"
locations-near"No nearby stores available"
all-delivery-promises"No delivery options available"

Example of the singular/plural form of locationsNearLabel:

document.querySelector('unify-delivery-offer-widget[target="locations-near"]').config = {
  locationsNearLabel: {
    one: 'Available in %count% nearby store',
    other: 'Available in %count% nearby stores',
  },
};

%limit_fees% is the free-shipping threshold (limit_shipping_fees), formatted as currency like %fees%. %address% is the store's street address (blank if the API doesn't return one for that location). %prep_delay% is the raw estimated_preparation_request value from the API — an integer count of stock splits needed to fulfill the order at that store, not a duration in hours; word your label accordingly (e.g. avoid implying a fixed unit).

%estimate% is a bare, no-connector estimate for location-click-and-go/location-click-and-collect/all-delivery-promises, so your label supplies the connector word (in %estimate%, by %estimate%...) since it depends on language. It resolves to a clock time (e.g. "6:00 PM", locale-formatted) when the offer's estimated_delivery_date_to falls today, otherwise to a locale-formatted day/hour count or range from estimated_delivery_from/estimated_delivery_to + unit, via Intl.NumberFormat's native unit range formatting (e.g. "2–3 days", with an en dash — "2–3 jours" in fr-FR). Example: allDeliveryPromisesHomeLabel: 'Home - in %estimate%' renders "Home - in 2–3 days". Set estimateRangeConnector (see Other) to override that native separator, e.g. estimateRangeConnector: ' à ' renders "2 à 3 jours" instead.

Status Messages (offer block)

OptionDefaultDescription
loadingMessage"Loading delivery information..."Text shown while the container is fetching
errorMessage"Error loading delivery information. Please try again later."Error message on load failure
missingParamsMessage"Missing item ID, source, amount or location."Message shown when a required attribute on the container is missing
unavailableMessagePer-target, e.g. "Click & Go - not available" for location-click-and-go (see below)Message shown when no offer is resolved for this block's target. Overrides the per-target default for every target uniformly; use ${target}UnavailableLabel (e.g. locationClickAndGoUnavailableLabel) to override a single target instead

Other (offer block)

OptionDefaultDescription
localenavigator.languageLocale used to format dates and currency
currency"EUR"Currency used to format %fees%
estimateRangeConnectorUnset (native Intl.NumberFormat range, e.g. "2–3 jours" in fr-FR)Overrides the separator between the two numbers of a multi-day %estimate% range (e.g. " à " renders "2 à 3 jours" instead of the browser's native en-dash range). Ignored for a single-value estimate or a same-day clock time

Other (container)

OptionDefaultDescription
cacheTtl300000 (5 minutes)How long, in milliseconds, a response is reused by containers sharing the same request (e.g. a desktop and a mobile layout of the same product page)
requestTimeout10000 (10 seconds)How long, in milliseconds, to wait for the delivery-promises request before aborting it and falling back to the error state, instead of leaving offer blocks stuck on loading indefinitely

Styling

CSS Custom Properties (recommended)

All visual tokens are exposed as CSS custom properties prefixed with --unify-. This is the stable styling API, set on the offer block (not the container, which renders nothing itself).

unify-delivery-offer-widget {
  --unify-delivery-promises-error-color: #b91c1c;
}

CSS Parts

Structural elements can be targeted using ::part(), on the offer block:

unify-delivery-offer-widget::part(delivery-offer) {
  font-weight: 600;
}

unify-delivery-offer-widget[target="location-click-and-go"]::part(delivery-offer--location-click-and-go) {
  color: #0066cc;
}

unify-delivery-offer-widget[target="all-delivery-promises"]::part(delivery-offer-item--store) {
  font-style: italic;
}

⚠️ Parts may change between versions. Use CSS custom properties where possible.

To find available parts, inspect an offer block's shadow DOM in DevTools — every element with a part attribute is targetable from outside.

Examples

Multiple offer blocks sharing one request

<unify-delivery-promises-widget
  api-key="your-api-key" item-id="ITEM-12345"
  source="web" amount="49.99" latitude="48.8566" longitude="2.3522"
>
  <unify-delivery-offer-widget target="location-click-and-go" location-id="LOC-789"></unify-delivery-offer-widget>
  <unify-delivery-offer-widget target="all-delivery-promises"></unify-delivery-offer-widget>
</unify-delivery-promises-widget>

Splitting all-delivery-promises by node

<unify-delivery-promises-widget
  api-key="your-api-key" item-id="ITEM-12345"
  source="web" amount="49.99" latitude="48.8566" longitude="2.3522"
>
  <h3>Home delivery</h3>
  <unify-delivery-offer-widget target="all-delivery-promises" type="home"></unify-delivery-offer-widget>
  <h3>Pickup points</h3>
  <unify-delivery-offer-widget target="all-delivery-promises" type="relay"></unify-delivery-offer-widget>
  <h3>Stores</h3>
  <unify-delivery-offer-widget target="all-delivery-promises" type="store"></unify-delivery-offer-widget>
</unify-delivery-promises-widget>

Using a structured address instead of coordinates

<unify-delivery-promises-widget
  id="promises" api-key="your-api-key" item-id="ITEM-12345"
  source="web" amount="49.99"
>
  <unify-delivery-offer-widget target="all-delivery-promises"></unify-delivery-offer-widget>
</unify-delivery-promises-widget>

<script>
  document.getElementById('promises').address = {
    zip_code: '75008',
    city: 'Paris',
    country_code: 'FR',
  };
</script>

Did this page help you?