REST API¶
Order status lookup (Dixa MIM Tools)¶
Used by Dixa's MIM Actions to answer "where is my order"-style questions. Deliberately lives outside the wc/v3 namespace and outside WooCommerce's own REST authentication — see Architecture if you're adding a similar endpoint.
Authentication¶
HTTP Basic Auth, checked independently of WordPress users and WooCommerce's Consumer Key/Secret REST auth:
The credentials are set in the site's .env file — see Configuration. Requests over plain HTTP are always rejected (401). Repeated failed authentication or lookup attempts from the same API key (or IP, if no credentials are supplied at all) are throttled — see Errors.
Other plugins (WordPress core's Application Passwords, some security plugins, Basic-Auth dev plugins) can also read Authorization: Basic credentials globally, for every REST request site-wide, and reject an unrecognized API key as an invalid WP username before this route is even reached. This endpoint clears any such conflicting global auth error for its own path so its own auth logic always gets the final say — see Architecture.
Request body (JSON)¶
POST is used instead of GET specifically so the verification data below never ends up in a URL query string (and therefore never in web server access logs, proxy logs, etc.).
This is enforced, not just a convention: order_number, email and phone are only ever read from the JSON body. If any of them are sent as query-string parameters instead — e.g. because a MIM Action tool was configured with "query" params rather than "body" — the request is rejected with 400 params_must_be_in_body rather than silently accepted, so a misconfiguration on the caller's side can't quietly reintroduce PII into request logs. When setting up the tool in Dixa, make sure order_number/email/phone are added under the request body, not under query parameters.
| Field | Required | Description |
|---|---|---|
order_number | Yes | The order's number (its WooCommerce order ID, unless a sequential order numbering plugin is active) |
email | One of email/phone | Billing email on the order |
phone | One of email/phone | Billing phone on the order. Normalized before comparison, so country code presence, spaces and dashes don't matter (e.g. +45 12 34 56 78, 004512345678, and 12345678 can all match the same order) |
order_number plus at least one of email/phone is required. If both are supplied, matching either one is enough — a customer's email on file can go stale while their phone stays current, or vice versa, so requiring every supplied field to match would only lock out legitimate customers. An order number alone is never sufficient to look up an order.
Phone number normalization on multi-market shops¶
A phone number typed without an explicit country code (e.g. 12345678) needs a default country to be interpreted correctly — 12345678 is a different number in Denmark than in the US. For shops that sell into more than one country, the shop's own base country is not a safe assumption, so the order's own billing/shipping country is tried first:
- The order's billing country
- The order's shipping country (if different)
- The shop's base country, as a fallback
- No default country at all — a number that already includes its own country code (e.g.
+45...) still matches even if none of the above is set or resolvable
This means an order can still be validated even when its country field is empty or wrong, as long as the phone number supplied is fully-qualified.
Example¶
curl -u <api-key>:<api-secret> \
-X POST "https://example.com/wp-json/dixa/v1/order-status" \
-H "Content-Type: application/json" \
-d '{"order_number":"1234","phone":"+45 12 34 56 78"}'
Response¶
{
"order_number": "1234",
"status": "processing",
"date_created": "2026-08-01T10:00:00+00:00",
"date_paid": "2026-08-01T10:05:00+00:00",
"date_completed": null,
"needs_payment": false,
"payment_method_title": "Credit card",
"total": "499.00",
"currency": "DKK",
"item_count": 3,
"shipping_method": "GLS Parcel Shop",
"shipping_country": "DK",
"is_refunded": false,
"estimated_delivery": null,
"tracking": {
"tracking_url": "https://..."
}
}
tracking is only populated when a shipping-label plugin providing dao_get_latest_shipping_label_from_order() is active and a label exists for the order.
Dixa's MIM Actions let the tool config narrow this down further with JMESPath on their side, so this endpoint intentionally returns the full flat object rather than supporting its own field-filtering query parameter.
dixa_order_status_data filter¶
A site can add, edit or remove fields on this response from its own theme/mu-plugin — e.g. to expose a custom field, rename a value, or drop something that shouldn't leave the site:
add_filter( 'dixa_order_status_data', function ( array $data, WC_Order $order ) {
$data['loyalty_points'] = get_user_meta( $order->get_customer_id(), 'loyalty_points', true );
return $data;
}, 10, 2 );
| Param | Type | Description |
|---|---|---|
$data | array | The response fields, keyed by field name (everything shown in the example response above, after estimated_delivery has already been computed) |
$order | WC_Order | The order being looked up |
Runs last, after every field this package computes — a callback sees (and can override) this package's own values, not just append new ones.
Estimated delivery¶
estimated_delivery is null unless the order both has a base date to count from and ships with one of the carriers below, identified by the order's shipping item method id (e.g. dao, dhl, bladkompagniet — as registered by barberklingen/package-dao-shipping, if active). When both conditions hold, business days (Mon–Fri) are added to the base date:
| Carrier | Business days added | Condition |
|---|---|---|
| DAO | 2 | — |
| DHL | 7 | Shipping country (fallback: billing country) is not DK |
| DHL | 3 | Shipping country (fallback: billing country) is DK |
| Bladkompagniet | 6 | — |
The base date is the order's completed date (date_completed) if set, falling back to the paid date (date_paid) if the order hasn't been marked completed yet.
Any other/unknown shipping method (or an order with neither a completed nor a paid date) leaves estimated_delivery as null.
dixa_order_status_estimated_delivery filter¶
The computed value above is only a default — a site can override or extend it (e.g. to add cut-off times, or handle a carrier not listed here) by hooking this filter from its own theme/mu-plugin:
add_filter( 'dixa_order_status_estimated_delivery', function ( $estimated, WC_Order $order ) {
// $estimated already holds this package's computed default (or null).
// Return an ISO 8601 date string, or null to leave it unset.
return $estimated;
}, 10, 2 );
| Param | Type | Description |
|---|---|---|
$estimated | string\|null | This package's computed default (see table above), or null |
$order | WC_Order | The order being looked up |
Return value must be a non-empty string (used as-is) or null.
Errors¶
Standard WordPress REST error shape ({code, message, data: {status}}):
| Status | Code | Meaning |
|---|---|---|
| 400 | params_must_be_in_body | order_number, email or phone was sent as a query-string parameter instead of in the JSON body |
| 400 | missing_params | order_number was supplied but neither email nor phone was |
| 401 | unauthorized | Missing/invalid Basic Auth credentials, or a non-HTTPS request |
| 404 | order_not_found | Generic — covers both "no such order" and "verification mismatch", by design, so a caller can't tell them apart |
| 429 | rate_limited | Too many failed attempts from this API key/IP; try again later |
Customer lookup¶
Returns WooCommerce customer data formatted for Dixa. Requires manage_woocommerce capability.
Query parameters¶
| Parameter | Type | Description |
|---|---|---|
email | string | Look up customer by email address |
phone | string | Look up customer by billing phone number (country prefix stripped) |
Exactly one parameter must be supplied. If neither matches a customer, an empty object {} is returned.
Example¶
curl -u admin:password \
"https://example.com/wp-json/wc/v3/dixa/v1/list?email=customer@example.com"
Response¶
The response shape is produced by DixaDataGenerator and mirrors the Dixa End User structure (name, email, phone, orders, subscriptions).