Stop the unread card offering mail that was read and deleted
The home-page unread-mail widget reads email_metadata.search_index directly — it must not spend an IMAP connection, which is the whole reason it exists — and th...
Keep support tickets out of the per-member unread badge
perSender is what puts a number on a person — their member-directory row, their hex head — so it has to mean "unread messages from them". It was summing every s...
An unread count names a sender only when a single member is the answer
senderId is what pins an unread badge to a person — their member-directory row, their hex head — so it has to mean "these messages are from them". It was set fo...
Inbound auto-attendant — music on hold + dial-by-extension
Inbound calls had nowhere to go: the `incoming` table was completely empty, so even a delivered call would have dead-ended. Adds the attendant context plus the ...
A per-mailbox add-on is not priced per user
PER_MAILBOX fell through to the default arm, so the public pricing page advertised the two e-mail add-ons as "/user/month" against a per-mailbox price. PER_EXTE...
One shared calendar per org, one primary per member
Provisioning had nothing underneath it, so two simultaneous first loads both inserted. Collapses what that already made — twenty duplicate rows across seven org...
Stop two first loads provisioning two of each calendar
The launchpad paints two calendar widgets and each fetches its own feed, so two requests reached listCalendars at once. Provisioning is find-then-create, both p...
Saving a settings page no longer repaints the tab as the platform
Signed into Wienerschnitzel, opened Settings → Branding, saved Company Info, and the entire workspace reverted to Kamo's logo and colours. Closing the tab and o...
Allowlist Telnyx in both fail2ban jails
Adds the Telnyx US signaling addresses (192.76.120.10, 64.16.250.10 — sip.telnyx.com) to ignoreip now that the carrier trunk is provisioned. Also overrides ign...
Predial hooks must Return(), not MacroExit() — Asterisk 21
Asterisk 21 removed the Macro() application family outright, and FreePBX reaches these hooks with Gosub() (extensions_additional.conf: **************** The stoc...
Make fail2ban actually run — enable Asterisk's security log
fail2ban has never started on this PBX. Its asterisk-security jail watches /var/log/asterisk/security, a file Asterisk never created: FreePBX generates logger_l...
Scan the shared-lib email.billing repository package
@EnableJpaRepositories lists packages explicitly, so MailboxBillingDayRepository was invisible and the service refused to start outright — caught by booting it,...
Never lock the provider an org is already using
Two production orgs run 26 and 41 live mailboxes while holding no mail add-on code, so the picker would have told them KamoMail requires the KamoMail Hosting ad...
Review emergency access, and gate the last two surfaces
The break-glass review endpoint had a client function that nothing called: every emergency chart access was recorded and there was no screen to read them on. A ...
Raise and send a claim from the queue that found the work
The revenue cycle read seven queues and could not raise a claim. Each write now hangs off the queue that surfaced the need for it rather than living on a screen...
Correct a patient's details
Details could be entered once and never fixed. The panel sits under the summary rather than in a settings screen, because the moment somebody notices a misspelt...
Rebuild against current kamo-shared-library
CI clones shared-lib at image build time. This service was last built before shared-lib gained GMAIL_PERSONAL and OUTLOOK_PERSONAL, so its deployed enum cannot ...
Set up visit types and working hours
The first screen a new practice needs, and the one that did not exist. Booking offers visit types from one table and searches availability against another, so a...
Write a prescription, an order and a vaccination
The chart could show all three and create none. One panel rather than three, because they are one moment: a clinician finishing a visit writes the prescription,...
Choose who pays for mailboxes, and show the count per invoice
The cost-mode control appears only once a billing group exists — with none the setting changes nothing, and offering it invites the belief it is broken. Invoice...
Schema for member-connected inboxes
The old member-only unique on member_imap_configs is discovered by shape rather than by its Hibernate-generated name, and dropped only after the wider key exist...
Close out SW4
Nine departures. The two that matter most: this plan's own Self-Review asserted a type was consistent across three places, and it was consistent and wrong; and ...
Edit demographics, set up the schedule, bill and post
The last four rights that named operations which did not exist. EDIT_PATIENT_DEMOGRAPHICS: a patient's details could be entered once and never corrected. A fie...
Rebuild against kamo-shared-library for SW4 quote conversion
An empty commit. QuoteConversionService, QuoteConversionBilling and the five quote defect fixes all live in the shared library, and none of them reaches anyone ...
POST /quotes/{uid}/convert
On QuoteController rather than POSController: this controller enforces a right on every handler, POSController enforces none on any order endpoint, and it resol...
Back the personal-mailbox API the console already calls
Eighteen client methods over twelve routes that had been shipping with no backend behind them. A member reaches only their own inbox and configures it only once...
Personal mailbox entities and two consumer providers
GMAIL_PERSONAL and OUTLOOK_PERSONAL were offered by the picker and were not members of the enum, so selecting either produced an unusable org.
A closed head spins away instead of blinking out
Right-clicking a head and choosing Close, or putting one in the goal — thrown in or carried in and dropped — now plays a 500ms exit: a counter-clockwise three-q...
Prescribe, order and immunise
The chart could display medications, orders and vaccinations and could produce none of them. The read paths, the FHIR renderers and the safety queues were all b...
Explain the mailbox charge where mailboxes are made
Console rows price mail per mailbox with the live count; the mailboxes tab, department picker and provider help all state what counts, that a shared mailbox is ...
Gate provider choice on the add-on, show mailbox charges
Locked cards say which add-on unlocks them rather than being hidden or silently dead, and the gate fails open so a billing outage cannot strand an org mid-migra...
22 of the 26 clinical rights were never enforced
Only the four population-level rights — bulk export, quality measures, portal administration and EHR settings — were ever checked. Prescribe, sign, cosign, plac...
Both mail add-ons are priced per mailbox
Prices unchanged at $5 and $2; only the unit changes. Copy now states what actually counts, and that aliases are unlimited and free.
Bill e-mail per mailbox, reconciled nightly
Quantity is server-computed under a reserved key a request body cannot reach, and pushed through the existing amend path — so Stripe's CREATE_PRORATIONS charges...
QuoteConversionResult.orderUid is a String, not a UUID
Order extends BaseEntity, whose id is a Long — only Quote, QuoteOption and QuoteLineItem extend BaseUuidEntity. The plan declared UUID and its own Self-Review a...
An accepted quote becomes an order
acceptPublic wrote four columns and appended an event. No Commitment, no Order, no Transaction — the Quote javadoc's claim that accepting mints a Commitment des...
SW4's dictionary keys land in the dictionary repo
Task 6a's file list named kamo-internal/messages/en.json, which is gitignored and cannot be committed. The step's prose was right; the list was not.
Count an org's billable mailboxes
Adds PER_MAILBOX pricing, the mail cost mode, and a stereotype-free aggregator: hosted bills every box on the server, third-party bills only what was mapped int...
Each org chooses what an order takes from an accepted quote
A radio group, not a switch — both are legitimate ways to sell, and neither is 'off'. Shows which is the default and what each does to a real charge: for a thre...
A contested primary is a 409, not a 500
The catch-all turned an expected refusal into a server error, which lost the message the member needed and made a routine conflict look like an outage in the lo...
Ask a host that exists for the organization ?org= names
A new organization was created with its logo, colours and three backgrounds. All of it provisioned correctly into public/wienerschnitzel/ — 15 objects, the righ...
An organization can be looked up by the reference ?org= carries
GET /org/ref/{ref} — the same projected record as /org/domain/{host}, for an organization named by its id or its web alias. Nothing could answer for one. The s...
The config body carries a scalar, and absence means unchanged
updatePOSConfig bound Map<String, List<String>>, a shape that cannot carry quoteConversionBilling at all. Widened to Map<String, Object>. A key that is absent ...
Each org chooses what an order takes from an accepted quote
There is no platform-wide right answer. A maintenance contractor on a three-year plan bills each period; a capital-equipment installer bills the contract up fro...
Say who holds a contested mailbox, and that aliases are free
The snackbar replaced the server's 409 message with a generic failure, so a manager could not tell which member held the mailbox. Adds the note that sharing a m...
Refuse a primary mailbox another member holds
Primary is a login identifier, so a silent takeover changes which account an address authenticates from a screen that never mentions logins. Assign still works ...
Like what you see shipping?
Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.