Widget API Reference
The Widget API powers the embeddable inventory widget. All endpoints are under /api/v1/widget/ and require API key authentication.
Interactive API docs are also available at /api/docs/ (Swagger UI) and /api/redoc/ (ReDoc).
Authentication
Every widget call is authenticated with a company API key, accepted three ways:
X-API-Key: <key>header (preferred for server-side calls),?api_key=<key>query parameter,api_keyin the JSON request body (for POSTs).
QR token flow (recommended for QR scans)
QR redirects no longer put the long-lived key in the URL — that leaks it into
browser history, server logs, and Referer headers. Instead, scanning a code
hits /go/<code>/, which redirects to the widget with a short-lived signed
token (?token=...). The widget exchanges it once for the key:
POST /api/v1/widget/exchange_token/
Content-Type: application/json
{ "token": "<token-from-redirect>" }
Response 200:
Tokens expire after ~10 minutes (410 Gone afterwards — re-scan to get a fresh
one). Keep the returned key in memory / sessionStorage; never put it back in
the URL. The SDK helper PortableInventory.exchangeToken(token) wraps this call.
Deprecated: embedding
?api_key=<key>directly in a QR-code URL. Use the token exchange above. Passingapi_keyon ordinary direct API calls is still supported. See API Versioning Policy.
Products
List Products
Returns all products with stock info for the company.
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
api_key |
string | Yes | Company API key |
location_id |
UUID | No | Filter stock by location |
Example:
Response:
{
"company": "Acme Corp",
"products": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Widget A",
"sku": "WA-001",
"stock_display": "150 units",
"engine_type": "counter"
}
],
"poly_products": []
}
Get Product Details
Returns detailed product info including stock breakdown by location.
Process Transaction
Add or subtract stock for a product.
Request Body:
{
"api_key": "abc123",
"operation": "add",
"quantity": 50,
"location_id": "550e8400-e29b-41d4-a716-446655440001",
"reason": "Purchase order received"
}
Response:
Movements
Stock Adjustment
Handle inbound/outbound stock adjustment via the InventoryOrchestrator. Direction
is taken from the sign of quantity: positive = inbound, negative = outbound
(there is no direction field).
Request Body:
{
"api_key": "abc123",
"product_id": "550e8400-...",
"location_id": "550e8400-...",
"quantity": 25,
"reason": "Receiving"
}
Stock Transfer
Transfer stock between two physical locations.
Request Body:
{
"api_key": "abc123",
"product_id": "550e8400-...",
"from_location_id": "550e8400-...",
"to_location_id": "550e8400-...",
"quantity": 10,
"reason": "Warehouse rebalance"
}
Inventory Queries
List Physical Items
List active physical items (serialized units) for a product.
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
product_id |
UUID | Yes | Product model UUID |
location_id |
UUID | No | Filter by location |
List Batches
List batches with remaining stock for a product.
Locations
List Locations
Returns active locations for the company (excludes VIRTUAL type).
Create Location
Request Body:
Get Location Inventory
Returns inventory breakdown for a specific location.
QR Codes
Get QR Info
Returns QR code status, target type, and target ID.
Configure QR Code
Assign a VIRGIN QR code to a product, item, or work order. Transitions status to CONFIGURED.
Request Body:
{
"api_key": "abc123",
"code": "QR-ABC-123",
"target_type": "PRODUCT",
"target_id": "550e8400-..."
}
Lock QR Code
Lock a CONFIGURED QR code, preventing further changes.
Work Orders
List Open Work Orders
Returns open work orders for the company.
Error Responses
All errors follow a consistent format:
{
"error": "Human-readable error message",
"code": "error_code",
"details": {
"field_specific": "info"
},
"request_id": "uuid-for-support"
}
Common Status Codes
| Code | Meaning |
|---|---|
| 400 | Validation error or bad request |
| 401 | Missing or invalid API key |
| 403 | API key lacks required permission |
| 404 | Resource not found |
| 409 | Conflict (insufficient stock, invalid state transition) |
| 429 | Rate limit exceeded |
Widget Iframe Security
When embedding the widget via an <iframe>, apply the sandbox attribute to limit capabilities:
<iframe
src="https://your-domain.com/widget/?api_key=abc123"
sandbox="allow-scripts allow-same-origin allow-forms"
referrerpolicy="strict-origin-when-cross-origin"
></iframe>
Recommended sandbox values:
| Directive | Purpose |
|---|---|
allow-scripts |
Required for widget JavaScript to run |
allow-same-origin |
Required for API requests to the host |
allow-forms |
Required for form submissions (transactions) |
Do not add allow-top-navigation, allow-popups, or allow-modals unless your integration specifically requires them. This limits the attack surface if the widget content is ever compromised.