Commit Graph
4 Commits
Author SHA1 Message Date
Matthew Meszaros 222c9d2554 feat: scope campaign sender resolution to the campaign's organization instead of its owner so a multi-org user can no longer send organization A's campaign from an organization B mailbox: GetByTags/GetAllActiveInScope/GetByCampaignSenders now take a repository.AccountScope keyed on organization_id where a scope with no organization resolves to no mailboxes rather than widening to the owner (tags carry no organization of their own, so one user's tag legitimately spans workspaces and the predicate is the only thing holding the boundary), the campaign scheduler and the preflight tracking-domain check build that scope from campaign.OrganizationID, unibox compose scores only the current workspace's mailboxes, the 'all' lanes of AccountHasActiveCampaign/CountActiveCampaignsForAccount join ea.organization_id = c.organization_id instead of ea.user_id = c.user_id, dead and broken PauseAllByUserID (which wrote the reason string into status and had no callers) is removed, the campaigns/unibox guides now state that senders resolve inside the campaign's workspace, and TestLiveOrglessCampaignDoesNotSendToSuppressedRecipient is updated because an orgless campaign now finds no senders before routing is consulted while the send gate it covers still refuses; live-tested in TestLiveSenderResolutionStaysInsideTheCampaignOrg, TestLiveSenderSchedulerNeverPicksAnotherOrgMailbox, TestLiveSenderSchedulerPicksTheCampaignOrgMailbox, TestLiveSenderScopeWithoutAnOrganizationReachesNothing and TestLiveActiveCampaignLookupIsOrgScoped 2026-08-24 09:20:41 -07:00
Matthew Meszaros f20300a280 feat: tag-scoped auto mailbox pick on compose: POST /unibox/compose accepts from_tag_id which restricts the automatic sender choice to active mailboxes carrying that tag (best-with-budget-first within the group, 400 when the tag has no active members, ignored with an explicit account; GetByTags is user-scoped so foreign tag ids resolve to no members) 2026-07-20 05:28:05 +02:00
Matthew Meszaros 6f8db3ae05 feat: de-template the cold-email humanizer (drop the quotable example asks and subject that models parroted verbatim, add a VARIATION section mandating a fresh skeleton per email and capping punchy standalone lines at one) and add compose drafts: autosaved per-user working copies (client-generated ids, idempotent PUT on a 1.2s debounce, migration 000072) with Saving/Saved in the window header, close-keeps-draft instead of the discard confirm, delete on send or when emptied, and a Drafts list under the rail Compose button to resume or delete, documented in the guide and endpoints scope map 2026-07-19 08:52:56 +02:00
Matthew Meszaros cf58c6d1d2 feat: add the compose backend: POST /unibox/compose sends a brand-new outbound email with org-wide recipient suppression enforced and optional auto mailbox selection, GET /unibox/compose/candidates scores every active mailbox for a recipient (conversation affinity from unibox history plus queued email tasks, remaining daily budget from daily_email_counts, domain-auth health) with human-readable reasons, a recommended pick, contact resolution, and suppression state 2026-07-19 07:27:23 +02:00