Delivery API
How to use our Delivery API
Access
Many endpoints in the delivery API supports both anonymous and authenticated requests. If a user is authenticated permissions and personalisation will take affect on a given endpoint changing returned data like prices, what content is available, discounts, assortments and other user dependent information.
Some endpoints always require authentication, e.g. endpoints related to users, order history and favorites.
Authentication
Authentication works through authenticating with a username and password. This returns a JWT containing the necessary information to identify the user and the validity of the token.
To authenticate a user and obtain a JWT, use the authenticate endpoint.
Using GET
:
GET <yourHost>/dwapi/users/authenticate?userName=DemoUser&password=TestPassword123
Using POST
:
POST <yourHost>/dwapi/users/authenticate
{
"username": "DemoUser",
"password": "TestPassword123"
}
Response would look like the following:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1laWQiOiIxMyIsIm5iZiI6MTY4NDgyMTQ1MywiZXhwIjoxNjg0ODIyMDUzLCJpYXQiOjE2ODQ4MjE0NTMsImlzcyI6IkR5bmFtaWN3ZWIgQS9TIiwiYXVkIjoiV2ViQVBJIn0.E6qnfrmb2adq3SvFpyatjXsy78xf2SZxuVhCr1EJTXQ"
}
This token has to be stored in the client (your app) and then subsequently used in most of the other endpoints where authentication is required to handle on a specific user or with specific permissions or personalization.
The returned JWT has to be used as a header authorization bearer token:
authorization: Bearer <token>
where <token>
is the value returned in the authenticate response, e.g.:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1laWQiOiIxMyIsIm5iZiI6MTcxMzg4ODk5OCwiZXhwIjoxNzEzOTc1Mzk4LCJpYXQiOjE3MTM4ODg5OTgsImlzcyI6IkR5bmFtaWN3ZWIgQS9TIiwiYXVkIjoiV2ViQVBJIn0.Q1xPxgNqp9ahjexB0nl7xz5iv52q22mTOljj5va1a5U
JWTs are valid for a specific time. Default expiration for JWT in Dynamicweb is 1800 seconds, which is 30 minutes. This can be made shorter or longer when calling the authenticate endpoint. Longer expiration times are more insecure.
Before the token expires, it has to be refreshed, so your app should keep track of when the token was created so it can be refreshed before it expires. If a refresh is not made before the timeout, the login expires and the user has to authenticate again.
Refreshing JWT token
Refreshing the token is done using the refresh endpoint and passing in the Bearer token as well:
authorization: Bearer <token>
GET <yourHost>/dwapi/users/authenticate/refresh?expirationInSeconds=600
Which will provide a new token with a new expiration.
Impersonation
Impersonating lets you act in the role as another specific user, as long as you have the proper permissions to do so. You will simply be granted another token to use, which will have the identity of the user that's being impersonated.
To list all the users that's allowed to be impersonated with the given JWT token in the request, use
GET <yourHost>/dwapi/users/impersonatees
Including a header such as
authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1laWQiOiIxMyIsIm5iZiI6MTY4NDgyMTQ1MywiZXhwIjoxNjg0ODIyMDUzLCJpYXQiOjE2ODQ4MjE0NTMsImlzcyI6IkR5bmFtaWN3ZWIgQS9TIiwiYXVkIjoiV2ViQVBJIn0.E6qnfrmb2adq3SvFpyatjXsy78xf2SZxuVhCr1EJTXQ
The response is a list of users that can be impersonated
[
{
"address": "Vejnavn 1",
"city": "Aalborg",
"company": "Company Customer DK",
"country": "Denmark",
"countryCode": "DK",
"email": "noreply@dynamicweb.dk",
"id": 87,
"name": "Company Customer DK",
"phone": "87654321",
"userName": "CompanyCustomerDK",
"zip": "9000",
},
{
"email": "noreply@dynamicweb.dk",
"id": 88,
"name": "Company Customer UK",
"userName": "CompanyCustomerUK",
}
]
Impersonating one of these users is now as simple as calling
GET <yourHost>/dwapi/users/impersonate?userId=87
With the authorization
header as the previous request.
The response will the contain a new token for the specified user
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1laWQiOiI4NyIsIm5iZiI6MTY4NDgyMzA0MCwiZXhwIjoxNjg0ODIzNjQwLCJpYXQiOjE2ODQ4MjMwNDAsImlzcyI6IkR5bmFtaWN3ZWIgQS9TIiwiYXVkIjoiV2ViQVBJIn0.SpwDL7pq2LjcXSoZiaZ3hqdT3nU3X0JE4ZIBzBPlEnA"
}
Content
The content
part of the Delivery API enables access to a range of endpoints that will deliver data and content to websites.
The endpoints available falls into the following categories;
- Areas
- GridRows
- Navigations
- Pages
- PageViews
- Paragraphs
- Translations
Areas
Getting all the areas
for a given DynamicWeb solution is possible through the area
endpoints. It will provide the base for the website depending on which area
is being used
GET <yourHost>/dwapi/content/areas/{id}
Returns the specified area
with all of its items
{
"id": 3,
"name": "Swift B2C demo shop",
"createdDate": "2021-01-04T15:53:06.073",
"updatedDate": "2023-04-04T14:10:56.26",
"item": {
"fields": [
{
"name": "Title",
"systemName": "Title",
"value": "Swift"
},
{
"name": "Header desktop",
"systemName": "HeaderDesktop",
"value": {
"pageId": 121,
"paragraphId": 0,
"url": "/swift-tools/layout/header/desktop",
"isExternal": false
}
},
{
"name": "Header mobile",
"systemName": "HeaderMobile",
"value": {
"pageId": 122,
"paragraphId": 0,
"url": "/swift-tools/layout/header/mobile",
"isExternal": false
}
},
...
],
"id": "6",
"systemName": "Swift_Master",
"pageID": 0,
"paragraphID": 0
},
"languages": [
{
"culture": "en-GB",
"firstPage": {
"id": 150,
"name": "Home"
},
"id": 3,
"isCurrent": true,
"isMaster": true,
"name": "Swift B2C demo shop",
"primaryDomain": ""
},
...
],
"ecomCountryCode": "DE",
"ecomCurrencyCode": "EUR",
"ecomLanguageId": "LANG1",
"ecomPricesWithVat": true,
"ecomShopId": "SHOP1"
}
GridRows
Getting GridRows
for specific pages is possible, providing the possibility for all the information to render a page
with its columns
and paragraphs
, it will be provided.
GET <yourHost>/dwapi/content/rows/{pageId}/{device}
Returns the structure of all the rows for the given page
based on the device
[
{
"id": 192,
"definition": {
"id": "1Column",
"name": "1 Column",
"description": "1 row with 1 column",
"template": "1Column.cshtml",
"columnCount": 1,
"itemType": "Swift_1ColumnFull",
"thumbnail": "/Files/Templates/Designs/Swift/Assets/Images/VisualEditor/DW_Row_Full.svg"
},
"columns": [
{
"columnNumber": 1,
"rowColumnCount": 1,
"paragraph": {
"id": 2269,
"name": "MyColumn",
"createdDate": "2021-05-17T08:39:56.04",
"updatedDate": "2023-02-24T09:06:46.717",
"pageID": 149,
"globalID": 0,
"text": "",
"item": {
"fields": [
{
"name": "Title",
"systemName": "Title",
"value": "MyColumn"
}
],
"id": "4",
"systemName": "Swift_ProductCatalogApp",
"pageID": 149,
"paragraphID": 2269,
"link": "/products#2269"
},
"image": null,
"imageFocalX": 0,
"imageFocalY": 0,
"imageHAlign": "left",
"imageVAlign": "top",
}
}
],
"templatePath": "\\Designs\\Swift\\Grid\\Page\\RowTemplates\\1Column.cshtml",
...
},
...
]
Navigation
A navigation
is a list of links pointing to important areas of a website, often presented as a horizontal bar at the top of every page on a website – but you can have several different types of navigation
on a website.
The navigations
for specific areas and pages can be fetched from the navigation
endpoint
GET <yourHost>/dwapi/frontend/navigations/{areaId}
The areaId
is required and the rest of the properties can enable more layers of navigations
being returned, such as the full tree. A request such as
GET <yourHost>/dwapi/frontend/navigations/3?ExpandMode=all&StartLevel=1&StopLevel=3&IncludeFoldersAndHidden=true
Will return all level 1
and level 2
navigations
regardless of them being hidden
or folders
.
{
"nodes": [
{
"pageId": 150,
"groupId": null,
"name": "Home",
"link": "/home",
"level": 1,
"isClickable": true,
"inPath": false,
"isActive": false,
"showInSitemap": true,
"showInBreadcrumb": true,
"showInMenu": true,
"nodes": []
},
{
"pageId": 149,
"groupId": null,
"name": "Products",
"link": "/products",
"level": 1,
"isClickable": true,
"inPath": false,
"isActive": false,
"showInSitemap": true,
"showInBreadcrumb": true,
"showInMenu": true,
"nodes": [
{
"pageId": 169,
"groupId": null,
"name": "Details",
"link": "/details",
"level": 2,
"isClickable": true,
"inPath": false,
"isActive": false,
"showInSitemap": true,
"showInBreadcrumb": true,
"showInMenu": true,
"nodes": []
},
{
"pageId": 1279,
"groupId": null,
"name": "Clothing",
"link": "/clothing-1",
"level": 2,
"isClickable": true,
"inPath": false,
"isActive": false,
"showInSitemap": true,
"showInBreadcrumb": true,
"showInMenu": true,
"nodes": []
},
...
]
},
...
]
}
Pages
Being able to list all pages out for a given area
or all child pages
for a page
is possible with the pages
endpoints.
They provide all the specific data about a page
, both the option to get a collection of pages
for a given area and all their items
and additional information. It defines the content structure of a website in backend, which in turn can be used to generate dynamic navigation menus in frontend and as containers for content coming from a paragraph or an item.
GET <yourHost>/dwapi/content/pages/{id}
Returns the specifics of the requested page
given the id
{
"id": 150,
"name": "Home",
"createdDate": "2021-01-05T15:07:55.703",
"updatedDate": "2023-04-17T14:50:28.807",
"title": "",
"description": "Description of a page.",
"keywords": "",
"areaID": 3,
"path": [
{
"id": 150,
"name": "Home"
}
],
"languages": [
{
"id": 3,
"name": "Swift B2C demo shop",
"culture": "en-GB",
"primaryDomain": "",
"isCurrent": true,
"isMaster": true,
"page": {
"id": 150,
"name": "Home"
},
"pageIsHidden": false,
"pageIsPublished": true,
"firstPage": {
"id": 150,
"name": "Home"
},
"firstActivePage": {
"id": 150,
"name": "Home"
}
},
...
],
"item": {
"fields": [
{
"name": "Title",
"systemName": "Title",
"value": "Home"
},
...
]
},
"propertyItem": {
"fields": [
{
"name": "Header position",
"systemName": "MoveThisPageBehindTheHeader",
"value": {
"options": [
{
"value": "sticky-top",
"name": "Normal",
"icon": "/FilesTemplates/Designs/Swift/Assets/Images/ItemTypes/HeaderFixed.svg",
"isSelected": true,
"sortIndex": 0
},
...
]
}
},
...
]
}
}
PageViews
A PageView
gives you information about the current page
that your module
is used in. It gives you access to properties like the page
, the areaId
, information like style sheets and templates that are effective and it provides access to the current user
of the page
.
Paragraphs
Paragraphs can be fetched based on pageId
, areaId
, itemType
, and a specific paragraphs id
. They're used to define the content structure internally on a page
and as containers for content
coming from the standard paragraph
fields.
GET <yourHost>/dwapi/content/paragraphs/{id}
Returns all the information about a specific paragraph
including its items
and specific details about the paragraph
itself.
{
"id": 6511,
"name": "Free Shipping",
"createdDate": "2022-01-06T15:32:47.323",
"updatedDate": "2022-09-01T11:05:43.373",
"pageID": 150,
"globalID": 0,
"text": "",
"item": {
"fields": [
{
"name": "Upload new icon",
"systemName": "Image",
"value": [
{
"extension": ".svg",
"name": "truck.svg",
"path": "/Files/Images/Swift Demo Content/Icons/truck.svg",
"focalX": 0,
"focalPositionFromLeft": 50,
"focalY": 0,
"focalPositionFromTop": 50,
"pathUrlEncoded": "%2FFiles%2FImages%2FSwift+Demo+Content%2FIcons%2Ftruck.svg"
}
]
},
{
"name": "Title",
"systemName": "Title",
"value": "Free Shipping"
},
{
"name": "Text",
"systemName": "Text",
"value": "From all orders over $100"
},
...
],
"id": "1260",
"systemName": "Swift_Feature",
"pageID": 150,
"paragraphID": 6511,
"link": "/home#6511"
},
"image": null,
"imageFocalX": 0,
"imageFocalY": 0,
"imageHAlign": "left",
"imageVAlign": "top",
}
Translations
Getting translations will return all the basic elements such as labels, buttons, etc. translated to the approrpriate language depending on the culture used.
When requesting for the translations, an areaId
which will map to a designName
can be used, this will default to the areas culture and give the translations as such.
There's no authentication required to access these endpoints.
GET <yourHost>/dwapi/translations/area/{areaid}
Response contains a list of all the translations for this area
[
{
"key": "Contact",
"defaultValue": "",
"value": "Contact"
},
{
"key": "Request for quote",
"defaultValue": "Request for quote",
"value": "Request for quote"
},
...
]
If they need to be translated to a specific language, the culture can be sent in to a different endpoint
GET <yourHost>/dwapi/translations/{designname}/{culture}
Using a culture such as da-DK
will have the translations as danish
[
{
"key": "Contact",
"defaultValue": "",
"value": "Kontakt"
},
{
"key": "Request for quote",
"defaultValue": "Request for quote",
"value": "Opret en tilbudsførespørgsel"
},
...
]
Some scenarios would require getting a full list of all translation keys
and the translations for each. In that case the endpoint needing the designname will provide the data necessary
GET <yourHost>/dwapi/translations/{designname}
Every property in the response object will be a translation key
and contain all the data necessary with all the translations it has with each culture.
{
"Contact": {
"name": "Contact",
"scope": "designsLocal",
"defaultValue": "",
"translations": {
"en-GB": {
"cultureName": "en-GB",
"value": "Contact"
},
"da-DK": {
"cultureName": "da-DK",
"value": "Kontakt"
},
"nb-NO": {
"cultureName": "nb-NO",
"value": "Kontakt"
},
"en-US": {
"cultureName": "en-US",
"value": "Contact"
},
"nl-NL": {
"cultureName": "nl-NL",
"value": "Contact"
}
},
"tagDefinitions": []
},
"Request for quote": {
"name": "Request for quote",
"scope": "designsLocal",
"defaultValue": "Request for quote",
"translations": {
"en-GB": {
"cultureName": "en-GB",
"value": "Request for quote"
},
"da-DK": {
"cultureName": "da-DK",
"value": "Opret en tilbudsførespørgsel"
},
"en-US": {
"cultureName": "en-US",
"value": "Request for quote"
},
"nl-NL": {
"cultureName": "nl-NL",
"value": "Vraag om citaat"
}
},
"tagDefinitions": []
},
...
}
Commerce
When using the Commerce endpoints there are some context related information to consider.
Most of the endpoints can return the same data in different contexts that control e.g. prices and languages why most of the endpoints accepts additional parameters - the most important ones being the following:
CurrencyCode
CountryCode
LanguageId
E.g.
GET /dwapi/ecommerce/products/search?GroupId=group5&CurrencyCode=DKK&CountryCode=DK&LanguageId=LANG1
CurrencyCode
parameter controls the currency of returned pricesCountryCode
controls the VAT or sales taxes used in price calculationsLanguageId
controls the language of the returned data, e.g. the name and description of products or name shipping methods
Other context related information
There are other parameters that can affect the price of a product and the calculation of orderline sub totals.
Depending on the setup and prices in the price matrix, prices can differ on the following additional parameters:
OrderDate
if prices change in periods in time, this parameter can be usedStockLocationId
when prices change depending on stock location or when creating carts with stock checks in the specified stock locationShopId
when prices differs in different channels
User and personalised prices
The logged in user can also affect the prices and discounts applied to products and orders and can affect which products are returned if assortments are used.
Use a valid JWT in a bearer token, see the JWTs section here.
Products
The POST /dwapi/ecommerce/products
and GET /dwapi/ecommerce/products/search
are essentially the same except one is POST and the other is a GET.
The endpoint has the following search methods:
GroupID
to return products from a specific product groupQueryName
to use a pre made query to return products (together withRepositoryName
parameter)ProductIds
to find a range of products from the specific product ids
Get products by group id
To return products from a group, specify the group id parameter and use a valid group id as the value:
GET /dwapi/ecommerce/products/search?GroupId=group5&CurrencyCode=DKK&CountryCode=DK&LanguageId=LANG1
Get products using a query
To return products using a query, specify the QueryName
parameter and use a valid name of a query and also specify the RepositoryName
parameter and use the name of the repository of the query:
GET /dwapi/ecommerce/products/search?RepositoryName=Products&QueryName=Products&CurrencyCode=DKK&CountryCode=DK&LanguageId=LANG1
The query is defined in the admin under Settings > System > Repositories. The parameters defined in the query can be used as additional parameters on the endpoint for searching as well.
Say the query has a parameter called ShowActive
that is used in an expression on the query:
GET /dwapi/ecommerce/products/search?RepositoryName=Products&QueryName=Products&CurrencyCode=DKK&CountryCode=DK&LanguageId=LANG1&ShowActive=True
Get products using product ids
The product ids is sent in as a list, so the request can look like the following:
GET /dwapi/ecommerce/products/search?ProductIds=10003&ProductIds=10004&CurrencyCode=DKK&CountryCode=DK&LanguageId=LANG1
Result from products endpoint
The return of this endpoint is a product list viewmodel. The list viewmodel contains information about the group, number of products and pages and the product items themselves.
{
"group": {
"id": "GROUP5",
"name": "Road bikes",
"title": "Road bikes",
"number": "",
"description": "",
"metaDescription": "",
"sorting": null,
},
"subGroups": [],
"products": [
{
"id": "10003",
"languageId": "LANG1",
"name": "Scattante CFR Elite",
...
},
{
"id": "10004",
"languageId": "LANG1",
"name": "Scattante CFR Max",
...
}
...
],
"pageSize": 10,
"pageCount": 2,
"currentPage": 1,
"totalProductsCount": 16,
"sortBy": null,
"sortOrder": null,
"spellCheckerSuggestions": null,
"facetGroups": null
}
These 2 endpoints can take a lot of parameters to control which products to return, which properties of the product view model to return.
Refer to the Swagger docs on /dwapi/docs for options.
Get a single product
To retrieve a single product, add the product id as part of the URL and also specify currency code, country code and language id to get it in the right context:
GET /dwapi/ecommerce/products/10003?CurrencyCode=DKK&CountryCode=DK
Getting a variant of a product also add the variant id as part of the URL:
GET /dwapi/ecommerce/products/10003/VO1.VO2?CurrencyCode=DKK&CountryCode=DK
The product returned:
{
"id": "10003",
"variantId": "",
"languageId": "LANG1",
"name": "Scattante CFR Elite ",
"title": "Scattante CFR Elite ",
"shortDescription": "<p>Representing the upper crust of cycling society, the CFR Elite flaunts a beautiful carbon frame and sophisticated components that’ll have you accelerating with speed, power and agility.</p>",
"longDescription": "<ul>\n<li>Representing the upper crust of cycling society, the CFR Elite flaunts a beautiful carbon frame and sophisticated components that’ll have you accelerating with speed, power and agility. Lightweight carbon monocoque frame with carbon/aluminum fork offers a stiff and efficient ride with less vibration</li>\n<li>Shimano Ultegra drivetrain blends race-proven technology with ergonomic perfection to give you a new level of shifting performance</li>\n<li>FSA SLK Light Carbon cranks with MegaExo bottom bracket deliver super-stiff, no-flex pedaling power for the road ahead</li>\n<li>Forté Precision handlebar and stem with carbon seatpost supply core-level strength, style and vibration damping performance</li>\n<li>Tektro R740 dual pivot calipers deliver strong, progressive stopping power and good modulation</li>\n<li>Continental Ultra Sport tires provide comfort and exceptional handling, especially on rough road surfaces</li>\n<li>Mavic Aksium wheels are lightweight, responsive and ensure a stable, reliable ride</li>\n</ul>",
"metaDescription": "",
"metaTitle": "",
"metaKeywords": "",
"number": "10003",
"created": "2021-05-11T11:07:39",
"updated": "2024-03-06T13:57:27.773",
"keywords": "",
"stockLevel": -2,
"stockStatus": null,
"stockDeliveryText": null,
"stockDeliveryValue": null,
"weight": 2,
"productType": "stock",
"width": 2,
"height": 2,
"depth": 2,
"purchaseMinimumQuantity": 0,
"purchaseQuantityStep": 0,
"cost": 1254,
"ean": "9998877652764353674",
"expectedDelivery": null,
"discontinued": false,
"discontinuedAction": 0,
"pointPrice": 0,
"defaultVariantId": "",
"defaultUnitId": "",
"variantName": "",
"active": true,
"rating": 0,
"replacementProduct": {
"productId": "",
"variantId": ""
},
"price": {
"showPricesWithVat": false,
"price": 11993.33,
"priceFormatted": "11.993,33 kr.",
"priceWithoutVat": 11993.33,
"priceWithoutVatFormatted": "11.993,33 kr.",
"priceWithVat": 14991.67,
"priceWithVatFormatted": "14.991,67 kr.",
"vat": 2998.34,
"vatFormatted": "2.998,34 kr.",
"vatPercent": 25,
"vatPercentFormatted": "25%",
"currencyCode": "DKK"
},
"priceInformative": {
"showPricesWithVat": false,
"price": 0,
"priceFormatted": null,
"priceWithoutVat": 0,
"priceWithoutVatFormatted": null,
"priceWithVat": 0,
"priceWithVatFormatted": null,
"vat": 0,
"vatFormatted": null,
"vatPercent": 0,
"vatPercentFormatted": null,
"currencyCode": null
},
"priceBeforeDiscount": {
"showPricesWithVat": false,
"price": 11993.33,
"priceFormatted": "11.993,33 kr.",
"priceWithoutVat": 11993.33,
"priceWithoutVatFormatted": "11.993,33 kr.",
"priceWithVat": 14991.67,
"priceWithVatFormatted": "14.991,67 kr.",
"vat": 2998.34,
"vatFormatted": "2.998,34 kr.",
"vatPercent": 25,
"vatPercentFormatted": "25%",
"currencyCode": "DKK"
},
"discount": {
"showPricesWithVat": false,
"price": 0,
"priceFormatted": "0,00 kr.",
"priceWithoutVat": 0,
"priceWithoutVatFormatted": "0,00 kr.",
"priceWithVat": 0,
"priceWithVatFormatted": "0,00 kr.",
"vat": 0,
"vatFormatted": "0,00 kr.",
"vatPercent": 0,
"vatPercentFormatted": "0%",
"currencyCode": "DKK"
},
"productDiscounts": [],
"prices": [],
"productFields": {
"Total_height": {
"systemName": "Total_height",
"name": "Total height",
"type": "Double",
"value": 0,
"listType": 0
}
},
"productCategories": {
"brand_information": {
"id": "brand_information",
"name": "Brand information",
"fields": {
"Brand_name": {
"systemName": "Brand_name",
"name": "Brand name",
"type": "Text",
"value": "Scattante",
"listType": 0
},
"Gear_no_of": {
"systemName": "Gear_no_of",
"name": "Gear (# of)",
"type": "Integer",
"value": 21,
"listType": 0
}
}
}
}}
"groups": [],
"primaryOrDefaultGroup": {
"id": "GROUP155",
"name": "All bikes",
"sorting": null,
"primaryPageId": 0
},
"variantInfo": {
"productID": "10003",
"variantID": "",
"optionID": null,
"optionName": null,
"optionColor": null,
"optionSort": 0,
"productName": "Scattante CFR Elite ",
"productNumber": "10003",
"productStock": -2,
"variantInfoGroupId": null,
"variantInfoGroupName": null,
"variantInfoGroupDescription": null,
"variantGroupDisplayType": "nothingSelected",
"optionImage": null,
"image": {
"value": "/Files/Images/missing_image.jpg",
"name": "Default",
"keywords": null,
"displayName": null
},
"price": {
"showPricesWithVat": false,
"price": 11993.33,
"priceFormatted": "11.993,33 kr.",
"priceWithoutVat": 11993.33,
"priceWithoutVatFormatted": "11.993,33 kr.",
"priceWithVat": 14991.67,
"priceWithVatFormatted": "14.991,67 kr.",
"vat": 2998.34,
"vatFormatted": "2.998,34 kr.",
"vatPercent": 25,
"vatPercentFormatted": "25%",
"currencyCode": "DKK"
},
"priceMin": {
"showPricesWithVat": false,
"price": 11993.33,
"priceFormatted": "11.993,33 kr.",
"priceWithoutVat": 11993.33,
"priceWithoutVatFormatted": "11.993,33 kr.",
"priceWithVat": 14991.67,
"priceWithVatFormatted": "14.991,67 kr.",
"vat": 2998.34,
"vatFormatted": "2.998,34 kr.",
"vatPercent": 25,
"vatPercentFormatted": "25%",
"currencyCode": "DKK"
},
"priceMax": {
"showPricesWithVat": false,
"price": 11993.33,
"priceFormatted": "11.993,33 kr.",
"priceWithoutVat": 11993.33,
"priceWithoutVatFormatted": "11.993,33 kr.",
"priceWithVat": 14991.67,
"priceWithVatFormatted": "14.991,67 kr.",
"vat": 2998.34,
"vatFormatted": "2.998,34 kr.",
"vatPercent": 25,
"vatPercentFormatted": "25%",
"currencyCode": "DKK"
},
"stock": -2,
"variantInfo": null
},
"defaultImage": {
"value": "/Files/Images/missing_image.jpg",
"name": "Default",
"keywords": null,
"displayName": null
},
"groupPaths": [],
"imagePatternImages": [],
"manufacturer": {
"id": null,
"name": null,
"address": null,
"zipCode": null,
"city": null,
"country": null,
"phone": null,
"fax": null,
"email": null,
"web": null,
"logo": null,
"description": null
},
"assetCategories": [],
"neverOutOfstock": false,
"stockUnits": [],
"unitOptions": [],
"relatedGroups": []
}
The amount of fields can be limited by using the FilledProperties
option`:
GET /dwapi/ecommerce/products/10003?CurrencyCode=DKK&CountryCode=DK&FilledProperties=Id,Name,Price
Which will make the returned document smaller:
{
"id": "10003",
"name": "Scattante CFR Elite ",
"price": {
"showPricesWithVat": false,
"price": 11993.33,
"priceFormatted": "11.993,33 kr.",
"priceWithoutVat": 11993.33,
"priceWithoutVatFormatted": "11.993,33 kr.",
"priceWithVat": 14991.67,
"priceWithVatFormatted": "14.991,67 kr.",
"vat": 2998.34,
"vatFormatted": "2.998,34 kr.",
"vatPercent": 25,
"vatPercentFormatted": "25%",
"currencyCode": "DKK"
}
}
One of the returned properties is Price, which is a view model it self with a number of properties. The return of those properties can also be limited by adding an additional parameter.
The amount of fields on the price viewmodel can be limited by using the PriceSettings.FilledProperties
option:
GET /dwapi/ecommerce/products/10003?CurrencyCode=DKK&CountryCode=DK&FilledProperties=Id,Name,Price&PriceSettings.FilledProperties=priceWithVatFormatted,priceWithoutVatFormatted
Which will make the returned document even smaller:
{
"id": "10003",
"name": "Scattante CFR Elite ",
"price": {
"priceWithoutVatFormatted": "11.993,33 kr.",
"priceWithVatFormatted": "14.991,67 kr.",
}
}
Carts
Carts are created by using POST /dwapi/ecommerce/carts/create
endpoint. The endpoint takes parameters to control currency, VAT and language as described here.
CurrencyCode
CountryCode
LanguageId
A body is required in the POST
of the create endpoint, but can be an empty document.
The cart can be created in the following contexts:
- For anonymous user
- For the current user
- For users that the current user can impersonate
For anonymous users, make an anonymous post.
POST /dwapi/ecommerce/carts/create?CurrencyCode=DKK&=DK&LanguageId=LANG1
{
"customerName": "John Doe"
}
For the current user, pass the JWT as a bearer token to create the cart in the context of the user to respect user specific prices and relate the cart to the user for later retrieval of stored carts. The id of the current user will be set on the cart automatically.
Authorization: Bearer <token>
POST /dwapi/ecommerce/carts/create?CurrencyCode=DKK&=DK&LanguageId=LANG1
{
}
For users that the current user can impersonate, pass the current user JWT as bearer and in the POST body document add the CustomerUserId with the id of user the cart should belong to. The CustomerUserId has to be a user that the current user can impersonate or a 403 will be returned.
Authorization: Bearer <token>
POST /dwapi/ecommerce/carts/create?CurrencyCode=DKK&=DK&LanguageId=LANG1
{
"CustomerUserId" : 123
}
In the above example the user id that is connected to the JWT is e.g. 100 and that user can impersonate user with id 123 and will create a cart on behalf of user 123.
The return of the POST
is an order viewmodel of the newly created cart with its id
and its secret
which has to be stored in your JS app and used in subsequent requests to the cart endpoints where {secret}
is used as identifier.
{
"id": "CART123",
"secret": "a480a17e0d9e40a9bd3499be74db1b8f",
"customerUserId" : 123,
"deliveryCountryCode": "DK",
"createdAt": "2024-04-23T18:26:03.2794348+02:00",
"modified": "2024-04-23T18:26:03.286099+02:00",
...
}
To retrieve the cart at any point in time, use the secret in the get:
GET /dwapi/ecommerce/carts/a480a17e0d9e40a9bd3499be74db1b8f
Adding products to cart
To add a product to the cart, a new orderline has to be created using the POST /dwapi/ecommerce/carts/{secret}/items
endpoint.
POST /dwapi/ecommerce/carts/a480a17e0d9e40a9bd3499be74db1b8f/items
{
"ProductId": "10100",
"ProductVariantId": "",
"Quantity": 2
}
The endpoint returns 200 when the product is added. To get an updated cart with the new prices, call the GET /dwapi/ecommerce/carts/{secret}
endpoint to get the updated instance of the cart.
Update quantity on orderline
To update an orderline and change i.e. the quantity, use the PATCH /dwapi/ecommerce/carts/{secret}/items/{itemId}
endpoint:
PATCH /dwapi/ecommerce/carts/a480a17e0d9e40a9bd3499be74db1b8f/items/OL12
{
"quantity": 12
}
The endpoint returns 200 when the product is added. To get an updated cart with the new prices, call the GET /dwapi/ecommerce/carts/{secret}
endpoint to get the updated instance of the cart.
Set shipping method
To set the shipping method of the order, call the PATCH /dwapi/ecommerce/carts/{secret}/shipping/{shippingId}
endpoint with a valid cart secret and a valid shipping id. The list of available shippings can be retrieved on the /shippings
endpoint.
PATCH /dwapi/ecommerce/carts/a480a17e0d9e40a9bd3499be74db1b8f/shipping/SHIP1
When the shipping method is applied, the price of the cart is updated to reflect the shipping fee.
Set payment method
To set the payment method of the order, call the PATCH /dwapi/ecommerce/carts/{secret}/payment/{paymentId}
endpoint with a valid cart secret and a valid payment id. The list of available payments can be retrieved on the /payments
endpoint.
PATCH /dwapi/ecommerce/carts/a480a17e0d9e40a9bd3499be74db1b8f/payment/PAY1
When the payment method is applied, the price of the cart is updated to reflect the payment fee.
Complete order
To convert a cart to an order there are the following options:
- Complete an order after handling payment gateway in your app or if the user pays by invoice
- Start checkout flow to collect payment from payment gateway
To convert a cart to an order without starting a payment flow make a POST to the createorder endpoint using the cart secret.
POST /dwapi/ecommerce/carts/a480a17e0d9e40a9bd3499be74db1b8f/createorder
After this post, the cart is converted to an order with an order id instead of cart id and complete is set to true - the cart secret remains the same. The cart can no longer be modified and retrieved on the other cart endpoints. The order can now be retrieved on the orders endpoints instead for authenticated users.
To start a checkout flow using a payment gateway, call the checkout endpoint using the cart secret.
The cart has to have a payment method set that has a checkouthandler defined and setup. The checkout handler also has to support headless checkouts.
GET /dwapi/ecommerce/carts/a480a17e0d9e40a9bd3499be74db1b8f/checkout
When this endpoint is called, the cart is converted to an order with an order id (and no longer a cart id), but its complete state is still false. The return of this call is not a regular JSON document. It is the result of passing the order to the checkout handler which will create a payment and return some markup that needs to be injected in your app to start the payment window.
<!--/checkout response-->
<div>
....
<script>//logic to start payment gateway</script>
....
</div>
When the payment flow in the payment window is successfully completed, the payment gateway will make server-to-server callback to the /callback endpoint (you do not have to do this) which will set the order as complete.
TBD: When and how to show receipt