Changelog
Dated to match API versions — additive changes that don’t need a version bump are noted here too, under the version they shipped alongside. Changes to the OAuth endpoints, which aren’t versioned, get their own dated entry.
2026-09-23 — Standard OAuth error responses
Section titled “2026-09-23 — Standard OAuth error responses”POST /oauth/token, /oauth/revoke, /oauth/introspect,
/oauth/device/authorize and /oauth/register now answer errors the way the
OAuth specifications define (RFC 6749 §5.2, RFC 7009, RFC 7662, RFC 8628 §3.5,
RFC 7591 §3.2.2), so standard OAuth libraries can read them. The OAuth code
moves from message to error, and any detail to error_description:
// before{ "statusCode": 400, "message": "invalid_grant: code already used", "error": "Bad Request" }
// now{ "error": "invalid_grant", "error_description": "code already used" }- Status codes:
400for most errors;401forinvalid_client, withWWW-Authenticate: Basic realm="OneBooks"when you authenticated with HTTP Basic;429withRetry-Afterand"error": "temporarily_unavailable"when a rate limit refuses the request;5xxwith"error": "server_error". Every error response carriesCache-Control: no-store. - Device authorization: an unknown, suspended or deactivated
client_idnow gets401invalid_client(it was400), and a scope your app didn’t register getsinvalid_scope, with the scopes named inerror_description. The request now takes the RFC 8707resourceparameter —invalid_targetunless it’s on this API’s origin, as on/oauth/token— and, like the other standard endpoints, ignores parameters it doesn’t recognize instead of answering400. - Client authentication on device authorization:
POST /oauth/device/authorizenow authenticates the client exactly as/oauth/tokendoes (RFC 8628 §3.1). HTTP Basic works — the body then needs noclient_id(it used to get400). ACONFIDENTIALclient must send its secret, and a secret you send must be correct whatever the client type: a missing or wrong one is401invalid_client, where it used to be accepted unchecked until the token poll. Failures count toward this endpoint’s own failed-authentication lockout. APUBLICclient sending itsclient_idalone is unaffected. See device authorization. - API reference: the OpenAPI document listed the
Authorizationheader as required on/oauth/tokenand/oauth/revoke. It’s optional there, as on/oauth/device/authorize: HTTP Basic is one way to send client credentials,client_id+client_secretin the body the other, and aPUBLICclient sends no secret. A client generated from the document can drop the header./oauth/introspectstill requires it — introspection reads client credentials from the header only. resourceon the authorization request:GET /oauth/authorize— the call the consent page makes with your authorization request — now checks an RFC 8707resourceby the token endpoint’s rule instead of ignoring it: anything but this API’s origin isinvalid_target, which the consent page shows the user; nothing is redirected to your app.- Unrecognized
token_type_hint:/oauth/revokeand/oauth/introspectnow ignore a hint other thanaccess_tokenorrefresh_token, as RFC 7009 requires. It used to get400— and the token wasn’t revoked. - Unchanged: every other endpoint — including OneBooks’ own
/oauth/*routes behind the consent and device-approval pages — keeps the{ statusCode, message, error, code }shape described in Errors. - Deprecated
message: for backward compatibility with older OneBooks clients, the error body still carriesmessagetoo, with the value it used to have ("invalid_grant: code already used"above). It will be removed in a later release and must not be relied on.
This isn’t a new API version and OneBooks-Version doesn’t affect it: it
applies to every client. What to update: if your code reads message from
a failed token, revocation, introspection, device-authorization or
registration request, or matches its text, read error instead. Where the old
message started with an OAuth code (invalid_grant: expired), that code is
now error and the rest is error_description; an error whose message was
only prose now carries the matching code — invalid_client for a 401,
otherwise invalid_request — with the prose as its description. Log
error_description, but don’t branch on it. A client built on a standard
OAuth library needs no change. The Node SDK exposes
the code as err.oauthError and reads both shapes. See
OAuth endpoint errors for every code.
2026-09-22 — App platform launch
Section titled “2026-09-22 — App platform launch”The partner API becomes an app platform: apps can run inside OneBooks, not just alongside it.
- Embedded apps — app pages and 15 UI extension targets rendered in sandboxed iframes, secured by short-lived session tokens and RFC 8693 token exchange. New: App Bridge protocol v1.
- App data — typed, app-owned fields on invoices, customers and 10 other resource types, optionally merchant-editable. See App data.
- Hosted functions — run your own JavaScript on OneBooks events in a sandboxed, declared-egress V8 isolate. See Hosted functions.
- Marketplace — reviewed, localized listings; ratings and reviews; managed one-click install. See Publishing your app.
- Event catalog expansion — 45 events across every domain module, among
them
purchase.updated,quote.deleted,purchase_return.refundedand thesupplier.redactprivacy notice, plus a durable Events API catch-up feed with 30-day retention. Webhook deliveries now carryeventIdandapiVersion, support signing-secret rotation with dualv1=signatures during the overlap window, and can be redelivered from the console. See the Event catalog and Webhooks. - New scopes:
app-data:read,app-data:write,events:read. See Scopes. - Per-app rate limits — OAuth-authenticated API calls are now limited per
app rather than per IP: 300 requests a minute per app per business, and
10,000 a minute per app across all businesses, with
RateLimit-*response headers describing the per-business budget. OnPOST /oauth/token, a confidential client now gets its own budget of 600 requests a minute across all its IPs; token, introspection and revocation requests are counted per IP and client (600 a minute with a secret, 300 without) under a 1,200-a-minute per-IP ceiling, and failed client authentications lock out that IP-and-client pair — not the whole IP — for a minute. See Rate limits. - Stricter, interoperable client authentication — any client secret a
request presents to
/oauth/*must be correct, whatever the client type; and a public client can now revoke its own tokens by sending just itsclient_id(RFC 7009), where it previously got401. See Revocation. - Date-based API versioning —
OneBooks-Version, pinnable (and unpinnable) per app in the console, 12 months of support per version; hosted functions’ctx.apifollows your app’s pin too. Current version:2026-09-22. See API versioning. - New SDKs:
@onebooks/node,@onebooks/app-bridge,@onebooks/functions, theonebooksCLI and theembedded-nodestarter template — in preview: the packages are being published to npm; until then, email developers@getonebooks.com for early access.