UnifyCart Developers

Orders

Reading orders, creating one, the two status fields, the snapshot principle, and what to do when a request times out.

GET  /api/v1/orders
GET  /api/v1/orders/{id}
POST /api/v1/orders

Reading needs read_orders. Creating needs write_orders.

Listing Orders

curl -s https://shop.example.com/api/v1/orders \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Accept: application/json"
Full response, one order of the page
{
  "current_page": 1,
  "data": [
    {
      "id": 115,
      "order_number": "UC-1115",
      "customer_id": 19,
      "customer_email": "abigail.rivera@x.dummyjson.com",
      "customer_name": "Abigail Rivera",
      "customer_phone": "+91 228-363-7806",
      "status": "shipped",
      "payment_status": "paid",
      "payment_method": "paypal",
      "payment_reference": "pay_NoNw6YLvWShoRO81",
      "payment_terms_days": null,
      "payment_due_at": null,
      "currency": "USD",
      "presentment_currency": null,
      "presentment_subtotal": null,
      "presentment_shipping_total": null,
      "presentment_tax_total": null,
      "presentment_discount_total": null,
      "presentment_grand_total": null,
      "exchange_rate": null,
      "settlement_currency": null,
      "settlement_grand_total": null,
      "settlement_rate": null,
      "market_code": null,
      "subtotal": "309.90",
      "shipping_total": "0.00",
      "shipping_method": null,
      "tax_total": "24.79",
      "tax_document_code": null,
      "discount_total": "0.00",
      "grand_total": "334.69",
      "coupon_code": null,
      "coupon_discount": "0.00",
      "promotion_discount": "0.00",
      "gift_certificate_amount": "0.00",
      "store_credit_amount": "0.00",
      "notes": null,
      "customer_notes": null,
      "ip_address": null,
      "fraud_score": null,
      "fraud_outcome": null,
      "fraud_details": null,
      "fraud_checked_at": null,
      "user_agent": null,
      "is_digital": false,
      "placed_at": "2026-10-04T02:57:30.000000Z",
      "shipped_at": "2026-09-05T17:07:58.000000Z",
      "delivered_at": null,
      "completed_at": null,
      "cancelled_at": null,
      "stock_restored_at": null,
      "created_at": "2026-10-04T02:57:30.000000Z",
      "updated_at": "2026-10-04T02:57:30.000000Z",
      "deleted_at": null
    }
  ],
  "first_page_url": "https://demo.unifycart.com/api/v1/orders?page=1",
  "from": 1,
  "last_page": 8,
  "last_page_url": "https://demo.unifycart.com/api/v1/orders?page=8",
  "links": [
    {
      "url": null,
      "label": "« Previous",
      "page": null,
      "active": false
    },
    {
      "url": "https://demo.unifycart.com/api/v1/orders?page=1",
      "label": "1",
      "page": 1,
      "active": true
    },
    {
      "url": "https://demo.unifycart.com/api/v1/orders?page=2",
      "label": "2",
      "page": 2,
      "active": false
    },
    {
      "url": "https://demo.unifycart.com/api/v1/orders?page=3",
      "label": "3",
      "page": 3,
      "active": false
    },
    {
      "url": "https://demo.unifycart.com/api/v1/orders?page=4",
      "label": "4",
      "page": 4,
      "active": false
    },
    {
      "url": "https://demo.unifycart.com/api/v1/orders?page=5",
      "label": "5",
      "page": 5,
      "active": false
    },
    {
      "url": "https://demo.unifycart.com/api/v1/orders?page=6",
      "label": "6",
      "page": 6,
      "active": false
    },
    {
      "url": "https://demo.unifycart.com/api/v1/orders?page=7",
      "label": "7",
      "page": 7,
      "active": false
    },
    {
      "url": "https://demo.unifycart.com/api/v1/orders?page=8",
      "label": "8",
      "page": 8,
      "active": false
    },
    {
      "url": "https://demo.unifycart.com/api/v1/orders?page=2",
      "label": "Next »",
      "page": 2,
      "active": false
    }
  ],
  "next_page_url": "https://demo.unifycart.com/api/v1/orders?page=2",
  "path": "https://demo.unifycart.com/api/v1/orders",
  "per_page": 20,
  "prev_page_url": null,
  "to": 20,
  "total": 150
}

Paginated, newest first, with the standard envelope: items in data, alongside current_page, last_page, total and per_page.

Reading one order adds its line items and both addresses, which the list leaves out.

