Restore the pipeline Quotes tab and wire quote conversion
`POST /quotes/{uid}/convert` shipped in SW4 and nothing has ever called it: `quotesApi` had ten methods and `convert` was not among them, so the endpoint was li...
The browser tab is named after the workspace, not the host
Enter Wienerschnitzel or Wendy's and the tab read "KamoCRM", while nothing inside the workspace said KamoCRM anywhere. ABC Mortgage was correct, which made it l...
Stop the prescribing-disclosure test being flaky
It passed alone and failed inside the full suite. The effect calls **************** and sets state in a .then(), so settling the mock's promise, running the con...
Volume bands stop pricing seats the plan does not sell
Starter admits 5 seats and Business 25, but the bands ran to 25 and 100 — four of the six priced seat counts nobody could reach. Starter's 10%-off-at-11 tier wa...
KI would not compile against the shared library on main
VendorDTO.uid became a String — a Long in the database, a string on the wire — and this call site still handed it to **************** which is a server-side req...
Stop offering 8 unroutable ICE candidates on every call
Callers hear several seconds of one-way audio at the start of a call — the other party says "hi" three or four times before the softphone user hears anything. ...
Our price comes from the catalog, and can't be typed again
The comparison pages hardcoded KamoCRM at $29 Starter and $64 Business while the catalog sold them at $34 and $72. Nine files, forty-two mentions, and the build...
The Traefik per-IP limit will 429 a QHIN invisibly
Comment only, in the file somebody opens when a partner complains. Traefik rate-limits per source IP across the whole api.* host, and a QHIN sits behind a sing...
Pin the exact JSON the clinical client parses
Every field in these bodies is read by name in app/lib/patients/ehrApi.ts and nothing was checking the server still emits them. The service tests pin that the e...
Recorded-call notice on extension-to-extension calls
Completes the third call path. Staff-to-staff calls never touch the attendant — they go from-internal -> ext-local, where the Dial belongs to FreePBX — so [from...
POST /service-jobs, gated on MANAGE_SERVICE_WORK
The create verb the work-order surface never had. app/commerce/service/new is a seventeen-line placeholder because there was nothing to call. Gated on MANAGE_S...
CreateJob, so a work order can be raised without an order
The one verb the service-job surface was missing. Until now the only thing on the platform that could produce a ServiceJob row was **************** off a paid o...
Rebuild to seed the four order and pricing rights
An empty commit built out of tree from origin's own tree. RoleRightsSyncService seeds org_role_rights at boot, so a right shipped without a SecurityService rebu...
Resolve the Kamailio question — SIP registration works end to end
Section 16 recommended a purpose-built config on stock Kamailio and recorded that "2600hz/kamailio does not exist". Both are corrected by evidence. Two further...
Match the SIP status code, not the reason phrase
sip-probe.py required the literal string "200 OK". 2600Hz's Kamailio config answers OPTIONS with "200 Rawr!!", so the probe reported a healthy, correctly config...
Mirror VIEW/MANAGE_ORDERS and VIEW/MANAGE_PRICING
The client half of closing the order-mutation hole. SecurityService now gates 39 order, pricing and tax endpoints on four new rights; these are the two mirrors ...
Gate the order and pricing surfaces on a right
Thirty-nine endpoints were protected only by "are you signed in". Every order handler on POSController and every pricing and tax handler on CommerceMarketContro...
VIEW/MANAGE_ORDERS and VIEW/MANAGE_PRICING, ids 273-276
No order-mutation right existed anywhere. POSController's order endpoints and every pricing and tax handler in CommerceMarketController read the org off the ses...
SIP registration works end to end
An endpoint can now REGISTER from the public internet and Kazoo's API reports it: **************** 200 OK via 47.181.8.87:5060. Four separate faults, each of w...
Show why the exchange network is unreachable
The outside-records panel could only say "not connected", which reads as an outage to wait out. The server now sends the reason alongside -- this practice has n...
The exchange status says what would make it connected
The status endpoint is asked before the UI offers a network query, so a practice that is not onboarded sees an explanation instead of a button that always fails...
Recorded-call notice on direct-dialled extensions too
A caller who dials an extension over the greeting now hears the notice as well. That path went through from-did-direct, where the Dial belongs to FreePBX's ext-...
Mirror the EHR stage, and guard the mirror
ServiceType.ts is a hand-maintained mirror of the Java enum and nothing was checking it. RoleRightType has had a parity guard since three EHR rights went missin...
Never auto-provision Patient Records into a tenant
Two of the three paths that would hand a clinical record system to orgs that never asked for one, now that ServiceType.EHR is COMPLETED and therefore available....
Recorded-call notice to both parties at once, then a bridge tone
When an agent answers a queued call, the caller hears the client notice and the agent hears the agent notice at the same time, then a shared 1000Hz tone marks t...
Publish Patient Records, and make that safe to do
ServiceType.EHR was held at TESTING long after the vertical was finished, because AppCatalogService derives availability from COMPLETED and three separate paths...
A vendor id leaves as a string, for every row
`commerce_vendors.uid` is `INT8 DEFAULT unique_rowid()` -- about 19 digits, where `Number.MAX_SAFE_INTEGER` has sixteen. A numeric id of that size is an IEEE-75...
Quote our own price per seat, not per user
The competitor pages priced KamoCRM in a unit we do not sell in — "$29/user/month Starter, $64/user/month Business" — on the same pages that describe competitor...
Say what the prescribing safety check did not cover
The panel rendered a safety block only when there were warnings, so a prescription that came back clean said nothing at all -- and this platform has no licensed...
Disclose that drug interactions are not checked
Prescribing ran three checks the practice's own data can answer and no drug-drug interaction check at all. Declining to build one was right -- interaction knowl...
4 GB per seat on every plan, and seats rather than users
Storage stops being a tier differentiator. The 2/4/5 ladder put one gigabyte between Starter and Business on a page that then had to explain what "pooled" meant...
Drop the CountryType frozen ordinal checks
CountryType has 196 constants, AFGHANISTAN(0) through ZIMBABWE(195), and every check written over it froze at 0..195. They are not one value away from failing -...
The per-plan allowance is per seat, and says so
Rename only — PlanStorage.gbPerUser is private to this class and the three call sites are all in it. The comment above it was the misleading half: the allowance...
Stop asking Graph for a message size the legacy type lacks
Every listing for Mike@OptionOne.com returned 400 RequestBroker--ParseUri ("Could not find a property named 'size' on type **************** and the mailbox was ...
The agreement sample quotes seats at catalog prices
The default agreement template shipped a worked example that contradicted the catalog on every line it mentioned: 50 users on a 25-seat plan, 25 GB of storage p...
Give the fallback cards the inherits eyebrow they were missing
The three upper fallback plans carried no inheritsFrom, so on a cold build — which is what a fresh deploy serves until the first revalidate — Starter showed "Up...
One storage allowance, seats not users, and stop parsing prose
Storage is 4 GB per seat on every plan. The cards need no special case to stop repeating it: ladderFeatures already shows a tier's delta over the one below, so ...
The platform save dropped every KamoPBX field client-side
Entering the credentials in the UI reported success and changed nothing. systemConfigApi.update serialises an explicit allowlist rather than the caller's object...
Zone must be named 'local' to match the node
The config.ini was loading correctly and the AMQP URI was right, but every node logged 'no local zone configured, adding default AMQP' and then retried ********...
Probe SIP for real, and assert the white-label holds on the wire
A TCP connect to 5060 proves almost nothing — it succeeds against anything listening, and on UDP it cannot fail at all. That ambiguity is exactly what led me to...
Close out SW6b
Eight departures. The one that mattered was in SW6a: PUT /{uid} accepted totals and sign-off verbatim, which made both of this plan's central rules decorative u...
Rebuild against kamo-shared-library for SW6b work-order writes
An empty commit, built out of tree from origin's own tree — NOT 'git commit --allow-empty', which permits an empty result but does not force one and would have ...
The six work-order tabs persist
SW6b Task 4. Labour, parts, time, vendors, sign-off and notes were React `useState` on `commerce/service/[id]` and lost everything on reload. They now write to ...
Eight work-order endpoints for lines, notes, vendors, sign-off and the clock
SW6b Task 3, controller half. POST/PUT/DELETE /{jobUid}/lines/..., POST /{jobUid}/notes, POST/DELETE /{jobUid}/vendors/..., PATCH /{jobUid}/signoff and PATCH **...
Line, note, vendor, sign-off and clock writes on a job
SW6b Task 3, library half. Eight new operations on ServiceJobService, plus the wire records for them and the three reads the detail page needs. Three things th...
Record what is running and the traps that cost a build each
Nine failure modes discovered the hard way, each of which would otherwise be rediscovered: the gitignored vendor tree, make re-fetching over it, ripgrep honouri...
Distinct Erlang node names for apps and ecallmgr
One image runs as two nodes. They need different names or they refuse to coexist, and both need the shared cookie that FreeSWITCH's mod_kazoo also uses.
Run the OTP release, not make release
The image now ships a relx release rather than the source tree, so passing 'make release' to the entrypoint failed with 'No rule to make target release'. foreg...
Like what you see shipping?
Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.