Changelog

What changed, and what never will.

The compatibility promise: changes to this API are additive. We add endpoints, we add fields, we add optional parameters. We do not rename or remove published fields, change types, or repurpose existing parameters. If a breaking change ever becomes unavoidable, it ships as a new endpoint next to the old one, and the old one keeps working.
2026-09-17
New: every authenticated response now carries two more rate-limit headers alongside the three it already had. RateLimit-Policy lists every ceiling that applies to your API key, each one named, with its quota (q) and its window in seconds (w). RateLimit reports the state of exactly one ceiling — the one closest to refusing you next — and says which, with r for what is left and t for the seconds until it resets. There are two names, general and write. Nothing about the limits themselves changed, and RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset are unchanged and still sent: r and t are the same two numbers as the last two of those. What the pair adds is the name, and that closes a real gap. RateLimit-Remaining always describes whichever counter is closest to refusing you, so across mixed traffic it steps between two of them — a write reports the write counter, the read after it the general one — and the trio carries nothing to say the counter changed, which reads like the server contradicting itself. RateLimit makes that step self-describing. RateLimit-Policy is built from configuration rather than from your request, so it is identical on every authenticated response and even a GET advertises the write ceiling: a client that has only read so far learns that a tighter ceiling is waiting for its first write, instead of discovering it by being refused. Both ride on the 429 as well, where RateLimit names the ceiling that refused, with r=0. The two new headers are the current draft of the IETF rate-limit headers (draft-ietf-httpapi-ratelimit-headers, draft-11); the three you already have are the same work as it stood earlier and are what most HTTP clients and libraries read today. Both sets are sent, so use whichever your stack already understands. Still no figure is published anywhere in the documentation — the ceilings are deployment configuration, and the headers remain the only place a number comes from.
2026-09-17
Breaking: /api now accepts only an access token minted from a Storekeeper API key, and the per-key rate limit this page has promised since the key-admin release is enforced. A token minted for a browser session — what GET /loginSso gives a merchant signing in from their backoffice — is refused with 401 on every /api route whatever role it carries, and the message names the fix: create a key at /keys and exchange its client_id and client_secret for an access token at your own account's OAuth token endpoint (client_credentials, RFC 6749 section 4.4). If you were calling /api with a session token, that stops working now; nothing else about the request changes. Pinning the credential down is what makes the limit definable in the first place — every session token carries the same deployment-wide client_id, so "per key" could not even be counted for that half of the traffic. The limit: requests are counted per API key, per minute, and writes (POST, PUT, PATCH, DELETE) have a second, tighter ceiling that they are charged against in addition to the general one. Past a ceiling the answer is 429 Too Many Requests with Retry-After in seconds — wait that long rather than retrying hot, which only spends the next window on refusals too. You do not have to be refused to learn where you stand: every response to an authenticated call now carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset. No figure is published, because the ceilings are deployment configuration and a number printed in documentation would be wrong the day one is tuned; the trade is that a nightly sync is sized by reading RateLimit-Limit off one call instead of copying a rate from a page. Give each integration its own key — sharing one across a fleet spends one budget between them, and revoking one must not take the others down with it.
2026-09-17
Breaking: the documentation moved off /api. The interactive reference is now /docs, the spec is /openapi.json (Dutch: /openapi.nl.json), and the guide, recipes, libraries, error catalog, changelog and overview pages lost their /api prefix — /guide, /recipes, /sdks, /errors, /changelog, /business, /technical. The homepage is the site root itself rather than a redirect to one. Health probes moved to /_status/livez, /_status/readyz and /_status/version. Nothing about the API itself changed: the same 25 operations, at the same paths, with the same contracts. The point is that /api now holds those operations and nothing else — documentation and probes used to share the prefix, told apart only by a list somebody had to maintain, and a page added there would have been published to all nine SDKs as an endpoint. Update any bookmark of /api/doc.json to /openapi.json; no redirects are left behind, because a redirect under /api would be the very thing this removes.
2026-09-17
New: GET /loginSso now signs you in and lands on /keys, a page where you manage the API keys your integrations authenticate with — create one, roll its secret, revoke it. It used to answer a JSON document with an access token in it, which is a breaking change to that route, though nothing in the browser flow depended on that body. A key's client_secret is shown exactly once, on the screen that mints it: Storekeeper keeps only a hash, so nobody can show it again. Give each integration its own key — the rate limit is per key, and revoking one must not take the others down with it. This closes the gap the previous entry left open: machine callers were told to use an API key while nothing in the product issued one. Also: every script, stylesheet and font this site serves now comes from this origin instead of a CDN. The pages where you paste or read a credential are the ones that matter, and a third party inside those pages can read what you type into them.
2026-09-17
Breaking: POST /api/auth/refresh is removed and POST /api/auth/logout moves to POST /logout. Refresh had no caller worth keeping: renewing a machine API with a browser-session cookie was the wrong shape, and an API key is the right one. An unattended integration should now ask its Storekeeper administrator for an API key and exchange it for an access token at its own account's OAuth token endpoint (client_credentials, RFC 6749 section 4.4, discoverable at https://api-<account>.storekeepercloud.com/.well-known/oauth-authorization-server) — that exchange happens at Storekeeper, and this API never sees or brokers the secret. An interactive browser session now lasts exactly one access token: open the app from your backoffice again when it expires. Logout moved because it is part of that browser session rather than the machine API; the sk_rt cookie's Path moved with it and is now scoped to the single route that reads it. Both auth POSTs therefore leave the published spec and the nine generated SDKs, which no longer document any write operation at all. Nothing was deployed and no integrator held a session, so no running integration stops working.
2026-09-16
Breaking: the read-only demo token is withdrawn. POST /api/auth/demo is removed, and with it the last route that took a password and the last credential anyone could mint from a terminal. Every /api/ call now authenticates with the same ConnectedApp access token: open the app from your Storekeeper backoffice, land on GET /loginSso, and roll a fresh token with the HttpOnly sk_rt cookie at POST /api/auth/refresh. Starting a session needs a browser — there is no anonymous, demo or long-lived credential left to fall back on — so the guide, the SDK quickstarts and the reference page now all begin from a token you already hold rather than minting one. The demo account was shared and read-only, so nothing private was exposed by it and no integration holding a real session stops working.
2026-09-16
Breaking: GET /api/me no longer returns token.read_only. The field answered "will every write be refused before the route runs", and only the read-only demo token — withdrawn in the entry above — ever made it true. With that credential gone the field could only ever say false, and a standing promise read out of a constant is worse than an absent field: a client branching on if (!me.token.read_only) would be branching on something that can never change. token.kind stays, but access_token is now its only value. authorization.gated stays too and is always true: the capability gate applies to every caller, with no exemption. The shape where gated was false and capabilities was null — meaning "not applicable", not "holds nothing" — is gone with the credential it described. Read what you may do from authorization.capabilities, which is a real answer for every caller: an empty map means a role that holds nothing.
2026-09-16
Breaking: POST /api/auth/login is removed. A merchant now opens the app from their Storekeeper backoffice, which lands on GET /loginSso?t= and returns {access_token, token_type, expires_in} plus an HttpOnly sk_rt refresh cookie scoped to /api/auth. POST /api/auth/refresh keeps its path but replaces its contract: no body, no bearer, just that cookie. New POST /api/auth/logout revokes the refresh and access tokens upstream and clears the cookie — the first real revocation this API has had. Keep sending Authorization: Bearer <access_token> on every other call; only how you obtain it changed. POST /api/auth/demo is unchanged, and because it still signs in with a password, password handling is not gone from the API — only from the login route. (That last sentence records what was true of this release. POST /api/auth/demo was itself withdrawn on 2026-09-16 — see the entry above — so password handling is now gone from the API entirely.)
2026-09-16
Breaking: long-lived sk_live_ API keys are withdrawn. No key was ever issued, and the commands that minted, listed and revoked them are gone. Every integration — interactive or unattended — now starts at GET /loginSso and refreshes its access token with the HttpOnly sk_rt cookie. One consequence worth planning around: POST /api/webhooks needs a durable Storekeeper credential to poll with, the API key was the only one that carried one, so subscriptions cannot be created until the SSO path gains one. Ask your Storekeeper contact if you were relying on either.
2026-09-16
Breaking: outbound webhooks are withdrawn. POST /api/webhooks and the list, get, delete and test endpoints are removed, together with the dispatcher behind them. No subscription was ever created — since sk_live_ keys were withdrawn nothing could create one — so no integration stops working. Push delivery is being reworked and will come back as a new design; until then poll the delta feeds, GET /api/orders/delta and GET /api/products/delta, which are the same feeds the dispatcher polled.
2026-07-31
Webhooks: POST /api/webhooks subscribes an HTTPS endpoint to order.updated and product.updated events. Deliveries are HMAC-signed batches, at-least-once, with a test ping endpoint and per-subscription delivery log. (Withdrawn on 2026-09-16 — see the entry above.)
2026-07-31
Long-lived API keys (sk_live_) for server integrations: no more 30-minute token refresh. Keys are user-bound, provisioned by Storekeeper, shown once and stored hashed. Ask your Storekeeper contact for one. (Withdrawn on 2026-09-16 — see the entry above.)
2026-07-31
New delta endpoints for incremental sync: GET /api/orders/delta and GET /api/products/delta. Start from a `since` timestamp, then follow the opaque `next_cursor` to pull only what changed. No more full drains.
2026-07-31
Launched the recipe cookbook at /recipes: task-oriented walkthroughs extracted from production integrations, plus this error catalog and changelog.
2026-07-23
All marketing and guide pages are bilingual (EN/NL). New /sdks page with 9 downloadable, generated SDKs (PHP, TypeScript, Python, C#, Java, Go, Rust, Kotlin, Swift). Clean operationIds across the spec.
2026-07-22
The surface grew from 8 to 24 endpoints: orders, products, prices, stock, customers, segments, and the reports family (payments, daily close, product sales). New human guide at /guide.
2026-07-20
Financial report turnover rows now carry product_group_code next to product_group_id, so ledger mapping can key on your own codes.
2026-07-09
New reference endpoints: /api/payment-methods (stable type_alias per method type) and /api/turnover-groups.