Credit a signup whose page views were never recorded
**************** was the ONLY thing in the fleet that ever inserted into MARKETING_VISITORS, so the whole conversion pipeline silently depended on a browser bea...
Let a manager accept the web chat they were just paged about
Clicking Accept on a web-chat request closed the popup and reopened it a moment later, unassigned and with no chat window — the same card, over and over, with n...
Pin which support queue a stranger's chat lands in
resolveTopic decides where a visitor from a customer's website reaches a person, and every way it can go wrong goes wrong silently — a chat filed under a plausi...
Don't file chats into a support topic the org deactivated
resolveTopic honoured a configured topic by id alone, so deactivating one left the integration still pointing at it and new website chats still landing there — ...
File a website's chats under the topic its integration chose
createSupportSession took the topic from whatever title the visitor's browser sent — the widget hardcodes "General Inquiry" — and when no topic matched that nam...
Tell the widget the org's name so it stops speaking as Kamo
The public-chat bootstrap payload carried orgId but no org name, so the widget had nothing to brand the assistant with and fell back to a hardcoded "Kamo AI". A...
Let an integration carry a key a hosted Kamo page can resolve
`publicSiteKey` was persisted only for SPECIFIC_AGENT integrations, because the multi-tenant MLOS portal was the only page that had to resolve its key by Host r...
Retry the member load every authenticated request makes
AuthHelper.getCurrentMember was the single most common casualty of a Yugabyte catalog bump in this service — eight of the conflicts logged across both pods in o...
Prove the deploy by digest, not by tag
The preceding commit stops `set image` being a silent no-op. This asserts the outcome: after the rollout, the tag is resolved to a digest at the registry and th...
A rebuild of the same commit deployed nothing and reported success
The image is tagged with the commit SHA, so rebuilding the same commit produces an identical image reference. `kubectl set image` then changes nothing, the Depl...
Build the sender's avatar URL from their org, not the request host
A chat notification's senderAvatar was resolved from X-Forwarded-Host, falling back to the server name. Both kamo-internal's BFF and APIService reach this servi...
Send the sender's avatar with the message and its push
The chat notification carried `senderAvatar: ""` with the comment "avatar resolved on frontend", and the push carried no avatar at all. A browser can resolve on...
Send an org's own hostname as the meeting OAuth redirect
The same rule the mailbox flow now follows, for Zoom and Teams meetings: an organization on its OWN app registered that app against api.<its own domain>, so tha...
Stop ten scheduled jobs from running twice now MediaService has two pods
Scaling MediaService to two replicas would have doubled every scheduled sweep it owns. Two of those send mail — MeetingReminderSweep and ChatEmailNoticeService ...
Make MediaService correct on more than one pod, and run two
MediaService has only ever run a single replica, so a deploy of it was a total chat outage and an OOM was the same. It could not run two, for reasons that were ...
Stop deleting the only chat pod before starting its replacement
Deploys replaced the only pod of each service with nothing to catch the requests in flight. Three settings, applied across the fleet: - preStop sleeps 10s befo...
An EMAIL type, and every push now names its org
EmailService raises the notification off IMAP IDLE and asks MediaService to deliver it through the internal dispatch endpoint that exists for exactly this — eve...
Publish needs GD, not an open god-mode window
The switch was invisible to god-eligible operators. It was gated on **************** — GD plus an opened break-glass window — and holding GD is not the same fac...
Let a god-mode operator file a system bug privately
Posting a report now offers a Publish switch to an operator with an open god-mode window. It defaults to off: the report stays with god mode until somebody rele...
Build against the two-field SystemBugDispatchDTO
Unrelated to the OAuth work in the previous commit — MediaService main has not compiled since kamo-shared-library ad78dfa8 added terminalSessionId and terminalT...
Resolve Canva, Meta, X and meeting credentials per organization
Every flow here now reads whichever app the org is on — its own registration if it brought one, otherwise Kamo's platform app. The Canva and Meta entry points ...
The incoming-call deep link must use kamocopilot://
KamoMobile's kamomobile:// scheme is step 2 of a rename that has not happened. The published binary registers kamocopilot alone, and a deep link to an unregiste...
Ring a member's phone for an incoming call
VoipStompRelayController relayed voip.call.* ringing events to the browser and deliberately never pushed them, reserving incoming-call alerting for 'the future ...
Announce a message only once it can be read
POST /sessions/{guid}/messages is @Transactional, and the NATS publish of the message has always waited for the commit — with a comment saying exactly why: publ...
Refuse an edit once another member has read the message
PATCH /api/media/messages/{id} was deliberately ungated on receipts, unlike the DELETE beside it. The reasoning written into it was that unsend makes read words...
Send the quote with a reply, not just the pointer
media_objs_msg.parent_id has been there all along and this endpoint has accepted parentId on the way in since threading was modelled, but a reply crossed the wi...
Spend the site deletes before the replacements are written
Editing a report came back "The bug could not be saved: Internal Server Error" whenever the reporter left a site ticked — so every edit about the title, the des...
Leave a group chat, and keep join/leave lines in the transcript
Leaving is DELETE /sessions/{guid}/membership, not removeMember aimed at yourself — that route refuses self-removal on purpose. Removal is done TO somebody and ...
Forward removedByMemberId to the browser
The relay copies named fields off ChatMessageDto onto the STOMP frame, so a field it does not copy is dropped even once the DTO declares it. MEMBER_REMOVED now ...
Name a group chat, star a conversation, remove somebody from a room
Three routes, and each refusal is a rule somebody has to be able to tell apart from an outage — so each carries its own code rather than a shared 4xx. DELETE *...
Serve a reopened window only what it missed, and audit a page in one write
Two costs, both paid on every single open of a chat window. ?since on GET /sessions/{guid}/messages. A window being reopened wants the messages after the one i...
A relay only forwards its own conversation
ChatSessionSubscriptionManager publishes to a topic named after the session it subscribed FOR, never after the session the frame came from. That is safe only wh...
Email the reporter when their bug or enhancement changes status
Triage was silent. Somebody filed a report and then had to keep opening the tab to find out whether anyone had looked at it — which is the same as not being tol...
Per-viewer read state, a live badge, and per-status counts
THE BADGE WAS ABOUT THE QUEUE, NOT ABOUT YOU. /new-count counted every NEW report platform-wide, so it could only fall when somebody moved a status — reading th...
Pin the attachment sniff to real encoder output
The existing cases build a header by hand, which tests the code against my belief about the format rather than against the format. These sixty-four-byte prefixe...
Attach a video to a bug report, not only a screenshot
A still cannot show the three clicks before the thing broke, so reporters were describing recordings in prose and posting the file into a chat instead. The atta...
An attachment that cannot be read says so instead of 500ing
The imaging proxy already answers 410 for revoked access and for a removed message, but let openRange's IOException escape uncaught — so the one case where the ...
Rebuild against the document upload fix
Picks up kamo-shared-library d21e01ce, which landed minutes after the previous build. The chat-attachment path already has f848f394; this carries the second hal...
Rebuild against the storage dedup fix
Picks up kamo-shared-library f848f394. A chat attachment whose upload failed left an ImgDat row that later uploads of the same file deduplicated against, so the...
A reaction toggled twice at once is a no-op, not a 500
setReaction read, then saved, then caught the constraint violation the read was supposed to prevent — which does not work: the violation surfaces at flush, afte...
React to a message with an emoji, and correct one you sent
POST **************** sets or clears the caller's emoji and answers with the message's whole reaction set, so a client that raced itself converges on the server...
A child org's lobby offers its own backgrounds, not the platform's
The folder holding an org's meeting backgrounds and theme config was derived from a hostname — the request's, or the org domain the sidecar passed — by taking t...
Use the shared availability engine
MemberAvailabilityService keeps the CRUD and becomes a BusySource; the arithmetic it used to own now lives in kamo-shared-library. Call sites are unchanged — th...
One phone, one alert — collapse duplicate push destinations
Members were getting the same chat message three-plus times on their phone. Registrations are unique on (device_token_id, org_id, push_kind), NOT on the push t...
Route getChatSessions' unread ambiguity through OwnAuthorshipAmbiguity
MediaController.getChatSessions hand-rolled the pre-extraction version of the "was this written by somebody else?" rule (rowVisitor != null && callerId ********...
Consistent error handling and layering on two review findings
Review findings on Tasks 14 and 15: - SystemBugController held a SystemBugRepository field and queried it directly for the new-bug count, while every other r...
Guard the unread-lookup itself, not just the broadcast
Review finding on Task 9: persistInbound is @Transactional, and only the broadcast() call inside the per-member loop was exception-safe (it swallows its own fai...
GET /api/webinar/presenting
The webinar types the CALLER is a presenter for. The calendar asks this before it offers an availability layer at all: hours declared by a member no type lists ...
Tell a member about a webinar booked on their behalf
Not gated on a webinar right, unlike the ticket counters: the case this exists for is one where the member took no action at all, so requiring the right to requ...
Count unreviewed bug reports for platform admins
Zero for an ungated caller rather than a 403: the nav polls this on every page load for every member, and an error they must routinely swallow is how real error...
Like what you see shipping?
Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.