Changelog
What's new.
Every improvement to the platform, as it ships.
· kamo-internal
Wire AvailabilityProvider into PresenceWrapper
· kamo-internal
Add useAvailabilityWebSocket hook and AvailabilityContext
· SecurityService
Auto-set member availability AVAILABLE on login, AWAY on logout
· MediaService
Add NATS→STOMP relay for member availability status
· SecurityService
Update availability endpoints to use MemberAvailabilityStatus enum
· SecurityService
Add AvailabilityPublisher for member status updates
· InitializerService
Migrate members.is_available to availability_status enum
· kamo-shared-library
Replace isAvailable boolean with MemberAvailabilityStatus enum
· kamo-asterisk-support
Enrich upload manifests with Asterisk CDR caller-ID data
Adds optional MySQL CDR enrichment so the softphone history tab can display who called (or was called). When ASTERISK_CDR_DSN is configured as a Forgejo repo secret, the helper: - Queries asteriskcdrdb.cdr for the call's uniqueId before uploading - Parses CLID into callerIdNum and callerIdName - Derives call direction (inbound/outbound) from src/dst vs extension - Includes a cdr{} block in the upload manifest CDR enrichment is opt-in and gracefully degraded: if the DSN is absent or the lookup fails, uploads proceed without caller-ID (existing behaviour). Adds go-sql-driver/mysql v1.7.1; Dockerfile runs go mod tidy to populate go.sum during the Docker build so no local Go installation is needed.
· ConversionService
Parse CDR caller-ID fields from upload manifest
When kamo-asterisk-support includes a cdr{} section in the upload manifest (populated when ASTERISK_CDR_DSN is configured), ConversionService extracts callerIdNum, callerIdName, callDirection, and disposition and passes them through RecordingMetadata so the Recording row is stored with full caller-ID context.
· InitializerService
Add CDR caller-ID columns to recordings table (order 40)
Adds caller_id_num, caller_id_name, call_direction, and call_result columns to the recordings table so the softphone history tab can display who called without relying on the FreePBX CDR pipeline being available.
· kamo-shared-library
Add CDR caller-ID fields to Recording entity
Adds callerIdNum, callerIdName, callDirection, and callResult columns to the Recording entity so kamo-asterisk-support can store Asterisk CDR metadata alongside the audio file. RecordingMetadata gains matching fields; a backwards-compatible constructor lets existing callers pass null for all four without change.
· kamo-internal
Add COMP payment method option to subscription order wizard
COMP is a valid backend PaymentMethod enum value for complimentary platform-admin grants, but was missing from the frontend type and wizard UI — only INVOICE and AUTO_BILL were offered. Added COMP to the SubscriptionPaymentMethod union and exposed it as a radio option.
· kamo-internal
Show platform kamocrm domain on custom domain entry
Surface the active *.kamocrm.com hostname on the add-domain flow and detect custom domains by excluding kamocrm URLs instead of default-flag alone.
· kamo-internal
Show Manage Domain button for top-level org owners and system members
· kamo-internal
Show Set Up Billing button for org owners with no account
When an org owner opens the plans-billing tab and no billing accounts exist, display a bootstrap prompt instead of the generic "contact your org owner" message. The button calls POST /api/security/account/ bootstrap-billing (idempotent) then reloads accounts on success.
· SecurityService
Add bootstrap-billing endpoint for pre-existing orgs
POST **************** triggers the same idempotent setup as the sub-org creation hook (BillingSetupService .setupForSubOrgCreation) but can be called on-demand by an org owner. This handles orgs that were created before the billing refactor and therefore never received an Account + AccountSubscription.
· kamo-internal
Wave 3 conversions, fallback fix, and auto-translate CI
- Replace all hardcoded English strings with t() calls across wave 3 components (chat, clubhouse, support, notes, pipeline, leads, nav, etc.) - Merge wave 3 fragments into messages/en.json (+19 keys) - Fix home section placeholder values in en.json (welcome, tagline, footer text, accountsDesc/networkDesc ICU interpolation) - Fix mobile welcome text in page.tsx to use t() instead of hardcoded strings - Fix request.ts: deep-merge English as base for all locale files so missing locale files (pre-translate-CI) serve English instead of crashing, and partially-translated locales fall back to English for missing keys - Update **************** to match kamo-marketing's translate-and-rebuild pattern: initial deploy → TranslateService port-forward → commit locale files → rebuild image with translations → redeploy
· kamo-register
Place nickname inline after name suffix on legal name row
· kamo-internal
Wire next-intl with member.language as locale source
Adds next-intl-based internationalization across kamo-internal without URL locale prefixes. Active locale is read from the kamo-locale cookie, which mirrors the authenticated member's `language` preference (set via the Regional Settings card in **************** Infrastructure: - i18n/locales.ts, i18n/request.ts: 22 supported locales matching kamo-marketing - /api/i18n/set-locale: writes the kamo-locale cookie - LocaleBootstrap: syncs cookie to Member.language on first authenticated load - I18nProviders: applies MUI core, x-data-grid, x-date-pickers locales and date-fns adapter locale to the active language - useFormatters: locale-aware date / number / percent / relative-time helpers - MyAccountTab Regional Settings save: writes the cookie + router.refresh() when the user changes their own language - scripts/translate.ts: build-time delta translation against TranslateService (mirrors kamo-marketing's pattern) - scripts/merge-fragments.ts, **************** tooling for aggregating per-area key fragments into messages/en.json Coverage: - messages/en.json seeded with shared common.*, nav.*, auth.* keys plus initial extraction across ~67 pages and components (dashboard, leads, commerce, settings, account, contacts, settings/features, settings/member, components/settings, components/tools/ai-chat, etc.). Many feature surfaces still need follow-up extraction passes.
· VOIPService
Fall back to imaging recordings for call history + voicemails
FreePBX 17 doesn't expose voicemail messages over REST or GraphQL on this install (the GraphQL voiceMail type returns mailbox config, not messages, and /admin/api/api/rest/voicemail 404s), and Asterisk's CDR pipeline is also non-functional here (ODBC DSN misconfig handled separately in KlusterServices). Without these the softphone tabs render empty even when call/VM recordings exist on the imaging side. When provider.getCallHistory or provider.getVoicemails returns nothing, fall back to listing rows from the kamo.recordings table scoped by (providerInstanceId, extension, isVm). Caller-ID metadata isn't carried on Recording today so we leave from/fromNumber blank — the row still plays back through the media-player tool via recordingId. Archive filtering is preserved across both paths so a row archived via the cache stays hidden in the fallback too.
· kamo-shared-library
Paginated Recording lookup by (instance, extension, isVm)
Used by the softphone tabs as the canonical source of truth for call recordings and voicemails when the upstream provider can't list them — e.g. FreePBX 17 has no REST/GraphQL voicemail-message API, and on this install Asterisk's CDR pipeline is also broken (separate ODBC misconfig fix in KlusterServices).
· SecurityService
Auto-provision KamoCRM seat on sub-org creation; promote-billing-owner endpoint
EntitlementService walks SubscriptionMember (not AccountMember) to compute member entitlements — the union of features across active licensee subs, plus the Free baseline. The Redis listener gains a SUBSCRIPTION:<uid> handler. New BillingSetupService runs in two flows: - createOrganization (sub-org under KamoCRM): auto-creates a KamoCRM-org Member for the creator if absent, finds-or-creates a KamoCRM Account with that Member as primary, opens a Free-tier AccountSubscription targeting the new sub-org, and seats the creator as licensee + sub-manager. - POST **************** carves out a fresh KamoCRM Account/sub for a member of an existing sub-org, optionally transferring named licensees. Org-owner-only. AccountController routes drop isLicensee / canManageSubscription from the account-level customer payloads — those flags now live on SubscriptionMember.
· InitializerService
Add member language + default_currency columns
Adds two NOT NULL columns to the members table backing the new Member.language (VARCHAR DEFAULT 'ENGLISH') and Member.defaultCurrency (VARCHAR(10) DEFAULT 'USD') fields. Existing rows are backfilled to the defaults so the NOT NULL constraint is satisfied without a downtime window.
· kamo-register
Add language step, country timezone picker, scroll + *** fixes
- New 'My Language' step prepended to the wizard, mirroring the country step's layout. Lists the 22 marketing-supported locales as flag tiles with native name + English tooltip; pre-selects from navigator.languages. - 'My Country' step now collects a timezone alongside the country, defaulted from **************** and editable before Next. Both fields are required to advance. - Registration body now sends language/timezone (plus the existing country) to the /register endpoint so they're persisted on the new Member. - Fix: Security Questions step content can now scroll vertically — the step was the only one without an overflow container, so long catalogs pushed the third question off-screen. - Fix: Finish button no longer stays disabled when *** auto-verifies before our listener attaches. Replaced the 100ms setTimeout race with a poll-until-mounted attach that also drains the widget's current state on attach for late listeners.
· kamo-internal
Add language + default currency to Regional Settings card
Member profile's Regional Settings card now exposes editable Language (22 supported locales mirroring the marketing site) and Default Currency (searchable Autocomplete) alongside Timezone. All three save together via PUT /api/security/members/{id}. View mode shows the native language name with English in parentheses, plus the currency symbol and full name.
· SecurityService
Persist member language/timezone/currency on signup
The /register endpoint now accepts language, timezone, and defaultCurrency alongside the existing country field. RegistrationService sets country and timezone on the new User and applies all four regional fields to the freshly created Member, deriving the default currency from the country's primaryCurrency mapping when one isn't supplied (and falling back to USD).
· kamo-shared-library
Map external customer address blocks into AccountAddress on pull
Add AdapterAddressMapper helper that converts a JSON address node from any provider into an EmbAddress and attaches it to an Account as a typed AccountAddress (SHIPPING/BILLING). Wire the helper into the four adapters whose customer endpoints expose address blocks: - ShopifyCustomerAdapter: default_address goes to both shipping+billing, remaining addresses[] become shipping records - WooCommerceCustomerAdapter: billing block -> BILLING, shipping block -> SHIPPING - BigCommerceCustomerAdapter: addresses[] split by address_type field - MagentoCustomerAdapter: addresses[] split by **************** ids Adapters with no address data in their customer payloads (Ecwid, Wix, Shift4Shop, PrestaShop, OpenCart, Squarespace) continue to compile unchanged.
· kamo-internal
/account/new copy-from-lead UX, full name fields, dual phones, address; account-level address management
Frontend: - /account/new: per-section "Copy from Lead" buttons + header "Copy all from Lead" button (when leadId param) - Name section: prefix, first, middle, last, suffix - Dual phones (personal + work) with extension and "Allow text" toggle - Address section using EmbAddress shape (line1, details, city, subdivision, postal, country) with "Use as billing too" toggle (default on) - Submit nested name/email/phoneNumbers payloads matching backend - accountsApi: typed AccountName, AccountPhoneNumbers, AccountAddress; new methods: getAddresses, addAddress, updateAddress, deleteAddress, setPrimaryAddress - AccountViewClient: render primary shipping address from new addresses array
· SecurityService
Support nested name/email/phoneNumbers + address management endpoints
- AccountController accepts nested name/email/phoneNumbers JSON on POST/PUT - Initial address on POST creates SHIPPING (and BILLING when useAsBillingToo set), sets primary pointers - Add 5 address endpoints: GET, POST, PUT, DELETE addresses; PUT /addresses/{id}/primary - Account summary/detail expose name object, phoneNumbers object, shipping/billing address lists, primary pointers - Drop account-level notes field references (Account.notes was removed earlier)

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