Documentation

Headless Checkout API - Frontend Integration


The Headless Checkout API is a backend solution designed to enable the creation of a custom checkout experience, eliminating the need for direct frontend integration.

In this article, we offer tips on how to implement the checkout UI using the data provided in our responses.

Presenting delivery options

Here is part of the response from headless checkout:

{
  "deliveries": [
    {
      "id": "1",
      "delivery_categories": [
        {
          "id": "home-delivery-1384f5e83e3b4143a1c2475214a1de2c",
          "delivery_type": "DELIVERY",
          "display_name": "Home Delivery",
          "delivery_options": [
            {
              "id": "0196f224-1037-7aff-a6aa-972b631188a2",
              "carrier": {
                "name": "InPost",
                "product_id": "inpost-d2d"
              }
            }
          ]
        }
      ]
    }
  ]
}



Important data fields:

deliveries - in most cases there is one delivery, in split checkout there will be multiple deliveries (one for each group of items)

deliveries[].delivery_categories - (often referred to as category) each category is an abstraction of delivery choices for user, eg "Home Delivery", "Pickup Points", "Pick in Store"

deliveries[].delivery_categories[].delivery_options[] - (often referred to as option) each option is an actual choice, eg

We expose sort_order for categories and options. Our api already sorts categories and options according to those values but if different sort order is needed, you can use this field to get original order.

Home delivery

Depending on the need, you can show:

For mulitple carriers for home delivery, check option.carrier - this is the carrier and carrier product that will deliver the package to the user, full list in Supported Carrier Products.

Useful display fields:

{
"deliveries": [
{
"id": "1",
"delivery_categories": [
{
"id": "home-delivery-1384f5e83e3b4143a1c2475214a1de2c",
"delivery_type": "DELIVERY",
"display_name": "Home Delivery",
"delivery_options": [
{
"id": "0196f224-1037-7aff-a6aa-972b631188a2",
"carrier": {
"name": "InPost",
"product_name": "Kurier",
"product_id": "inpost-d2d"
},
"etd": {
"relative": {
"unit": "TIME_UNIT_BUSINESS_DAY",
"earliest": 4,
"latest": 5
}
},
"expire_time": "2025-05-25T22:00:00Z"
}
]
}
]
}
]
}

Pickup / Instore delivery

In most cases there will be one category for pickup points and each pickup point will have it's own option. Each pickup point typically is presented as a list of locations with a name and address on a map or on a list. User can pick only one pickup point.

It is possible to have one category with multi-carrier pickup point locations. Check option.carrier - for carrier and carrier product that will deliver the package to the pickup/instore location, full list in Supported Carrier Products.

Useful display fields:

{
"deliveries": [
{
"id": "1",
"delivery_categories": [
{
"id": "pickup-bc63c26bfdd4480d815a9d4c6f221094",
"delivery_type": "PICKUP",
"display_name": "Pickup Lockers",
"sort_order": 1,
"options_source": "CARRIER",
"delivery_options": [
{
"id": "0196f224-1036-7f2d-8bcf-0b3ac7267a63",
"carrier": {
"name": "DHL eCommerce Netherlands",
"product_name": "DHL Service Point",
"product_id": "dhl-svp",
"sort_order": 2
},
"pickup_location": {
"id": "PL-4513121",
"external_id": "PL-4513121",
"location_type": "STORE",
"title": "Żabka",
"visiting_contact": {
"address": {
"country_code": "PL",
"postal_code": "50-128",
"city": "Wrocław",
"address_lines": [
"Św. Mikołaja 16"
],
"coordinates": {
"lat": 51.111725,
"lng": 17.027769
}
}
},
"operational_hours": {
"monday": [
"06:00-23:00"
],
"tuesday": [
"06:00-23:00"
],
"wednesday": [
"06:00-23:00"
],
"thursday": [
"06:00-23:00"
],
"friday": [
"06:00-23:00"
],
"saturday": [
"06:00-23:00"
],
"sunday": [
"11:00-18:00"
]
},
"distances": {
"walking": {
"meters": "58",
"minutes": "1"
},
"driving": {
"meters": "414",
"minutes": "1"
}
}
},
"etd": {
"relative": {
"unit": "TIME_UNIT_BUSINESS_DAY",
"earliest": 6,
"latest": 8
}
},
"expire_time": "2025-05-25T22:00:00Z"
}
]
}
]
}
]
}

Selecting delivery options

For each deliveries you need to keep track of deliveries[].delivery_categories[].delivery_options[].id that user selected. User can select exactly one option per delivery and all deliveries needs to have an option selected.

To help with initial choice you can use deliveries[].delivery_categories[].delivery_options[].preselected to show selection when user opens the checkout page for the first time. There will be only one preselected option per each delivery.

Other topics

Delivery time

Delivery time can be presented to the user based on etd (estimated time of delivery) object available on option level.

