Skip to content
Online store

Cart

Create the cart, add items, apply a code, and choose shipping with the Storefront API.

Andrea buys two T-shirts from Miraflores, in Lima. She uses the code BIENVENIDA10 and asks for delivery on Thursday at 10:00. This guide builds that cart up to checkout.

Requirements

  • The store slug or a configured domain, in x-Store-Domain. You don't need an access token.
  • A published variant and active shipping methods. See Headless store and Zones and rates.

Create the cart

POST /v1/carts creates a cart in the store's currency. Keep token: it identifies the cart in every call.

curl -X POST https://storefront.ecomiq.pe/v1/carts \
  -H "x-Store-Domain: mi-tienda" \
  -H "Content-Type: application/json" \
  -d '{ "email": "andrea.ramos@example.com" }'
Response (excerpt)
{
  "token": "kP3vX9qL2mT7wR4nB8sD1A",
  "cartNumber": "C-04325176",
  "currencyCode": "PEN",
  "status": "inProgress"
}

The examples below reuse API, headers, and cart.

  • email is optional. You can also send it with the buyer's details.
  • If the customer is signed in, create the cart with their X-Customer-Session. The cart then uses their prices and their reserved codes. See Customer accounts.

Read the cart

GET /v1/carts/{cartToken} returns the full cart. Use it when the page reloads.

curl https://storefront.ecomiq.pe/v1/carts/<cart-token> \
  -H "x-Store-Domain: mi-tienda"
  • A cart with no activity moves to abandoned. Reading or changing it reopens it.
  • A completed or cancelled cart no longer accepts changes. Create a new one.

Add items

POST /v1/carts/{cartToken}/items adds a variant. This and every other operation return the recalculated cart.

curl -X POST https://storefront.ecomiq.pe/v1/carts/<cart-token>/items \
  -H "x-Store-Domain: mi-tienda" \
  -H "Content-Type: application/json" \
  -d '{ "variantId": "7204558912004325377", "quantity": 2 }'
Response (excerpt)
{
  "items": [
    {
      "id": "7204558912004325450",
      "title": "Polo de algodón",
      "quantity": 2,
      "unitPrice": 79
    }
  ],
  "totals": {
    "subtotal": 158,
    "discountTotal": 0,
    "shippingTotal": 0,
    "total": 158
  }
}
  • Adding the same variant again increases its line.
  • Stock is checked against the variant's total quantity in the cart.
  • Adding, changing, or removing an item that ships clears the shipping chosen for its group. Choose shipping last.

Change the quantity

PATCH /v1/carts/{cartToken}/items/{cartItemId} sets a line's quantity. Use the item id, not the variant id. With 0, the line is removed.

curl -X PATCH https://storefront.ecomiq.pe/v1/carts/<cart-token>/items/7204558912004325450 \
  -H "x-Store-Domain: mi-tienda" \
  -H "Content-Type: application/json" \
  -d '{ "quantity": 3 }'

Remove an item

DELETE /v1/carts/{cartToken}/items/{cartItemId} removes the line.

curl -X DELETE https://storefront.ecomiq.pe/v1/carts/<cart-token>/items/7204558912004325450 \
  -H "x-Store-Domain: mi-tienda"

Apply a code

POST /v1/carts/{cartToken}/promotion-codes applies a coupon. Lowercase is fine: the code is stored in uppercase.

curl -X POST https://storefront.ecomiq.pe/v1/carts/<cart-token>/promotion-codes \
  -H "x-Store-Domain: mi-tienda" \
  -H "Content-Type: application/json" \
  -d '{ "code": "bienvenida10" }'

The code shows up in promotionCodes. 10% of S/ 158 brings totals.discountTotal to -15.8 and totals.total to 142.2.

  • If the cart doesn't meet the coupon's conditions, the code is still saved, but it won't discount anything until the cart meets them.

Remove a code

DELETE /v1/carts/{cartToken}/promotion-codes/{code} removes the code and recalculates the total.

curl -X DELETE https://storefront.ecomiq.pe/v1/carts/<cart-token>/promotion-codes/BIENVENIDA10 \
  -H "x-Store-Domain: mi-tienda"

Add the buyer's details

PATCH /v1/carts/{cartToken}/buyer saves the buyer's contact details and ID document.

curl -X PATCH https://storefront.ecomiq.pe/v1/carts/<cart-token>/buyer \
  -H "x-Store-Domain: mi-tienda" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "andrea.ramos@example.com", "phone": "+51987654321",
    "firstName": "Andrea", "lastName": "Ramos",
    "identificationType": "DNI", "identificationNumber": "45678912"
  }'
  • Send every field on each call: the ones you leave out are cleared. Only invoiceType is kept when you omit it.
  • For an invoice (factura), send invoiceType: "invoice", the RUC in entityTaxId, and the company name in entityName.

Build the address form

The department, province, and district pickers come from the ubigeo catalog. Request each level with the code of the one above: departments, provinces, and districts.

curl https://storefront.ecomiq.pe/v1/geo/departments -H "x-Store-Domain: mi-tienda"
curl "https://storefront.ecomiq.pe/v1/geo/provinces?departmentCode=15" -H "x-Store-Domain: mi-tienda"
curl "https://storefront.ecomiq.pe/v1/geo/districts?provinceCode=1501" -H "x-Store-Domain: mi-tienda"

Each area has an id, a code, and a name, for example 150122 and Miraflores. In Peru, code is the INEI ubigeo and source is inei.

  • Without countryCode, it uses PE. Each list is sorted by name and returns up to 200 areas.
  • To prefill a saved address, resolve its ubigeo with GET /v1/geo/resolve. Send countryCode=PE, source=inei, and the ubigeo in code.

