Search Documentation

Search for a documentation page...

Orders API

REST API endpoints for browsing and managing orders.

Order visibility. A request made for a person – through an OAuth app or the AI Assistant – only reaches the orders that person's role may see: orders outside it are missing from lists and answer 404 on every /orders/:id endpoint. API keys see every order.

List Orders

GET /api/v1/orders

Returns a paginated list of orders, sorted by creation date (newest first).

Query Parameters

ParameterTypeDefaultDescription
limitnumber10Orders per page (1-100)
offsetnumber0Orders to skip
workstationstring–Only orders queued at this workstation (id), or unassigned for orders at none
customStatusstring–Only orders in this custom status (its key); a group includes its sub-statuses. An unknown key is a 400.
binstring–archive lists only archived orders, trash only trashed ones. Without it, every order except trashed ones is listed, archived ones included.

Response

{
"data": [
{
"id": "0191abc0-1234-7def-8000-000000000001",
"orderNumber": 1042,
"status": "completed",
"totalAmount": "5000",
"currency": "usd",
"locale": "en-US",
"customer": {
"id": "0191abc0-0000-7000-8000-000000000010",
"email": "jane@example.com",
"name": "Jane Doe"
},
"createdAt": "2024-01-15T10:30:00.000Z"
}
],
"meta": {
"count": 42
}
}

