API
Stockroom has a REST API over the same data the app uses: catalog, stock, orders, reports. It is
JSON over HTTPS, versioned under /v1, and scoped to your organization — there is no cross-tenant
access of any kind.
The full endpoint-by-endpoint reference is served by the API itself, so it is never out of date:
- API reference — browse and try every endpoint.
- OpenAPI specification — generate a client from it.
API access is included on some plans and not others — see pricing for the current
matrix. If your organization is not on a qualifying plan, requests return 403 api_not_in_plan, and
the message names the plans that do include it.
Create a key
Keys are created in the app, by an admin, under Admin → API keys. They are never created over the API itself — that way a leaked key cannot mint replacements for itself.
When you create one you choose:
- A name — what it is for. You will be glad of this in a year.
- Permissions — see below.
- A location, or all of them. A location-restricted key behaves as though no other location exists.
- An expiry — 30, 90 or 180 days, or a year. A key cannot last longer than a year.
- Live or test. A test key is a live key with a lower rate limit and a distinguishing prefix. It is not a sandbox: it reads and writes the same real data.
The key is shown once, when you create it. Only a hash is stored, so nobody — including us — can recover it afterwards. If it is lost, revoke it and create another.
Authentication
Send the key as a bearer token:
curl https://api.stockroom-ims.com/v1/products \
-H "Authorization: Bearer sk_live_your_key_here"
Permissions
A key carries scopes, which mirror the same areas roles use — see roles & permissions. Each area is granted at read or write, and write implies read:
| Scope | Grants |
| --- | --- |
| inventory:read / inventory:write | Products, variants, stock levels, the movement ledger |
| transfers:read / transfers:write | Stock transfers between locations |
| serials:read / serials:write | Serial units and their chain of custody |
| customers:read / customers:write | Customers |
| sales:read / sales:write | Sales orders and payments |
| purchasing:read / purchasing:write | Suppliers, purchase orders, receiving |
| audits:read / audits:write | Count sessions |
| reports:read | P&L, valuation, best sellers, tax |
| dashboard:read | Dashboard figures |
| admin:read / admin:write | Organization settings |
| keys:read | List this organization's keys. There is no keys:write. |
Give a key the least it needs. A request outside its scopes returns 403 insufficient_scope naming
the scope that was missing.
Conventions worth reading first
These are the things integrators most often get wrong.
- Money and quantities are strings, not JSON numbers.
"19.99", not19.99. They are exact decimals; parsing them as floats will eventually cost you a cent. - Stock lives on the variant, never the product. Every product has at least one variant — a simple product has one default variant. Product-level figures are sums across variants.
- The stock ledger is append-only, which makes it safe to use as a change feed. Quantities on hand are a cache derived from it.
- Purchase order lines are in purchase units. If you buy in cases of six, a line quantity of 2 means 12 individual units.
- Sales order lines carry
qtyFulfilledseparately fromqty. An order can be partially shipped, with the remainder tracked as a backorder. - New fields and endpoints may appear in
/v1at any time. Ignore ones you do not recognise. Removals and changes of meaning ship as/v2.
Errors
Errors are application/problem+json with a stable
code. Switch on code, never on title — titles are prose and may be reworded. The full
vocabulary is served at
/v1/meta/errors.
| Code | Meaning |
| --- | --- |
| invalid_token | The key is not recognised. |
| key_revoked / key_expired | The key was revoked, or reached its expiry date. |
| insufficient_scope | The key is valid but lacks the scope for this endpoint. |
| api_not_in_plan | Your plan does not include API access. |
| feature_not_in_plan | Your plan does not include the feature this endpoint covers. |
| rate_limited | Slow down; see the RateLimit-* response headers. |
Rate limits
Every response carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Policy. Limits are
per key, so one busy integration cannot starve another. A 429 includes Retry-After.
Revoking a key
Revoke a key under Admin → API keys. It stops working from the next request — there is no cache and no grace period. Keys also expire on their own, which is the point of the one-year ceiling: a key nobody remembers issuing is the one that leaks.
Every key shows when it was last used and how many requests it has served, so you can tell a live integration from an abandoned one before you turn it off.
Getting help
Something missing, or an endpoint you need? Get in touch — we would rather hear it than have you work around it.