Save the address

PATCH /v1/carts/{cartToken}/addresses saves the shipping address. If you don't send billingAddress, checkout uses the shipping address.

curl -X PATCH https://storefront.ecomiq.pe/v1/carts/<cart-token>/addresses \
  -H "x-Store-Domain: mi-tienda" \
  -H "Content-Type: application/json" \
  -d '{
    "shippingAddress": {
      "name": "Andrea Ramos", "phone": "+51987654321",
      "address1": "Av. Larco 1234", "address2": "Dpto. 501",
      "department": "Lima", "departmentCode": "15",
      "province": "Lima", "provinceCode": "1501",
      "district": "Miraflores", "districtCode": "150122",
      "city": "Lima", "postalCode": "15074", "countryCode": "PE"
    }
  }'
  • In Peru, department, province, and district are required, along with name, address1, city, and countryCode.
  • If you send codes, send all three. Each one starts with the previous one: 15, 1501, 150122. Shipping quotes and slots use those codes.
  • Each PATCH replaces the address: without codes, its previous codes are cleared.
  • If shipping is already chosen, changing the address quotes it again. If it no longer applies or its price changed, it is removed.

List shipping options

GET /v1/carts/{cartToken}/shipping-options quotes shipping for the cart's address. It groups items by seller and shipping profile.

curl https://storefront.ecomiq.pe/v1/carts/<cart-token>/shipping-options \
  -H "x-Store-Domain: mi-tienda"
Response (excerpt)
{
  "groups": [
    {
      "sellerId": "7204558912004325305",
      "shippingProfileId": "7204558912004329819",
      "rates": [
        {
          "shippingMethodId": "7204558912004325207",
          "rateId": "7204558912004322088",
          "name": "Delivery Lima",
          "type": "localDelivery",
          "target": "location",
          "targetId": "7204558912004325563",
          "price": 10,
          "serviceCode": null
        }
      ]
    }
  ]
}
  • Choose one rate per group.
  • If no item needs shipping, groups is empty.
  • Without an address, you can quote with ?postalCode=15074.

Check delivery slots

GET /v1/carts/{cartToken}/shipping-methods/{shippingMethodId}/slots returns the slots for one day. Send the rate's target, targetId, and rateId, and the day in date.

curl -G https://storefront.ecomiq.pe/v1/carts/<cart-token>/shipping-methods/7204558912004325207/slots \
  -H "x-Store-Domain: mi-tienda" \
  -d target=location -d targetId=7204558912004325563 \
  -d rateId=7204558912004322088 -d date=2026-10-08
Response (excerpt)
{
  "date": "2026-10-08",
  "capacity": 30,
  "scheduledCount": 12,
  "slots": ["10:00:00", "12:00:00"]
}
  • Only pickup and localDelivery methods have slots.
  • Times are in the store's local time. capacity is the day's limit and scheduledCount is what is already booked.
  • If the day is closed or full, slots is empty. Try another day.

Choose shipping

PUT /v1/carts/{cartToken}/shipping-method saves the chosen rate. Copy shippingMethodId, rateId, target, targetId, and serviceCode from the rate, and sellerId and shippingProfileId from the group. In the code, shippingOptions is the shipping options response.

curl -X PUT https://storefront.ecomiq.pe/v1/carts/<cart-token>/shipping-method \
  -H "x-Store-Domain: mi-tienda" \
  -H "Content-Type: application/json" \
  -d '{
    "shippingProfileId": "7204558912004329819",
    "shippingMethodId": "7204558912004325207", "rateId": "7204558912004322088",
    "target": "location", "targetId": "7204558912004325563",
    "scheduledDate": "2026-10-08", "scheduledTime": "10:00:00"
  }'

The response has the shipping in shippingMethods. totals.shippingTotal is 10 and totals.total is 152.2.

  • With target: "automatic", targetId is null.
  • Send scheduledDate and scheduledTime together or not at all. Use a time from slots, with no time zone.

Remove shipping

DELETE /v1/carts/{cartToken}/shipping-method removes the chosen shipping from every group. With sellerId or shippingProfileId, only from that group.

curl -X DELETE "https://storefront.ecomiq.pe/v1/carts/<cart-token>/shipping-method?shippingProfileId=7204558912004329819" \
  -H "x-Store-Domain: mi-tienda"

Go to checkout

Checkout needs items, an email, a shipping address, and shipping for every group with items that ship. Continue in Checkout and payment.

Checkout recalculates the discounts. If a code stopped applying, it returns 409 with Promotions.CodeNoLongerApplies; if an automatic promotion ran out, with Promotions.NoLongerApplies.

Errors

Read code in the error body.

codeStatusWhat to do
Carts.NotFound404The token doesn't exist in the store. Create a new cart.
Carts.NotActive400The cart was already bought or cancelled. Create a new one.
Checkout.InProgress409A checkout is running. Wait and read the cart.
Common.Validation400A field is missing or malformed. Check errors.
Catalog.VariantNotFound404The variant doesn't exist or isn't published in the store.
Catalog.InsufficientStock400Not enough stock for that quantity. Lower it.
Promotions.CodeNotFound400The code doesn't exist.
Promotions.CodeLimitReached400The coupon ran out.
Promotions.CodeInactive400The coupon is inactive, hasn't started, or expired.
ShippingMethods.RateNotFound400The rate no longer applies. List the options again.
ShippingMethods.GroupRequired400Send the group's sellerId and shippingProfileId.
ShippingMethods.InvalidSchedule400That slot isn't available. Check the slots again.

With a token that doesn't exist, the other operations return 404 with no body. More coupon codes in Promotions and coupons.

Next steps

Last updated:

On this page