External Orders API
The External Orders API allows authorised external systems — ERPs, order management platforms, custom scripts — to push orders directly into the GSM Middleware database in real time.
Overview
| Detail | Value |
|---|---|
| Endpoint | POST /wp-json/gsm-middleware/v1/external/orders |
| Authentication | Bearer API key (per-site, managed in the admin) |
| Content-Type | application/json |
| Response on success | 201 Created |
The inserted order flows through the standard rm_order / rm_lineitems / rm_address tables and is immediately available in the Orders viewer and picked up by the Business Central export cron.
Authentication
Generating an API Key
- In the WordPress admin, go to API Keys (the menu between API Connections and DB Environments).
- Click Generate New Key.
- Select the site the key should be authorised for.
- Enter a descriptive label (e.g.
ERP Integration – Production). - Click Generate Key.
- Copy the key immediately — it is shown only once and cannot be recovered.
Using the Key
Send the key in the Authorization header as a Bearer token:
POST /wp-json/gsm-middleware/v1/external/orders HTTP/1.1
Host: yoursite.com
Authorization: Bearer gsm_abc123def456...
Content-Type: application/json
Alternatively use the X-API-Key header:
X-API-Key: gsm_abc123def456...
The key is tied to a specific site. You do not need to specify a site_id in the request body — it is determined from the key itself.
Request Body
All top-level fields listed below form the JSON body.
Top-level fields
| Field | Type | Required | Description |
|---|---|---|---|
order_number | string | ✓ | Unique order identifier. Max 20 characters. |
date | string | ✓ | Order date. ISO-8601 date (YYYY-MM-DD) or full datetime accepted. |
payment_method | enum | ✓ | One of: stripe, nmi, klarna, credova, sezzle, paypal |
transaction_id | string | Payment gateway transaction ID. Max 20 characters. | |
contact_id | integer | Navision contact ID. Defaults to 27111. Automatically set to payment-method defaults when not provided (Klarna → 27593, Sezzle → 37457, PayPal → 38407). | |
shipping_agent_code | string | Shipping carrier code, e.g. FEDEX, UPS. Default: FEDEX. | |
shipping_agent_service | string | Shipping service, e.g. GROUND_HOME_DELIVERY. Default: GROUND_HOME_DELIVERY. | |
billing_address | object | ✓ | See Address object. |
shipping_address | object | Defaults to billing_address when omitted. See Address object. | |
line_items | array | ✓ | See Line item object. Min 1, max 500. |
Address object
Used for both billing_address and shipping_address.
| Field | Type | Required | Notes |
|---|---|---|---|
first_name | string | ✓ | Max 50 chars after truncation |
last_name | string | ✓ | Max 50 chars |
company | string | Max 10 chars | |
address1 | string | ✓ | Max 50 chars |
address2 | string | Max 50 chars | |
city | string | ✓ | Max 30 chars |
state | string | ✓ | Two-letter abbreviation (e.g. TX). Max 30 chars |
postal_code | string | ✓ | Max 20 chars |
country | string | ISO 3166-1 alpha-2 (e.g. US). Default: US | |
phone | string | Max 30 chars | |
email | string | ✓ | Valid email. Max 58 chars. Used as the primary contact email for this order |
Line item object
| Field | Type | Required | Notes |
|---|---|---|---|
line_number | integer | ✓ | 1-based position within the order |
sku | string | ✓ | Product SKU. Must exist in rm_items (except system SKUs) |
quantity | integer | ✓ | Minimum 1 |
unit_price | number | ✓ | Price per unit, excluding tax |
coupon_code | string | Coupon code applied to this line. Max 50 chars | |
discount_amount | number | Discount amount (positive value). Default: 0 | |
weight | number | Weight in pounds. Default: 0 |
System SKUs
The following SKUs are reserved and do not need to exist in rm_items:
| SKU | Purpose |
|---|---|
COUPON | Coupon / discount line |
SHIPPING | Shipping charge line |
SALES TAX | Sales tax line |
STORE CREDIT | Store credit applied |
Example Request
curl -X POST "https://yoursite.com/wp-json/gsm-middleware/v1/external/orders" \
-H "Authorization: Bearer gsm_abc123def456..." \
-H "Content-Type: application/json" \
-d '{
"order_number": "ERP-78945",
"date": "2026-04-06",
"payment_method": "nmi",
"transaction_id": "TXN98765432",
"shipping_agent_code": "FEDEX",
"shipping_agent_service": "GROUND_HOME_DELIVERY",
"billing_address": {
"first_name": "Jane",
"last_name": "Smith",
"address1": "123 Main St",
"city": "Austin",
"state": "TX",
"postal_code": "78701",
"country": "US",
"phone": "512-555-0100",
"email": "[email protected]"
},
"line_items": [
{
"line_number": 1,
"sku": "PHX-1234-BLK",
"quantity": 2,
"unit_price": 49.99,
"weight": 1.5
},
{
"line_number": 2,
"sku": "SHIPPING",
"quantity": 1,
"unit_price": 12.95
}
]
}'
Example Response (Success)
HTTP/1.1 201 Created
Content-Type: application/json
{
"success": true,
"order_number": "ERP-78945",
"message": "Order ERP-78945 has been successfully ingested into the middleware."
}
Error Responses
| HTTP Status | Error Code | Description |
|---|---|---|
400 | rest_missing_callback_param | A required field is absent from the request body |
400 | rest_invalid_param | A field value fails validation (wrong type, out of range, etc.) |
400 | invalid_date | The date field cannot be parsed as a valid date |
401 | rest_forbidden | API key is missing, invalid, or revoked |
422 | unknown_skus | One or more SKUs do not exist in rm_items. The response body includes an unknown_skus array |
500 | db_error | A database write failed |
Example 401
HTTP/1.1 401 Unauthorized
{
"code": "rest_forbidden",
"message": "Invalid or revoked API key.",
"data": { "status": 401 }
}
Example 422 — Unknown SKUs
HTTP/1.1 422 Unprocessable Entity
{
"code": "unknown_skus",
"message": "One or more SKUs do not exist in the inventory table (rm_items). Ensure all products are synced before submitting orders.",
"data": {
"status": 422,
"unknown_skus": ["WIDGET-999-RED", "GADGET-XL"]
}
}
SKU Validation
Before inserting any data, the endpoint verifies that every non-system SKU in line_items exists in the rm_items table. If any SKU is unknown, the entire request is rejected with HTTP 422 and an unknown_skus list.
To pre-check:
- Ensure product inventory has been fully synced (run the inventory sync for the relevant site).
- Use the Items viewer in the admin to search for a SKU before submitting.
Field Length Constraints
Fields are silently truncated to the following database column limits (inherited from Order_Repository::FIELD_LENGTHS):
| Field | Max length |
|---|---|
order_number | 20 |
transaction_id | 20 |
first_name | 50 |
last_name | 50 |
company | 10 |
address1 | 50 |
address2 | 50 |
city | 30 |
state | 30 |
postal_code | 20 |
country | 10 |
phone | 30 |
email | 58 |
coupon_code | 50 |
company is limited to 10 characters by the underlying database schema.
Key Management
Revoking a Key
- Go to WordPress Admin → API Keys.
- Click Revoke on the relevant row.
- Confirm the dialog. Revocation is immediate and permanent.
- Generate a new key if continued access is required.
Rotating a Key (Best Practice)
- Generate a replacement key.
- Update the external tool with the new key.
- Revoke the old key only after confirming the new key works.
Security Notes
- API keys are stored as SHA-256 hashes in
rm_api_keys; the plaintext is never persisted. - Keys are scoped to a single site and cannot write orders to any other site.
- All endpoints require HTTPS in production.
- There is no scope for reading data — the key grants
write:ordersonly. - Rotate keys regularly and revoke any key that may have been exposed.
Related Pages
- Orders Viewer — view ingested orders in the admin
- REST API Reference — full list of all middleware REST endpoints
- Order Sync — standard sync from BigCommerce/WooCommerce