curl -s https://shop.example.com/api/v1/orders/115 \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Accept: application/json"
Full response for a single order, with items and addresses
{
  "data": {
    "id": 115,
    "order_number": "UC-1115",
    "customer_id": 19,
    "customer_email": "abigail.rivera@x.dummyjson.com",
    "customer_name": "Abigail Rivera",
    "customer_phone": "+91 228-363-7806",
    "status": "shipped",
    "payment_status": "paid",
    "payment_method": "paypal",
    "payment_reference": "pay_NoNw6YLvWShoRO81",
    "payment_terms_days": null,
    "payment_due_at": null,
    "currency": "USD",
    "presentment_currency": null,
    "presentment_subtotal": null,
    "presentment_shipping_total": null,
    "presentment_tax_total": null,
    "presentment_discount_total": null,
    "presentment_grand_total": null,
    "exchange_rate": null,
    "settlement_currency": null,
    "settlement_grand_total": null,
    "settlement_rate": null,
    "market_code": null,
    "subtotal": "309.90",
    "shipping_total": "0.00",
    "shipping_method": null,
    "tax_total": "24.79",
    "tax_document_code": null,
    "discount_total": "0.00",
    "grand_total": "334.69",
    "coupon_code": null,
    "coupon_discount": "0.00",
    "promotion_discount": "0.00",
    "gift_certificate_amount": "0.00",
    "store_credit_amount": "0.00",
    "notes": null,
    "customer_notes": null,
    "ip_address": null,
    "fraud_score": null,
    "fraud_outcome": null,
    "fraud_details": null,
    "fraud_checked_at": null,
    "user_agent": null,
    "is_digital": false,
    "placed_at": "2026-10-04T02:57:30.000000Z",
    "shipped_at": "2026-09-05T17:07:58.000000Z",
    "delivered_at": null,
    "completed_at": null,
    "cancelled_at": null,
    "stock_restored_at": null,
    "created_at": "2026-10-04T02:57:30.000000Z",
    "updated_at": "2026-10-04T02:57:30.000000Z",
    "deleted_at": null,
    "items": [
      {
        "id": 391,
        "order_id": 115,
        "product_id": 8,
        "variant_id": null,
        "vendor_id": null,
        "shipping_origin_id": null,
        "name": "Dior J'adore",
        "sku": "DUM-00008",
        "type": "physical",
        "qty": 2,
        "unit_price": "89.99",
        "line_total": "179.98",
        "tax_amount": "0.00",
        "discount_amount": "0.00",
        "weight": null,
        "options": null,
        "meta": null,
        "created_at": "2026-10-04T02:57:30.000000Z",
        "updated_at": "2026-10-04T02:57:30.000000Z"
      },
      {
        "id": 392,
        "order_id": 115,
        "product_id": 37,
        "variant_id": null,
        "vendor_id": null,
        "shipping_origin_id": null,
        "name": "Red Onions",
        "sku": "DUM-00037",
        "type": "physical",
        "qty": 3,
        "unit_price": "1.99",
        "line_total": "5.97",
        "tax_amount": "0.00",
        "discount_amount": "0.00",
        "weight": null,
        "options": null,
        "meta": null,
        "created_at": "2026-10-04T02:57:30.000000Z",
        "updated_at": "2026-10-04T02:57:30.000000Z"
      },
      {
        "id": 393,
        "order_id": 115,
        "product_id": 47,
        "variant_id": null,
        "vendor_id": null,
        "shipping_origin_id": null,
        "name": "Table Lamp",
        "sku": "DUM-00047",
        "type": "physical",
        "qty": 2,
        "unit_price": "49.99",
        "line_total": "99.98",
        "tax_amount": "0.00",
        "discount_amount": "0.00",
        "weight": null,
        "options": null,
        "meta": null,
        "created_at": "2026-10-04T02:57:30.000000Z",
        "updated_at": "2026-10-04T02:57:30.000000Z"
      },
      {
        "id": 394,
        "order_id": 115,
        "product_id": 48,
        "variant_id": null,
        "vendor_id": null,
        "shipping_origin_id": null,
        "name": "Bamboo Spatula",
        "sku": "DUM-00048",
        "type": "physical",
        "qty": 3,
        "unit_price": "7.99",
        "line_total": "23.97",
        "tax_amount": "0.00",
        "discount_amount": "0.00",
        "weight": null,
        "options": null,
        "meta": null,
        "created_at": "2026-10-04T02:57:30.000000Z",
        "updated_at": "2026-10-04T02:57:30.000000Z"
      }
    ],
    "billing_address": {
      "id": 231,
      "order_id": 115,
      "type": "billing",
      "first_name": "Abigail",
      "last_name": "Rivera",
      "company": null,
      "address_line_1": "850 Union Avenue",
      "address_line_2": null,
      "city": "Brooklyn",
      "state": "NY",
      "postal_code": "11201",
      "country_code": "US",
      "phone": "+91 228-363-7806",
      "created_at": "2026-10-03T10:22:40.000000Z",
      "updated_at": "2026-10-03T10:22:40.000000Z"
    },
    "shipping_address": {
      "id": 232,
      "order_id": 115,
      "type": "shipping",
      "first_name": "Abigail",
      "last_name": "Rivera",
      "company": null,
      "address_line_1": "850 Union Avenue",
      "address_line_2": null,
      "city": "Brooklyn",
      "state": "NY",
      "postal_code": "11201",
      "country_code": "US",
      "phone": "+91 228-363-7806",
      "created_at": "2026-10-03T10:22:40.000000Z",
      "updated_at": "2026-10-03T10:22:40.000000Z"
    }
  }
}

