Skip to content
Online store

Tracking and reviews

Show the order, track the shipment, and collect reviews with the Storefront API.

Andrea, in Arequipa, buys a jacket and a pair of boots from two sellers. From her account she sees both orders, follows the courier, and leaves a review once they arrive.

Requirements

  • An order created by the checkout. See Checkout and payment.
  • To show a split purchase, the customer session in X-Customer-Session. See Customer accounts.
  • The /review/{reviewToken} route on the store's primary domain.
  • For the product page, the product id. See Headless store.

Show the purchase

Without a session, the buyer arrives through each order's status link: GET /v1/orders/status/{statusToken}, covered in Checkout and payment. With a session, GET /v1/customer/orders/group/{orderGroup} brings together the orders of one purchase. If the cart had products from several sellers, there is one order per seller.

curl https://storefront.ecomiq.pe/v1/customer/orders/group/7204558912004325350 \
  -H "x-Store-Domain: mi-tienda" \
  -H "X-Customer-Session: $SESSION_ID"
Response (excerpt)
{
  "orderId": "7204558912004325350",
  "orderNumber": "ORD-2026-0001",
  "orderGroup": "7204558912004325350",
  "orderIds": ["7204558912004325350", "7204558912004325351"],
  "status": "inTransit",
  "total": 449.8,
  "currencyCode": "PEN",
  "orders": [
    { "orderNumber": "ORD-2026-0001", "status": "inTransit", "total": 289.9 },
    { "orderNumber": "ORD-2026-0002", "status": "confirmed", "total": 159.9 }
  ]
}
  • orderGroup comes in the checkout response and in every row of GET /v1/customer/orders.
  • orderNumber and status belong to the first order. total adds up all of them.
  • Each entry in orders contains the same buyer detail as GET /v1/customer/orders/{orderId}: items, amounts, addresses, payments, statusTimeline, and shipments. totalPaid adds up the recorded payments.

Track the shipment

shipments carries carrierName, trackingNumber, and trackingLink. It comes in GET /v1/orders/status/{statusToken} and in GET /v1/customer/orders/{orderId}, and in each entry of the purchase group orders. With the number, GET /v1/tracking/{trackingNumber} returns the shipment status. No session needed.

curl https://storefront.ecomiq.pe/v1/tracking/UR1029384756 \
  -H "x-Store-Domain: mi-tienda"
Response (excerpt)
{
  "trackingNumber": "UR1029384756",
  "carrierName": "Urbano",
  "trackingLink": "https://…/UR1029384756",
  "status": "shipped",
  "delivered": false,
  "events": [
    { "name": "Pedido creado", "date": "06/10/2026 09:12" },
    { "name": "Pedido en ruta", "date": "08/10/2026 08:40" }
  ],
  "message": null
}
  • status is the fulfillment status: pendingAssignment, pending, inProgress, readyToPickUp, shipped, delivered, or cancelled. It turns delivered when the carrier confirms the delivery.
  • events arrive as the carrier reports them. name and date are plain text: show them as they are.
  • Without a connected carrier, events is empty and message says why. Link to trackingLink when present.

Once the order is delivered, Ecomiq's emails send the buyer to https://<primary-domain>/review/{reviewToken}. On that route, read the order with GET /v1/orders/review-context/{reviewToken}. The token is enough: no session needed.

curl https://storefront.ecomiq.pe/v1/orders/review-context/<review-token> \
  -H "x-Store-Domain: mi-tienda"
Response
{
  "orderId": "7204558912004325350",
  "orderNumber": "ORD-2026-0001",
  "customerName": "Andrea",
  "email": "andrea.ramos@example.com",
  "deliveredAt": "2026-10-09T16:20:00Z",
  "items": [
    {
      "productId": "7204558912004320011",
      "title": "Casaca de alpaca",
      "variantTitle": "M / Gris",
      "thumbnail": "https://…/casaca.webp"
    }
  ]
}
  • items lists each product of the order once. Show one form per product.
  • It returns 404 Orders.ReviewContextNotFound if the token doesn't exist, or if the order wasn't delivered or was cancelled.

Post the review

POST /v1/products/{productId}/reviews saves the review for one product. Send the reviewToken and, in reviewerEmail, the context's email. The examples call the previous step's response context.

