Unknown query parameters stop being ignored on 2026-09-03
From 2026-09-03 the flat routes reject a query parameter they do not declare with 400, instead of silently ignoring it. Until then every affected response carries a BeeL-Warning header naming the parameter, so you can find them before the date. The rate limits you are actually subject to are now written down.
From 2026-09-03 the flat routes reject a query parameter they do not declare with 400, instead of silently ignoring it. Until then every affected response carries a BeeL-Warning header naming the parameter, so you can find them before the date. The rate limits you are actually subject to are now written down.
What else changed
- What changes, and when. The canonical routes —
/v1/companies/{company_id}/…and/v1/accounts/{account_id}/…— have always answered400naming an unrecognised query parameter. Nothing changes for them. The flat aliases (/v1/invoices,/v1/customers,/v1/products, …) still ignore it today: you get200, but the filter, ordering or flag you sent was never applied. From 2026-09-03 they answer like the canonical routes. - How to find out if this affects you, before the date. Any alias response that dropped a parameter carries a
BeeL-Warningheader naming it. Log that header and you have the exact list of requests to fix. The most common case by far isper_page, which in this API is spelledlimit— if you sendper_pagetoday you are being served an unpaginated default and probably have not noticed. - Which status you get, and why. A value outside a parameter's vocabulary — an unknown enum, a malformed UUID — is
400: the fault is in the request target, not in its content.422is reserved for a body that parses but is semantically wrong. One crossover: a parameter that parses but whose value the operation will not take (alimitunder its minimum, acursorthat is not one of ours) answers422, and the operations where that happens declare it. - Rate limits are now documented. Three tiers — global, standard and a stricter one for the heavier operations — on a fixed 60-second window. They are enforced at the network edge and independently at each location, so short bursts may exceed the nominal figure before throttling engages: read each figure as the rate at which you may start receiving
429, not as an exact quota. - The bucket depends on how you authenticate. With an API key the window is counted per key: it is your bucket and nobody else spends it. With OAuth2 or a dashboard session it is counted per client, on a budget you share. If you integrate at any volume, use an API key — it is the only credential with a quota of its own.
- Operations carrying a
Sunsetheader are never canonical, whatever their shape: they keep the lax behaviour until they are removed.
Semantic rejections answer 422 and name the rule they broke
A body that parses but breaks a rule now answers `422` with a code and populated `details`, instead of `400` with an empty `details` object. `400` is left for what is wrong with the request target.
Tell a voided invoice from a totally rectified one, without a second call
Every voided invoice now carries `void_cause`, `void_reason` and `voided_at`. `VOIDED` was reachable two ways — a direct void, or a TOTAL corrective issued over the invoice — and until now telling them apart meant looking for a corrective invoice yourself. The fields come back on the invoice object, both on the single read and on every row of the list.