Matthew Meszaros
57adf4f9b2
feat: route notification.deliverEmail through GenerateNotificationHTML and drop the hand-rolled htmlEscape/inline-fragment email body
2026-06-28 05:27:28 +00:00
Matthew Meszaros
2c910dcdeb
feat: make a campaign step's Original a first-class weighted A/B arm driven by a single draggable traffic-split bar, persisting the control share as an is_control variant row
2026-06-15 08:11:53 +02:00
Matthew Meszaros
03acdb87e3
fix: thread organization_id through contact, CRM, and unibox reads and writes so non-owner org members see and edit their workspace data, and backfill contacts.organization_id for single-membership users (migration 000049)
2026-06-15 08:11:53 +02:00
Matthew Meszaros
c5dfa5e4e7
feat: remove the HTTP-request action from campaign steps and automations in favor of signed webhooks, keep fire_event for custom payloads, and drop the now-unused outbound quota plumbing
2026-06-15 08:11:35 +02:00
Matthew Meszaros
9d0c432be7
feat: let OAuth apps subscribe to webhooks by declaring a url/events/secret and materializing per-org endpoints gated by each grant's scopes, with secret rotation and delivery observability (migration 000050)
2026-06-15 08:11:20 +02:00
Matthew Meszaros
173736a004
feat: add a Stripe-grade webhook platform with a typed event catalog, HMAC-signed delivery, retries with backoff, endpoint ownership verification, per-endpoint throttling, an audit-spine event bridge, and firehose emit sites (migration 000048)
2026-06-15 08:11:20 +02:00
Matthew Meszaros
fcafac4195
refactor: remove the campaign 'notify (webhook)' step type now that the HTTP request step covers outbound calls and there is no webhook setup surface
2026-06-14 10:48:06 +02:00
Matthew Meszaros
ab24d8bbc8
feat: add a 'Fire event' action and campaign step that publish custom events to the realtime gateway (no public URL), an HTTP-request campaign step, a configurable automation dry-run test with per-step toggles, and fix the false 'updated by a teammate' toast on your own save
2026-06-14 09:52:49 +02:00
Matthew Meszaros
fcdb31cda7
feat: OAuth apps always issue a client secret (drop the public/PKCE-only client type, secret required for the token exchange), add an app-logo upload endpoint, and align the docs to OAuth2 with optional PKCE
2026-06-14 09:52:49 +02:00
Matthew Meszaros
8c175d4221
feat: add an OAuth 2.1 authorization server (migration 000047 apps/codes/grants, app registration CRUD, authorization-code-with-PKCE authorize+token+refresh-rotation+revoke endpoints, RFC 8414 discovery, bearer-token validation wired into the auth middleware reusing the API-permission gates, scopes mapped to API permission bits)
2026-06-13 13:56:39 +02:00
Matthew Meszaros
20935ef061
feat: add a nil-safe per-org daily outbound-action quota (Redis daily counter, anti-abuse ceiling on the HTTP-request automation node, wired in both backend and consumer, fail-open) to bound webhook relay abuse
2026-06-13 13:41:20 +02:00
Matthew Meszaros
1fa9c65ada
fix: harden every user-supplied-URL outbound path against SSRF with a shared dial-time guard (resolves the host, blocks private/loopback/link-local/metadata IPs, pins the validated IP to defeat DNS rebinding, re-validates redirects) and log automation HTTP requests + blocked attempts with org attribution
2026-06-13 13:34:19 +02:00
Matthew Meszaros
501b5009b4
feat: add an on-error branch to automation action nodes (try/catch routing, rose on-error handle, executor follows the error edge and treats the failure as handled instead of failing the run)
2026-06-13 13:15:41 +02:00
Matthew Meszaros
9aa3300f46
feat: capture per-action output in automation run history (HTTP status/ok, set-variables values, rendered channel/url/message) and render it under each action in the builder History panel
2026-06-13 13:11:58 +02:00
Matthew Meszaros
331745ecc5
feat: add a generic per-automation inbound webhook trigger (inbound.webhook event, token-gated POST /api/v1/integrations/inbound/automation/:token, migration 000046 inbound_token, background graph run with the JSON body as event payload)
2026-06-13 13:04:10 +02:00
Matthew Meszaros
f1cf470121
feat: render Discord notifications as sky-themed rich embeds and Slack notifications as sky-accented attachment cards with contact and subject fields instead of plain text lines
2026-06-13 12:41:26 +02:00
Matthew Meszaros
534928cfdd
feat: add a safe Set-variables (transform) automation node — computes named values from Go templates against the event + prior step output and writes them back for later steps to reuse, running the same sandboxed text/template engine as every other action value (no I/O, no arbitrary code execution)
2026-06-13 12:18:45 +02:00
Matthew Meszaros
f4a2b87c50
feat: add a configurable warmbly.http_request automation action (the generic send-a-webhook/call-any-API node) — templated method/url/headers/query/body, SSRF-guarded + bounded retry, captures the response back into the event data under an output key so downstream nodes use {{.response.body...}} and conditions branch on {{.response.ok}}
2026-06-13 12:02:11 +02:00
Matthew Meszaros
57e8705a82
feat: make public API list cursors opaque base64 tokens (internal/utils/paging) instead of raw record UUIDs, decoding on input with a 400 on a bad cursor; admin endpoints keep transparent UUID cursors
2026-06-13 07:18:23 +02:00
Matthew Meszaros
c5911fe37a
feat: fire run_automation on campaign instant branches (reply/open/click) through a wired AutomationRunner instead of stamping the action node sent without ever running the flow
2026-06-13 06:26:48 +02:00
Matthew Meszaros
ba89a932cd
feat: run the label_email action on reply — campaign instant and scheduled paths via advanced LabelThread/LabelLatestThreadForContact, automations via execNativeAction reading thread_id and _user_id from the reply payload — and move the native-actions adapter to a shared package wired into the consumer too so reply/bounce/warmup automation actions stop silently failing
2026-06-12 16:44:16 +02:00
Matthew Meszaros
8de7e32857
feat: treat a campaign step's own email as the A/B control arm so contacts split across the original plus active variants by weight, and redesign the step variants editor to show the original and variants as one weighted set with live split percentages
2026-06-12 08:04:03 +02:00
Matthew Meszaros
0fd3d03f55
feat: add org-wide team presence privacy columns (presence_show_online, presence_show_activity) with update plumbing and a PRESENCE_POLICY_UPDATED realtime event so a settings change re-gates connected sockets live
2026-06-12 05:59:01 +02:00
Matthew Meszaros
8547fe97eb
feat: scope unibox MarkSeenBulk by organization so a non-owner member opening a thread clears the shared org-wide unread state instead of updating zero rows
2026-06-12 05:14:50 +02:00
Matthew Meszaros
4db027a342
feat: scope unibox GetByThread by organization instead of user_id so non-owner members see the full conversation they already see in the org-scoped inbox list, not an empty thread
2026-06-12 05:10:00 +02:00
Matthew Meszaros
6a3b8ac0eb
fix: scope the unibox unread-count endpoint by organization_id so the sidebar Inbox badge counts the workspace's unread mail instead of always showing 0 for non-owner members
2026-06-11 18:05:18 +02:00
Matthew Meszaros
594c696bf6
fix: scope the unibox inbox list and overview by organization_id (via the workspace's email accounts) instead of the logged-in user_id, so every member sees the org's incoming mail; per-user thread labels and snoozes stay personal
2026-06-11 17:57:52 +02:00
Matthew Meszaros
21032a8fce
fix: scope the contacts list by organization_id instead of user_id so all members see the workspace's contacts (and per-contact campaign badges); categories badge still user-scoped pending an organization_id column on categories
2026-06-11 17:52:36 +02:00
Matthew Meszaros
88e241d536
fix: scope the analytics dashboard (overall stats, recent activity, top campaigns, daily trend, account health) by organization_id instead of user_id, so every workspace member sees the org's analytics rather than an empty dashboard
2026-06-11 17:49:39 +02:00
Matthew Meszaros
a21dc57e07
fix: scope campaign list/detail/count queries by organization_id instead of user_id, so all workspace members see the org's campaigns rather than only the creator
2026-06-11 17:47:04 +02:00
Matthew Meszaros
6ae5e52c4e
fix: scope email-account list/detail queries by organization_id instead of the logged-in user_id, so every member of a workspace sees the org's mailboxes (not just the owner who connected them)
2026-06-11 17:43:56 +02:00
Matthew Meszaros
709b77cdc6
fix: notification email links use the configured APP_URL instead of a hardcoded app.warmbly.com, so dev and self-hosted deployments link to the right host
2026-06-11 12:43:17 +02:00
Matthew Meszaros
3c6de16321
fix: forgotten-password reset now revokes all existing sessions, so a reset done because access was lost or compromised fully cuts off prior devices
2026-06-11 12:41:55 +02:00
Matthew Meszaros
f209eca9ad
fix: Slack notification delivery now resolves a real channel (connection config, then the org's configured Slack automation channel) instead of an always-empty connect-time field that silently dropped every message; docs and UI corrected to match
2026-06-11 12:40:31 +02:00
Matthew Meszaros
3c040649c0
fix: changing your password now revokes every other session (keeping the current device), matching the security promise in the sign-in alert and docs
2026-06-11 12:39:24 +02:00
Matthew Meszaros
5bcc2baaa8
feat: implement the coming-soon security features — logged-in change-password (verify current, policy-checked, POST /me/password) with a real dialog, and new-device sign-in alerts (security notification category fired from the token service on an unrecognized OS+browser, delivered in-app and by email), removing the comingSoon stub helper and updating docs
2026-06-11 12:30:44 +02:00
Matthew Meszaros
160dc0bc76
feat: implement the coming-soon notification delivery channels — Email (SES/SMTP to the account email) and Slack (posts to the org's connected workspace via a new integration NotifySlack), wired in both backend and consumer with per-channel gating, real toggles replacing the coming-soon labels, and updated docs
2026-06-11 12:21:48 +02:00
Matthew Meszaros
39a9752d63
feat: real tokenized invite-accept link — public /invite landing page with safe preview (org, inviter, roles), accept-by-token plus the previously-broken accept-by-invitation-id, public preview + admin copy-link endpoints, login next-param redirect, and a Copy button that yields a working /invite?token link
2026-06-11 11:57:45 +02:00
Matthew Meszaros
19f66507b4
feat: fix multi-role review findings — atomic member+roles insert on invite accept (no partial-failure stranding), gate role deletion on the actor holding the role's permissions (blocks team-managers de-privileging admins), and hydrate+chip pending-invitation role sets
2026-06-11 11:32:57 +02:00
Matthew Meszaros
8540f7db15
feat: members can hold multiple roles — join tables for member/invitation role sets (migration 000044) with effective permissions as the bitwise OR recomputed on every assignment and role edit/delete, role_ids in invite/update APIs, multi-select checkbox role picker with colored chips in roster and invite flow, freely deletable roles
2026-06-11 11:24:19 +02:00
Matthew Meszaros
3473d09bcc
feat: fix review findings in the roles redesign — GetMembers role_id column (members endpoint 500), TransferOwnership role_id hygiene, accept-time role re-resolution, race-free in-use delete guard covering invitations, assignment anti-escalation with self-role-change block, canManage-gated members UI, colored RolePills, fresh currentOrganization on refetch, dev JWT_SECRET wiring for make realtime, docs corrections
2026-06-11 10:45:21 +02:00
Matthew Meszaros
091b39f34e
feat: roles become workspace data — seed editable Admin/Manager/Viewer rows per org (migration 000043 with member backfill), require role_id for invites and role changes, add role colors with a shared colored RoleSelect dropdown in the roster and invite flow, and rebuild Roles & access around real roles with an Owner reference column
2026-06-11 10:18:45 +02:00
Matthew Meszaros
edd4524007
feat: custom organization roles backend (organization_roles table with write-through member propagation, CRUD endpoints with anti-escalation and in-use guards, custom-role invites and assignment via role_id, audited as role entity)
2026-06-11 09:39:52 +02:00
Matthew Meszaros
014cbe79fa
feat: label machine opens (Apple MPP prefetch, UA-less fetchers) with human-open upgrade semantics, exclude them from open-triggered automations, and surface the auto-open count in workspace and campaign analytics (migration 000040)
2026-06-11 08:33:09 +02:00
Matthew Meszaros
abdfd05c34
feat: org-scope realtime events (inbox, campaign, tracking, account health) and emit EMAIL_SENT/EMAIL_REPLIED/EMAIL_DELETED pulses so the whole team's dashboard updates live
2026-06-11 07:43:57 +02:00
Matthew Meszaros
03134317ca
feat: seal integration tokens and config with the organization DEK
...
OAuth access/refresh tokens and pasted credential configs are sealed
and opened with the connection's OrganizationID instead of
ConnectedByUserID, which is now attribution-only. An org's CRM and
Slack connections keep working when the user who connected them is
removed from the organization.
2026-06-10 17:17:03 +02:00
Matthew Meszaros
ae0c433084
feat: seal mailbox validation credentials with the organization DEK
...
SMTP/IMAP passwords on the validation round-trip are now encrypted and
decrypted under the organization DEK, carried as OrgID on
EventWorkerEmailValidation. Onboarding requires an organization before
a mailbox can be validated.
This fixes a latent key mismatch: the payload never set UserID, so the
worker decrypted with the zero-UUID platform key while the backend had
encrypted with the user key.
2026-06-10 17:16:56 +02:00
Matthew Meszaros
ac3c11bf9a
feat: seal outbound email content with the organization DEK
...
The send pipeline (publisher subject/body encryption, the S3 emsg blob,
and the worker-side decrypt) now keys off models.SendEmail.OrgID
instead of UserID. EmailMessage loses its UserID field entirely:
emailSender.Send derives the cipher identity from the email account's
OrganizationID and refuses to send for an account without one.
This also fixes two latent bugs. Emails sent through user_email_task
never set UserID, so they were silently encrypted under the zero-UUID
platform key. campaign_task's discarded Encrypt() pair is replaced with
an explicit DEK warm that fails fast when KMS is unavailable.
2026-06-10 17:16:42 +02:00
Matthew Meszaros
b9a5871308
feat: key the cipher service by organization ID
...
cipher.CipherService.Cipher(ctx, orgID) now resolves, generates, and
caches DEKs per organization (Redis key decrypted_key:<orgID>).
Platform-level secrets keep the zero-UUID identity, renamed to
platformCipherID since it no longer partitions against user keys.
2026-06-10 17:16:26 +02:00
Matthew Meszaros
5954ac53c2
feat: add automation execution safeguards
...
Support chained automation execution from native actions, guard automation recursion depth, carry idempotency context through campaign-launched automations, allow team task assignment in backend action execution, and reject deletes while campaign steps still reference an automation.
2026-06-09 10:56:56 +02:00