External Shipping Engine: Storefront API Integration Draft
This document is a draft and what it contains is not available yet in any environment, and is subject to change.
This page covers the Storefront API surface for ESE integrations — querying shipping methods, selecting them, fetching pickup locations, and reading provider metadata. For the backend webhook contract, see the ESE protocol reference. For a reference of all new shipping fields and types introduced alongside ESE, see New Shipping Capabilities in Storefront API.
Get Shipping Methods
The frontend controls when Centra queries the ESE by choosing whether to include shippingMethods in its selection set. Background notifications happen automatically — the frontend does not control them and does not need to handle them.
shippingMethods is a field on CheckoutSelection, which is the type of the checkout field on Selection. When any Storefront API operation returns a Selection and the client has included checkout { shippingMethods } in its selection set, Centra queries the ESE synchronously before responding. If shippingMethods is not requested, Centra may still notify the ESE in the background but responds immediately without waiting for shipping options.
The following fragment includes all available fields on ShippingMethod:
fragment SelectionWithShipping on Selection {
checkout {
shippingMethods {
id
externalId
name
selected
hasLocation
carrierName
serviceCode
deliveryType
description
iconUrl
price {
value
currency
formattedValue
}
originalPrice {
value
currency
formattedValue
}
etd {
relative { min max units }
absolute { from to }
custom
}
labels {
type
displayName
description
iconUrl
}
customerChoices {
id
displayName
description
type
default
price { value currency formattedValue }
options { key displayName description price { value currency formattedValue } }
}
selectedLocation {
id
displayName
address {
address1
city
zipCode
country { name }
}
latitude
longitude
distance { mode meters minutes }
openingHours { periods { open { day hour minute } close { day hour minute } } specialDays { date { year month day } isClosed comment } }
openingHoursText
brickAndMortar
shippingProviderMetadata { provider data { key value } }
}
locations {
id
displayName
address {
address1
city
zipCode
country { name }
}
latitude
longitude
distance { mode meters minutes }
openingHours { periods { open { day hour minute } close { day hour minute } } specialDays { date { year month day } isClosed comment } }
openingHoursText
brickAndMortar
shippingProviderMetadata { provider data { key value } }
}
shippingProviderMetadata { provider data { key value } }
}
}
}
How Centra Calls the ESE
Centra calls the ESE in two distinct modes:
-
Notified: A fire-and-forget call Centra makes to the ESE backend whenever a session-level change occurs — for example, a shipping address update, a language change, or an item being added to the cart. No shipping options are returned. This happens transparently in the background; the frontend receives an instant response. Centra reserves the right to determine which session changes trigger a notification.
-
Queried: A blocking call Centra makes when the frontend explicitly requests
shippingMethodsonCheckoutSelection. This field is specific to the checkout flow. Centra waits for the ESE to respond before completing the API response (up to 10 seconds), and the returned shipping options are surfaced to the frontend.
Behaviour per Operation
| Operation | shippingMethods requested | ESE notified | ESE queried | Response |
|---|---|---|---|---|
selection (query) | No | No | No | Instant |
selection (query) | Yes | No | Yes | Slow (up to 10s) |
setAddress | No | Yes | No | Instant |
setAddress | Yes | Yes | Yes | Slow (up to 10s) |
addItem | No | Yes | No | Instant |
addItem | Yes | Yes | Yes | Slow (up to 10s) |
The same pattern applies to all operations that modify session state. Read queries (such as selection) only trigger an ESE call when shippingMethods is requested.
Filtering by Display Target
shippingMethods accepts an optional optimizeFor string argument. When provided, Centra forwards the value to the ESE so it can return a tailored set of shipping options for that specific display context — for example, a reduced set without customer choices or location pickers for express payment surfaces, or a context-specific set for a custom integration.
The value is an enum. Your ESE implementation can recognise these values and return an appropriate set of options for each. The full list of values is subject to change as new display targets are added.
| Value | Context |
|---|---|
"applepay" | Apple Pay payment sheet |
"googlepay" | Google Pay payment sheet |
"ams" | AMS |
query {
selection {
checkout {
shippingMethods(optimizeFor: "applepay") {
id
name
price { value currency formattedValue }
}
}
}
}
When optimizeFor is omitted, the full standard set of shipping options is returned.
ShippingMethod Fields
| Field | Type | Description |
|---|---|---|
id | Int! | Unique identifier for the shipping method |
externalId | String | The original shipping method identifier returned by the ESE |
name | String! | Human-readable name shown to the customer |
price | MonetaryValue! | The effective price of this shipping method |
originalPrice | MonetaryValue | Pre-discount price, if applicable — use to display a crossed-out original price |
selected | Boolean! | Whether this method is currently selected on the checkout |
hasLocation | Boolean! | Whether this method supports selectable pickup/drop-off locations. Use this as the authoritative capability flag |
locations | [ShippingMethodLocation] | Pickup/drop-off locations pre-populated by the ESE for the current shipping address. May be empty even when hasLocation is true — when empty, call getShippingMethodLocations with the order address to retrieve locations |
carrierName | String | Name of the shipping carrier |
serviceCode | String | Carrier service identifier |
deliveryType | DeliveryType | Fulfillment method. One of: TO_DOOR, PICKUP, LOCKER, MAILBOX, OTHER. null when the method does not originate from an external shipping provider |
description | String | Additional details about the option |
iconUrl | String | URL to the carrier's icon |
etd | ShippingETD | Estimated time of delivery. Any combination of sub-fields can coexist. When custom is present, we recommend displaying it in place of any label derived from relative or absolute — Centra does not enforce this on the backend |
labels | [ShippingLabel] | Visual indicators attached to the option (e.g. sustainability badge, signature required) |
customerChoices | [CustomerChoice] | Customer-configurable selections for this option — INPUT (text entry), CHECKBOX (toggle), CHOICE (dropdown with sub-options), or TIMESLOT (delivery time window selection) |
selectedLocation | ShippingMethodLocation | The pickup/drop-off location currently attached to this method, or null if none has been set or the method does not support locations |
shippingProviderMetadata | [ShippingProviderMetadata] | Provider-defined key/value metadata attached to this shipping method by the ESE. Same structure as Session.shippingProviderMetadata |
Advantages
- No new API parameters or separate triggering calls needed — the ESE is queried purely based on what fields are selected.
- Easy to reason about: if you want shipping methods, include them in your query; Centra does the rest.
- Frontend controls the tradeoff between response speed and data completeness.
Considerations
- Requesting
shippingMethodswill significantly slow the response (up to 10 seconds). - If the ESE is slow or unavailable, the entire mutation response is delayed. Design your loading states accordingly.
- The frontend is responsible for choosing the right moment to trigger an ESE query.
Recommended Checkout Flows
Both patterns share the same starting point:
1. User adds item to cart — instant (ESE notified in background)
mutation {
addItem(item: { product: 1, quantity: 1 }) {
selection {
lines { quantity product { name } }
}
}
}
2. User arrives at checkout — instant, page renders
query {
selection {
checkout {
shippingAddress { city country }
}
}
}
From here, two patterns diverge depending on your UX requirements.
Pattern 1: Combined
Set the address and fetch shipping methods in a single call.
3. User sets address and fetches shipping methods — up to 10 seconds (ESE queried)
mutation {
setAddress(shippingAddress: { ... }) {
selection {
checkout {
shippingAddress { city country }
shippingMethods { id name selected hasLocation price { value currency } }
}
}
}
}
Trade-off: Simpler orchestration, but the address update blocks for up to 10 seconds while the ESE responds.
Pattern 2: Deferred (Recommended)
Set the address instantly, then fetch shipping methods in a separate background call.
3. User sets address — instant (ESE notified in background)
mutation {
setAddress(shippingAddress: { ... }) {
selection {
checkout { shippingAddress { city country } }
}
}
}
4. Immediately fire: fetch shipping methods — up to 10 seconds (ESE queried)
query {
selection {
checkout {
shippingMethods { id name selected hasLocation price { value currency } }
}
}
}
Trade-off: More orchestration on the frontend, but the address update is instant and the shipping selector appears once ready.
Shipping Methods per Delivery Group
When an order spans multiple delivery groups (e.g. items from different warehouses), each group can have its own set of available shipping methods. shippingMethods is a new field added to SelectionDeliveryGroup — the delivery group type within CheckoutSelection — and is not present on the base DeliveryGroup type used elsewhere (e.g. in order history).
query {
selection {
checkout {
deliveryGroups {
id
shippingMethods {
id
name
hasLocation
price { value currency }
}
items {
quantity
product { name }
}
}
}
}
}
Including shippingMethods inside deliveryGroups triggers a single ESE query, the same as checkout.shippingMethods — the ESE response is what determines the options available per group.
Get Shipping Method Locations
Some shipping methods — such as parcel shop networks or click-and-collect points — require the customer to choose a physical pickup or drop-off location. These are fetched via getShippingMethodLocations, which maps to the ESE's optionLocation request.
Fetching Locations
Only call getShippingMethodLocations when ShippingMethod.hasLocation is true — only render the location picker UI for methods that support it.
The ESE is not required to populate ShippingMethod.locations inline — locations may be empty even when hasLocation is true. When locations is empty, call getShippingMethodLocations with the order address as the search address; Centra handles the location lookup. If locations is already populated, you may not need to call it at all — but you can still use getShippingMethodLocations to search at a different address or retrieve more results.
getShippingMethodLocations accepts a shipping method ID and an optional location hint. The hint can be an Address, geographic coordinates, or omitted entirely — in which case the selection's current shipping address is used.
# Using an address
query {
getShippingMethodLocations(
shippingMethodId: 42
address: {
address1: "14 Downing Street"
city: "London"
zipCode: "SW1A 2AA"
country: "GB"
}
) {
id
displayName
address {
address1
city
zipCode
country { name }
}
openingHours {
periods { open { day hour minute } close { day hour minute } }
specialDays { date { year month day } isClosed comment }
}
}
}
# Using coordinates
query {
getShippingMethodLocations(
shippingMethodId: 42
latitude: 51.5033
longitude: -0.1276
) {
id
displayName
address {
address1
city
zipCode
country { name }
}
openingHours {
periods { open { day hour minute } close { day hour minute } }
specialDays { date { year month day } isClosed comment }
}
}
}
# Defaulting to the selection's shipping address
query {
getShippingMethodLocations(
shippingMethodId: 42
) {
id
displayName
address {
address1
city
zipCode
country { name }
}
openingHours {
periods { open { day hour minute } close { day hour minute } }
specialDays { date { year month day } isClosed comment }
}
}
}
Exactly one location hint strategy applies per call. Providing both address and coordinates is an error.
ShippingMethodLocation Fields
| Field | Type | Description |
|---|---|---|
id | String! | Unique identifier for this location |
displayName | String! | Human-readable name of the location |
address | Address | Physical address of the location |
latitude | Float | Geographic latitude of the location |
longitude | Float | Geographic longitude of the location |
distance | [ShippingMethodLocationDistance] | Distance entries from the shipping address to this location, one per travel mode. Each entry has mode (DRIVING or WALKING), meters (Int), and minutes (Int) — at least one of meters or minutes is present. |
openingHours | OpeningHours | Opening hours, including regular periods and special days |
openingHoursText | String | Human-readable opening hours summary |
brickAndMortar | Int | ID of a Centra brick-and-mortar store that may fulfill the order |
shippingProviderMetadata | [ShippingProviderMetadata] | Provider-defined key/value metadata attached to this location by the ESE. Same structure as Session.shippingProviderMetadata |
Set Shipping Method
Pass back the id received from ShippingMethod.id in the shippingMethods response — no difference in behaviour whether the method comes from Centra or an ESE.
If the method supports locations (hasLocation: true), you can attach the customer's chosen location in the same call via the optional locationId parameter (using ShippingMethodLocation.id from getShippingMethodLocations). The selected location is then readable back on checkout.shippingMethod.selectedLocation.
Use setShippingMethod only when the selection has no delivery groups, and setDeliveryGroupShippingMethod only when it does. Using the wrong mutation for the selection's structure will result in an error.
# Without a location
mutation {
setShippingMethod(id: 12) {
selection {
checkout { shippingMethod { id name } }
}
}
}
# With a location
mutation {
setShippingMethod(id: 12, locationId: "loc-7") {
selection {
checkout {
shippingMethod {
id
name
selectedLocation {
id
displayName
address { address1 city zipCode country { name } }
}
}
}
}
}
}
Setting a Shipping Method per Delivery Group
When the checkout has multiple delivery groups, each group may have its own set of available shipping methods. A dedicated setDeliveryGroupShippingMethod mutation handles this case. This mutation is new and specific to ESE delivery group checkouts.
# Without a location
mutation {
setDeliveryGroupShippingMethod(deliveryGroupId: 1, shippingMethodId: 12) {
selection {
checkout {
deliveryGroups {
id
shippingMethods { id name selected }
}
}
}
}
}
# With a location
mutation {
setDeliveryGroupShippingMethod(deliveryGroupId: 1, shippingMethodId: 12, locationId: "loc-7") {
selection {
checkout {
deliveryGroups {
id
shippingMethods {
id
name
selected
selectedLocation { id displayName }
}
}
}
}
}
}
Session as Integration Anchor Point
Because shipping options are tied to the session, the session ID can serve as a stable anchor for frontend integrations with third-party shipping providers. For example, a provider like Ingrid can associate customer-specific delivery promises — such as "This item can be with you in 3 days" — directly with the Centra session, without requiring a separate authentication flow on the frontend.
Your frontend widget can contact the provider directly using the session token, and the provider's backend can correlate that with the corresponding Centra ESE session context.
Shipping Metadata
The ESE may return shippingProviderMetadata in both NOTIFY and CHECKOUT responses. Centra attaches this metadata at three levels — Session, ShippingMethod, and ShippingMethodLocation — without the frontend needing to do anything extra. Keys and values are defined by the ESE provider — Centra passes them through without interpretation. Entries are grouped by provider, with each group containing a data array of key/value pairs.
query {
session {
shippingProviderMetadata { provider data { key value } }
}
}
query {
selection {
checkout {
shippingMethods {
shippingProviderMetadata { provider data { key value } }
locations {
shippingProviderMetadata { provider data { key value } }
}
}
}
}
}
Nota Bene: Backend-Only Calls
The ESE protocol includes additional request types — testConnection and orderCreated — that Centra uses for backend-to-backend operations. These are handled entirely server-side and have no frontend surface. You do not need to account for them in your Storefront API integration.
Express checkout flows (e.g. Shop Pay, Apple Pay) are fully supported. Centra handles the ESE integration transparently — no additional frontend implementation is required.