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, and fetching pickup locations. 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
name
selected
comment
hasLocation
carrierName
serviceCode
deliveryType
description
iconUrl
price {
value
currency
}
originalPrice {
value
currency
}
etd {
relative { min max units }
absolute { from to }
custom
}
labels {
type
displayName
description
iconUrl
}
customerChoices {
id
displayName
description
type
default
price { value currency }
options { key displayName description price { value currency } }
}
selectedLocation {
id
displayName
address {
address1
city
zipCode
country { name }
}
latitude
longitude
distanceMeters
openingHours { dayOfWeek opens closes }
openingHoursText
brickAndMortar
}
locations {
id
displayName
address {
address1
city
zipCode
country { name }
}
latitude
longitude
distanceMeters
openingHours { dayOfWeek opens closes }
openingHoursText
brickAndMortar
}
}
}
}
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.
ShippingMethod Fields
| Field | Type | Description |
|---|---|---|
id | Int! | Unique identifier for the shipping method |
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 |
comment | String | Optional commentary about the shipping method |
hasLocation | Boolean! | Whether this method supports selectable pickup/drop-off locations. Use this as the authoritative capability flag — an empty locations array does not mean locations are unavailable, only that none were returned for the current address context |
locations | [ShippingMethodLocation] | Pickup/drop-off locations pre-populated by the ESE based on the current shipping address. May be empty — use getShippingMethodLocations to search for locations at a different address or to retrieve more results |
carrierName | String | Name of the shipping carrier |
serviceCode | String | Carrier service identifier |
deliveryType | String | Fulfillment method — recommended values: TO_DOOR, PICKUP, LOCKER, MAILBOX, STANDARD_SHIPMENT |
description | String | Additional details about the option |
iconUrl | String | URL to the carrier's icon |
etd | ShippingETD | Estimated time of delivery. At least one of relative, absolute, or custom is provided — custom takes precedence when present |
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), or CHOICE (dropdown with sub-options) |
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 |
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.
When ShippingMethod.locations is already populated, you may not need to call getShippingMethodLocations at all — the ESE has pre-fetched nearby locations as part of the checkout response. Use getShippingMethodLocations when you need to search at a different address, retrieve more results, or when locations came back empty.
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 {
dayOfWeek
opens
closes
}
}
}
# Using coordinates
query {
getShippingMethodLocations(
shippingMethodId: 42
latitude: 51.5033
longitude: -0.1276
) {
id
displayName
address {
address1
city
zipCode
country { name }
}
openingHours {
dayOfWeek
opens
closes
}
}
}
# Defaulting to the selection's shipping address
query {
getShippingMethodLocations(
shippingMethodId: 42
) {
id
displayName
address {
address1
city
zipCode
country { name }
}
openingHours {
dayOfWeek
opens
closes
}
}
}
Exactly one location hint strategy applies per call. Providing both address and coordinates is an error.
ShippingMethodLocation Fields
| Field | Type | Description |
|---|---|---|
id | Int! | 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 |
distanceMeters | Float | Distance in meters from the shipping address to this location |
openingHours | [OpeningHoursEntry] | Opening hours per day of week |
openingHoursText | String | Human-readable opening hours summary |
brickAndMortar | Int | ID of a Centra brick-and-mortar store that may fulfill the order |
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: 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: 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. When Centra receives them, it silently attaches them to the Session — they are available on the session without the frontend needing to do anything extra. This allows the ESE to propagate provider-specific metadata (delivery window tokens, session identifiers, widget configuration, delivery promises, etc.) that the frontend or widget can read back from the session. Keys and values are defined by the ESE provider — Centra passes them through without interpretation. Each entry also carries provider and version to identify the source.
query {
session {
shippingProviderMetadata {
key
value
provider
version
}
}
}
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.
The impact of ESE on express checkout flows (e.g. Shop Pay, Apple Pay) is still under discussion.