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:
CurrencyCodeCountryCodeLanguageId
E.g.
GET /dwapi/ecommerce/products/search?GroupId=group5&CurrencyCode=DKK&CountryCode=DK&LanguageId=LANG1
CurrencyCodeparameter controls the currency of returned pricesCountryCodecontrols the VAT or sales taxes used in price calculationsLanguageIdcontrols 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 order line sub totals.
Depending on the setup and prices in the price matrix, prices can differ on the following additional parameters:
OrderDateif prices change in periods in time, this parameter can be usedStockLocationIdwhen prices change depending on stock location or when creating carts with stock checks in the specified stock locationShopIdwhen 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.
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:
GroupIDto return products from a specific product groupQueryNameto use a pre made query to return products (together withRepositoryNameparameter)ProductIdsto 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
Important
Please note that in a headless setup, macros are not available as they depend on a frontend context to run. Instead set a parameter (e.g. ShopId) and call it as a custom parameter through the /dwapi/query parameter.
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.
The endpoint takes parameters to control currency, VAT, and language as described earlier:
CurrencyCodeCountryCodeLanguageId
A body is required in the POST call but can be an empty JSON document.
Carts can be created in the following contexts:
- For anonymous users
- For the current authenticated user
- For users that the current user is allowed to impersonate
Anonymous users
For anonymous users, make a POST request without authentication:
POST /dwapi/ecommerce/carts/create?CurrencyCode=DKK&CountryCode=DK&LanguageId=LANG1
Content-Type: application/json
{
"customerName": "John Doe"
}
Current user
For the current authenticated user, pass a valid JWT as a bearer token. The cart will be created in the user’s context, respecting user-specific prices, assortments, and discounts.
Authorization: Bearer <token>
POST /dwapi/ecommerce/carts/create?CurrencyCode=DKK&CountryCode=DK&LanguageId=LANG1
Content-Type: application/json
{}
The CustomerUserId will automatically be set to the ID of the authenticated user.
Impersonation (creating carts on behalf of another user)
The POST /dwapi/ecommerce/carts/create endpoint also supports creating a cart on behalf of another user.
This is useful when a sales representative or customer service agent needs to create a cart for a specific customer.
This scenario uses two user identifiers:
- CustomerUserId – the user who owns the cart and whose prices, discounts, and assortments are applied.
- SecondaryUserId – the authenticated user (from the JWT) who creates the cart on behalf of the customer.
When a cart is created with a CustomerUserId different from the authenticated user, the system automatically sets SecondaryUserId and verifies that impersonation is allowed.
If impersonation is not allowed, the endpoint returns HTTP 403 Forbidden.
Example
A sales representative, Alice, is logged in and creates a cart for Bob’s Hardware.
| User | ID | Role |
|---|---|---|
| Alice | 42 | Sales representative |
| Bob’s Hardware | 101 | Customer |
Alice is allowed to impersonate Bob.
Authorization: Bearer <JWT for Alice>
POST /dwapi/ecommerce/carts/create?CurrencyCode=DKK&CountryCode=DK&LanguageId=LANG1
Content-Type: application/json
{
"CustomerUserId": 101,
"Name": "Bob’s order",
"OrderLines": [
{ "ProductId": "P-1001", "Quantity": 3 }
]
}
What happens internally:
- The API authenticates the current user (Alice, ID
42). - The body specifies
CustomerUserId = 101, so impersonation is triggered. - The API validates impersonation rights via the configured
userImpersonationService. - If valid, a new cart is created with:
CustomerAccessUserId = 101(the cart belongs to Bob)SecondaryUserId = 42(Alice created it)
- All price and discount calculations are based on Bob’s customer data.
Example response:
{
"id": "CART12345",
"secret": "b9c81b7b27a54a37a2cd89b962aa4bfa",
"customerUserId": 101,
"secondaryUserId": 42,
"deliveryCountryCode": "DK",
"createdAt": "2024-04-23T18:26:03.279Z",
"name": "Bob’s order",
...
}
This creates a clear audit trail showing that the cart belongs to Bob, but was created by Alice.
Quick reference
| Scenario | Authentication | Body parameter | Result |
|---|---|---|---|
| Anonymous cart | None | none | Creates anonymous cart |
| Current user cart | Bearer token for current user | none | Creates cart for current user |
| Impersonated customer cart | Bearer token for current user | CustomerUserId = target user ID |
Creates cart for another user (if impersonation is allowed) |
Retrieve cart
The return of the POST is an order ViewModel of the newly created cart with its id and secret, which should be stored and used in subsequent requests where {secret} is required.
{
"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 a cart, use the secret in a GET request:
GET /dwapi/ecommerce/carts/a480a17e0d9e40a9bd3499be74db1b8f
Adding products to cart
To add a product to the cart, a new order line 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.
The body is an order line view model. The properties used when adding a product are:
| Property | Required | Notes |
|---|---|---|
ProductId |
Yes | Together with ProductVariantId and the cart language, this is what identifies the product to add. The product must resolve in the cart's context. |
ProductVariantId |
Yes | Empty string for the master product, otherwise the variant id (e.g. VO1.VO2). |
Quantity |
Yes | Must be greater than 0 (unless the DoNotDeleteOrderLinesWithZeroQuantity setting is enabled). |
ProductLanguageId |
No | Defaults to the cart's language. If set, it must match the resolved product's language or the line is not created. |
UnitId |
No | The product unit. If empty, the product's default unit is used during price calculation. |
StockLocationId |
No | The stock location used for stock checks and, where applicable, price lookup. |
Reference |
No | A free-text reference stored on the order line. |
ProductName |
No | Overrides the displayed product name on the line. If omitted, the catalog product name is used. |
ProductNumber |
No | See below. |
OrderLineType |
No | See below. |
UnitPrice |
No | See Fixed order lines and custom prices below. |
Important
The price is never sent as part of a normal add-to-cart payload. The unit price, totals, VAT, and discounts are always calculated server-side from the product catalog in the cart's context (currency, country, user/assortment, quantity, unit). After adding a line, call GET /dwapi/ecommerce/carts/{secret} to read the recalculated prices. Setting a price in the payload is only honored in the specific scenarios described under Fixed order lines and custom prices.
ProductNumber
ProductNumber does not participate in finding the product — lookup is done exclusively from ProductId + ProductVariantId + language. If you send ProductNumber, it only overrides the product number stored and displayed on the order line. If omitted, the order line inherits the number from the catalog product. Sending both ProductId and ProductNumber is therefore not a conflict; the ProductNumber value is purely cosmetic for the line.
OrderLineType
OrderLineType can be sent in the payload, but it must be sent as the numeric enum value, not the name. The Commerce delivery API binds enums from numbers only, so a string such as "product" or "fixed" is ignored (it deserializes to no value, and the order line keeps its default type, Product). The numeric values are:
| Value | Type |
|---|---|
0 |
Product |
1 |
Discount |
2 |
Fixed |
3 |
ProductDiscount |
4 |
Tax |
5 |
PointProduct |
6 |
GiftCard |
For a normal catalog product you should not send OrderLineType at all — leave the line as a Product line so the price is calculated from the catalog. The type is mostly relevant for non-catalog lines (see below).
Fixed order lines and custom prices
A Fixed order line with a price supplied in the payload is possible, but the behaviour depends on whether the ProductId resolves to a catalog product, and on a global setting.
1. Non-catalog (custom) line — ProductId does not resolve a product
If the ProductId cannot be resolved to a product (e.g. an arbitrary id, or no id) and a UnitPrice is supplied in the payload, a custom line is created from the payload values. This is the intended way to add a line that does not exist in the catalog (a fee, a manual line, an external product, etc.).
POST /dwapi/ecommerce/carts/a480a17e0d9e40a9bd3499be74db1b8f/items
{
"ProductName": "Assembly service",
"Quantity": 1,
"OrderLineType": 2,
"UnitPrice": {
"priceWithoutVat": 200
}
}
- The price is taken from
UnitPrice(priceWithoutVat, orpriceWithVatif only that is provided — VAT is then calculated on top). - If
OrderLineTypeis omitted, the line defaults toFixed(2) for these custom lines. - Without a
UnitPrice, a line cannot be created this way and the endpoint returns 404 (no product found and no price to use).
2. Catalog product with an overridden unit price
If the ProductId does resolve to a catalog product, the payload price is ignored by default. It is only honored when the administrator has enabled the setting /Globalsettings/Ecom/Cart/AllowSetUnitPriceOnViewmodel (disabled by default) and the product is of type Stock or Service. In that case the supplied UnitPrice overrides the catalog price and the order line is converted to a Fixed line automatically.
POST /dwapi/ecommerce/carts/a480a17e0d9e40a9bd3499be74db1b8f/items
{
"ProductId": "10100",
"Quantity": 1,
"UnitPrice": {
"priceWithoutVat": 150,
"priceWithVat": 187.5
}
}
The UnitPrice view model accepts priceWithoutVat, priceWithVat, vat, and vatPercent. If both priceWithoutVat and priceWithVat are supplied, both are used as given; if only one is supplied, the other is calculated.
Note
Because overriding catalog prices is gated behind AllowSetUnitPriceOnViewmodel and limited to Stock/Service products, for a default Dynamicweb installation adding a catalog product will always use the calculated catalog price regardless of any UnitPrice in the payload.
Update quantity on orderline
To update an order line 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 order line is updated. 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 shipping methods 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.
Receipts
There is no dedicated "receipt" endpoint. A receipt is produced from the completed order data, and you have two complementary options.
On-screen receipt (headless)
After a cart has been completed — either via POST /carts/{secret}/createorder or after a successful payment-gateway flow — the order can be retrieved as an OrderViewModel and rendered as a receipt in your app:
GET /dwapi/ecommerce/orders/{secret}
The cart secret is preserved when the cart becomes an order, so you reuse the same value. This endpoint returns the full order (lines, totals, taxes, shipping/payment, addresses) and is what you build your on-screen receipt from.
Note
Once a cart is completed it can no longer be read on the /carts/{secret} endpoints — use /orders/{secret} instead. For authenticated users, previous orders can also be listed via GET /dwapi/ecommerce/orders/search.
For the payment-gateway checkout flow, the receiptUrl you pass to the checkout endpoint is the page in your app that the gateway returns the customer to after a successful payment; on that page you call GET /dwapi/ecommerce/orders/{secret} to show the receipt.
Email receipt
The headless completion endpoints (createorder / checkout) do not themselves send an order-confirmation email — unlike the classic frontend cart flow, no confirmation mail is sent as part of the API call.
To send an email receipt in a headless setup, configure the "Order Emails" scheduled task in the administration (Settings > System > Scheduled tasks). It selects orders by order type, flow and state, renders a configurable email template, and sends the mail to the customer, recording an email-history key so the same order is not mailed twice. Drive it by completing orders into a flow/state that the task is configured to pick up.
Important
Because the email is sent by a scheduled task rather than synchronously by the API call, the confirmation email is sent on the task's next run after the order reaches the configured state — not at the instant createorder/checkout returns.