# OpenSpec Change: enterprise-api-plugin-campaigns - **Change ID**: enterprise-api-plugin-campaigns - **Package**: `@suaveplan/api-plugin-campaigns` - **Category**: api/comms - **Target path**: `api/comms/api-plugin-campaigns` - **Status**: PROPOSED - **Date**: 2026-04-24 - **Reference spec**: `/mnt/s/web/suaveplan/enterprise/api-plugin-campaigns.md` --- ## Summary Deliver `@suaveplan/api-plugin-campaigns` on the genesis branch from scratch at enterprise quality. Email/SMS marketing campaign plugin: campaign CRUD (draft/scheduled/active/paused/completed), audience segments (contact list filters from `@suaveplan/api-plugin-contacts`), template selection via `@suaveplan/templates`, scheduled dispatch via a background job scheduler, per-recipient delivery tracking (sent/opened/clicked/bounced/unsubscribed), aggregate stats (open rate, click rate, bounce rate), and suppression enforcement via `@suaveplan/suppression`. Routes under `/campaigns`. Per-table SurrealQL. Typed bus events: `campaign:started`, `campaign:completed`, `campaign:unsubscribed`. --- ## Available workspace deps on genesis | Package | Role | |---|---| | `@suaveplan/api` (`workspace:^`) | `definePlugin` | | `@suaveplan/api-shared` (`workspace:^`) | Helpers | | `@suaveplan/api-plugin-auth` (`workspace:^`) | `requireAuth`, `requireRole` | | `@suaveplan/api-plugin-database` (`workspace:^`) | SurrealDB | | `@suaveplan/api-plugin-contacts` (`workspace:^`) | Contact audience | | `@suaveplan/api-plugin-notifications` (`workspace:^`) | Delivery layer | | `@suaveplan/templates` (`workspace:^`) | Message templates | | `@suaveplan/suppression` (`workspace:^`) | Suppression list | | `@suaveplan/events` (`workspace:^`) | Event bus | | `@suaveplan/error` (`workspace:^`) | `CampaignError` hierarchy | | `@suaveplan/rate-limiter` (`workspace:^`) | Rate limits | | `hono` + `@hono/zod-openapi` + `zod` | Routes | | `@opentelemetry/api` | OTel | --- ## Public API surface ```ts function createCampaignsPlugin(config?: CampaignsPluginConfig): PluginDescriptor // Services: CampaignService, SegmentService, DeliveryTrackingService, StatsService // Routes: /campaigns, /campaigns/:id, /campaigns/:id/send, // /campaigns/:id/stats, /campaigns/:id/recipients, // /campaigns/unsubscribe (public webhook) ``` ### Error codes ```ts type CampaignErrorCode = | "CAMPAIGN_NOT_FOUND" | "CAMPAIGN_ALREADY_SENT" | "CAMPAIGN_NO_RECIPIENTS" | "CAMPAIGN_TEMPLATE_MISSING" | "CAMPAIGN_SEGMENT_EMPTY" ``` --- ## File layout ```text api/comms/api-plugin-campaigns/ package.json tsconfig.json vitest.config.ts surql/ (campaign, campaign_recipient, campaign_stats tables) src/ index.ts events.ts types/ errors/ schemas/ services/campaign/ services/segment/ services/delivery-tracking/ services/stats/ routes/campaigns/ tests/integration.test.ts ``` --- ## Impact Prerequisites: `@suaveplan/api`, `@suaveplan/api-plugin-auth`, `@suaveplan/api-plugin-database`, `@suaveplan/api-plugin-contacts`, `@suaveplan/api-plugin-notifications`, `@suaveplan/templates`, `@suaveplan/suppression`.