{
"delivery_options": [
{
"id": "0196f224-1037-7aff-a6aa-972b631188a2",
"carrier": {
"name": "InPost",
"product_name": "Kurier",
"product_id": "inpost-d2d",
"external_id": "asdf"
},
"etd": {
"relative": {
"unit": "TIME_UNIT_BUSINESS_DAY",
"earliest": 4,
"latest": 5
}
},
"expire_time": "2025-05-25T22:00:00Z"
}
]
}

Please check swagger for all possible options in etd object.

Example js:


const formatRelativeTime = (relative: { earliest: number, latest: number, unit: string }) => {
let unitText = '';
const isPlural = relative.latest !== 1;

switch(relative.unit) {
case 'TIME_UNIT_MINUTE':
unitText = isPlural ? 'minutes' : 'minute';
break;
case 'TIME_UNIT_HOUR':
unitText = isPlural ? 'hours' : 'hour';
break;
case 'TIME_UNIT_DAY':
unitText = isPlural ? 'days' : 'day';
break;
case 'TIME_UNIT_BUSINESS_DAY':
unitText = isPlural ? 'business days' : 'business day';
break;
case 'TIME_UNIT_WEEK':
unitText = isPlural ? 'weeks' : 'week';
break;
case 'TIME_UNIT_MONTH':
unitText = isPlural ? 'months' : 'month';
break;
default:
unitText = isPlural ? 'days' : 'day';
break;
}

if (relative.earliest === relative.latest) {
return <span>Delivered in {relative.earliest} { unitText }</span>
}
return <span>
Delivered in {relative.earliest}-{relative.latest} { unitText }
</span>
}

const formatAbsoluteTime = (absolute: { earliest_time: string, latest_time: string }) => {
// date: 2025-06-18T06:00:00Z
if (!absolute || !absolute.earliest_time || !absolute.latest_time) {
return <span className="text-gray-400">Invalid estimated time: { JSON.stringify(absolute) }</span>;
}
const earliestDate = new Date(absolute.earliest_time);
const latestDate = new Date(absolute.latest_time);

const options: Intl.DateTimeFormatOptions = {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
};

return <span>
Delivered between {earliestDate.toLocaleString('en-PL', options)} and {latestDate.toLocaleString('en-PL', options)}
</span>;
}

const formatEtd = (etd: ETD) => {
if (!etd) {
return <span className="text-gray-400">No estimated time</span>;
}
if (etd.custom !== undefined) {
return <span>{etd.custom.text}</span>;
}
if (etd.relative !== undefined) {
return formatRelativeTime(etd.relative);
}
if (etd.absolute !== undefined) {
return formatAbsoluteTime(etd.absolute);
}
return <span className="text-gray-400">No estimated time</span>;
}

Relative delivery time

Here are some examples of how to present relative delivery time to the user:

Earliest Latest
Unit
Ingrid Widget Example
1 2
TIME_UNIT_DAY
Delivered in 1-2 days
1 2
TIME_UNIT_BUSINESS_DAY
Delivered in 1-2 business days
141 151
TIME_UNIT_HOUR
Delivered in 141–151 hours
5123 5124
TIME_UNIT_MINUTE
Delivered in 5123-5124 minutes
1 1
TIME_UNIT_WEEK
Delivered next week
1 1
TIME_UNIT_MONTH
Delivered in 1 month

Absolute delivery time

Here are some examples of how to present absolute delivery time to the user:

Earliest
Latest
Ingrid Widget Example
2025-05-22T08:29:32Z 2025-05-22T08:29:32Z Delivered on 2025-05-22
2025-05-22T08:29:32Z 2025-05-22T08:29:32Z Delivered next day
2025-05-22T08:29:32Z 2025-05-23T08:29:32Z Delivered in 1-2 days
2025-05-22T08:29:32Z 2025-05-23T08:29:32Z Delivered in 1-2 business days

Warehouse

There's option to present warehouse information to the user. It is available in category.

{
"deliveries": [
{
"id": "1",
"delivery_categories": [
{
"id": "home-delivery-1384f5e83e3b4143a1c2475214a1de2c",
"delivery_type": "DELIVERY",
"warehouse": {
"id": "swe-62aac37054cb4b60862e7aebc33b38ab",
"address": {
"country_code": "SE",
"postal_code": "111 34",
"city": "Stockholm",
"address_lines": [
""
]
}
}
}
]
}
]
}

Addons

Addons are selectable extra paid services that can be added to the delivery. Example: "gift wrapped", "extra fast".

Addons are available on category level and can be selected by the user.

{
"deliveries": [
{
"id": "1",
"delivery_categories": [
{
"id": "delivery-a20772d216ff43aa9b45182e6fcfbdc1",
"delivery_type": "DELIVERY",
"display_name": "Home Delivery",
"delivery_options": [],
"addons": [
{
"display_name": "Express delivery",
"price": "500",
"addon_type": "CUSTOM",
"id": "19051ba0-682b-11ee-ae2a-9ee5ca455892"
}
]
}
]
}
]
}

If user selects addon, addon id needs to be sent to backend in complete session request.

Last updated: Thu, Jun 19, 09:58 AM