An organization's and a member's own page transition
The page transition is now chosen at three levels, resolved member, then organization, then platform — for the choice of transition and, one transition at a tim...
An organization's and a member's own page transition
The page transition is now chosen at three levels. The platform's stays on Platform Administration → Transitions; an organization can choose its own on Behavior...
Page_transition_overrides, an organization's and a member's own page transition
One row per owner (ORG with the org id, MEMBER with the member id): a catalogue token or NULL to follow the level above, and properties for only the transitions...
Five more page transitions, each with its own properties
Aperture, Curtain Split, Venetian Blinds, Liquid Wipe and Shatter join Tile Dissolve. Every transition is now an effect in app/lib/transitions/effects, drawn by...
Add recipients and Send to latest on a campaign that has gone out
- A campaign's progress page has Add recipients: the recipients step in a dialog (upload, IDs, the All boxes, addresses), saved in slices as the editor save...
Running campaign counts, and Send to latest
Counts: - Every status change moves a campaign's counts by exactly what it changed: a send +1 sent, a later bounce report -1 sent +1 bounced, an add +n, a r...
Tile Dissolve's tile size and tile colour, set per transition on the Transitions tab
Each transition now carries its own properties, stored per transition beside the active one, so switching transitions keeps each one's choices. Tile Dissolve ha...
Tune each page transition, tile size and colour to start
GET and PUT /api/security/page-transition answer options for every catalogue id, normalised from PAGE_TRANSITION_OPTIONS: a missing, unreadable or out-of-range ...
**************** beside the transition
A TEXT column holding each transition's properties as JSON keyed by transition id. No backfill: null means every transition's defaults, like page_transition bes...
Each page transition's properties, kept per transition
**************** maps PAGE_TRANSITION_OPTIONS, a JSON object keyed by transition id, e.g. **************** Keyed by transition so tuning one survives switching ...
Shorten admin bar labels to Billing, Stats, Roles, Block, Log
The panel heading keeps each screen's full name via titleKey, as White Labeling and Apps already do.
The page cover and its Tile Dissolve reveal, in place of the loading screen
Between pages the main view area (never NavTop or NavPri) is now covered in the organization's primary colour with its full logo centred on it, painted with the...
A Transitions tab to choose and preview the page transition
Platform Administration → Configuration → Transitions, gated by the new MANAGE_PLATFORM_TRANSITIONS platform right and registered everywhere a console tab is: t...
The campaign recipient list is paginated, with totals
The recipients grid on a campaign now pages like a list of billions should: First, Previous, Next and Last, rows per page, "101–150 of 3,916" and "Page 3 of 79"...
A campaign's recipient list pages both ways, with totals, at any size
The list was already read a page at a time, but only forwards, as an endless scroll with no total — and a status filter found its rows by walking the list in ad...
The page transition setting, and the right that chooses it
**************** holds a catalogue token, NULL meaning the shipped default. MANAGE_PLATFORM_TRANSITIONS gates Platform Administration → Configuration → Transiti...
**************** ahead of every reader
A catalogue token, NULL meaning the shipped TILE_DISSOLVE, so nothing is backfilled. Order 0 because the entity maps the column from this build on, and an earli...
Every tab reads the page transition, and an operator chooses it
GET /api/security/page-transition answers any signed-in session, since every tab needs it to uncover a page; an unset or unknown value reads as TILE_DISSOLVE. T...
"All leads / accounts / applications" on a campaign, at any size
Each id box on the campaign editor now has an "All …" box beside it. Ticked, the box folds into a live panel for everyone of that kind the signed-in member can ...
A campaign can go to every lead, account or application a member can see
"All leads / accounts / applications" is a rule, not a list. It is stored with the campaign (the bulk-provider blob, campaignAudiences — no DDL), runs on the ac...
The preference page in every language the product speaks
The page behind every unsubscribe link only spoke English. It now speaks all 22 languages the product UI ships: English, Chinese, Hindi, Spanish, French, Arabic...
Pages refetch on org change events instead of loading once
Every successful write through forwardToApi now publishes rt.org.{orgId}.{domain} on core NATS (the domain is the first backend path segment, skipping security/...
An unsubscribe by email is honoured, not dropped
Every bulk and shared-article message offered unsubscribe@<sender's domain> in its List-Unsubscribe header, but nothing read that mailbox - for a tenant it was ...
The Opt Out tab and the lead chip update live
Both subscribe to **************** (EmailService and SecurityService publish after every committed change; MediaService relays), so a recipient unsubscribing, a...
Opt-out screens refresh live when anyone changes the list
The ledger now tells OptOutChangePublisher after every committed change - a recipient on the preference page, a bounce, a member in Settings - and it publishes ...
A lead's opt-out change refreshes open screens live
The ledger here tells OptOutChangePublisher after the change commits, which publishes rt.org.{orgId}.email-opt-outs on core NATS (this service's JetStream strea...
The opt-out ledger says when an organization's list changed
An optional listener, told after the change commits, so the screens showing the list can refresh live instead of loading once. It carries only the organization:...
Mail to unsubscribe@hello.kamocrm.com reaches EmailService
Every bulk and shared-article message offers an unsubscribe by email (List-Unsubscribe mailto), but mail to that address went to postmaster and was never acted ...
The sales agent honours the organization's email opt-out list
Before any email touch the outbound gate now asks the organization's email opt-out ledger about the contact's address. The address is the one the contact key po...
Relay org change events to /topic/rt/org/{orgId}/{domain}
Writers publish rt.org.{orgId}.{domain} on core NATS after a successful change; every pod relays it to the org's topic so open screens refetch instead of loadin...
A hidden, per-visitor site hunt for a capped promotion, and signals that lead visitors into it
The hunt: seven locks per visitor, generated from a seed and HUNT_SECRET, enciphered with the previous answer, hidden on a page that moves every UTC midnight, w...
Issue personal single-use codes from a template promotion
The kamocrm.com site hunt hands each solver a code of their own, and only the first N solvers ever get one. A shared code could not do that: the first solver to...
A real preference page behind every unsubscribe link
The unsubscribe link opened a bare confirm-or-leave card. Someone who only wanted fewer emails had no way to say so, and nothing told them why they were being m...
Email opt-out chip and panel on the lead page
Beside the lead's email address there is now a chip saying whether the person may be emailed: Emails allowed, Opted out of all email, Paused until a date, Some ...
See and change a lead's email opt-out from the lead
A member working a lead can now see whether that person may be emailed, and change it, without the org-wide opt-out settings list. GET **************** answers...
An org admin can see and change the whole opt-out list
Settings -> Email -> Opt Out now has its API under /api/email/opt-outs, behind MANAGE_EMAIL_OPT_OUTS (god bypasses): a summary with the last 30 days, the list f...
Settings -> Email -> Opt Out, the organization's do-not-email list
Everyone who asked not to be emailed, in one place. A masthead says how many are off all email and why (unsubscribed, bounced, marked as spam, asked us to stop)...
Square, bevelled buttons and a 2px-cornered thumb on every scrollbar
The bar's four buttons (jump to start, step back, step forward, jump to end) are now 20px squares with hard corners, and the thumb keeps only a 2px radius. Both...
Every suppression check and bounce goes through the opt-out ledger
SuppressionService now asks EmailOptOutLedger, so a check sees the do-not-email list, pauses and topics alike, and a bounce lands in the same history as an unsu...
One opt-out ledger, topics, pauses and a history of every change
The do-not-email list could be written to but not read, changed or explained. This adds the one place it is read and written from, for every service: email_o...
Outbound e-mail hosts, send windows, per-host warm-up and the staged outbox
- Settings → Platform → Configuration gains an Outbound E-Mail Hosts tab, behind the platform right MANAGE_OUTBOUND_EMAIL_HOSTS: the hosts shared with every o...
Several outbound hosts per org, sent round-robin, each warmed on its own, inside a send window
An organization's campaign mail now leaves through any number of outbound hosts, one message at a time in rotation, instead of one bulk provider. - Hosts: an o...
Postfix-bulk accepts any sender from EmailService [skip ci]
The relay is now a platform outbound host that every organization's campaign mail may go through, as whatever From that organization sends as. Only the null sen...
MANAGE_OUTBOUND_EMAIL_HOSTS, the platform console's outbound e-mail hosts
A platform right for the outbound e-mail hosts campaign mail leaves through: the hosts shared with every organization and the ones kept for the platform organiz...
Delete, filter and switch off NoReply Outbound rows
The audit page gains a subsystem column and filter, checkbox selection with "select all" and "select all matching", and deletes for one row, the selected rows, ...
The NoReply ledger can be filtered by subsystem, pruned, and told what to record
Each row now names the subsystem that sent it and, for a template send, the template key. EmailTemplateService and the campaign sender open a NoReplyOutboundSou...
The unique (campaign, address) index, for a fresh environment
EmailService used to create **************** on every startup; DDL on this cluster belongs here instead. Idempotent, auto-committed (YugabyteDB builds indexes o...
The campaign editor writes edge to edge, merge tokens in their own column
The subject sits under the campaign title and the audience beside the status chip, so the compose step is the email body alone, filling the middle of the page, ...
A warm-up day nothing was sent on does not count
A warm-up day used to be the N-th 24 hours from the start whether or not anything went, so turning the warm-up on days before the first campaign silently spent ...
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.
