# Inventory

The **Inventory** page shows how many units of each SKU your logistics providers are holding right now, side by side in one table. Use it to spot the SKUs that are running out before orders start waiting for stock.

Prerequisites

* At least one [logistics integration](https://docs.odus.com/docs/guides/order-management/integrations.md) that is **active**, has its **API connection** switched on, and has its credentials saved. Integrations that miss any of the three are not shown.
* Products marked **Shippable** and **Active**, each with a **SKU**.

## Opening the page

In the **Orders** view, open **Inventory**. Every Order Management role — admin, developer, operator, support agent and viewer — can read the page.

If you have no integration with an API connection yet, the page says so and links you to **Integrations** instead of showing an empty table.

note

If you enabled Order Management before Inventory shipped, your API key predates the permission Inventory needs to read your product catalog. Order Management grants it to itself the first time the page is opened, so usually there is nothing to do. Granting it needs a dashboard role that can manage API keys, so if yours cannot, the page asks you to repair the addon — ask an admin to open Inventory once, or to click **Repair** on **Order Management** under **Settings > Addons**. See [repairing the addon](https://docs.odus.com/docs/guides/order-management/setup.md#repairing-the-addon).

## What the table shows

There is one row per SKU. Stock belongs to the SKU, not to a catalog product: two products that share a SKU describe the same physical units, so they appear once, with one total and one status. The SKU's own page lists every product that carries it.

| Column | What it shows                                             |
| ------ | --------------------------------------------------------- |
| SKU    | The SKU, as your warehouse ships it                       |
| Total  | The units available across every connected provider       |
| Status | Whether the total is healthy, running low, or unavailable |

To find a product in the list, use the **Product** filter — see [finding what needs attention](#finding-what-needs-attention).

A **Total** is only ever a figure your providers actually reported. Where none of them did, it reads **—** rather than a number, and the status beside it says why: a provider that cannot be reached leaves the total **unknown, not zero** (see [when a provider cannot be reached](#when-a-provider-cannot-be-reached)), and a SKU no provider has been told about reads **Not mapped** (see [when a SKU is not mapped](#when-a-sku-is-not-mapped)).

The list stays deliberately short — it answers "is this running out?" and nothing else. Which warehouse holds what, and which products carry the SKU, are follow-up questions, and live on the SKU itself.

A shippable product with no SKU is left out of the list: without a SKU there is nothing to reconcile against a warehouse.

## Stock status

The status is a traffic light calculated from the **Total** column against that SKU's **low-stock threshold**:

| Status           | Total units                                                              |
| ---------------- | ------------------------------------------------------------------------ |
| **Out of stock** | 0 — a provider answered, and the answer is none left                     |
| **Low stock**    | 1 up to and including the threshold                                      |
| **In stock**     | more than the threshold                                                  |
| **Unknown**      | a provider did not answer and what was read is at or below the threshold |
| **Not mapped**   | no provider reports this SKU at all, and none of them has it mapped      |

**Out of stock** and **Not mapped** are deliberately different answers. The first is a warehouse telling you the shelf is empty; the second is that no warehouse was ever asked, because nothing knows the SKU. Only one of them is fixed by ordering more stock.

Every SKU starts on the merchant-wide default of **10 units**, and you can give any SKU a threshold of its own — see [setting a low-stock threshold](#setting-a-low-stock-threshold). A pallet item and a fast-moving consumable rarely want the same reorder point, which is what makes the traffic light worth reading.

The status is worked out when you read the page, not when the stock snapshot was taken, so a threshold you have just changed takes effect immediately — including for the **Stock status** filter and the **Status** sort.

## Opening a SKU

Selecting a row opens that SKU's own page at `/inventory/<SKU>`. It is an ordinary URL: reload it, bookmark it, or paste it into a support thread. It shows the combined total, the stock status, the threshold behind it, and one line per connected provider under **Stock by integration**:

| Column      | What it shows                                                                                                                                                                                                                                    |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Integration | The integration, as you named it                                                                                                                                                                                                                 |
| Matched as  | The value that provider is asked about for this SKU — your [product mapping](#mapping-a-sku-to-a-provider) where you have one; otherwise **Matched by internal SKU** for a provider that matches by SKU, or **Not mapped** for one that does not |
| Stock       | The units that provider reports                                                                                                                                                                                                                  |

The **Stock** figure carries one of three things:

* a **number** — what the provider reports;
* **—** — the provider reports nothing for this SKU: it holds no record under the value it was asked, or it was not asked because the SKU is **Not mapped** to it;
* **N/A** — the provider could not be reached. If it is one the SKU depends on (see [when a provider cannot be reached](#when-a-provider-cannot-be-reached)), its stock is **unknown, not zero**, and the total above reads **—** rather than a figure that leaves it out.

Providers differ in what they are asked without a mapping, because a SKU can be carried by more than one product:

* **NextSmartShip** matches by SKU, so an unmapped SKU is still asked for under your own SKU and reads **Matched by internal SKU**. An empty **Stock** figure beside it means NextSmartShip holds nothing under that SKU: check that the two catalogs spell it the same way, or [map the SKU](#mapping-a-sku-to-a-provider) if the provider knows it as something else.
* **ShipBob** matches by product name, and a SKU shared by two products has no single name to ask for. An unmapped SKU therefore reads **Not mapped** with no stock figure, and ShipBob's stock only counts towards the total once you [map the SKU](#mapping-a-sku-to-a-provider). Orders are not affected: exports and automatic submissions keep matching an unmapped item by its product name, as described under [product matching](https://docs.odus.com/docs/guides/order-management/integrations.md#product-matching).

warning

If a SKU is held by ShipBob alone and you have not mapped it, its row reads **Not mapped** rather than a figure. Map the SKU on its page and the figure appears on the next refresh, which happens as soon as the mapping is saved.

The stock figures come from the same snapshot as the list, so the two can never disagree.

Below **Stock by integration**, **Products with this SKU** counts and lists every product in your catalog that carries the SKU. Select a product to open it — that is where its name, price and SKU are edited. A SKU that no product carries says so instead of showing an empty list.

## Mapping a SKU to a provider

Each provider row carries its own mapping. A mapped row offers **Edit** and **Remove**; an unmapped one offers to create the mapping. The SKU is not asked for — you are already on its page, so only the provider's half of the mapping is a choice:

* for a provider that publishes a catalog and has an API key stored, you pick from that catalog, and ODUS remembers the product's id and SKU alongside the name;
* otherwise you type the name or SKU exactly as that provider spells it.

What the action is called says what the mapping does. On a **NextSmartShip** row it reads **Map manually**, because there is nothing to repair: the SKU is already matched by your internal SKU, and a mapping is the **override** for a SKU that provider spells differently. On a **ShipBob** row it reads **Map product**, because the mapping is what brings that provider's stock figure in. Either way it applies to that SKU alone — the dialog says which value it replaces — and every other SKU goes on being matched exactly as before.

The stock snapshot is rebuilt as soon as a mapping changes, so the row's **Stock** figure is right by the time the dialog closes.

Removing a mapping does not delete anything at the provider — it only stops ODUS claiming the two are the same product. Orders with that SKU go back to being matched by your product name or your internal SKU, whichever that provider [matches by](https://docs.odus.com/docs/guides/order-management/integrations.md#product-matching), so they keep exporting either way. On the Inventory page a NextSmartShip row goes back to **Matched by internal SKU**; a ShipBob row reads **Not mapped** until you map it again, and the confirmation says so.

Mapping needs a dashboard role that can change product mappings. A read-only role sees the **Matched as** column but is offered no action.

## Setting a low-stock threshold

On a SKU's page the threshold is shown beside the total as **Low stock threshold**, with **Edit** next to it. It is the number of units at or below which the SKU counts as **Low stock** — the status you see on the SKU and can filter the list by. An empty stock is always **Out of stock**, whatever the threshold, and nothing is sent when a SKU crosses either line. The threshold must be a whole number, and `0` is allowed — it means the SKU is never **Low stock**, only **In stock** or **Out of stock**, which suits a made-to-order item.

Every SKU starts on the merchant-wide default. Edit the value to give this SKU a threshold of its own; a SKU you never touch keeps using the default.

The status updates as soon as you save, and the list agrees with it on the way back.

Setting a per-SKU threshold needs a dashboard role that can write to inventory, so both admins and operators can do it. Everyone who can read the page can see the threshold. The merchant-wide default is a different setting, and changing it still needs a role that can change Order Management configuration.

note

A threshold belongs to the **SKU**, not to one catalogue product. If two products share a SKU they describe the same physical stock, and one threshold covers both.

## Finding what needs attention

The list behaves like the other Order Management lists. Filter it by **SKU**, **Product** or **Stock status**, sort it by **SKU**, **Total** or **Status**, and page through the results.

The **Product** filter is a picker over your catalog: it lists your products a page at a time as you scroll, and typing searches them by name, so a product far down a long catalog is found without scrolling to it. Pick as many products as you want and the list shows the SKU rows they are on — where two products share a SKU, picking either shows that one row, and picking both still shows it once. The filter names the products, and the list stays one row per SKU.

Sorting by **Status** ascending brings the SKUs that need attention to the top: out of stock first, then low stock, then the ones whose total is unknown, then the ones nothing is mapped to.

**Stock status** filters on **Unknown** and **Not mapped** too, which is how you list everything a provider outage is currently hiding, or every SKU still waiting to be mapped. A **Total** filter leaves both out: there is no number to compare.

## Keeping the figures current

The numbers come from a stored snapshot that is refreshed at most every 15 minutes, so the page stays fast even with several providers connected. The page shows when the snapshot was last updated, and **Refresh** asks every provider again.

### When a provider cannot be reached

Its stock reads **N/A** on every SKU, and every **Total** that depends on it reads **—** with a status of **Unknown** — never a figure or an **Out of stock** you would act on. A provider that is merely slow to answer must not empty your catalog on screen.

A SKU depends on a provider that would be asked about it: one the SKU is [mapped](#mapping-a-sku-to-a-provider) to, or NextSmartShip, which is asked by the SKU itself. A ShipBob integration the SKU is **not** mapped to reports nothing for that SKU whether or not it can be reached, so its outage changes nothing for that SKU — the total and status stay as they were, and a SKU that reads **Not mapped** keeps reading **Not mapped**.

The verdict is still given wherever the missing warehouse could not change it: stock already above a SKU's threshold is **In stock** whatever else turns up, because the unreported stock can only add to it. So one provider blipping greys out the SKUs that were near the line, not all of them.

Refresh once the provider is reachable again, and the figures come back.

### When a SKU is not mapped

If no connected provider reports a figure for a SKU and none of them has it mapped, the row reads **Not mapped** with a **Total** of **—**. Nothing is out of stock: no provider recognised the SKU, or none was asked about it. Select the row to open the SKU, where every provider row says how it is matched and offers to map it — see [mapping a SKU to a provider](#mapping-a-sku-to-a-provider). The figures appear on the next refresh, which happens as soon as the mapping is saved.

While a provider the SKU depends on is unreachable — NextSmartShip, or a provider the SKU is mapped to — the row reads **Unknown** instead, because that provider might have recognised the SKU. Refresh once it is reachable again to learn which it is. An unreachable ShipBob integration the SKU is not mapped to makes no difference: the row stays **Not mapped**, and mapping the SKU is still what changes it.

## Read-only by design

Inventory only reads from your providers. Order Management System never writes stock levels back to a 3PL, so counts, corrections and restocks are always made in the provider's own platform and appear here on the next refresh.
