Show the correct draft batch total before send
Draft batches keep totalAmount at zero until send — it is a post-send snapshot, not a running total — so the compose UI now sums line amounts for the KPI tile a...
Normalize memberId when relaying line add/update requests
The browser keeps member ids as strings to avoid IEEE-754 rounding on CockroachDB unique_rowids. Normalize to Long before relaying so the credit lands on the in...
Aliases and shared-mailbox access name their members as text
GET and POST /api/email/aliases (and /aliases/member/{id}) and GET and POST **************** answered with the entities themselves, whose Long memberId and gran...
A number's owner travels as exact text, so saving it no longer re-owns it
GET /api/voip/numbers wrote ownerMemberId as the entity's Long. A member id is an INT8 past 2^53, so the settings page read it rounded: the owner was never foun...
The account view names its primary member
GET /customers/{uid} sent the primary member only as primaryMemberId, while the account view reads a primaryMember object — so its Primary member row read "---"...
An article's version history draws, and names who edited each version
kbservice answers /articles/{guid}/versions with a Spring Page of its entity. The history component read the body as a list and called .map on the Page, so it n...
A face that opens a card has a name for a screen reader
A MemberHoverCard around a face alone was a focusable button with nothing to be named by. It takes an optional label, used only when the trigger has no text of ...
A document's history loads, and names the people on it and on its shares
GET /imaging/history/changes and /history/downloads built each row with Map.of, which throws on a null — and a row with no IPv6 address, no prior data or no mem...
An article's byline names its author, and opens their card
kbservice answers GET /articles/{guid} with its entity, so authorMemberId is a JSON number, and JSON.parse rounds a 19-digit member id. The article route compar...
The Enter that opens a card goes no further
A name's trigger handled Enter and Space but let the key bubble on, so a list around it that acts on Enter itself — the timecard approvals queue opens the membe...
A card opened in a tool window knows who is online
Presence and availability are provided inside AuthedChrome, but the ToolDock — every chat, ticket and social window, and the chats switcher — is mounted above i...
A name with no card to open draws no hover underline
MemberName drew its dotted hover underline even when the surface had no member id, so a plain name promised a card it could not open. The underline now comes on...
An account note names the member who wrote it, not "System"
GET /customers/{uid}/notes sent each note's authorMemberId but no authorName, and the account view shows a note with no name as written by the system — so every...
A client's message pops its window into the front row, not a strip behind it
Reported 2026-09-17: Ian wrote into his Exec2Exec chat and the desk never saw it pop up; it was found only by going into the Interaction Center. Nothing upstre...
The Webinars section reads the lead API that exists
It called /api/webinar/leads/{id}, which MediaService never served (live 404), and a 404 reads to this section as "the feature is off" — so it rendered nothing,...
The chat head menu acts on the person in the chat, not on the window id
A head's id is its window's id (`chat-3`), and the menu read it as the member it was a chat with. So it fetched /api/security/members/chat-3 — a 404 — and with ...
A meeting from a chat window invites the conversation, and one email goes to everyone
Starting a meeting from a conversation window's meet button, or from a chat hexhead's "Invite to Meeting", invited nobody. The dialog closed as though the invit...
The public demo page shows no one's contact details, and a site's key books only its own org
Demo requests from the marketing site come from visitors with no account. That path already needed none: the attendee row is the name and email the form collect...
My Scheduled Webinars and Join Existing read the booking the server actually sends
The Support badge now takes a presenter to My Scheduled Webinars when a session is booked with them (mediaservice 0212445). That tab — and the wizard's Join Exi...
The scheduler lists the org's webinar types, and a booking badges its presenter
/support/schedule-webinar said "No webinar types are currently available." for every organization, however many types it had. The wizard loads its picker from G...
Short one-time codes can no longer be guessed without limit
Every short code the platform mails or texts was checked with no count: - POST /api/recover/email/verify-code took the 8-hex-character reset code ALONE and mat...
No OAuth client secret in kamo-internal's source; the anonymous token route is gone
**************** assigned the client-credentials secret for four production client ids (kamo:1:crm:internal, kamo:4:kamo:internal, kamo:5:ebonix:internal, kamo:...
Integration forms send only the credentials that were typed
The org CalDAV/CardDAV tab sent all four server fields on every save of an Apple or custom server. The fields never show what is stored, so they were empty, and...
Saving a calendar/contacts integration never blanks or drops its stored credentials
A contact integration's credentialsJson was replaced by whatever a save sent. The org CalDAV/CardDAV tab sends all four server fields (empty — they never show w...
No secret has a literal fallback in code or config; the Janus API secret never goes to a browser
The Janus API, token and admin secrets and the TURN password were committed four times over: as @Value defaults in WebRTCController and JanusService, as default...
Securityservice never stores a password as typed
Two writers put plaintext into USERS.PASSWORD. Both called User.encodePassword(), which needs a static encoder that only initializerservice ever sets **********...
Retail store connections are reachable only by their own org's commerce settings managers
The retail provider-config handlers on CommerceMarketController resolved the session's org and then used whatever market and config uid the path named. Update, ...
A Canva connect only returns the browser to the org's own console
POST /api/media/canva/connect-start stored whatever returnUrl the body named, and the OAuth callback on the API host redirects to it after Canva's consent scree...
The System User acts only inside the organization it was entered into
There is one System User for the whole platform. Platform operators, and support staff holding a grant scoped to a single ticket's organization, enter a tenant ...
Send the typed address with the reset code, and forward the caller's address
The reset page sent the 8-character code from the letter on its own, and SecurityService checked it against every account's outstanding reset at once, with no c...
A shared mailbox's access list is only readable inside its own organization
GET **************** called **************** which read the access rows for any mailbox id. Every other access operation (update, delete, grant, revoke) loads t...
Only an org's owner, its CONFIGURE_SYSTEM members or active god mode can change its settings
PUT /api/security/org/{id} took no request at all. ResourceServerConfig permits every request and APIService relays /api/security/**, so one anonymous request c...
An OAuth app save keeps its client secret unless a new one is typed; owners' credentials stay out of org JSON
PUT /api/oauth-config/{id} stored whatever arrived under clientSecret. Responses mask the secret, and the edit form sends a blank back while promising "Leave bl...
The provider form never holds the stored API key
AIService no longer returns provider API keys (only hasApiKey and a last-four mask). The edit button used to copy the returned key into the form, so it was in t...
Provider API keys never leave AIService, and only MANAGE_AI_SETTINGS can change a provider
GET /api/ai/providers and GET /api/ai/providers/{id} answered with each provider's DECRYPTED API key. The list is what every member's AI chat model picker loads...
Keep every credential field out of JSON, and guard the rule library-wide
Follow-up to 034378d2, which covered the columns NAMED as ciphertext. Read-only audits of origin/main in every repository found 61 more fields holding credentia...
Overlays clear the tool windows, scrollbars stack with their layer, and the rest of the shell geometry
What was left after pages stopped painting over the shell. Overlays opened BEHIND tool windows. Hand-rolled modals, menus and toasts named levels inside the sh...
Start a Facebook / Instagram connection through the authenticated connect-start
The Connect button navigated to **************** MediaService trusted both values with no session, so anyone who knew a connection id could bind their own page ...
Only an authorized member can connect a Facebook or Instagram page, and only back to their own console
The Meta connect flow trusted its URL. GET **************** took connectionId and returnUrl from the query string with no session, and state was unsigned base64...
Refuse webhook posts for Discord connections, which only ever receive DMs over the Gateway
Discord DMs reach MediaService only through DiscordGatewayManager's WebSocket, which publishes straight to social.inbound.raw. But every social connection has a...
Pages stay under the nav, the Interaction Center and the tool windows
The same two mistakes as /calendar's, everywhere else they were made. AuthedChrome's content box names no z-index, so a z-index in page code is a level in the R...
A person taken out of a drip stays out
The engine moves on up to 200 due people per drip from copies it read at the start of a pass, up to 25 seconds old by the time each is used. Meanwhile a member ...
The drip enroll dialog finds leads by email
The lead search behind it now matches a lead's email (kamo-shared-library 992569a6, live with SecurityService 5c7319a), so the dialog says so: name, email, comp...
A lead search matches the lead's email too
The /leads grid's boxes and the drip enroll dialog send what was typed as search terms, OR-ed across the lead's names, company, vendor lead id and number. The e...
The calendar stays under the nav and the tool windows
/calendar positioned itself over the viewport — fixed at 56/80 with z-index 1000 — instead of filling the content box AuthedChrome gives every page. That box na...
Keep every ciphertext column out of JSON, and guard the rule library-wide
Follow-up to 096feaa1 (Organization's GoDaddy credentials were being served by an endpoint that needs no session). A scan of every **************** found fourte...
Stop GET /org/{id} shipping the GoDaddy credentials, and lock it to this service's mapper
The fix is in kamo-shared-library 096feaa1: Organization.godaddyApiKey and godaddyApiSecret are now @JsonIgnore. That change reaches production only when a serv...
Never serialize the GoDaddy key and secret ciphertext
GET /api/security/org/{id} (and /org/domain/{host}) returns the Organization entity itself and answers without a session, and that body carried godaddyApiKey an...
An unconfigured build stops asking SecurityService for org ""
getHuntOffer fetched the promotion status even with no hunt configuration, so that the attempt would tag every prerendered page site-hunt. With no HUNT_ORG_ID t...
People in a paused drip still leave it when their lead moves on
A paused drip's exit rules (status change, reassignment, goal click) are now listened for, so resuming it never sends to someone who should have left while it w...
Like what you see shipping?
All of it arrives in your workspace on its own. Start on the free plan and read this page again in a month.