curl -X POST https://storefront.ecomiq.pe/v1/products/7204558912004320011/reviews \
  -H "x-Store-Domain: mi-tienda" \
  -H "Content-Type: application/json" \
  -d '{
    "rating": 5,
    "title": "Abriga mucho",
    "content": "Llegó en tres días. La talla M me queda perfecta.",
    "reviewerName": "Andrea R.",
    "reviewerEmail": "andrea.ramos@example.com",
    "orderId": "7204558912004325350",
    "reviewToken": "<review-token>"
  }'
Response
{
  "id": "7204558912004330101",
  "productId": "7204558912004320011",
  "isVerifiedPurchase": true,
  "rating": 5,
  "title": "Abriga mucho",
  "content": "Llegó en tres días. La talla M me queda perfecta.",
  "reviewerName": "Andrea R.",
  "createdOn": "2026-10-16T14:05:00Z"
}
  • rating goes from 1 to 5. content is required, up to 4000 characters; title and reviewerName, up to 200.
  • The review waits for moderation. The merchant approves it in the admin or with PATCH /v1/catalog/product-reviews/{reviewId}/status.
  • One review per product and order. orderId is optional; if you send it, it must match the link's order.

Show the reviews

GET /v1/products/{productId}/reviews lists the approved reviews, newest first. No session needed.

curl "https://storefront.ecomiq.pe/v1/products/7204558912004320011/reviews?limit=10" \
  -H "x-Store-Domain: mi-tienda"
Response (excerpt)
{
  "data": [
    {
      "id": "7204558912004330101",
      "isVerifiedPurchase": true,
      "rating": 5,
      "title": "Abriga mucho",
      "content": "Llegó en tres días. La talla M me queda perfecta.",
      "reviewerName": "Andrea R.",
      "createdOn": "2026-10-16T14:05:00Z"
    }
  ],
  "hasNextPage": true,
  "nextCursor": "MjAyNi0xMC0xNlQxNDowNTowMFo",
  "totalCount": 24
}
  • limit defaults to 20. While hasNextPage is true, send nextCursor in cursor. totalCount only comes on the first page.
  • rating filters by stars and search looks in the title, text, and name. sortBy=rating&sortDirection=desc puts the best ones first.
  • If the product doesn't exist, the list comes back empty.

GET /v1/products/{productId}/related fills the recommendations block of the product page. It starts with similar products and fills up with the same category, the same brand, and the rest of the catalog.

curl "https://storefront.ecomiq.pe/v1/products/7204558912004320011/related?limit=4" \
  -H "x-Store-Domain: mi-tienda"
Response (excerpt)
[
  {
    "id": "7204558912004320012",
    "name": "Chompa de alpaca",
    "url": "/products/chompa-de-alpaca",
    "imageUrl": "https://…/chompa.webp",
    "price": 189.9,
    "compareAtPrice": 229.9,
    "isAvailable": true
  }
]
  • limit defaults to 8. The maximum is 12.
  • It only returns available, visible products, never the one on the page.
  • If the product doesn't exist, it returns [].

Errors

codeStatusWhat to do
Orders.NotFound404Check orderGroup: it doesn't exist or is another customer's.
Customer.RegistrationRequired403Complete the registration before showing the orders.
Common.NotFound404The tracking number doesn't belong to this store.
Common.Conflict409The number matches more than one shipment. Use trackingLink.
Orders.ReviewContextNotFound404The link is not valid. Show a notice and link to the store.
ProductReview.ReviewLinkNotFound404The token doesn't exist. Use the one from the email.
Product.NotFound404The product no longer exists. Remove its form.
ProductReview.OrderInvalid400The order isn't delivered or has a return.
ProductReview.OrderOwnerMismatch400Send the context's email in reviewerEmail.
ProductReview.ProductNotPurchased400Use a productId from items.
ProductReview.OrderMismatch400Send the context's orderId or leave it out.
ProductReview.AlreadyExists409A review already exists. Mark the product as reviewed.
Common.Validation400errors says which field failed.

Tracking is rate limited. On 429, wait for Retry-After. See Rate limits.

Next steps

Last updated:

On this page