Each order also carries:

  • customStatus – the store's own workflow status on top of status: { key, name, color, isDefault }. name is null for a default the merchant hasn't renamed (show the order status's own name then). See List Order Statuses.
  • archivedAt / trashedAt – when the order was put in the Archive or Trash bin, null otherwise.

Get Order

GET /api/v1/orders/:id

Returns the full order with line items, customer info, shipping address, and payment details. With the orders:read scope it also carries customStatus, archivedAt and trashedAt, like the list; an archived or trashed order is still returned here. Storefront callers (order:lookup) never get these fields: custom status names are for the merchant's team only.

The orderData snapshot is a copy of the cart, so each lineItems[].productVariant in it arrives with the merchant-private fields blanked — cost, digital, externalId, supplierAliases, reservedQuantity and unavailableQuantity. This endpoint also reads orders that are not paid yet (bank transfer, cash on delivery), so a paid download's files are never in the response; the buyer receives them as encrypted links once the payment is recorded. See Merchant-private fields.

Response

{
"id": "0191abc0-1234-7def-8000-000000000001",
"orderNumber": 1042,
"status": "completed",
"totalAmount": "5000",
"currency": "usd",
"locale": "en-US",
"lineItems": [
{
"id": "0191abc0-0000-7000-8000-000000000100",
"productVariantId": "0191abc0-0000-7000-8000-000000000200",
"quantity": 2,
"unitPrice": "2500",
"product": {
"name": "Classic Tee",
"slug": "classic-tee"
}
}
],
"customer": {
"id": "0191abc0-0000-7000-8000-000000000010",
"email": "jane@example.com",
"name": "Jane Doe"
},
"shippingAddress": {
"line1": "123 Main St",
"city": "San Francisco",
"state": "CA",
"postalCode": "94102",
"country": "US"
},
"paymentInstructions": null,
"createdAt": "2024-01-15T10:30:00.000Z"
}

Cash on delivery

An order placed with cash on delivery (paymentProvider: "cashOnDelivery") is created unpaid: the courier collects the money and the merchant marks it paid afterwards. While the payment is still outstanding, paymentInstructions carries the merchant's note for the shopper, so a storefront can repeat it on its own order page:

{
"paymentProvider": "cashOnDelivery",
"paymentInstructions": "Exact change, please - the courier carries no cash."
}

It turns null once the payment is recorded (orderData.paymentCollectedAt is set), when the merchant has left no instructions, and on every order paid by any other provider.

Language

locale is the language version of the store the order was placed on (en-US, pl-PL, …), copied from the cart at checkout. Every email about the order renders in it. null for orders placed before the storefront reported a language, imports and orders created in the admin — those fall back to the store's default storefront language.

Prices and tax

Every money field in an order's orderData snapshot is net, exactly like the live catalog's. Gross twins are returned alongside them, so a storefront prints a past order the same way it prints a cart:

Net fieldGross twin
orderData.lineItems[].productVariant.price…productVariant.priceGross
orderData.shipping.priceorderData.shipping.priceGross

The line variants carry the full set of twins described in Prices and tax (originalPriceGross, calculatedPriceGross, and so on). They are computed from the tax rate frozen into the snapshot — each line from its own product's rate, delivery from the shipping method's — never from the product's current rate, which may have changed since. shipping.priceGross is the listed delivery charge; whether the order actually earned free delivery is already settled in the order's totals.

Both GET /api/v1/orders and GET /api/v1/orders/:id return them.


Update Order

PATCH /api/v1/orders/:id

Updates an order's fulfillment status and/or tracking information, or corrects the contact email the order was placed with.

Request Body

FieldTypeDescription
statusstringprocessing, shipped, delivered, or canceled
trackingNumberstringShipping carrier tracking number. Only for orders fulfilled outside the platform – orders whose shipping method links a carrier addon (gls, inpost, dpd, fedex) are rejected with 409. Use the Shipment API for carrier-managed orders.
emailstringContact email for this order – where its notifications (shipped, delivered) are sent. Allowed at any status.
workstationIdstring | nullQueue the order at a workstation, or null for unassigned. Counts as a manual choice, so the assignment rules never move it afterwards. Growth plan; 404 for an unknown workstation, 409 for one that takes no orders.
customStatusstringMove the order to a custom status (its key). A status linked to an order status changes status too, with the same emails and events; an unlinked one only relabels the order. Can't be sent with status (400). 404 for an unknown key; 409 when the order status can't move there (a reservation, a refunded order, or a cancelled one whose stock went back).
archivedbooleantrue archives the order (it leaves the admin's default list), false takes it out of the archive.
trashedbooleantrue moves the order to Trash: it leaves order lists, counts, analytics, exports and customer stats, but can still be read by id. false restores it. Nothing is ever deleted.

The response echoes the order's email alongside id, status, trackingNumber, shipment, billingAddress and shippingAddress. When customStatus, archived or trashed is sent, it adds customStatus, archivedAt and trashedAt.

curl -X PATCH \
-H "Authorization: Bearer your_api_key" \
-H "Content-Type: application/json" \
-d '{
"status": "shipped",
"trackingNumber": "1Z999AA10123456784"
}' \
https://your-store.yns.store/api/v1/orders/0191abc0-1234-7def-8000-000000000001

Contact Email

email corrects this order only. The customer record and any shopper account keep their own address, so a typo caught after checkout can be fixed without touching the customer's profile.

Two cases are rejected with 409:

  • The order has no customer attached – there is nothing to email, so correct its addresses instead.
  • A KSeF e-invoice has already been filed for the order – the filed invoice is the record of the buyer.

Customer Notifications

Only two statuses email the buyer, and a real transition into one of them sends the same email the admin dashboard triggers:

statusCustomer email
shippedorder_shipped
deliveredorder_delivered
processingNone
canceledNone

processing and canceled still record the status change and fire the store events, but they notify nobody – there is no customer template for them. The shipped and delivered emails only go out when the store has that email event enabled at /manage/emails, and they carry the order's tracking number and link when one is known, whether it came with this request or was stored earlier. Retrying the same status update does not re-send the notification.

Order Statuses

API StatusInternal StatusDescription
processingprocessingBeing prepared
shippedshippedIn transit
deliveredcompletedDelivered to customer
canceledcancelledCancelled

List Order Statuses

GET /api/v1/order-statuses

The store's custom order statuses: the merchant's own two-level workflow (groups such as Verification, Picking or At courier, each with optional sub-statuses) on top of every order's status. Requires the orders:read scope. Statuses are managed in the admin under Settings → Order statuses.

  • Every order status has a default custom status. Its key is the order status itself (paid, shipped, …), defaultFor names it, and id is null until the merchant edits it.
  • A status with an effectiveLink changes the order's status to it when an order is moved there. A sub-status without its own linkedStatus uses its group's.
  • name is null for a default that keeps the order status's own name.
  • When an order's status changes some other way (a payment, a carrier, a refund), a linked custom status that no longer matches falls back to the new status's default.
{
"data": [
{
"key": "completed",
"id": "0192e3a0-1234-7def-8000-000000000010",
"name": "Zrealizowane",
"color": "emerald",
"linkedStatus": "completed",
"effectiveLink": "completed",
"defaultFor": "completed",
"children": [
{
"key": "0192e3a0-1234-7def-8000-000000000011",
"id": "0192e3a0-1234-7def-8000-000000000011",
"name": "Delivered COD",
"color": "teal",
"linkedStatus": null,
"effectiveLink": "completed",
"defaultFor": null,
"children": []
}
]
},
{
"key": "0192e3a0-1234-7def-8000-000000000012",
"id": "0192e3a0-1234-7def-8000-000000000012",
"name": "Verification",
"color": "blue",
"linkedStatus": null,
"effectiveLink": null,
"defaultFor": null,
"children": []
}
]
}

Create Shipment

POST /api/v1/orders/:id/shipment

Creates a carrier shipment for the order. The carrier is resolved from the order's shipping method (its linked shipping addon) – you don't specify the carrier in the request. The store's environment (live/test) determines whether the carrier's sandbox or production API is used.

Request Body

All fields are optional. The request body can be empty – every field has a server-side default or returns a 422 explaining what's missing.

InPost Fields

An InPost order ships to a Paczkomat locker, unless its shipping method has the courier type: then it ships by InPost courier to the buyer's shipping address. Locker fields on a courier order (and parcel on a locker order) return 400.

FieldTypeDescription
templatestringLocker orders: locker size small, medium, or large (maps to gabaryt A/B/C). Omit to use dimension-based suggestion, then the store's fallbackTemplate. Fails with 422 if neither is available.
targetPointstringLocker orders: InPost locker code (e.g. KRA012). Defaults to the buyer's checkout selection.
parcelobjectCourier orders: { lengthCm, widthCm, heightCm, weightKg }, the packed parcel's size in cm (up to 350 × 240 × 240) and weight in kg (0.01–50). Omit to use the product variants' dimensions and weights; fails with 422 when any are missing.
sendingMethodstringHow the parcel is handed to InPost. Locker orders: parcel_locker (drop-off at a locker, default) or dispatch_order (courier pickup). Courier orders: dispatch_order (default) or pop (drop-off at a PaczkoPunkt).

GLS Fields

FieldTypeDescription
weightKgnumberParcel weight in kg (0.01–50). Defaults to the sum of the order's shippable variant weights.
datestringPickup/drop date (YYYY-MM-DD). Defaults to the next working day.
pointIdstringGLS parcel-shop id (e.g. GLS_PL-12345). Defaults to the buyer's checkout selection; required for parcel-shop rates.

FedEx Fields

FedEx ships by courier to addresses in Poland, with the FedEx service of the order's delivery method. The label is ready at once; 422 names what FedEx can't take (an address abroad, a missing phone number, an address line over 35 characters).

FieldTypeDescription
weightKgnumberParcel weight in kg (0.01–68). Defaults to the sum of the order's shippable variant weights (1 kg when none has a weight).
sizeCmobjectOptional { lengthCm, widthCm, heightCm }.
pickupAddressIdstringThe store pickup address the parcel leaves from (Pickup Locations). Defaults to the FedEx addon's defaultPickupAddressId.
handoverstringcourier (default: FedEx collects the parcel on your regular pickup) or dropoff (you take it to a FedEx location).

Cash on delivery (GLS and DPD)

GLS and DPD shipments accept these fields to collect cash from the buyer. Amounts are in PLN minor units.

FieldTypeDescription
codbooleanOmit to collect the order total when the order is an unpaid cash-on-delivery order. false ships without COD. true fails with 422 when the order has no cash to collect.
codAmountintegerAmount to collect in minor units. Defaults to the order total and cannot exceed it. For DPD, cod: false cannot be combined with codAmount (400).

DPD collects cash by courier and at DPD Pickup points that take COD, never at lockers. When the cash cannot be collected, nothing is sent to the carrier and the response is 422 with code: "cod_refused", a reason and a hint describing what to change (for example, pass the pointId of a Pickup point that takes COD, or record the payment first).

Passing fields that belong to the other carrier returns 400 – e.g. sending weightKg for an InPost order is rejected.

# InPost shipment
curl -X POST \
-H "Authorization: Bearer your_api_key" \
-H "Content-Type: application/json" \
-d '{"template": "medium"}' \
https://your-store.yns.store/api/v1/orders/0191abc0-1234-7def-8000-000000000001/shipment

# InPost courier shipment, dropped off at a PaczkoPunkt
curl -X POST \
-H "Authorization: Bearer your_api_key" \
-H "Content-Type: application/json" \
-d '{"parcel": {"lengthCm": 40, "widthCm": 30, "heightCm": 12, "weightKg": 2.5}, "sendingMethod": "pop"}' \
https://your-store.yns.store/api/v1/orders/0191abc0-1234-7def-8000-000000000001/shipment

# GLS shipment (empty body – uses defaults)
curl -X POST \
-H "Authorization: Bearer your_api_key" \
-H "Content-Type: application/json" \
-d '{}' \
https://your-store.yns.store/api/v1/orders/0191abc0-1234-7def-8000-000000000001/shipment

Response (201)

InPost:

{
"carrier": "inpost",
"shipmentId": "123456789",
"status": "created",
"trackingNumber": null,
"labelReady": false,
"delivery": "locker",
"template": "medium",
"templateSource": "explicit"
}

An InPost courier shipment answers "delivery": "courier" with parcel and parcelSource (explicit or suggested) instead of template and templateSource.

GLS:

{
"carrier": "gls",
"shipmentId": "987654321",
"parcelNumbers": ["12345678901"],
"trackId": "T12345",
"trackingUrl": "https://gls-group.eu/track/T12345",
"labelReady": true,
"labelWarning": null
}

Error Responses

StatusDescription
400Invalid request data, carrier-mismatched fields, locker fields on an InPost courier order (or the reverse), or a sending method that delivery kind doesn't offer
404Order not found
409Order already has a shipment, or shipping method has no supported carrier
422Missing required carrier data (e.g. no locker code or template for an InPost locker order, no parcel size and weight or no street address for an InPost courier order)
502Carrier API error

Get Shipment

GET /api/v1/orders/:id/shipment

Returns the current shipment status for an order.

Response (200)

InPost:

{
"carrier": "inpost",
"shipmentId": "123456789",
"status": "confirmed",
"trackingNumber": "620123456789012345678901",
"trackingUrl": "https://inpost.pl/sledzenie-przesylek?number=620123456789012345678901",
"labelReady": true
}

GLS:

{
"carrier": "gls",
"shipmentId": "987654321",
"parcelNumbers": ["12345678901"],
"trackId": "T12345",
"trackingUrl": "https://gls-group.eu/track/T12345",
"labelReady": true
}

Error Responses

StatusDescription
404Order not found or order has no shipment
409Shipping method has no supported carrier
502Carrier API error (InPost only – GLS uses locally persisted state)

Delete Shipment

DELETE /api/v1/orders/:id/shipment

Deletes the shipment for an order. Supported for GLS (while the consignment is in the preparing box) and FedEx (cancelled at FedEx until it has the parcel; the order can then get a new label). InPost shipments must be cancelled in the InPost Parcel Manager.

curl -X DELETE \
-H "Authorization: Bearer your_api_key" \
https://your-store.yns.store/api/v1/orders/0191abc0-1234-7def-8000-000000000001/shipment

Response (200)

{
"ok": true,
"deleted": 1
}

Error Responses

StatusDescription
404Order not found or order has no shipment
409InPost shipments cannot be deleted through this API, or the GLS shipment is no longer deletable
502GLS or FedEx API error, e.g. FedEx no longer cancels a parcel it already has

Get Shipment Label

GET /api/v1/orders/:id/shipment/label

Downloads the shipping label as a file. Returns the raw file (PDF, ZPL, or EPL) with appropriate Content-Type and Content-Disposition headers.

Query Parameters

ParameterTypeDefaultDescription
formatstringpdfInPost only: pdf, zpl, or epl. GLS uses the store-wide labelMode setting (change via PUT /api/v1/addons/gls). A FedEx label is the file stored when the shipment was made, in the labelFormat of that moment. Passing format for a GLS or FedEx order returns 400.
# Download PDF label
curl -H "Authorization: Bearer your_api_key" \
-o label.pdf \
https://your-store.yns.store/api/v1/orders/0191abc0-1234-7def-8000-000000000001/shipment/label

# Download ZPL for thermal printer (InPost only)
curl -H "Authorization: Bearer your_api_key" \
-o label.zpl \
"https://your-store.yns.store/api/v1/orders/0191abc0-1234-7def-8000-000000000001/shipment/label?format=zpl"

Error Responses

StatusDescription
400Invalid query parameters, or format used with GLS
404Order not found or order has no shipment
409InPost label not ready yet (poll GET .../shipment until labelReady is true), or shipping method has no supported carrier
502Carrier API error

Customer Orders

GET /api/v1/customers/:id/orders

Returns orders for a specific customer, paginated.

Query Parameters

ParameterTypeDefaultDescription
limitnumber20Orders per page (1-100)
offsetnumber0Orders to skip

Response

{
"items": [
{
"id": "0191abc0-1234-7def-8000-000000000001",
"orderNumber": "1042",
"status": "completed",
"totalCents": 5000,
"currency": "usd",
"createdAt": "2024-01-15T10:30:00.000Z"
}
],
"pagination": {
"total": 8,
"offset": 0,
"limit": 20,
"hasMore": false
}
}

Get Order History

GET /api/v1/orders/:id/history

Everything that happened to one order, newest first: status changes with the previous status, edits with each field's old and new value, packing scans and workstation assignments, payments, refunds and chargebacks, comments and issues. Requires the orders:read scope.

Every entry names who did it in actor:

actor.kindWho
userA team member; name is their current name
customerThe shopper (placing or paying for the order)
ruleA store rule, such as a workstation rule
integrationAn outside system, named by source: baselinker, allegro, stripe, paypal, inpost, api, …
systemYour Next Store itself
assistantThe AI Assistant

actor.viaSupport is true when Your Next Store support did it while signed in as a team member.

kind is event for history entries, comment for team comments and issue for problems such as a label that failed. Address and email values in changes are replaced with a placeholder when the API key's member has no access to customer details.

Query Parameters

ParameterTypeDefaultDescription
categorystringAllOnly one history: status, shipping, change, pick_pack, payment, document or other
limitnumber50Entries per page (1-200)
beforestringNoneOnly entries older than this time. Pass the previous page's meta.nextBefore
curl \
-H "Authorization: Bearer your_api_key" \
"https://your-store.yns.store/api/v1/orders/0191abc0-1234-7def-8000-000000000001/history?category=status"

Response

{
"data": [
{
"id": "0192f0a1-2b3c-7d4e-8f00-000000000010",
"kind": "event",
"category": "status",
"type": "order.status_changed",
"occurredAt": "2026-10-13T12:04:00.000Z",
"severity": "info",
"actor": {
"kind": "integration",
"id": null,
"name": null,
"source": "inpost",
"viaSupport": false
},
"title": null,
"message": null,
"before": { "status": "processing" },
"after": { "status": "shipped" },
"changes": null,
"metadata": null
}
],
"meta": {
"limit": 50,
"nextBefore": null
}
}

An edit lists its changes, one per field:

"changes": [
{ "field": "line_quantity", "label": "Ceramic mug", "from": 1, "to": 2 },
{ "field": "address", "address": "shipping", "part": "city", "from": "Warsaw", "to": "Kraków" },
{ "field": "tags", "added": ["vip"], "removed": [] }
]

List Refunds

GET /api/v1/orders/:id/refunds

Returns the refunds recorded against an order, newest first.

Query Parameters

ParameterTypeDefaultDescription
limitnumber50Refunds per page (1-100)
offsetnumber0Refunds to skip
curl \
-H "Authorization: Bearer your_api_key" \
https://your-store.yns.store/api/v1/orders/0191abc0-1234-7def-8000-000000000001/refunds

Response

{
"data": [
{
"id": "0191abc0-1234-7def-8000-00000000000a",
"orderId": "0191abc0-1234-7def-8000-000000000001",
"stripeRefundId": "re_3Abc123",
"amount": 2500,
"currency": "usd",
"reason": "requested_by_customer",
"reasonNote": "Customer changed their mind",
"status": "succeeded",
"refundType": "partial",
"refundApplicationFee": false,
"restockItems": true,
"shippingAmount": null,
"createdAt": "2024-05-02T10:00:00.000Z"
}
],
"meta": {
"count": 1,
"offset": 0,
"limit": 50
}
}

Create Refund

POST /api/v1/orders/:id/refunds

Issues a refund against the order's original payment. The order's own payment provider decides how it is processed: PayNow orders refund through PayNow, everything else through Stripe. Providers this API cannot refund on your behalf are refused with a 400 – see Payment Providers below.

Body Parameters

ParameterTypeRequiredDescription
amountnumberYesRefund amount in cents (smallest currency unit)
reasonstringYesOne of requested_by_customer, damaged_or_defective, duplicate, fraudulent, order_error, other
reasonNotestringNoAdditional detail about the reason
refundTypestringYesfull or partial
refundApplicationFeebooleanNoRefund the Stripe application fee. Defaults to false.
restockItemsbooleanNoReturn refunded items to stock. Defaults to true.
lineItemsarrayNoPer-item breakdown for partial refunds with item tracking
shippingAmountnumberNoPortion of amount that refunds shipping, in cents. Must not exceed amount.

Each lineItems entry takes lineItemIndex (position in the order), productVariantId, quantity, and amount in cents.

Restocking is bounded by the order itself. A line is restocked up to the quantity the order contains for that variant – asking for more restocks only the ordered quantity – and a line naming a variant the order does not contain is ignored. Ignored lines do not fail the request: the refund is already recorded and the money has moved.

curl -X POST \
-H "Authorization: Bearer your_api_key" \
-H "Content-Type: application/json" \
-d '{
"amount": 2500,
"reason": "requested_by_customer",
"refundType": "partial",
"restockItems": true
}' \
https://your-store.yns.store/api/v1/orders/0191abc0-1234-7def-8000-000000000001/refunds

Payment Providers

ProviderBehaviour
StripeRefunded through Stripe against the original charge
PayNowRefunded through PayNow; the refund row stays pending until PayNow's settlement webhook lands
Frame400 – refund it in the Frame dashboard, then set the order status to Refunded
Bank transfer400 – refund it manually from your bank
Cash on delivery400 – refund the customer yourself, then set the order status to Refunded

Errors

StatusMeaning
400Order status can't be refunded, the payment provider can't be refunded through this API, no Stripe payment found, the amount exceeds what's still refundable, or shippingAmount exceeds amount
404Order not found

Only orders with status paid, processing, shipped, completed, or partially_refunded can be refunded.

Remaining headroom counts committed refunds – both succeeded and pending rows. A refund still awaiting its provider's settlement webhook already consumes headroom, so amount plus every committed refund on the order may not exceed the order total.


Get Refund

GET /api/v1/orders/:id/refunds/:refundId

Returns a single refund belonging to the order.

curl \
-H "Authorization: Bearer your_api_key" \
https://your-store.yns.store/api/v1/orders/0191abc0-1234-7def-8000-000000000001/refunds/0191abc0-1234-7def-8000-00000000000a