Changelog
Changes you can see from a client, newest first. Each entry that needs something from you says what. Internal work, and anything not yet running in production, is not listed.
/v1 only grows: new fields and endpoints are added, nothing is removed without 90
days' notice through Deprecation and Sunset headers. Build clients that
ignore fields they do not know. Labels:
Added Changed (behaviour you may need to handle)
Fixed Security.
#
- Fixed The published OpenAPI document now matches the traffic it describes. The server did not change; the spec was wrong in these places:What to do: Regenerate any client built from an earlier
- Completing an upload returns status
uploaded(orready); the spec listedready,pending,rejected. - Completing a multipart upload returns the same two fields as a single-part completion, not a full attachment.
- A key created on the account plane has no
tenant_idin its response; only the operator plane returns one. - Rooms carry
e2ee; registered devices carry a nullableapp_id; downloads includeposter_url; attachment init acceptsposter_of. DELETE /v1/push/subscriptionsreturns{"removed": …}, notunsubscribed.GET /v1/push/vapid-public-keyneeds no credential.- Minting a token declares the 400 (unknown role) and 403 (role above the member's).
PATCH /internal/v1/rooms/{room_id}declares its body (status,expires_at).
openapi.json. A hand-written client should acceptuploadedas an upload status and must not requiretenant_idon an account-plane key. - Completing an upload returns status
#
- Security A minted session token can no longer carry more rights than the member has. The requestedWhat to do: If you mint with an explicit role, check it never exceeds the member's.
rolemust beowner,memberorviewer(400 otherwise) and at or below the member's stored role (403 otherwise). Before, a higher role was granted. - Changed Both WebSockets re-check membership every 30 seconds. A socket of a principal removed from the room closes withWhat to do: Treat
4403.4403as final: do not reconnect. - Changed A Web Push endpoint must be
httpswith a public host. Cleartext, and loopback, private or link-local IP addresses, are refused with 400. - Changed FCM credentials must use Google's own
token_uri; anything else is refused with 400 when you store them. - Security Two concurrent verifies of the same OTP can no longer both succeed: exactly one wins.
- Fixed One slow Web Push service no longer delays every other push: each delivery has a 10-second deadline.
#
- Added What to do: Call it on sign-out and for "log out everywhere".
POST /v1/directory/logoutrevokes the presented directory session, or every session of that user with{"all_devices": true}. - Changed Rate-limited responses (429) carryWhat to do: Back off by
Retry-After, and the message names which limit was hit.otp/requesthas four: per app, per phone number, per IP and a resend cooldown.Retry-Afterrather than a fixed delay. - Changed
POST /v1/directory/contacts/lookupis limited to 20 calls per session per minute. - Fixed Erasing a principal now also removes their directory identity: phone number, display name, sessions, devices and OTP rows. Erasing a tenant also removes its account.
- Changed On Android, an incoming-call push is data-only, so your app can show a full-screen call. A message push still carries a notification.What to do: Implement
onMessageReceived, or calls show nothing on Android. - Changed A ring expires after 45 seconds, so a device that was offline does not ring late, and a cancel replaces its ring.
- Fixed A call ending no longer reaches Web Push as
new_message. Web Push always carriestype,room_idand, for calls,call_id.
#
- Added Native push: the backend sends FCM and APNs notifications itself, with your own credentials, set once per directory app (
PUT /v1/account/directory-apps/{app_id}/push). - Changed Pushes identify themselves, in FCMWhat to do: Route on
dataand beside APNsaps:typeandroom_idalways;call_idon the two call types;senderexcept on a message in an E2EE room. APNs pushes now play a sound, and E2EE rooms setmutable-contentso a Notification Service Extension can decrypt.typeandroom_idinstead of parsing titles, and treatcall_idandsenderas optional. - Changed A ring is deduplicated byWhat to do: Cancel the notification for that
call_id, so a call is no longer swallowed by a message sent just before it. When the caller hangs up, a silentcall.endedpush goes to the members who were rung.call_idwhencall.endedarrives. - Added Every operation has a recorded request and response in
api-examples.json.
#
- Added The turnkey directory: phone-OTP sign-up, profile, contact lookup, direct and group conversations, room tokens and push devices, under
/v1/directory/*. Directory apps are created on the account plane and deliver codes through your webhook or your own Twilio, Plivo or MSG91 account. - Added
GET /v1/directory/inbox: the chat list in one call, with names, last-message previews and unread counts. - Added
call.startedandcall.endedevents on the chat socket, to webhooks and as push, so an offline member can be rung.
#
- Added Self-serve accounts:
/v1/account/signup,loginandlogout, and API-key management (create, rotate, delete). - Changed AWhat to do: Recreate test fixtures with your test key.
testkey now works in its own data namespace and never sees live data. Anything a test key created before this is no longer visible to it.::is reserved in tenant ids. - Added The
/v1stability policy: changes are additive, and a removal is announced 90 days ahead withDeprecationandSunsetheaders. - Added Optional scheduled retention per plan: messages older than the plan's window are deleted, together with their attachments and stored files. Off unless set for your plan.
#
- Added Erasure:
DELETE /v1/server/rooms/{room_id}(scoperooms:delete) andPOST /v1/server/principals/{principal_id}/erase(scopedata:erase) delete rows and files. - Added Plans: per-key rate tiers, and optional monthly quotas on messages sent and attachment bytes. Over a quota the call fails with 402What to do: Do not retry a 402 blindly: it lasts until the quota resets at the start of the month or the plan changes. Retry after either.
quota_exceeded. - Changed Push, key-package and
/v1/e2ee/*endpoints re-check that the caller is still a room member: a removed member gets 403 at once, not at token expiry. Push unsubscribe stays open to them. - Security Push subscriptions belong to the principal that created them; another principal can no longer overwrite or delete them.
#
- Added Usage metering:
GET /v1/server/usage(scopeusage:read) sumsmessage.sentandattachment.bytesover a window.
#
- Added The customer server API,
/v1/server/*, with tenant-pinned, scopedak_keys. - Added Webhooks:
/v1/server/webhookswith signed delivery, retries, a delivery log and replay.