Skip to content

Email approval workflow for broadcasts #59

Description

@JuansesDev

Problem

In a workspace with multiple roles (developer, admin, owner), there is no gate between drafting a broadcast and firing it to every subscriber. Today any role with CapBroadcast can send instantly. For real teams that is unacceptable: marketers should be able to compose without the power to mass-send, and owners should sign off before anything goes out.

Proposal

Add a status machine to broadcasts so the team can split who composes from who approves:

draft → in_review → approved → sending → sent
                ↘ rejected (back to draft)

Schema (broadcasts already have a status column for "sending"; extend it):

ALTER TABLE campaigns ADD COLUMN approval_status VARCHAR(16);  -- draft | in_review | approved | rejected
ALTER TABLE campaigns ADD COLUMN submitted_by UUID REFERENCES users(id);
ALTER TABLE campaigns ADD COLUMN reviewed_by UUID REFERENCES users(id);
ALTER TABLE campaigns ADD COLUMN reviewed_at TIMESTAMPTZ;
ALTER TABLE campaigns ADD COLUMN review_comment TEXT;

New endpoints:

  • POST /campaigns/{id}/submit — sender = composer, sets in_review. Required for non-owner/admin roles before send.
  • POST /campaigns/{id}/approve — only owner/admin, sets approved, captures reviewer + timestamp + optional comment. Triggers the same dispatch path that the current Send button uses.
  • POST /campaigns/{id}/reject — only owner/admin, sets rejected with a comment. Composer can edit and resubmit.

Capability tweak:

  • developer role: CapCampaignsWrite (already), but not the new CapCampaignsApprove.
  • admin and owner: both caps.
  • Workspaces on free / Pro tier: feature absent (no approval flow, the existing direct-send path stays as-is).

UI:

  • Campaigns list shows status badges: Draft, In review, Approved, Rejected.
  • Detail view: composer sees "Submit for review" instead of "Send"; admin/owner see "Approve" + "Reject" with comment field when status is in_review.
  • A small inbox-style indicator in the dashboard for pending reviews, scoped to workspaces where you have approval rights.

Acceptance criteria

  • A developer cannot send a campaign directly; the only available action when status is draft is "Submit for review".
  • An admin/owner approving a campaign moves it to the existing send pipeline; nothing about scheduling or per-subscriber dispatch changes.
  • Audit log records campaign.submit, campaign.approve, campaign.reject with reviewer info.
  • The flow is gated behind the Team plan: workspaces without a Team license keep the current behavior (direct send for any role with CapCampaignsWrite).

Why Team-tier

This is the feature that turns "a workspace with roles" into "a team that can actually operate safely". Without it, the developer/admin/owner split is just labels; with it, the workflow physically prevents a junior from accidentally blasting 50k addresses. That separation is what justifies the upgrade above Pro.

Metadata

Metadata

Assignees

No one assigned

    Labels

    backendenhancementNew feature or requestfrontendroadmapPlanned for a future releaseteamTeam-tier feature (Workspaces + collaboration, gated by Team license)

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions