Add seven more ringtones to the catalog
Adds Battleship Horn, Demon Horn, Land Line, 1985 Ring, Office Phone, Ringing Old Phone, and Olé under /public/sounds/ringtones/ and wires them into RINGTONE_CA...
Add Sci-Fi and Buzzer ringtones to the catalog
Drops two new ringtone assets under /public/sounds/ringtones/ and registers them in RINGTONE_CATALOG so they appear in the Ringtones subtab and can be set as th...
Configure STUN so browser ICE can complete bidirectionally
Two-party browser-to-browser calls connected at the SIP layer but had no audio either direction. Voicemail (one-way Asterisk → browser) worked because Asterisk'...
Dump WebRTC negotiation state on Established
To diagnose "call connects but no audio between parties", log the peer-connection state (ice / dtls / signaling) along with sender and receiver tracks (kind / e...
Live audio that survives load-interrupts, centered spinner, outbound ringback
Three coordinated fixes for the in-call audio path: 1. AbortError on remoteAudio.play() (visible in production console as "The play() request was interrupte...
Hangup tears down ALL live sessions, not just the primary
Self-call edge case left a session leaked: dialing your own extension creates an outbound session, then Asterisk loops the call back as a second inbound session...
Instant connecting feedback on outbound dial
Same problem the inbound Answer button had: clicking Call kicked off the SIP INVITE round-trip (1-3 seconds) but the button kept its idle "Call" appearance the ...
Connecting animation + audio that actually plays + ringtone unlock
Three coordinated fixes for the inbound-call path: 1. Visual feedback while accepting. Clicking Answer left both buttons responsive-looking but unresponsive...
Mount SoftphoneProvider above ToolDock so tool windows can read it
Opening the Softphone popup crashed the page with "This page couldn't load" because tool windows mount inside ToolDock, which lives in ToolProviders — a SIBLING...
Use Grid size prop so AV panels fill the row
MUI v7's Grid is flexbox-based, so the gridColumn sx I'd been using was a no-op — items rendered at their natural width and left empty space on the right. Switc...
Migrate stale ringtone IDs to default on load
If a user's saved AV preferences reference a ringtone that no longer exists in the catalogue (e.g. 'classic' from before the catalogue was trimmed), the Rington...
Three-column audio layout, drop saved-banner, fill width
Lays the Audio subtab out as Microphone | Speaker | Advanced across the full row on lg+ screens (md still pairs them two-up, xs stacks). The Advanced card switc...
Split AV settings into Audio/Video/Ringtones subtabs
Reorganizes the Audio/Video tab into three internal subtabs and lays each one out horizontally so settings fit within the viewport without the wall of vertical ...
Add Audio / Video Settings tab and wire into softphone
Adds a per-device AV preferences store (localStorage, machine-specific, not roaming) plus a fully-featured tab on /settings/member/<id> for configuring and test...
Always-on SIP session with auto-popup + ringtone on inbound calls
The softphone used to register only while the popup tool window was open — close it and inbound calls were silently missed. Lift the session into a SoftphonePro...
Dialpad digits now register on touch devices
The dialpad buttons called e.preventDefault() in both onTouchStart and onTouchEnd. That stops the browser from firing the synthetic click event that follows a t...
Seed pbx.k1.kluster.kamocrm.com as a static domain
The browser softphone needs SIP-over-WSS to land on a hostname with a real TLS cert. Add the FreePBX admin / WS hostname to STATIC_DOMAINS so the auto-cert seed...
Host browser SIP WSS on internal.kamocrm.com (real cert)
Browsers refused the WebSocket connection to **************** with close code 1006 because Traefik was serving its self-signed default cert for that hostname (n...
Dynamic title and description from org config.json
Inline JS in title.html derives the themed CDN path from the hostname, fetches config.json, and sets the title to "Meets - {siteNameShort}" and description meta...
Use org short name and description in HTML head
Replace org-API-based title logic with config.json siteNameShort so the title follows the same pattern as other sites: "Register - {siteNameShort}". Adds descri...
Use org short name and description in HTML head
DynamicMetaLoader now sets document.title to "Login - {siteNameShort}" and writes the org description into the meta description tag. Static layout.tsx fallback ...
Use org short name and description in HTML head
DynamicMetaLoader now sets document.title to siteNameShort (was siteName) and writes the org description into the meta description tag. Static layout.tsx fallba...
Include org description in config.json
Add description field to buildConfigJson() and pass org.getDescription() from writeConfigFiles() so the themed config.json carries the org's description for fro...
Separate globe presence for marketing live chat vs members
Public Chat widget tickets keep org placeholder as requestorId; use only media session presence for those rows. Internal and other tickets use member presence (...
Pipe sip.js init steps to server log via beacon
The browser-side SDK init failure is currently invisible — the popup just says "Phone SDK failed to initialize" with no message, the WebSocket never opens, and ...
Periodic STOMP presence heartbeat for public support
Refresh Redis TTL for public session presence so agents see accurate online state.
Globe uses media session presence for marketing visitors
Request public-chat session GUIDs in presence snapshot; treat online if either session or requestor member is connected. Handle snapshot envelope and sessionGui...
Track public support chat by session GUID in Redis
Marketing visitors use /ws/public-chat-native without memberId; tickets still reference the org placeholder member as requestor. Increment PRESENCE_PUBLIC keys ...
Surface SIP.js init/transport errors to browser console
Empty "Phone SDK failed to initialize" messages were hiding the actual failure from sip.js. Turn on logBuiltinEnabled so the library writes its transport / regi...
Don't wipe member assignment on sync from providers that lack metadata
**************** unconditionally overwrote PhoneExtension.assignedMemberId with whatever the provider returned. FreePBX (and any provider that doesn't track whi...
Add includeCompany to market popup and companyName to leads
Market add/edit popup (LeadMarketManager): - Radio control "Track Company Name" / "Individual Only" (required) - Defaults to unset on new market; pre-fills on e...
Add include_company and company_name columns
LeadCompanyColumnRunner (Order 40) idempotently adds: - lead_markets.include_company (BOOLEAN, nullable) — tracks whether a market requires company name on le...
Expose includeCompany in intake-config and market-config responses
Both **************** and **************** now include the includeCompany flag so the frontend knows whether to show the company name field on lead creation.
Add includeCompany to LeadMarket and companyName to Lead
- LeadMarket entity gets include_company (Boolean, nullable) - Lead entity gets company_name (String, max 50, nullable) - LeadMarketDTO, CreateLeadMarketRequest...
Show provider name in view mode + own extension in edit dropdown
Two bugs on **************** 1. View mode dumped the raw UUID — "Instance 32f520b0-347a-…" — because the instance list was only loaded inside handleVoipEdit...
Serialize ExtensionDto.assignedMemberId as a string
overflows JS's safe-integer range (e.g. 1168485658174423046 round-trips as 1168485658174423000 once a JSON number hits the browser). The frontend already typed ...
Add diagnostic logging to /names endpoint
Log orgId, member count, and per-member name resolution to diagnose why the assigned-member name is not resolving correctly.
Make WebRTC config survive pod restart
The previous approach (http_custom.conf with [general] override) didn't actually take effect because Asterisk's INI parser doesn't honor multiple [general] sect...
Enable Asterisk built-in HTTP server on :8089 for WebRTC /ws
A chan_pjsip `protocol=ws` transport rides on top of res_http_websocket, which in turn requires Asterisk's built-in HTTP server to be running. The default FreeP...
Show sales type path in Add Lead subtitle
Append the market's Sales Type and Sub-Type to the "Adding to" line so the form mirrors the labels used in the Edit Market popup at **************** Renders as ...
Prioritize real first/last name over usernameAlias for display
usernameAlias is functionally a login handle (often equal to username), not a display name. Real human-readable names live on User.name (EmbNameFull). Apply to ...
Move Create Lead action into header, drop sticky bar
Replace the top-right Cancel button with the primary Create Lead action and remove the bottom sticky action bar entirely. Add a back IconButton to the left of t...
Scope Add Lead scroll and footer to its content area
The form was using minHeight: 100vh inside a parent that's already calc(100vh - 56px), forcing an outer scrollbar even when content fit. The action bar also use...
Unified PhoneSdk supporting RingCentral + sip.js (FreePBX)
The softphone hook used to instantiate ringcentral-web-phone unconditionally, which threw on any FreePBX credential shape because that SDK is hard-coded to Ring...
WSS-aware SIP credentials for browser softphones
The /api/voip/sip/credentials endpoint now returns credentials a browser-side SIP stack can actually use: WSS transport, public domain, and a wsServers URL. - ...
Like what you see shipping?
Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.