Order Endpoint (Orders)

Endpoint

Orders v2.32.0

Orders: Export, Import (POST/PUT), and Delete via the generic OrderBuilder service.


Overview

OperationMethodEndpointScope
ExportGET/xpanel/xoport/Export/JSON/ordersorders:read
CreatePOST/xpanel/xoport/Import/JSON/ordersorders:write
UpdatePUT/xpanel/xoport/Import/JSON/ordersorders:write
DeleteDELETE/xpanel/xoport/Delete/JSON/ordersorders: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:

ModeBehaviorUsage
auto DefaultNo `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.
recalcReloads 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.
verbatimThe 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_flagproducts_price isot_subtotalot_taxot_total
0 (Net)netΣ(net × qty)Σ(net × rate/100), addedSubtotal + Shipping + Tax
1 (Gross)grossΣ(gross × qty)For informational purposes (“incl. VAT”)Subtotal + Shipping (Tax NOT included)
2 (Tax-Free)net = grossΣ(price × qty)0Subtotal + 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:

  1. Payload value (if set)
  2. customers.language_id from the customer master
  3. DEFAULT_LANGUAGE from the store configuration (code lookup in the languages table)
  4. 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): Expected ot_total amount after calculation.
  • strict_totals (boolean, default false):
    • 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 / BehaviorTypeDescription
Address Auto-FillautoIf 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 resolutionautoItems 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_attributesbool (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_shippingbool (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_emailbool (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 RecalculationautoIn 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_addressstringAdditional 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

CodeMeaning
INVALID_PAYLOADRequired fields are missing or the data type is incorrect.
CUSTOMER_NOT_FOUNDcustomers_id does not exist.
PRODUCT_NOT_FOUNDproducts_id does not exist.
TOTAL_DRIFTexpected_total ≠ calculated total and strict_totals=true.
STOCK_INSUFFICIENTStock is insufficient (if auto_stock=true).
DUPLICATE_EXTERNAL_REFERENCEThe 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:

ParametersTypeDescription
orders_idintIndividual order
customers_idintFilter by customer
orders_statusintFilter by status
date_from / date_todateOrder date range (YYYY-MM-DD)
limit / offsetintPagination (Default: 100)