Create an order
Records a checkout from your website. DUMA prices it from the products you name, matches or creates the customer by email, keeps the address and takes the stock off at once.
https://api.duma-business.com/v1/store/ordersorders:writeSecret key only
Idempotent by externalReference: the same reference with the same shopper and lines returns the original order with status 200 and replayed: true; with a different basket it is 409 idempotency_conflict.
A size that has run out fails the whole order with 409 out_of_stock and an items list of what was short. Nothing is recorded.
If expectedTotal disagrees with DUMA’s total the answer is 409 total_mismatch, carrying total, items, deliveryFee and discount.
A pending order holds its stock and promo code until expiresAt; unclaimed by then, it expires and gives both back.
A promo code that cannot be used is a 400 promo_invalid; one used up as the order was placed is 409 promo_unavailable.
Request example
curl -X POST "https://api.duma-business.com/v1/store/orders" \
-H "Authorization: Bearer dk_sec_…" \
-H "Content-Type: application/json" \
-d '{
"externalReference": "WEB-10482",
"customer": {
"email": "[email protected]",
"firstName": "Amelia",
"lastName": "Hart",
"phone": "07700 900123",
"marketingOptIn": true
},
"items": [
{
"variantId": "3d7e9b1a-6c4f-4e2d-b8a5-9f0c1e2d3a4b",
"quantity": 2
}
],
"fulfilment": {
"type": "shipping",
"method": "Royal Mail Tracked 48",
"fee": "4.95",
"address": {
"recipientName": "Amelia Hart",
"phone": "07700 900123",
"line1": "14 Elm Grove",
"city": "Bristol",
"postcode": "BS6 6AH",
"country": "GB"
}
},
"payment": {
"provider": "stripe",
"reference": "pi_3PqR8sLkdIwHu7ix0abc1234",
"method": "card",
"status": "paid"
},
"expectedTotal": "100.95"
}'Request body
JSON, sent with Content-Type: application/json.
externalReferencestringRequired- Your own order number, up to 120 characters. Sending it again returns the same order.
customerobjectRequired- The shopper. Matched to an existing customer by email, or created.
customer.emailstringRequired- Lower-cased for you. Up to 255 characters.
customer.firstNamestringRequired- Up to 100 characters.
itemsarrayRequired- The basket, 1 to 100 lines. Name each line’s product with
productId,variantIdorsku— prices always come from DUMA. items[].quantitynumberRequired- A whole number from 1 to 999.
fulfilmentobjectRequired- How the order reaches the shopper.
fulfilment.typestringRequired- Shipping and delivery need an
address; collection does not. shippingdeliverycollectionfulfilment.address.recipientNamestringRequired- Up to 200 characters.
fulfilment.address.line1stringRequired- Up to 200 characters.
fulfilment.address.citystringRequired- Up to 120 characters.
fulfilment.address.postcodestringRequired- Up to 20 characters.
fulfilment.address.countrystringRequired- A two-letter country code, such as GB. Upper-cased for you.
paymentobjectRequired- Who took the money, and whether it has been taken.
payment.providerstringRequired- Who took the money, e.g. "stripe", "paypal". Up to 50 characters.
Optional
customer.lastNamestringDefault ""- Up to 100 characters.
customer.phonestring- Up to 30 characters.
customer.marketingOptInbooleanDefault false- The shopper ticked “email me news and offers” at checkout.
items[].productIdstring (uuid)- The product. A product that comes in sizes also needs
variantIdorsku. items[].variantIdstring (uuid)- The size. On its own it names its product.
items[].skustring- The size by SKU, instead of
variantId. Up to 100 characters. items[].discountCodestring- Deprecated: a catalogue discount’s code for this line. Use
promoCodeinstead. fulfilment.methodstring- Your name for the service, e.g. “Royal Mail Tracked 48”. Up to 100 characters.
fulfilment.feestringDefault "0"- Delivery fee as a decimal string such as "4.95". Shipping and delivery only; collection is always free.
fulfilment.addressobject- Required for shipping and delivery.
fulfilment.address.phonestring- Up to 30 characters.
fulfilment.address.line2string- Up to 200 characters.
fulfilment.address.regionstring- Up to 120 characters.
payment.referencestring- Their reference for it — a payment intent or charge id.
payment.methodstringDefault "card"- Up to 50 characters.
payment.statusstringDefault paidpaid: your website took the money.pending: not paid yet (e.g. a bank transfer staff will check).claimed: the shopper says they have paid.paidpendingclaimedpayment.expiresInMinutesnumberDefault 60- How long a
pendingorder holds its stock and promo code unclaimed, 5–10080 minutes. currencystring- The three-letter currency the shopper pays in; the workspace’s if omitted. Every line needs a price in it, or the request fails with 409
price_unavailable. promoCodestring- A promo code for the whole order (promotions module). Case and spaces do not matter. Up to 60 characters.
expectedTotalstring- What your website charged, items plus delivery, as a decimal string. If DUMA’s total differs, nothing is recorded.
notesstring- Up to 1000 characters.
Response
{
"data": {
"id": "6b2d8f4a-1e3c-4a5b-9d7e-0f1a2b3c4d5e",
"externalReference": "WEB-10482",
"status": "pending",
"paymentStatus": "paid",
"payment": {
"status": "paid",
"provider": "stripe",
"reference": "pi_3PqR8sLkdIwHu7ix0abc1234",
"method": "card"
},
"expiresAt": null,
"totals": {
"items": "96.00",
"deliveryFee": "4.95",
"discount": "0.00",
"tax": "16.00",
"total": "100.95"
},
"currency": "GBP",
"total": "100.95",
"tax": "16.00",
"deliveryFee": "4.95",
"fulfilment": {
"type": "shipping",
"method": "Royal Mail Tracked 48",
"address": {
"recipientName": "Amelia Hart",
"phone": "07700 900123",
"line1": "14 Elm Grove",
"line2": null,
"city": "Bristol",
"region": null,
"postcode": "BS6 6AH",
"country": "GB"
}
},
"customerId": "9c1e3a5b-7d2f-4b6a-8e0c-1d3f5a7b9c2e",
"items": [
{
"productId": "8f1c2a4e-3b6d-4c1e-9a7f-2d5b8e0c4a13",
"variantId": "3d7e9b1a-6c4f-4e2d-b8a5-9f0c1e2d3a4b",
"sku": "HOOD-BLK-M",
"name": "Oversized Hoodie — M / Black",
"quantity": 2,
"unitPrice": "48.00",
"subtotal": "96.00"
}
],
"createdAt": "2026-10-28T10:42:17.000Z"
}
}