Order Endpoint (Orders)
Orders v2.32.0
Orders: Export, Import (POST/PUT), and Delete via the generic OrderBuilder service.
Overview
| Operation | Method | Endpoint | Scope |
|---|---|---|---|
| Export | GET | /xpanel/xoport/Export/JSON/orders | orders:read |
| Create | POST | /xpanel/xoport/Import/JSON/orders | orders:write |
| Update | PUT | /xpanel/xoport/Import/JSON/orders | orders:write |
| Delete | DELETE | /xpanel/xoport/Delete/JSON/orders | orders:write |
Note: Import/Delete require orders:write. Inventory posting occurs automatically upon creation. Existing audit logs (status history) are retained in an audit-proof manner.
Import (POST / PUT)
Creates or updates an order. POST = Create, PUT with orders_id = Update. All fields are validated and persisted via the central OrderBuilder service.
Minimum Payload (Create)
{
"data": [{
"customers_id": 51,
"payment_method": "PayPal",
"products": [
{"products_id": 42, "quantity": 2, "products_price": "29.99"}
]
}]
}
Full Payload with All Options
{
"data": [{
"customers_id": 51,
"language_id": 2,
"payment_method": "Stripe",
"shipping_method": "DHL",
"tax_flag": 1,
"totals_mode": "auto",
"expected_total": "59.98",
"strict_totals": true,
"currency": "EUR",
"products": [
{
"products_id": 42,
"quantity": 2,
"products_price": "29.99",
"products_tax": "19.00",
"attributes": [
{"option_id": 1, "value_id": 5}
]
}
],
"external_reference": {
"source": "amazon",
"external_id": "AMZ-12345"
},
"tracking": [
{"service_provider": "DHL", "code": "1234567890", "link": "https://nolp.dhl.de/?piececode=1234567890"}
]
}]
}
Totals Modes (v2.21.0)
The ` totals_mode ` parameter controls how order totals are calculated:
| Mode | Behavior | Usage |
|---|---|---|
auto Default | No `totals` values in the payload → The shop recalculates everything from scratch. If `totals` values are present → they are used. | Default behavior: The caller has no totals; the shop calculates them. |
recalc | Reloads products_price + products_tax from the database (customer group price + specials + category/customer discount, country-aware) and recalculates subtotal, tax, and total. Additionally, cost price (products_ek_price) and GTIN are reloaded. Manual line items (products_id=0) remain unchanged. | Force a safe recalculation; dropship parity at checkout. |
verbatim | The shop adopts the submitted total values exactly as is, WITHOUT recalculation. | Marketplace imports with a fixed total amount (Amazon, Otto, eBay). |
tax_flag Auto-Derive & Price Interpretation
If tax_flag is not explicitly set, it is derived from the customer group:
customers_groups.show_tax = 1+DISPLAY_PRICE_WITH_TAX = true→tax_flag = 1(gross)customers_groups.show_tax = 0→tax_flag = 0(Net)- Tax-exempt groups (e.g., EU VAT ID) →
tax_flag = 2(Tax-Exempt)
Interpretation of ` products_price ` based on ` tax_flag`
tax_flag | products_price is | ot_subtotal | ot_tax | ot_total |
|---|---|---|---|---|
0 (Net) | net | Σ(net × qty) | Σ(net × rate/100), added | Subtotal + Shipping + Tax |
1 (Gross) | gross | Σ(gross × qty) | For informational purposes (“incl. VAT”) | Subtotal + Shipping (Tax NOT included) |
2 (Tax-Free) | net = gross | Σ(price × qty) | 0 | Subtotal + Shipping |
⚠️ Important for marketplace imports: If the marketplace sends net prices, tax_flag: 0 MUST be explicitly set—otherwise, the prices (for a gross customer group) will be interpreted as gross.
language_id Auto-Derive (v2.21.0)
If language_id is not included in the payload, it is resolved using cascading rules:
- Payload value (if set)
customers.language_idfrom the customer masterDEFAULT_LANGUAGEfrom the store configuration (code lookup inthe languagestable)- Fallback:
1(English)
Note: $_SESSION['languages_id'] is intentionally NOT a fallback in the xoPort context—otherwise, the language of the Admin API caller would be unintentionally applied to the customer order.
Drift check: expected_total + strict_totals
Optionally, the caller can include the expected total amount—the shop compares it and raises an alert if there is a discrepancy:
expected_total(decimal): Expectedot_total amountafter calculation.strict_totals(boolean, defaultfalse):false→ Drift is reported as a warning in the response; the order is created anyway.true→ The order is rejected if the calculated total ≠expected_total(tolerance: 0.01 €).
Usage: Marketplace imports where the marketplace already knows the final total—the store validates that price × quantity × tax + shipping also add up to the same total locally.
Convenience Fields & Checkout Parity (v2.28.0–v2.32.0)
For integrations that only know the customer number, a different shipping address, and SKU/quantity—the order is created so that it contains prices, attributes, shipping, and confirmation exactly as a manual shop order (dropship parity, e.g., SPV → SST).
| Field / Behavior | Type | Description |
|---|---|---|
| Address Auto-Fill | auto | If customers_id > 0 and billing is missing or incomplete, the customer's default billing address is loaded. Explicitly provided fields take precedence. If required fields are missing, delivery falls back to billing. v2.28.0 |
SKU/products_model resolution | auto | Items can only {products_model, quantity} be delivered — `products_id ` and name are resolved from the store database. Unknown model → ` PRODUCT_MODEL_NOT_FOUND` warning. v2.28.0 |
apply_default_attributes | bool (default false) | For each item without its own attributes, retrieves the preselected option values including the signed surcharge—including display attributes (option type 5, e.g., “Version”/“Protection Class”)—as they appear in the checkout. v2.28.0 / v2.30.0 |
compute_shipping | bool (Default false) | If `totals.shipping ` is not explicitly set, the cheapest shipping option is calculated “headless” via the frontend shipping stack (`shipping::cheapest()`) for the shipping address and written as `ot_shipping`. Best-effort: Error → Warning SHIPPING_AUTO_FAILED / SHIPPING_AUTO_EMPTY; the order is retained. v2.30.0 |
send_confirmation_email | bool (Default false) | After successful creation, the standard order confirmation is sent to the customer (including copies from SEND_EXTRA_ORDER_EMAILS_TO). Result stored as ` confirmation_email_sent ` (true/false) in the record. v2.29.0 |
Category/Customer Discount in Recalculation | auto | In recalc mode, the category/customer discount (customers_discount + customers_groups_discount, including subcategory inheritance) is applied in addition to group prices and specials—the lower effective price takes precedence. Problem → Warning CATEGORY_DISCOUNT_UNAVAILABLE. v2.31.0 |
additional_address | string | Additional address per address block — is applied after customers_additional_address, delivery_additional_address, and billing_additional_address. v2.32.0 |
Tax line per rate (v2.32.0): ot_tax is written for each tax rate with a rate-specific title in frontend format — “plus 19% sales tax” (net) or “including 19% sales tax” (gross), provided that MODULE_ORDER_TOTAL_PREFIX=true.
Sub-Resources
external_reference — Marketplace integration
Links the order to an external source. Allowed fields:
source(string, required): e.g.,amazon,ebay,otto,kaufland.external_id(string, required): ID/number on the marketplace.marketplace_id(int, optional): Internal XONIC marketplace ID.
tracking — Shipment Tracking
Array of tracking entries. Per entry, the following are allowed:
service_provider(alias:method): Carrier name (DHL, DPD, UPS, …).code(alias:tracking_number): Tracking number.link: Tracking URL.label_link: Shipping label URL (PDF).external_id: ID assigned by the shipping provider.is_retoure(bool): Return shipment instead of outbound shipment.
status_history — Status history
Append-only audit log. Per entry: orders_status_id, comments, customer_notified (0/1), date_added.
customer_notified at the order level
Boolean; controls whether a confirmation email is triggered when the status changes.
Sample Request (Create)
curl -X POST "https://shop.de/xpanel/xoport/Import/JSON/orders" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": [{
"customers_id": 51,
"payment_method": "PayPal",
"tax_flag": 0,
"expected_total": "71.38",
"products": [{"products_id": 42, "quantity": 2, "products_price": "29.99", "products_tax": "19.00"}]
}]
}'
Example Response (Success)
{
"success": true,
"api_version": "2.32.0",
"type": "orders",
"operation": "create",
"data": [{
"orders_id": 46482,
"customers_id": 51,
"ot_subtotal": "59.98",
"ot_tax": "11.40",
"ot_total": "71.38",
"totals_mode": "auto",
"tax_flag": 0,
"language_id": 2
}],
"warnings": []
}
Sample Response (Drift Check Failed, strict_totals=true)
{
"success": false,
"api_version": "2.32.0",
"error": {
"code": "TOTAL_DRIFT",
"message": "Calculated ot_total 71.38 differs from expected_total 70.00 (diff: 1.38).",
"details": {"expected": "70.00", "calculated": "71.38", "tolerance": "0.01"}
}
}
Error Codes
| Code | Meaning |
|---|---|
INVALID_PAYLOAD | Required fields are missing or the data type is incorrect. |
CUSTOMER_NOT_FOUND | customers_id does not exist. |
PRODUCT_NOT_FOUND | products_id does not exist. |
TOTAL_DRIFT | expected_total ≠ calculated total and strict_totals=true. |
STOCK_INSUFFICIENT | Stock is insufficient (if auto_stock=true). |
DUPLICATE_EXTERNAL_REFERENCE | The combination of source and external_id already exists. |
Non-fatal warnings (order is created anyway; returned in warnings[]): PRODUCT_MODEL_NOT_FOUND, RECALC_PRODUCT_NOT_FOUND, CATEGORY_DISCOUNT_UNAVAILABLE, SHIPPING_AUTO_FAILED, SHIPPING_AUTO_EMPTY, TOTAL_DRIFT (unless strict_totals=true), UNKNOWN_FIELD.
Export (GET)
Read orders — Query parameters:
| Parameters | Type | Description |
|---|---|---|
orders_id | int | Individual order |
customers_id | int | Filter by customer |
orders_status | int | Filter by status |
date_from / date_to | date | Order date range (YYYY-MM-DD) |
limit / offset | int | Pagination (Default: 100) |