Changelog
What's new.
Every improvement to the platform, as it ships.
· APIService
Public passthrough for email OAuth provider callback
Forward GET **************** to EmailService sessionlessly (strips auth, no redirect following), mirroring the RingCentral OAuth callback. This is the fixed public redirect URI registered with Microsoft/Google/Zoho.
· EmailService
Fixed public redirect + gateway-forwarded callback
Platform email OAuth (Microsoft 365 / Google / Zoho) had no valid public redirect URI: the redirect was derived from the request host, which in prod is the cluster-internal service address, and the only callback handler was POST-only. Add a single fixed, publicly-reachable redirect URI used for both the authorization request and the token exchange, and a public GET callback that the provider redirects the browser to (org is carried in state, so no session is required). Returns a self-closing HTML page.
· kamo-internal
Gate lead contact-info by assignment + new privacy rights
Mirror the three new CRM rights. On the lead detail page, mask/lock the Primary Contact, Mailing Address and Property Address fields for leads not assigned to the current member unless they hold the view/edit contact rights (and, for unassigned leads, VIEW_UNASSIGNED_LEADS); the state field is always visible. Switch the Assignment-pane reassign gate to ASSIGN_LEADS. On the /leads grid, gate the Unassigned filter option by VIEW_UNASSIGNED_LEADS, drop the now-empty email/street filters and the email column value, and show property-vs-mailing state via a flip.
· SecurityService
Enforce lead contact-info privacy rights
getAllLeads now returns the lean LeadListDTO and hides unassigned leads from members without VIEW_UNASSIGNED_LEADS. getLeadById masks contact info (state preserved) for leads not assigned to the caller unless they hold VIEW_LEAD_CONTACT_INFO_OTHERS, and 403s unassigned leads for those lacking VIEW_UNASSIGNED_LEADS. updateLead gates manual reassignment (ASSIGN_LEADS) and contact-info edits **************** counting mortgage property fields only when actually changed. God bypass.
· kamo-shared-library
Lead contact-info privacy rights + lean list DTO
Add three CRM rights (161-163): "Assigned To Others: View/Edit Lead Contact Info" and "View Unassigned Leads". Add LeadListDTO — a lean projection for the /leads grid carrying no contact info except the state field. Add **************** to strip phone/ e-mail/mailing+property address (state preserved) for gated readers.
· kamo-register
Send membership type from the /member and /team paths
/team signups create a TEAM_MEMBER (created PROSPECT, pending admin activation); /member signups create a MEMBER. RegisterFlow takes a memberType prop supplied by each path wrapper and includes it in the register request body; the backend creates the matching membership type.
· SecurityService
/team creates a TEAM_MEMBER (PROSPECT), /member creates a MEMBER
Public registration now honors the memberType the register site sends (from the /member vs /team path): /team creates a TEAM_MEMBER — as PROSPECT, so an admin must activate them before they can sign in (per the login gate) — and /member creates a base MEMBER. Applied to both the new-user and existing-user-reuse paths via createRegistrationMembership(). The securityProvider mirror is unchanged (always a base MEMBER, and skipped when the org is its own security provider). Previously every register signup created a base MEMBER, so a /team applicant landed as a MEMBER and then couldn't be added as a team member via the UI (409 already-a-member).
· SecurityService
Expose team-member work phone extension in team-members-contractors
The /leads Agent filter needs the work-phone extension to label options as "Name (ext 8080)". The extension already exists on TeamMember (getPhoneWorkExtension) but was not serialized — only the bare number was.
· kamo-internal
Request-support popup shows parent-org shared topics
Point the ticket-creation topic dropdown at the new /api/support/topics/available endpoint (own-org + parent-shared topics) so child-org users can contact their parent org's support via the parent's configured topic.
· MediaService
Surface parent-org shared topics in request-support popup
Child-org users could not see their parent org's shared support topics (assignToOwnOrg=false) when requesting support, because the popup only fetched own-org topics. Add GET /api/support/topics/available (own + parent-shared, no MANAGE_SUPPORT gate) and fix getParentOrgTopics to use the corrected AssignToOwnOrgFalse query. Filing against a parent-shared topic already routes the ticket to the parent org via createTicket.
· SecurityService
New child orgs inherit their parent's securityProvider
When creating a child org from kamo-internal, set the new org's securityProvider to the parent org's securityProvider (the org it's created under) instead of letting it default to self. Pairs with the data backfill that points every existing org's securityProvider at the top-level KamoCRM org, so membership mirroring engages for all orgs and their future children.
· kamo-login
Show account-linked notice on ?linked=1
When register links an existing account to a new org, it redirects here with ?linked=1. Show "You already have a Kamo account — we've added this organization to it. Sign in with your existing password." and prefill the email. Mirrors the existing ?email_verified handling.
· kamo-register
Handle account-linked response for existing users
When the entered email already belongs to an ACTIVE account, the backend links this org to it and returns ACCOUNT_LINKED. Route those users straight to sign-in (?linked=1) instead of the email-verification flow — they sign in with their existing password. New users and placeholder-claim signups keep the verify-email path.
· kamo-internal
Wire up member/team-member create + Prospect-gate UI
- Add the missing POST proxy routes /api/security/members/create and /create-team-member (these 405'd before, so creation never reached the backend). Status + JSON body pass through so the form's 409 "already a member" reuse handling works. - MemberCreateForm: fix the Membership Status field where the floating label overlapped the "Select a status" placeholder (shrink label + notched OutlinedInput), and add a note that only "Active" team members can sign in.
· SecurityService
Reuse existing users, mirror to securityProvider, gate team-member sign-in
Reuse (no duplicate accounts for the same person): - MemberCreateController now reuses an existing user by personal email case-INSENSITIVELY (was case-sensitive, missing legacy mixed-case emails). - SecurityController.register reuses an existing account instead of always creating a new user: already a member of the org -> 409 ALREADY_MEMBER ("please sign in"); existing ACTIVE account -> link the org, keep their password, return ACCOUNT_LINKED; unactivated placeholder (never verified AND never logged in) -> claim it with the typed password. A public form can never overwrite an active account's global password. securityProvider mirror: - Both internal member/team-member creation and public register now call **************** so every new membership also lands under the org's securityProvider org (as a base Member). Team-member activation gate: - login blocks a TeamMember whose membership status != ACTIVE with "Your account exists but is not active at this time." Regular members and active team members pass. Org-resolution hardening (X-Forwarded-Host siblings): - getExternalProviders and PortalController.resolveOrg use the alias-fallback resolver and drop the getServerName() fail-open (fail closed instead of leaking the platform org).
· kamo-shared-library
SecurityProvider membership mirror + reuse/patch groundwork
- **************** userId): mirror a user into the org's securityProvider org as a base Member (deduped; no-op when the org secures itself or the user is already there). Backs the rule that every new membership also lands under the platform/security-provider org. - **************** case-insensitive, multiplicity-safe email lookup for cross-org account reuse (email is not globally unique, so a single-Optional finder would throw). - **************** stop resetting securityProvider to self on every org edit (only backfill when null), so a child org keeps its link to the platform provider the mirror depends on.
· kamo-internal
Wire Tasks tab to real backend + delete action + live count
Adds the Next proxy routes for /leads/{id}/tasks[/{taskId}] (the tab's getTasks/ addTask/updateTask/completeTask calls were 404ing), a deleteTask action in the task grid, and derives the 'Open Tasks' stat from the live task list so it stays current as TASK_CHANGED events refetch tasks.
· SecurityService
Lead tasks REST API + real-time TASK_CHANGED
Implements the backend the /leads/view Tasks tab was already calling but that never existed: GET/POST/PUT/DELETE **************** (LeadTaskController + LeadTaskService), org-guarded like the lock endpoints. Each create/update/delete emits TASK_CHANGED so open lead views refresh live.
· kamo-shared-library
LeadTask entity for lead follow-up tasks
Backs the /leads/view Tasks tab (previously a frontend-only stub with no persistence). LeadTask (table LEAD_TASK) mirrors the frontend TaskItem shape **************** + LeadTaskDTO and repository. Task mutations broadcast TASK_CHANGED on the existing lead real-time channel.
· kamo-internal
Real-time datagrid + live lead view with per-pane edit locks
/leads grid is now fully live over websocket (NATS→STOMP): rows add/remove/update against the active filter automatically and changed cells blink once for other members' edits; the dead refresh button is removed. /leads/view/[id] applies live lead/history/task/stats updates and gains per-pane exclusive edit locks with a 'currently editing' roster, live countdowns, and auto-save on idle/disconnect. Adds useLeadsRealtime hook, leadLocksApi + Next proxy routes, and a reusable MemberContactCard (name + work extension, click-to-call softphone / open-chat). Lead updates merge null-preservingly so partial payloads never blank cells.
· MediaService
Org-guard the /topic/leads/rt/{orgId} realtime subscription
Cross-tenant SUBSCRIBE guard for the new lead real-time topic, mirroring the existing /topic/social/feed guard: a member may only subscribe to their own org's lead stream. The existing **************** already relays security.leads.rt.* to /topic/leads/rt/*, so no relay change is needed.
· SecurityService
Per-pane edit-lock REST API + auto-save sweeper
Adds /api/security/leads/{id}/locks **************** backed by LeadLockService: exclusive per-pane reservation, draft-buffering heartbeats that reset a 5-min idle TTL, and a scheduled sweeper that auto-saves the buffered draft and releases on idle-expiry or presence-grace timeout. Idle-expiry is emitted as absolute epoch millis. Config under lead.lock.* (configmap).
· kamo-shared-library
Real-time NATS publishing + per-pane edit-lock entity
Publish org-scoped lead events on security.leads.rt.{orgId} (created/updated/ deleted with changed-field keys, history-appended) so the /leads grid and /leads/view page can update live. **************** now returns the changed registry keys used to drive the UI blink. Adds the LeadPaneLock entity (table LEAD_PANE_LOCK, unique (LEAD_UID,PANE)) and repository backing the exclusive per-pane edit locks; the publisher emits the idle-expiry as absolute epoch millis (timezone-safe number) for the countdown.
· kamo-internal
Integrate primary tabs into the header on remaining Style A pages
Follow-up to the Style A→B sweep, covering pages that don't route through SettingsView: - /kb: KbNavBar becomes a gradient hero header with the Articles/Resources/ Reviews tabs integrated and the New Article action right-aligned; layout gets the shared radial background. - **************** the mini back-arrow header + General/ Domains MUI tabs become a gradient hero header with integrated tabs. - /leads/view/[id]: the primary record tabs move into the HeaderBar gradient header; the inline status/sub-status editor is appended to the header's right-aligned actions (styled for the gradient), replacing the separate tabs card.
· kamo-internal
Relocate bottom count bars into the header across social, imports tabs
Applies the vendors-tab treatment to the sibling CRM settings surfaces so the standalone "x • y" summary bar at the bottom no longer eats page real estate: - Social Connections & Automated Imports: header actions bottom-align and the count line stacks directly above the Refresh + Add/Create buttons. - Manual Imports sub-tabs (Past Imports, Import Templates): the count moves to the left of the lone Refresh icon (space-between), dropping the bottom bar.
· kamo-internal
Move vendor/product count above the header actions, bottom-align Refresh + Add Vendor
Recaptures the page real estate the standalone bottom summary bar used to occupy — the "x vendors • x products" line now stacks above the action buttons in the header, and the buttons bottom-align with the title block.
· kamo-internal
Integrate primary tabs into the page header (Style B) across Style A pages
Every gradient-hero page that rendered its primary tabs in a separate card below the header now renders them inside the gradient header itself, matching /account and /support. - SettingsView: tab bar moved into the gradient header (converts all ~23 /settings/* pages at once); help column, headerAction, backHref, and the tab icon/glow/alignRight extras preserved; hideTitle still keeps its tabs. - /leads/reports: reports tabs moved into the header. - /commerce (PipelineView): per-market tabs (with vertical icons) moved into the header; single-market/onboarding states unchanged.
· kamo-internal
Back arrow in header returns to /leads
Add an optional backHref/backTooltip to SettingsView that renders a white back arrow left of the title, and wire the CRM settings page to send the member back to /leads. Tooltip reuses the existing localized leads.view.header.backToBrowser ('Back to leads') string.
· kamo-internal
Bold 'CRM Settings:' prefix, pipe separators, hover-only underline on tab links
Preface the CRM settings tab links with a bold 'CRM Settings:' label (new **************** key), separate links with ' | ' instead of a bare space, and switch the links from always-underlined to underline-on-hover.
· kamo-internal
Add CRM settings tab links above refresh, gated like settings menu
Render a right-aligned row of white, underlined, space-separated links to each /settings/features/crm tab above the Leads header refresh button. The links reuse the CRM topic + gating from settingsMenu.ts, so they only show when the org is a CRM org and the member holds MANAGE_CRM_SETTINGS, and the market-gated tabs (Vendors & Products, Manual/Automated Imports) stay hidden until an active market exists.

Your universe is waiting.
Start free in minutes. Add products whenever you are ready — no migration, no new logins.
Free forever — no credit card required