Changelog
Newest first. Every entry says whether it can break something you already built.
Entries are written in the commit that changes the API, not afterwards — CI refuses a change to the published surface that arrives without one.
2026-08-23
Added · xlsx exports actually produce a spreadsheet.
POST /api/exports accepted format: xlsx and the job then failed. It now
writes a real .xlsx, the file name ends in .xlsx, and the download carries
the spreadsheet content type instead of text/csv.
Added · Rate limiting, for real. The introduction had promised
60 req/min/token and no limiter existed. There are now two: one for tokens
and one for server-side event ingestion, both announced in the introduction and
in X-RateLimit-* headers. Possibly breaking if you were sending more than
the published budget — the headers tell you how close you are before anything
is refused.
Changed · Listing endpoints cap per_page. Seven listings —imports,
exports, message templates, webhook endpoints, channel integrations,
notifications and phone numbers— served whatever number you asked for.
per_page=100000 now comes back capped. See
Lists, paging and filters.
Fixed · GET /api/audit-logs scoped to your location. It was neither
filtered by location nor authorised, so a token could read the audit trail of
other accounts, old and new values included.
Fixed · 33 path parameters were named wrong in the specification.
@urlParam contact on /api/contacts/{id} and thirty-two like it. A generated
client built the URL from the declared name, so it failed at run time instead
of at generation time. Possibly breaking for a client generated before this
date: regenerate it.
Changed · The specification is OpenAPI 3.1. It validates against a standard validator in CI. The Postman collection is unchanged.
Changed · POST /api/contacts/{id}/verify-email returns the documented error
shape. It answered 422 without an errors object, breaking the
{message, errors} contract the introduction promises.
Removed · The chat widget's endpoints left this reference. api/widget/*
is the embeddable chat's own contract, protected by a domain allow-list rather
than by a token. It was never integrator API and being here misdescribed how it
is secured. Nothing changed about the endpoints themselves.