Inventory Integration REST API
Documented build: Pro 1.1.0-dev.15. Set your own approved WooCommerce site URL and token; never publish a real credential. Production requests require HTTPS.
Authentication
All routes use:
Authorization: Bearer <integration-id.secret>
The credential must be active and have the required read/write permission. Location restrictions are enforced after authentication.
Base namespace: /wp-json/leanmerce-availability/v1.
| Method | Route | Permission |
|---|---|---|
| GET | /locations |
read |
| GET | /locations/{id} |
read |
| GET | /products?sku={exact-sku} |
read |
| GET | /products/{id}/inventory |
read |
| PUT | /inventory/balances |
write |
| POST | /inventory/balances/batch |
write |
| GET | /inventory/batches/{external_batch_id} |
read |
| GET | /integration/status |
read |
Read examples
: "${STORE_URL:?Set STORE_URL to the approved HTTPS WooCommerce site base URL}"
curl -i -H "Authorization: Bearer TOKEN" "$STORE_URL/wp-json/leanmerce-availability/v1/locations"
$site = (Read-Host 'Approved HTTPS WooCommerce site base URL').TrimEnd('/')
$base = "$site/wp-json/leanmerce-availability/v1"
$headers = @{ Authorization = 'Bearer YOUR-INTEGRATION-ID.YOUR-SECRET' }
Invoke-WebRequest -Method Get -Uri "$base/locations" -Headers $headers -SkipHttpErrorCheck
Success is HTTP 200 with {"locations":[...]}. Each row has id, label, active, archived, and capabilities. A credential restricted to online receives only that location. GET /locations/store-1 returns HTTP 403 when outside scope.
Negative test:
try {
Invoke-WebRequest -Method Get -Uri "$base/locations" -Headers @{ Authorization = 'Bearer invalid' } -UseBasicParsing
} catch {
[int]$_.Exception.Response.StatusCode
$_.ErrorDetails.Message
}
Expected HTTP 401, code invalid_auth.
Product reads
GET /products?sku=DEMO-MUG-001 resolves one exact non-empty SKU. Unknown SKU is 404; duplicate SKU is 409. The response contains id, product_id, variation_id, stock_owner_id, sku, and type.
GET /products/{id}/inventory adds inventory_mode and balances filtered to allowed locations.
Absolute On Hand write
Prerequisites: write permission; allowed active location; Inventory Mode active; authority external; this integration recorded as authority source.
: "${STORE_URL:?Set STORE_URL to the approved HTTPS WooCommerce site base URL}"
curl -i -X PUT "$STORE_URL/wp-json/leanmerce-availability/v1/inventory/balances" \
-H "Authorization: Bearer TOKEN" -H "Content-Type: application/json" \
-d '{"external_event_id":"erp-2026-09-14-001","sku":"DEMO-MUG-001","location_id":"online","on_hand":"12.00000000"}'
$eventId = "example-event-$([DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds())"
$body = @{ external_event_id=$eventId; sku='YOUR-CONTROLLED-SKU'; location_id='online'; on_hand='12.00000000' } | ConvertTo-Json -Compress
$first = Invoke-WebRequest -Method Put -Uri "$base/inventory/balances" -Headers $headers -ContentType 'application/json' -Body $body -SkipHttpErrorCheck
$replay = Invoke-WebRequest -Method Put -Uri "$base/inventory/balances" -Headers $headers -ContentType 'application/json' -Body $body -SkipHttpErrorCheck
[pscustomobject]@{ FirstStatus=[int]$first.StatusCode; ReplayStatus=[int]$replay.StatusCode; ReplayBody=$replay.Content }
Success returns changed or unchanged, identifiers, On Hand, Reserved, Available, and idempotent_replay. The write sets absolute On Hand while preserving Reserved. A later finalize/synchronize warning can return warning: reconciliation_required; inspect diagnostics before retrying with a new event.
Reusing the same event ID and identical identity returns the stored result with idempotent_replay: true. Reusing it for different product/location/quantity returns HTTP 409 idempotency_conflict. An in-progress duplicate can return 409 idempotency_in_progress.
Batch write
{
"external_batch_id": "erp-batch-001",
"items": [
{"external_event_id":"erp-item-001","sku":"DEMO-MUG-001","location_id":"online","on_hand":"12"}
]
}
POST it to /inventory/balances/batch. A batch contains 1–1000 items. Up to 100 items are processed synchronously; larger valid batches are queued. A queued response is HTTP 202; completed responses are HTTP 200. Poll /inventory/batches/erp-batch-001. Reusing the batch ID with different content is HTTP 409. Batch items can partially fail; inspect counts and each indexed result.
Errors and sequencing limits
Typical codes: invalid_auth 401; permission_denied 403; insecure_transport 403 in non-SSL production; invalid_event_id/invalid_quantity 400; unknown_product/unknown_sku/unknown_location 404; duplicate_sku, location_inactive, inventory_mode_off, reconciliation_required, external_source_not_owner, and idempotency conflicts 409; oversized batch 413; persistence failure 500.
Idempotency covers one integration/event identity or batch content. It is not a versioned compare-and-swap contract across unrelated external events. Concurrent absolute writes with different event IDs can arrive in either order; the connector must provide sequencing. Do not write WooCommerce _stock, reservations or Leanmerce tables directly. Revoke a credential to make subsequent authentication fail.