The desk is never a client of itself
Sage and Erik both hold EXEC2EXEC_ACCESS, and each owns organizations of their own. Signed into one, the owner arm of Exec2ExecEligibility gave each a "client" ...
A saved campaign reopens inside its template, and the From is always the bulk sender's
The body is saved between two comment markers inside the finished email, and reopening a campaign puts only that part back in the middle of the template, where ...
Every campaign is sent as the org's bulk sender, and its From never pauses it
A campaign's own from_email/from_name are no longer read: the From is always the address and name on Settings > Email > Bulk Email Sender (else the org's NoRepl...
Startup checks for the one-address-per-campaign index instead of creating it
EmailService ran CREATE UNIQUE INDEX on every startup. DDL on this cluster costs every service about fifteen minutes of "schema version mismatch", too much to r...
Read the platform's hex secret as hex, and stretch a short key instead of refusing it
JWTUtils decoded security-service.*** as Base64 only. The platform's secret is 64 hex digits (32 bytes), which Base64 turns into 48 meaningless bytes; those wer...
The campaign editor's Recipients and Send steps fill the width instead of an 860px column
Review fixes to the provider blob, delivery reports and test send
The provider blob is always written in a transaction of its own, so its lock query stays the first statement and a lost lock race can be retried even when a cal...
Review fixes for the bulk sender, warm-up and pacing banner
A pass over everything the bulk sender and warm-up screens are built from, fixing what it found: - a warm-up poll that started while a write was on the wire co...
A from the platform relay would refuse pauses the campaign instead of failing its recipients
Failing the recipients made every one of them contacted, so once the From was fixed the campaign could never send to those addresses. The send path now checks t...
Email settings redirect lists its hook dependencies
The rights check that sends a member without MANAGE_EMAIL_SETTINGS back to /settings ran in an effect that listed only `loading`, so the linter flagged the miss...
The platform relay is recognised by one host-and-port check everywhere
Refusing an off-domain From matched the relay by host and port, while the warm-up's delivery tracking and the test send's advice matched by host alone, so a pro...
Live campaign progress reaches browsers on every replica
The STOMP broker is in-heap per pod and progress is raised on whichever pod holds the dispatch lease or received a delivery report, so browsers on the other pod...
The bulk sender tab fills the width it is given
The tab sat in a 1120px column centred in the settings card, so on a wide screen most of the card was empty margin around a narrow form. The column is gone, alo...
This relay only sends as kamocrm.com
Anything in mynetworks (every pod, and the LAN) could relay through 47.181.8.84 as any domain, which receivers treat as spoofing and charge to this IP; that is ...
The delivery-events sidecar drops a batch EmailService rejects as invalid
A 400/413/422 means the batch itself will never be accepted; retrying it forever would hold back every result queued behind it. It is logged and dropped. 401/40...
The smtp envelope sender is set to the bare from address
Campaign mail now carries a display name in its From, and the relay it goes through accepts only its own sender domain, judged by MAIL FROM. mail.smtp.from is n...
A relay that never signs in counts as a configured bulk sender
/marketing/email let an organization send campaigns only once its bulk provider had a stored password. The platform relay listens on port 25 with no TLS and no ...
Recipient grid fills name, source and error from either row shape
The campaign recipient grid read name, sourceType and errorMessage from rows that only carried email, status, sentAt and lastError, so three of its five columns...
A missing mailing address no longer blocks sending
The preflight treated an org with no address on file as an error and kept Send and Schedule disabled. The address still goes in the footer when there is one; wh...
A missing mailing address no longer blocks sending
assertSendable refused to schedule or send a campaign without a physical address. The org's address still goes in the footer when it has one on file; when it ha...
A hold'em invitation is gated on the right to play hold'em
HOLDEM_INVITE was in neither the gated nor the ungated map, so it shipped ungated and failed the test that exists to catch exactly that.
A line break in a sender's display name can no longer start a header
Jakarta Mail encodes commas, quotes and non-ASCII in a display name but passes a raw CR/LF through, so displayedAs now flattens control characters to spaces bef...
Keep hello.kamocrm.com off this relay, and stop three errors on every start
- A @hello.kamocrm.com sender is refused relay here with a 4xx, so bulk mail can only leave through postfix-bulk; delivery to our own domains is unaffected....
The NoReply recorder names the constructor Spring should use
It has two, the second for tests, and with neither marked Spring picks neither: the context failed with "No default constructor found" and took EmailSmtpService...
The campaigns grid read counts the API never sent
EmailService's CampaignDTO carries a campaign's counters flat (totalCount, sentCount, failedCount, suppressedCount, bouncedCount, queuedCount), but every screen...
The launch bar ends at the form column, clear of a docked chat window
On /network/create the readiness meter and the Launch organization button sat in a bar across the whole page, under the brand preview too - exactly where docked...
An echo of the censored English is not a translation
translatedLocales compared each translation with the English title as stored. The translator is given the censored title (ChangelogSanitizationService masks sec...
The General tab counts what the Organizations tab calls Active
6c8d789 moved the platform overview's organization count onto findAllLive(), which also drops orgs created by synthetic (is_fake) accounts — so the General tab ...
Binders and Uploads read as live buttons in the org's brand
The two tabs looked disabled because they were: a MUI Tab with no <Tabs> parent defaults to textColor="inherit", which draws the whole tab at 60% opacity unless...
Order sitemap <url> children as the schema does, and give each chunk one address
The sitemap XSD is a sequence: loc, lastmod, changefreq, priority, then extension elements. The hreflang links were written straight after <loc>, which Google t...
Count the files a document keeps only as old versions
A category that keeps versions holds every earlier file of a document in img_versions, each on its own content-addressed file. The storage meter, the quota and ...
Sharing a document works again, as someone who can read it
The share dialog sends a document id as a string — an img id is an int64 past 2^53, which a JSON number would round — and createShare cast it to Number. Every s...
The rim's lead current stays white-hot all the way round
Its gradient was pinned to the hexagon's box rather than travelling with the arc, so for a third of every lap the current was gold on the gold rim and simply va...
A window's scrollbars no longer show through the window on top of it
Maximizing a tool window left a column of scrollbar buttons from the window behind it floating in the middle of the pane, and two overlapping floating windows s...
MUI text is set in Geist, not Helvetica and Arial
I18nProviders handed the Geist font to createTheme as an overlay, and overlays are merged in after the theme is built. By then MUI has already written its Robot...
Every Chats sub-tab fits, with the same gap between each
The Interaction Center's Chats strip gave its seven tabs seven equal shares of the 550px pane, 78px each whatever they said. "Team Members" needs 91px, so it wa...
Drop the remaining third-party plan and cost wording
- zoho-mail, ecwid: the FAQ answering which of the vendor's plans, free or paid, the integration needs is removed. Both items take i18nKey "<slug>-r2" so no...
Quote and borrower-portal links only name a live domain
QuoteEmailClient settled for any active domain, verified or not, so an org still setting up its white-label domain had quote links mailed to sign.<that domain>....
Chat and bug emails link to the console the recipient can reach
ChatEmailNoticeService named the recipient org's first domain whatever its state. The reported case: Exec2Exec letters to makeitpopsigns.com, sent before its DN...
Template logos come from a live theme host
resolveLogoUrls built the logo URL from the org's first domain whatever its state, so an org still setting up its domain got a logo URL that could not be fetche...
Signing and document-manager links only name a live domain
**************** settled for any active root domain, so an org still setting up its domain had signing links mailed to sign.<that domain>, which answered nothin...
Legal, training and careers emails link only to live hosts
- Legal: the org's console (OrgLinkHosts.internalOrigin) and the member page's Legal & Compliance tab. It linked the bare apex, which serves the marketing s...
Link the steward to the console the org can reach
StewardRecipientResolver built the steward link from the org's first domain row whatever its state, so an org still setting up its white-label domain was mailed...
An organization's own domain is named in a link only once it is live
OrgLinkHosts is the one rule for the host a link to an organization may name: its own root domain once it is active, ownership-verified and SSL-confirmed ******...
An organization's .svg names show its own art, not the platform's
A new organization's folder is seeded from the base theme, so it starts out with KamoCRM's art under img/logo-full.svg and favicon/favicon.svg. An organization ...
Quote only our own price, never another company's
The comparison pages set each competitor's list prices, add-on prices, "a typical team pays" estimates, TCO and implementation figures, and "N% cheaper" verdict...
Strip identity headers on myloan.*/api/ before the portal BFF relays them
KamoMLOS's BFF routes under /api/ (the /api/proxy/* passthrough, /api/session/extend, /api/chat/routing, /api/chat/subject-state) copy every client header excep...
Rerun a webhook event whose first attempt died, and refuse one the SDK cannot read
A losing claim on an unprocessed row always answered 500, so an event whose first attempt failed (a unique-constraint race inside the invoice.created/finalized/...
Only a failed charge starts dunning, and Stripe must agree before dunning acts
invoice.finalized carries status=open with an amount due for every auto-charged invoice before the charge is attempted, and the invoice handler started the deli...
Never forward client-supplied identity headers upstream
forward(), forwardWebhook() and forwardCallback() copied every inbound header except Host (and, for the sessionless two, the credential headers) onto the upstre...
Like what you see shipping?
All of it arrives in your workspace on its own. Start on the free plan and read this page again in a month.
