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.
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.
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.
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.
The /api/v1/internal/dek route parameter is now :orgID to match the
org-scoped encryptedkeys store. Workers fetch the organization DEK for
the account they are operating on instead of a user DEK.
Mailboxes, integration tokens, and message content are organization
assets. Keying their DEK by the connecting user meant offboarding that
user made every ciphertext they created unreadable.
Migration 000039 drops user_encrypted_keys and creates
organization_encrypted_keys keyed by organization_id. The new table has
no FK on purpose: platform secrets live under the zero UUID (no
organizations row), and DEK rows must never cascade-delete because a
lost DEK is unrecoverable.
Pre-production, so there is no data migration; ciphertexts sealed under
the old per-user DEKs are abandoned with the table.
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.
Restructure the docs navigation into product-focused pages, add automation expression reference content, and refresh related template and personalization surfaces.
Adds the shared template helper function map, send-path spintax expansion, server-side template preview validation, and the campaign template preview endpoint.
Wires notification and two-factor services into the backend and consumer, adds the authenticated notification feed/preferences endpoints, and exposes the public 2FA login verification endpoint.
Adds the persisted notification feed, user preferences, realtime notification events, and advanced-service notification hooks for replies and deliverability health events.