Pin the well-known folders to the top of the sidebar
Inbox, Drafts, Sent, Junk, Trash and Archive stop moving. They hold that order at the top of the folder list, and everything the member made sits underneath und...
Rebuild the index as a map of the product
It was one recursive list of all three hundred articles, indented by depth. Everything was on the page and nothing was findable, and the page weighed 3.9 MB. T...
ReportDefinition never shipped the narrower rights its resolver reads
ReportScopeResolver, its test and this class's own javadoc all name alsoGrantedBy, and ReportSecurityScope has had SELF/TEAM/ORGANIZATION since the framework la...
Reach the conversation on screen when it is pages down the list
Switching the navigator to the right tab was only half an answer. The list pages by last activity, 30 rows at a time, and the conversation a member has maximize...
The Report Studio — a library, and one renderer for every report
/reports is a shelf, not a card grid: two hundred identical rounded boxes is a wall to scan, and a row gives a report's name room to be read. Saved views nest u...
A list read shipped every article's body, and a category page listed nothing
Two defects on the public reads, both visible on kamocrm.com/help-center. The index served 3.9 MB of HTML. tree-by-parent-slug returns every node with its full...
Stop the offline banner standing as a column beside the mail
Same defect the search results bar had, in the same container. MessageBrowser's root was a horizontal flex row whose children are the columns of the mail client...
Run, export and schedule the Report Studio's reports
The service half of the framework: the catalogue, the run, the pickers, four exporters, saved views and schedules. Authorization is explicit on every path, bec...
The Report Studio framework, and nine reports built on it
A report is now a Java class here and nothing anywhere else. ReportDefinition describes what a report is, what it may be asked, which columns it has and how eac...
Say that the status-label save is a whole set, not a patch
The endpoint's meaning changed underneath this client: a status left out of the list is now a deletion rather than a no-op, which is what lets a label be remove...
The importer, and three things it taught us
Publishes the corpus by driving the real KBService API rather than writing rows -- a direct INSERT skips the version row, the kb.article.published event that is...
Keep made-up article paths out of the index
The canonical check already stops a wrong path rendering the article -- it serves the not-found page instead. What it cannot do is change the status code: this ...
Pin the status-label body an org sends to drop a label
The two shapes that used to fail: an empty list, which is what clearing the last remaining label sends and what the handler rejected as malformed, and an entry ...
Send a cleared status label as a removal, not as silence
Emptying a custom label box and saving did nothing. The save built its payload from the labels that had text in them, so a box the org had just cleared was not ...
Accept a blank status label as the removal it is
The PUT is the org's complete set of custom labels, so it has to be able to carry an emptied box. Two things stopped it: an empty list was rejected as a malform...
Let an org put a custom status label back to blank
A status label had no way to be removed. The settings screen saved through an upsert-only bulk write, so the only thing a request could say was "this status is ...
Do not claim nothing matched when a filter hid the matches
The empty-list explanation added with the search results bar keyed off the query alone, so it also fired when the search had matched plenty and the Unread or St...
Article translation has never worked
deleteByArticleUidAndLocale is a @Modifying query and JPA refuses to run one outside a transaction. Nothing on this path ever opened one, so every locale of eve...
Stop mail search opening a strip down the side of the screen
MessageBrowser's root element is a horizontal flex row whose children are the columns of the mail UI. The search results bar was written as a bottom bar -- bord...
Follow the maximized chat window's tab in the navigator
Maximizing a chat popup left the conversation navigator on whatever tab it was already showing. `visibleRows` drops every row outside the active bucket, so the ...
Rebuild against the shared library that persists the quote contact
The market writer landed one commit after this service was last built, and CI clones kamo-shared-library at image-build time — so the deployed jar still has the...
The Report Studio design — Java-defined reports, a generic renderer
Reports are defined as Java classes in kamo-shared-library and rendered by one capability-driven frontend, so adding a report is a new class and nothing else. ...
Persist a market's quote-contact rule, and refuse a half-stated one
The DTOs carried quoteContactMode and quoteContactMemberId but no writer read them, so the setting round-tripped through the form and was silently dropped. Both...
Choose who a quote names as the customer's contact
The market dialog gains "Quote's Future Contact" under Enable quotes: the lead's assigned agent, a choice made per quote, or one explicit person picked with the...
Saving an article blocked on 21 translation calls
@Async was inert. **************** called translateArticle on the same bean, so the call went straight to this and never reached the proxy that makes it asynchr...
Stop Send To AI resetting the boxes while they are being typed in
A member filed this against the Send To AI dialog: the description "often resets back to the original description while you are editing it". It reset the instru...
Honour a market's contact rule wherever a quote names somebody
The staff detail, the staff preview and the customer's own page now resolve the contact through one method, so they cannot disagree about who is printed on the ...
The article corpus, and the pipeline that builds it
307 articles across 16 categories and 29 sections, from the 248 draft articles under Documentation/ plus 59 written here: every category and section landing pag...
A market decides who a quote names as the customer's contact
QuoteContactMode gives a LeadMarket three answers: the lead's assigned agent (resolved per read, which is what every quote does today and what a null column mea...
Columns for a market's quote contact and the per-quote snapshot
A market names its answer in QUOTE_CONTACT_MODE (+ QUOTE_CONTACT_MEMBER_ID when that answer is one explicit person), and each quote keeps the contact it was rai...
A window that raised itself has not been read
The chat unread badge was invisible everywhere — the Chat button, the Interaction Center, the hexhead — and the reason was not the badge. All three read one cou...
Point at the real org, keep the tree in the URL, show related articles
KB_ORG_ID was "1". The help center lives in org 1168485648209608710, so every KB read on this site missed -- /help-center rendered its empty state no matter wha...
Announce a message only once it can be read
POST /sessions/{guid}/messages is @Transactional, and the NATS publish of the message has always waited for the commit — with a comment saying exactly why: publ...
Curate an article's related articles
KbRelatedPanel is one component in three places: it curates in the composer and the editor, and reads back on the article view. Adding an article here also adds...
Related articles, and a public path that keeps the tree
Three things, all reaching the same page. Related articles. KbArticleRelationService owns the symmetric "see also" links: list, set, suggest, and a clear that ...
Migration for KB_ARTICLE_RELATIONS
Mirrors KbArticleRelation in kamo-shared-library. Written explicitly rather than left to ddl-auto so the UID and DATE_CREATED defaults live in the database, whi...
Article-to-article relations
KB_ARTICLE_RELATIONS holds the curated "see also" links between articles in one knowledge base. Rows are per direction and the pair is kept symmetric by KbArtic...
Add the missing form-fillable route, and stop offering ungranted actions
Two defects behind one report: the PDF editor would not open, and delete was offered to a member who may not delete. **The editor.** DocManager posts to /api/d...
Ride down to your own message when you had scrolled up
Sending while scrolled up into the history left the member parked where they were, with their own message appended below the fold. From the seat it looks exactl...
Design for the Help Center KB build, related articles, and public wiring
Covers the 248 source drafts under Documentation/, the tree they map to under the existing Help Center article, the new KB_ARTICLE_RELATIONS feature, and the th...
Page and search the org directory, and stop two row builders drifting
The networks endpoints answer with the whole set in one body. That is fine for a member of a dozen orgs and impossible for an operator on an estate meant to rea...
Take the two "could not start" fallbacks out of the source
Both SMS refusal paths quote the server when it gives a sentence and fall back to one of their own when it gives only a machine code. Those two fallbacks were E...
Starter base up $10 on every term
69 / 59 / 49, from 59 / 49 / 39. The commitment ladder is untouched by design — still exactly $10 off for a year and $20 for three — which is the standard every...
Offer the billing term up front, and stop hiding it over Enterprise
Two separate ways this page withheld the choice. The org console HAD a three-term control, but it was tucked into the right of the "Choose plan" heading behind...
Quote the commitment saving in dollars on the plan cards
The discount is a flat $10 a year and $20 on three years, which is how the catalog sets it and how it is advertised everywhere else. Rendered as a percentage it...
Say which subscription the launchpad pill's gate is about
"Not already paying" means the VIEWER's own seat — the same question the Plans & billing page answers when it decides whether to offer them one. Worth stating, ...
Stop offering the internal platform-owner plan for sale
listPlansForMarket filtered on isActive alone, and active is not the same as on the menu. OWNER_UNLIMITED ("Platform Owner — Internal") is active, carries no pr...
The running offer, on Plans & billing and on the launchpad
The console quoted list prices while kamocrm.com advertised a third off the same plan, because nothing in kamo-internal could see that an offer existed. It now ...
Serve the running offer, and say whether a seat is actually PAID for
Adds GET /markets/{id}/promotion. There was no way for an in-app buying surface to know an offer was running at all — the marketing site read promotions through...
Like what you see shipping?
Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.