Lock the shared sweeps, and run two pods
Nine of EmailService's ten scheduled sweeps have a shared effect and now take a named lease before running. The most consequential is CampaignDispatcher: at two...
Let a rollout finish what the old pod was doing
Deploys replaced the only pod of each service with nothing to catch the requests in flight. Three settings, applied across the fleet: - preStop sleeps 10s befo...
Open the message, not the mailbox
The app has a route per (folder, uid). Being told about a mail and then landing on a list to hunt for it is the kind of small wrongness that makes a notificatio...
Buzz a member's phone when mail arrives
MessageIndexer is already a durable consumer of email.inbox.> and already resolves the mailbox OWNER, so it is where this belongs — one notification per genuine...
Preview variables for the two email-change letters
Both new canonical templates render in the console the moment this service is rebuilt onto the shared library that carries them; without a sampleVariables arm t...
System mail must not send as a domain we do not authenticate
Transactional mail always went out as NoReply@{org primary domain} through our own relay. That is only safe while Kamo hosts the org's mail: then we control the...
Seeding is bounded by the JAR, not by the database — and rebuild
The lazy seedForOrg call in sendToUser reads as though a missing template always heals itself. It does not. The catalogue is CanonicalTemplateLoader's and lives...
Personal mailboxes read the OAuth registry, not dead config
Connecting a personal Gmail or Outlook mailbox has been building its consent URL with client_id= empty, and every attempt was refused by the provider. The cred...
Resolve mailbox OAuth credentials per organization
Google, Microsoft 365 and Zoho mailbox connects now read whichever app the org is on — its own registration if it brought one, otherwise Kamo's platform app. T...
A library of footer badges, and the row each scope prints
The signature footer went from one image to a row of them. The library is org-wide; the selection and its order are per scope, so an image uploaded while editin...
Internal endpoint for importing contacts from an external system
VOIPService's JustCall integration needs to land a customer's contacts in the right member's address book, and this is where contacts are written: a Contact map...
Let a sent message carry images inside its body
The body had four possible shapes and send() could only build three. The multipart/related branch — the one that puts an image where the HTML's cid: points at i...
Restore the transaction the attachment overload left behind
baf3c88 added an attachment parameter to sendToUser by moving the whole body into a new six-argument overload. The @Transactional stayed on the five-argument si...
Stop an attachment holding an IMAP connection open forever
getAttachment returned Jakarta Mail's lazy IMAPInputStream straight out of withStore, which puts the Store back in the pool the instant the lambda returns. Two ...
Show a pasted screenshot instead of a broken image
Jakarta Mail hands back a MIME header exactly as it arrived, folds and all: RFC 5322 lets a mail header run over several lines by breaking it at a CRLF followed...
Report a failed association at WARN, not at DEBUG
Lead association swallows its failures on purpose — a problem attaching a message must never break the sweep that was reading the mailbox — so the log line is t...
Keep the words typed after an inserted photo
The same reported message had a sign-off underneath its photo. That did not arrive either — the reading pane showed an empty body above the attachment. There i...
Let a photo attached from a phone be opened
A member sent themselves a message from an outside account with a file attached. The message arrived; the file was nowhere in it — no preview, no download link,...
Give a folder with an ampersand in its name its message count
A mailbox called "Bugs & Enhancements" showed no count in the sidebar while every folder beside it showed the right one, and the mailbox totals were short by ex...
Send the meeting invitation the guest list implied
Adding somebody to an event's guest list did not tell them. Only a reminder ever wrote to a guest, and a reminder reaches a member in-app and an outside address...
Make a listed message searchable, not merely stored
The index is written from two directions. Opening a message runs the full indexer, which writes the row and then computes its search_vector. Listing a folder wr...
Make label: search read the table that records labels
The clause tested si.label_ids, an array column on the search index. Nothing has ever written it — labelling a message inserts into email_metadata.message_label...
Index a message in one statement instead of a select-then-write
The search index row's key is assigned rather than generated, so handing a built entity to save() was a merge: Spring Data SELECTed the whole row — body text in...
Let a transactional send carry one attachment
Added for scheduled reports, which are a rendered PDF or workbook that has to reach people who may have no account — a link would not be a delivery for them. T...
Serve smart folders and labels as messages, not as index rows
Both endpoints answered out of email_metadata.search_index and handed the client what the table holds. A smart folder returned SearchIndexEntity — messageUid, f...
A member is only told about apps they have
A notification kind belongs to an app — a lead callback is Leads, a meeting reminder is Meetings — but raise() asked only whether the member had muted it. So a ...
Preview variables for the bug-status-changed template
The admin preview renders a template against a sample variable map, and this one has no arm — so an org administrator opening SYSTEM_BUG_STATUS_CHANGED would se...
Persist the two company phone toggles
Reads and writes userCompanyPhoneMain / userCompanyPhoneTollFree on the signature field config, so the Content tab's new switches survive a save. saveFieldConf...
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 ...
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...
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...
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...
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...
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 ...
Build the org logo URL from its alias, not its domain
Transactional email is sent to real inboxes, so the header and footer logos have to resolve without any client-side theme JavaScript — they are built exactly th...
Conflict detection now finds conflicts
Delegates to findBusyForMemberInRange and excludes the event being edited in Java, where a null exclusion cannot poison a SQL predicate. The test it replaces m...
Pin kamo-shared-library 1.6.0
The library moved to 1.6.0 while every consumer stayed on 1.5.0. Consumer CI builds the library from a fresh clone into a cold ~/.m2 and ci-settings.xml mirrors...
Serve Google's OAuth callback on /api/email/oauth/callback
The platform console needs an address to show an operator setting up the Google Workspace registration, the way VOIP has /api/voip/oauth/callback. The only emai...
Actually move an event when its calendar is changed
The dialog has always offered a calendar picker when editing and the PUT has always carried the chosen calendarId, but updateEvent had no parameter for it — so ...
Serve the home-page unread digest from the index, never from IMAP
A widget on the home page is on screen for everyone all day, whether or not anybody is reading mail — and a member's IMAP connections are a shared budget of ten...
Serve and announce the home-page calendar widgets
CalendarWidgetService answers what a widget actually needs and the calendar page hasEarlier/hasLater so an arrow greys out instead of offering an empty page. Th...
Scan the org's mailboxes for a lead's past e-mail on request
Serves the "Associate Past E-Mails w/ Lead" tick-box. Given a lead and the address just recorded on it, read INBOX and Sent of every mailbox in that lead's orga...
Size the pod above its own JVM heap ceiling [skip ci]
The image starts the JVM with -XX:MaxRAMPercentage=70 -XX:+AlwaysPreTouch, so the heap alone may take 70% of the container limit and pre-touch keeps every commi...
Campaigns take the bulk-email rights, like the recipient resolver already did
Bulk email was half-enforced and the halves were in different services. **************** in SecurityService has gated recipient resolution on MANAGE_BULK_EMAIL ...
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.