The Two Status Fields

Every order carries two, and conflating them is the most common integration bug we see.

status is fulfilment: pending, processing, shipped, delivered, completed, cancelled.

payment_status is money: unpaid, paid, partially_refunded, refunded, failed.

They move independently. A trade order on terms is routinely processing and unpaid for weeks.

Decide deliberately which one you key off

An accounting integration keyed to status books revenue for orders nobody has paid for. A warehouse integration keyed to payment_status refuses to ship legitimate orders on terms. Most integrations need to read both and make the rule explicit.

A merchant can also add their own fulfilment statuses, so do not assume the six above are the complete set. Treat an unrecognised status as something you do not act on rather than something you reject.

An Order Is a Snapshot

Everything on an order was recorded at the moment it was placed: product names, prices, SKUs, chosen options, addresses, tax.

Changing a product afterwards does not change any order.

Read line data from the order, never from the product

Looking up product_id to find out what was sold gives the wrong answer for anything renamed, repriced, or whose options changed since. The order is the record of the transaction. The product is the current state of your catalogue, which is a different question.

Line Items

Each line carries product_id, variant_id, name and sku as sold, qty, unit_price, line_total, tax_amount, discount_amount, weight, options and meta.

options is a list of {label, value} objects, not a map:

"options": [
  { "label": "Size", "value": "Medium" },
  { "label": "Use", "value": "Personal Use" }
]

A map would silently drop the second of two identically labelled options, and order matters for display.

meta carries flags set by the shop, such as whether the line is a damaged copy or a pre-order.

Creating an Order

curl -s -X POST https://shop.example.com/api/v1/orders \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "customer_id": 42,
    "customer_email": "buyer@example.com",
    "customer_name": "Alex Morgan",
    "items": [
      { "product_id": 1, "variant_id": 7, "qty": 2 }
    ],
    "notes": "Phone order"
  }'
Field Required Notes
customer_id yes An existing customer
customer_email yes Recorded on the order
customer_name yes Recorded on the order
items yes At least one line
notes no Internal note on the order
What comes back when the order is created
{
  "message": "Order created.",
  "data": {
    "order_number": "UC-100001",
    "customer_id": 1,
    "customer_email": "api.test@example.com",
    "customer_name": "API Test",
    "status": "pending",
    "payment_status": "unpaid",
    "currency": "USD",
    "subtotal": "5.99",
    "shipping_total": "0.00",
    "tax_total": "0.00",
    "discount_total": "0.00",
    "grand_total": "5.99",
    "notes": null,
    "ip_address": "47.148.22.63",
    "user_agent": "curl/7.61.1",
    "placed_at": "2026-10-03T22:36:36.000000Z",
    "updated_at": "2026-10-03T22:36:36.000000Z",
    "created_at": "2026-10-03T22:36:36.000000Z",
    "id": 152,
    "items": [
      {
        "id": 512,
        "order_id": 152,
        "product_id": 38,
        "variant_id": null,
        "vendor_id": null,
        "shipping_origin_id": null,
        "name": "Rice",
        "sku": "DUM-00038",
        "type": "simple",
        "qty": 1,
        "unit_price": "5.99",
        "line_total": "5.99",
        "tax_amount": "0.00",
        "discount_amount": "0.00",
        "weight": "1.800",
        "options": null,
        "meta": null,
        "created_at": "2026-10-03T22:36:36.000000Z",
        "updated_at": "2026-10-03T22:36:36.000000Z"
      }
    ]
  }
}

The response carries the order as stored, including the number it was given. Keep order_number rather than the id if you are going to quote it to anybody: it is what the merchant and the customer both see.

A created order is a real order

It takes stock, fires the same events, sends the same emails and appears in the merchant's reports. There is no test mode. An integration creating orders to check itself is reducing somebody's inventory and emailing their customer.

Stock and Payment

Stock comes off when the order is placed, not when it is paid. An order created through the API and never paid is holding goods.

A payment that fails returns the stock automatically. An order that will never be paid should be cancelled, which also returns it.

Timeouts, and Why There Is No Idempotency Key

If a request times out you do not know whether the order was created. There is no idempotency key, so a blind retry can create a duplicate.

Handle it like this. Put something of your own in notes that identifies the request. On a timeout, read recent orders back and look for it before retrying. That is two requests in the rare case rather than a duplicate order in the merchant's shop.

Reading Changes

Use webhooks rather than polling: order.placed, order.shipped, order.refunded, order.cancelled. See Webhooks.

Where you must poll, keep the window small and remember that an order placed last month can become relevant today, when an offline payment is finally marked paid. Polling by placed date alone will miss it.

Money on an Order

Every monetary field is a decimal string in the shop's base currency: subtotal, tax_total, shipping_total, discount_total, grand_total.

Read Errors and Conventions before parsing any of them.