Commerce & blogs

Orders

Read Shopify and WooCommerce orders in one shape, with line items, tax, refunds and attribution, and keep them in sync with webhooks instead of polling.

Orders from Shopify and WooCommerce come back in the same shape: line items with unit prices, tax per line, discounts, shipping and fee lines, billing details, payment status, refunds and where the buyer came from. That is enough to rebuild an invoice, feed an analytics tool or attribute a sale to a post without knowing which store it came from. Orders are read-only.

#Before you start

Store What it needs
Shopify The orders.read capability, which needs the optional scopes read_orders and read_all_orders. Without read_all_orders, Shopify returns only the last 60 days. Check with GET /v1/commerce/store and send the owner to grantPermissionsUrl
WooCommerce Nothing extra: any user who can manage WooCommerce can read orders

Shopify also gates buyer details behind its protected customer data approval. Where the approval does not cover them, customer, billingAddress and shippingAddress are null and the rest of the order is complete.

#List orders

Lists come newest first. Filter by createdAfter and createdBefore, or pass updatedAfter to page forward from the last change you saw: results then come oldest first. Shopify pages hold up to 25 orders, WooCommerce pages up to 100.

curl "https://api.pozy.ai/v1/commerce/orders?accountId=6710c4b2e4b0d2f1a8c95e3a&updatedAfter=2026-10-29T00:00:00Z&limit=25" \
  -H "Authorization: Bearer $POZY_API_KEY"
One order (trimmed)
{
  "id": "6148123456",
  "accountId": "6710c4b2e4b0d2f1a8c95e3a",
  "platform": "shopify",
  "number": "#1042",
  "status": "awaiting_fulfillment",
  "platformStatus": "unfulfilled",
  "paymentStatus": "paid",
  "currency": "EUR",
  "taxesIncluded": true,
  "subtotal": { "amount": "58.00", "currency": "EUR" },
  "discountTotal": { "amount": "8.70", "currency": "EUR" },
  "shippingTotal": { "amount": "4.90", "currency": "EUR" },
  "taxTotal": { "amount": "9.40", "currency": "EUR" },
  "total": { "amount": "54.20", "currency": "EUR" },
  "discountCodes": ["INSTA15"],
  "lineItems": [
    {
      "id": "15011223344",
      "productId": "8214569812345",
      "variantId": "45123456789012",
      "sku": "BEANIE-GRY",
      "title": "Merino beanie",
      "quantity": 2,
      "unitPrice": { "amount": "29.00", "currency": "EUR" },
      "total": { "amount": "49.30", "currency": "EUR" }
    }
  ],
  "refunds": [],
  "createdAt": "2026-10-29T14:02:11Z",
  "updatedAt": "2026-10-29T14:02:15Z"
}

Get one order with GET /v1/commerce/orders/{orderId}?accountId=....

#Read the fields

Field Meaning
status pending_payment, on_hold, awaiting_fulfillment, partially_fulfilled, in_transit, delivered, completed or cancelled. Raw value in platformStatus
paymentStatus pending, authorized, partially_paid, paid, partially_refunded, refunded, voided or failed
subtotal, unitPrice Before discounts
total After discounts. taxesIncluded says whether amounts include tax
lineItems[].taxLines, shippingLines, feeLines, taxLines Tax and charges per line, each with title, ratePercent and amount
refunds Each refund with amount, note and the refunded line items. refundedTotal sums them
fulfillments Carrier, tracking number and tracking URL
customer, billingAddress, shippingAddress Buyer details, or null where the store does not share them
buyerNote, discountCodes, paymentMethods As entered at checkout

All amounts are in the store currency as decimal strings.

#Attribution

attribution says where the order came from. Use it to credit a sale to a post, a campaign or a channel.

JSON
"attribution": {
  "ready": true,
  "firstVisit": null,
  "lastVisit": {
    "occurredAt": "2026-10-29T13:55:00Z",
    "landingUrl": "https://acme.com/products/merino-beanie?utm_source=instagram&utm_campaign=winter",
    "referrerUrl": "https://l.instagram.com/",
    "source": "instagram",
    "sourceType": "SOCIAL",
    "utm": { "source": "instagram", "medium": "social", "campaign": "winter", "term": null, "content": null }
  },
  "customAttributes": [{ "key": "gift_message", "value": "Happy birthday" }]
}
Store What it records
Shopify First and last visit from the customer journey. Shopify attributes a new order shortly after it is placed: until then ready is false and both visits are null. No event fires when it becomes ready, so read the order again later
WooCommerce The last visit only (firstVisit and occurredAt are always null). Needs WooCommerce 8.5 or later with Order Attribution turned on, for orders placed after that

customAttributes holds cart and note attributes on Shopify and public order meta on WooCommerce.

Tag your links

Attribution is only as good as your UTM parameters. Put utm_source, utm_medium and utm_campaign on every link you publish, and you can group orders by post or campaign straight from lastVisit.utm.

#Keep in sync with webhooks

Polling works, but webhooks are cheaper. When a store grants order access, Pozy subscribes to its order changes (on WooCommerce, when the site is connected). Subscribe your webhook endpoint to the three events below. They carry the store and the order id; fetch the full order by id.

commerce.order.created
{
  "id": "evt_6f1c2a9b8d7e4c3a",
  "event": "commerce.order.created",
  "timestamp": "2026-10-29T14:02:16.000Z",
  "store": { "accountId": "6710c4b2e4b0d2f1a8c95e3a", "platform": "shopify", "shopId": "64822345678" },
  "resource": { "type": "order", "id": "6148123456", "status": "awaiting_fulfillment", "platformStatus": "unfulfilled" }
}

A refund usually fires commerce.order.updated as well as commerce.order.refunded. A WooCommerce site connected before order webhooks were available gets them the next time it reconnects or a catalog sync is created.

Event When it fires
commerce.order.created Commerce order created.
commerce.order.updated Commerce order updated.
commerce.order.refunded Commerce order refunded.

#Sync without gaps

To backfill or recover after downtime, page forward with updatedAfter:

  1. Store the newest updatedAt you have processed.
  2. Start each sync a few minutes before it, because an order edited while you page can shift the list.
  3. Drop duplicates by id.

#Endpoints

Endpoint What it does
GET /v1/commerce/orders List orders
GET /v1/commerce/orders/{orderId} Get an order
GET /v1/commerce/store Get a store