Matthew Meszaros
06f4a270cc
feat: keep the mailbox imports menu live and self-explaining (deferred rather than dropped progress events, a five-second refresh while an import runs, each import's state and what it waits on in words, a badge for imports that need you, and an × that hides an import for the workspace through POST /emails/imports/:id/dismiss with migration 000211, stopping a running one first), show the vendor's own status and the Microsoft and Google time expectations on rows being authorized, record a vendor row's mail host so it gets its provider icon and a working Sign in, drop the Fix button from rows the vendor is authorizing, and count warming mailboxes rather than connected ones in the pool banner, refreshed when mailboxes change
2026-09-24 17:46:46 +02:00
Matthew Meszaros
e32b4f8893
feat: let a vertical wheel over a ScrollStrip at its edge scroll the page, name the seed section consistently in the deliverability guide, and keep the warmup placement live tests to their own receipts by dating the sweep fixture decades back and checking the live-counted flag directly
2026-09-24 05:48:26 -07:00
Matthew Meszaros
b196b3c8be
feat: count warmup placements exactly once through a consumer sweep that seeds history in batches instead of a locking migration backfill, add a concurrent unplaced-receipt index, publish WARMUP_PLACEMENT only when a receipt is counted, share one spam-flag list across warmup, placement tests and unibox folders, name ranked mailboxes from their own rows, default from to 30 days before to, keep ScrollStrip scrolling to itself, refresh account statuses at most every 5 minutes, and document rescued as requested
2026-09-24 05:28:51 -07:00
Matthew Meszaros
b2d54fc873
feat: record where every warmup email lands (inbox, Gmail tab, spam) per sender, day and recipient host in warmup_placement_daily, serve it from GET /analytics/warmup/placement, cap mailbox health at the measured 7-day inbox rate, and show it as an Inbox column, a mailbox Deliverability tab and a workspace placement section; replace every visible native checkbox in the dashboard with a themed Checkbox and make the mailbox drawer's tab bar scroll
2026-09-24 05:28:51 -07:00
Matthew Meszaros
7557d97866
feat: run a saved mailbox's placement, events, webhook and load on their own context so an expired caller deadline cannot skip them, bound bulk connects per row instead of per batch, have the worker reconciler remember the worker a load actually placed or moved a mailbox onto, keep an ADD_EMAIL that follows a REMOVE_EMAIL while the earlier load is still dialing by running it after that load, and show an import waiting only on vendor authorization as being authorized rather than importing
2026-09-24 11:58:49 +02:00
Matthew Meszaros
549fb00be6
feat: keep mailbox credential checks and imports from timing out behind a worker's command queue by loading mailboxes off the bus loop (a republish never dials twice, commands wait for an in-flight load, a failed load raises the account's auth or server error), storing Kafka offsets for background commit instead of a synchronous commit per message, reconciling moved, unplaced and dead-worker mailboxes at once while spreading the safety-net republish over 30 minutes, sending checks over each worker's Redis channel with failover in placement order, retrying unanswered import rows within the lease, finishing a connect the browser left and an import row a restart interrupted, and connecting InboxKit Google and Microsoft mailboxes with no sign-in through a vendor-authorized grant that covers only the domains the vendor account lists
2026-09-24 11:44:58 +02:00
Matthew Meszaros
ee70b7697a
Merge remote-tracking branch 'origin/main' into fix/unibox-forward-original-message
...
# Conflicts:
# docs/content/docs/api/reference/unibox.mdx
# docs/content/docs/guides/unibox.mdx
# docs/public/openapi.json
# web/src/components/app/unibox/ReplyComposer.tsx
# web/src/components/app/unibox/replyComposerDraft.test.tsx
2026-09-23 21:48:54 -07:00
Matthew Meszaros
a5dfe169e4
Merge remote-tracking branch 'origin/main' into fix/unibox-forward-original-message
2026-09-23 21:38:59 -07:00
Matthew Meszaros
bbd7942313
feat: unibox forward requires READ_UNIBOX alongside WRITE_UNIBOX, restores click tickets in a forwarded Warmbly send to their destinations, fills an empty-placeholder HTML note from its text, previews an empty-note forward in the Scheduled view, keeps a reply's leading blank lines, shares the stored-body read with GET /unibox/:id, and shows the Reply/Forward bar when a forward's message is no longer in the thread
2026-09-23 21:38:59 -07:00
Matthew Meszaros
408f1fea94
Merge pull request #671 from warmbly/feat/vendor-connect-fields
...
feat: connect inbox vendors with only an API key and pick mailboxes by workspace
2026-09-24 04:35:42 +00:00
Matthew Meszaros
462df7d025
Merge pull request #673 from warmbly/feat/unibox-reply-sender-picker
...
feat: let a Unibox reply or forward choose its sending mailbox, keep the provider thread only for the mailbox holding it, and scope scheduled sends to the organization (#670 )
2026-09-24 04:34:30 +00:00
Matthew Meszaros
a544847fcb
feat: resolve a Unibox reply's Gmail thread per sending mailbox (its own copy of the conversation, or the thread its earlier reply started, and none for other providers or on a failed lookup keeps the handle), honour an API key's mailbox allowlist on scheduled list and cancel, fall back from a saved mailbox that is gone and block Send until the sender can send, fetch From candidates only when the picker opens, and fix the Unibox threading and drafts docs
2026-09-23 21:29:24 -07:00
Matthew Meszaros
6c4931c28a
feat: unibox forward carries the original message, attached server-side from a new forward_message_id on POST /unibox/reply and stored on the queued task (migration 000208) so it goes out under the note and signature with its From, Date, Subject, To and Cc lines, sanitized HTML and text part; the composer allows an empty note and previews the forwarded message ( #668 )
2026-09-23 21:22:17 -07:00
Matthew Meszaros
ce5eb4fd25
feat: let a Unibox reply or forward choose its sending mailbox in From (the shared MailboxPicker without Auto, kept in the draft and the undo-send), send the provider thread handle only from a mailbox that holds the thread so a switched reply threads on In-Reply-To alone, and scope scheduled sends, their cancel, count and cap to the organization whose mailboxes send them ( #670 )
2026-09-23 21:18:20 -07:00
Matthew Meszaros
39bb5567dc
feat: order campaign Step performance by the canvas's step order (position, then created_at) instead of created_at, which the wizard writes identically for every step, number each row as its place among the email steps so it matches the canvas's Email N, label unnamed steps Email N in the dashboard, and document the steps order and position in the analytics API reference, guide and OpenAPI schema ( #669 )
2026-09-23 21:12:33 -07:00
Matthew Meszaros
e35baa7f58
feat: connect every inbox vendor with only its API key by discovering the workspaces and organizations the key reaches (InboxKit, Zapmail, Infraforge, ScaledMail), carry the workspace in vendor mailbox and domain ids with a lookup for ids stored before, skip a workspace the key may not read, refuse a key that reaches none with mailbox_vendor_no_workspace, and add a workspace switcher to the mailbox picker that filters, counts picks and selects a whole workspace
2026-09-23 21:12:09 -07:00
Matthew Meszaros
0eb4e30fec
feat: record a Microsoft 365 grant only when the consenting sign-in holds the Global Administrator or Privileged Role Administrator role, validate a tracking host before any vendor DNS write, clear Graph delta cursors when an Outlook mailbox moves onto a grant, keep a switched-off mailbox off when it moves unless its sign-in stopped it, treat pool-link mailboxes as managed, park a delegated mailbox without a grant as inactive, honour MAILVENDOR_SANDBOX_URL only when APP_ENV is dev, drop a vendor's cached domains with its key, retry a failed import redirect setup, keep address:password pastes whose password has a comma, and move the vendor import components to import/vendors so the Go vendor ignore rule no longer drops them
2026-09-23 09:03:25 -07:00
Matthew Meszaros
e58921484d
feat: rebuild mailbox import around column mapping and automatic host and sign-in detection (CSV, XLSX, pasted lists, saved mappings, retryable rows with fixes, migrations 000205-000206), connect whole Google Workspace domains and Microsoft 365 organizations through a proved administrator grant, import from inbox vendors (InboxKit, Zapmail, Mailforge, Infraforge, Maildoso, Cheap Inboxes, ScaledMail) with vendor-managed forwarding and DNS, add a sending domains page with per-domain tracking and verified root redirects, unify Add account into one Google and one Microsoft entry with per-method choices, mark per-mailbox Google sign-in as retiring with in-place moves to the admin grant or an app password, allow the loopback security mode in the credential columns (migration 000207), read semicolon-separated CSVs, and add a mock vendor API to the sandbox
2026-09-23 08:41:01 -07:00
Matthew Meszaros
1c734411a3
feat: keep warmup out of Unibox notifications and the unread badge: tie reply notifications to their unibox message (notifications.unibox_email_id, migrations 000202-000204) so they are removed with it and read when it is read, link them to the conversation, count the badge as unread Inbox conversations without snoozed ones and refetch it instead of incrementing, hold a live arrival from the sender of an unconfirmed warmup send until its id is known, and never send warmup without a token ( #659 )
2026-09-23 07:30:09 -07:00
Matthew Meszaros
95934eb697
Merge pull request #657 from warmbly/feature/open-tracking-device-client
...
feat: show the device and mail client behind every open and click, with image proxies reported as device hidden instead of their fake browser and location
2026-09-23 09:11:52 +00:00
Matthew Meszaros
331aeb4db3
Merge origin/main into feature/open-tracking-device-client
2026-09-23 02:06:04 -07:00
Matthew Meszaros
fd96a8c47c
Merge pull request #654 from warmbly/fix/stale-state-after-mailbox-removal
...
feat: close Advisor findings about a mailbox, campaign or step inside its delete transaction, keep re-evaluating workspaces that still hold open findings after their last mailbox is gone, re-evaluate the Advisor right after a mailbox change or a campaign/step delete with coalesced reruns, and refresh the unread badge, inbox, campaigns, analytics and Advisor caches on every mailbox removal including a teammate's
2026-09-23 09:06:00 +00:00
Matthew Meszaros
727ee432cb
feat: keep a manual verdict set while a requested verification check runs, restore a lapsed evidence override from the row's own check status so a check landing mid-rescore wins, give member re-verify requests at most half of each verification batch while the backlog has work, and compare rescores against the stored verification status
2026-09-22 22:35:10 -07:00
Matthew Meszaros
19eb68ecd5
feat: re-read stored opens and clicks by the live origin rules in batched, resumable consumer passes under an advisory lock instead of a boot-time SQL backfill, never read a proxy string on a click as Apple Mail or Gmail, keep recognising Outlook, Proton Mail, Yahoo Mail and HEY by name, show the mail app behind a click in the expanded timeline row, and note that other iOS mail apps count as Apple Mail
2026-09-22 22:31:27 -07:00
Matthew Meszaros
3cf743a0cd
Merge remote-tracking branch 'origin/main' into fix/millionverifier-reverification-source
2026-09-22 22:17:04 -07:00
Matthew Meszaros
72272cfbe7
feat: queue a contact re-verify ahead of the backlog whatever its evidence or manual verdict while the current verdict stands, count MillionVerifier renewing subscription credits, let a paid verifier's fresh answer outrank mail older than 30 days, keep what each check said, and show Verified with MillionVerifier plus a live Re-verify button on the contact Deliverability card
2026-09-22 22:17:04 -07:00
Matthew Meszaros
eca5416a6f
Merge origin/main into feature/open-tracking-device-client
2026-09-22 22:16:51 -07:00
Matthew Meszaros
d44fd38d90
feat: show the device and mail client behind every open and click (iPhone · Apple Mail app, Windows PC · Outlook app, Gmail · device hidden) from a new mailclient detector that recognises Gmail, Yahoo, Apple MPP, HEY, Fastmail and Seznam image proxies instead of reporting their fake browser and data-centre location, record app vs webmail and device_hidden on the open and click logs, backfill stored opens by the same rules, let the logs accept the scanner reason, and surface it on the contact timeline, a new How they read overview, the live campaign feed, recent activity, the campaign Device breakdown (surfaces), webhooks and realtime
2026-09-22 22:16:51 -07:00
Matthew Meszaros
c75b3b3b0d
Merge pull request #651 from warmbly/feature/import-existing-custom-fields
...
feat: map import columns onto the workspace's existing custom fields
2026-09-23 04:45:33 +00:00
Matthew Meszaros
7813d77efc
feat: send nothing to TypeSafe when an import's header row holds an address, date or number, skip placeholder Column N headers, accept only the options each column was offered, never infer Subscribed, Categories or Email, prefer an exact existing field name before a folded match on the server as the client does, compute value kinds once per preview, and make Enter in the empty field search a no-op
2026-09-22 21:29:22 -07:00
Matthew Meszaros
25652476ba
feat: store the timezone a new workspace is created with, return a mailbox's own and workspace timezone from its PATCH, pin following campaigns and mailboxes to the workspace zone on the 000198 down migration, page through every campaign and mailbox before the Timezones summaries and Set all, let the campaign wizard and onboarding pick up a workspace zone that loads late, and qualify the workspace timezone copy to mailboxes that follow it
2026-09-22 20:38:18 -07:00
Matthew Meszaros
7a86186f5b
feat: close Advisor findings about a mailbox, campaign or step inside its delete transaction, keep re-evaluating workspaces that still hold open findings after their last mailbox is gone, re-evaluate the Advisor right after a mailbox change or a campaign/step delete with coalesced reruns, and refresh the unread badge, inbox, campaigns, analytics and Advisor caches on every mailbox removal including a teammate's
2026-09-22 20:29:13 -07:00
Matthew Meszaros
c1acded32c
feat: place import columns no header matched with one TypeSafe Jev choice call per preview (headers, value kinds and field names only, never a cell; 0.70 confidence floor, one column per field, 4s fallback to the deterministic mapping), map a column of addresses to Email by its values, report inferred_columns on both import previews, mark them in the mapper, and document what is sent in data control
2026-09-22 20:22:40 -07:00
Matthew Meszaros
38f8382cf8
Merge remote-tracking branch 'origin/main' into feature/sending-window-timezone
2026-09-22 20:13:55 -07:00
Matthew Meszaros
45c045a5bb
feat: give each workspace a timezone that mailbox warmup and campaign sending windows follow by default (organizations.timezone, migration 000198), let campaigns and mailboxes follow it or pin their own, add a Timezones control centre on Settings > Profile with inline per-campaign and per-mailbox zones, default new workspaces and the campaign wizard to the browser zone, expose effective_timezone on campaigns, and move the first-email delay from the Schedule tab and wizard into campaign Settings > First email
2026-09-22 20:13:55 -07:00
Matthew Meszaros
2bdbfe5f68
feat: list the workspace's existing custom fields in the contact import and Google Sheets column mapper (searchable, with inline create), auto-map headers to existing fields ignoring case and separators in one shared server-side suggester, flag new fields, near-duplicates and columns sharing a field, and document the matching
2026-09-22 20:07:57 -07:00
Matthew Meszaros
1520da2fa8
feat: look up moved mail against each skipped folder with one SELECT per folder and cap the reconciliation by searches rather than rows, never settle a row whose lookup failed, refuse a skip name that is a saved folder the matcher keeps and purge only what it will stop following, drop the map entries of parked arrivals too, ignore UIDVALIDITY 0 when matching a rename into the skipped subtree, carry the listing memo and pending mark across a rename, ask before ticking a folder in the drawer since imported mail is removed, share one inbox-deleted publisher between the consumer's removal paths, and say in the CLI help that an emptied list does not restore removed mail
2026-09-22 05:45:38 -07:00
Matthew Meszaros
9a7ef088cf
Merge remote-tracking branch 'origin/main' into feature/imap-folder-exclusion
2026-09-22 05:17:37 -07:00
Matthew Meszaros
5a967cc3ce
feat: let an IMAP mailbox exclude folders from sync (email_accounts.sync_skip_folders, migration 000196) so a folder another tool fills never reaches the unified inbox: the worker drops skipped folders and their subfolders before the walk, retires an already-synced one with its stored mail, and removes mail that later moves into one only when its Message-ID is found there; PUT /emails/:id/sync and the drawer's Sync card set the list, GET reports it with the server's folder list, warmbly mailbox skip-folders mirrors it, with docs, OpenAPI and error-code invalid_sync_folder
2026-09-22 05:15:49 -07:00
Matthew Meszaros
62ea7ef906
feat: gate the dashboard version pill's update actions on a session that presented a second factor, carry session_mfa_verified on the web User model, pass the API error code into the permission-denied event and give admin_mfa_required its own two-factor dialog variant linking to Settings > Security instead of the Roles & access advice, and document the rule on the updates page
2026-09-22 03:42:56 -07:00
Matthew Meszaros
e1989f9116
Merge remote-tracking branch 'origin/main' into fix/password-change-session-revocation
...
# Conflicts:
# internal/app/auth/reset_password.go
2026-09-21 04:56:50 -07:00
Matthew Meszaros
eac3f6d615
Merge remote-tracking branch 'origin/main' into fix/sso-link-existing-password-account
...
# Conflicts:
# docs/content/docs/guides/security.mdx
2026-09-21 04:28:20 -07:00
Matthew Meszaros
db0f0c6132
feat: carry the caller's session into ReissueSession from the request instead of looking it up, evict every revoked session from the cache and write a revoked tombstone where the delete is refused, and answer a password change whose reissue failed with the distinct 409 password_changed_sign_in_again that the dashboard turns into a sign-out, documented in error-codes, the endpoint reference and OpenAPI
2026-09-21 04:23:46 -07:00
Matthew Meszaros
ff2204f8ef
feat: route the sso_link completion through the one login path (completeLogin) so the ban check, the 2FA gate, login recording and device memory apply before a parked identity is attached, carry the session provider into every 2FA challenge so a Google or Apple sign-in on a 2FA account is recorded as such, make IdentityRepository.Link report a pair another account holds as ErrIdentityTaken instead of silently updating nothing, refuse a spent address budget before charging a link try, drop the dead expiry check and the duplicate link fields on the web Session model
2026-09-21 03:57:29 -07:00
Matthew Meszaros
4578980b34
feat: fall back to My Organization when the first name is blank in displayname.DefaultWorkspace, and only treat a scheme as a link when a non-space follows its colon so names like Big Data: EU pass in both the Go and web validators
2026-09-21 03:42:17 -07:00
Matthew Meszaros
a1b7a8ad9b
feat: attach a parked federated identity only after the ban check and, on a 2FA account, only once the second factor passes by carrying it through the 2FA pending record into twofa.VerifyLogin, charge each sso_link password attempt atomically before the check, treat an identity a parallel challenge already linked as a re-login instead of a refusal, ask for no password when the identity cannot be linked, end an exhausted or expired challenge with sso_link_expired so the dashboard returns to the email step, and document the code in error-codes, the API reference and OpenAPI
2026-09-21 03:35:17 -07:00
Matthew Meszaros
9426c0da51
feat: validate every person, workspace and company name through internal/pkg/displayname on each write path (profile, onboarding, setup, IdP sign-in, org create and rename, org import, enterprise inquiry, admin testers, warmblyctl) with a 400 invalid_name code, render stored names in platform email through the same rules, mirror them in the web forms, check the org slug format, and document the rules in error-codes, security and AGENTS.md
2026-09-21 03:34:03 -07:00
Matthew Meszaros
0f60fd9b84
feat: attach a Google, Apple or OIDC identity to an existing password account only after that account's password is presented: resolveFederatedUser parks the sign-in as link_required with a single-use sso_link pending token, POST /auth/sso/link checks the password against the provider-asserted address on the sign-in failure budget and links then issues the session through finishLoginAs, the dashboard collects it on a new login step, and the API reference, endpoints list, security guide and OpenAPI spec describe the third login result
2026-09-21 03:25:29 -07:00
Matthew Meszaros
7626aaefe4
feat: end every session on a password change, the calling one included, and answer POST /auth/me/password with the token pair of a fresh session for that device; the dashboard stores the new pair, and the endpoint reference, security guide and OpenAPI document the response
2026-09-21 03:18:27 -07:00
Matthew Meszaros
0a5e7947b4
feat: return a failed warmup retention delete from the worker so the bus redelivers it up to five times, re-key a Graph message in the map on every move so the sender copy's body can be dropped, never expunge a whole IMAP folder for one message (UID EXPUNGE, else MOVE to Trash, else refuse), build the two retention indexes concurrently in their own migrations 000193 and 000194, keep the dashboard stepper off 1 and 2 days, and describe retention as applying wherever the placement files warmup mail
2026-09-21 01:11:22 -07:00