Skip to main content
Version: 1.2

9.9 Notification Campaigns

Controller: NotificationCampaignController · Route: api/v1/NotificationCampaign · GroupName: notifications.campaigns · 16 endpoints — the largest single controller in the framework. A "campaign" is a scheduled, recurring, or multi-step ("workflow") bulk-notification entity, targeting a resolved audience. See Chapter 6 for how an individual dispatch works underneath a campaign step.

Verb + routePermissionRequestResponse (data)
POST GetDataTableNotificationCampaigns.ViewAllDtParametersDtResult<NotificationCampaignDto> (unwrapped)
GET GetDetails/{id}NotificationCampaigns.ViewNotificationCampaignDetailsDto
GET GetEdit/{id}NotificationCampaigns.EditNotificationCampaignEditDto
GET GetDropdownList/{findBy}NotificationCampaigns.ViewrouteNotificationCampaignViewListDto[]
GET PreviewRecipients/{id}NotificationCampaigns.PreviewNotificationCampaignAudienceDto[]
GET DryRun/{id}NotificationCampaigns.DryRunNotificationCampaignEditDto
POST CreateNotificationCampaigns.CreateNotificationCampaignEditDtoGuid
PUT Update/{id}NotificationCampaigns.EditNotificationCampaignEditDtoGuid
DELETE DeleteNotificationCampaigns.DeleteDeleteDto[] (restore hardcoded false)Guid
PUT RestoreNotificationCampaigns.DeleteDeleteDto[] (restore hardcoded true)Guid
PUT Duplicate/{id}NotificationCampaigns.CreateGuid (new campaign ID)
PUT ActiveNotificationCampaigns.ManageDeleteDto[]Guid
PUT DeactiveNotificationCampaigns.ManageDeleteDto[]Guid
PUT Pause/{id}NotificationCampaigns.ScheduleGuid
PUT Resume/{id}NotificationCampaigns.ScheduleGuid
PUT Cancel/{id}NotificationCampaigns.CancelGuid

Note: unlike most controllers, Delete and Restore are two separate routes here (not one route with a ?restore= query flag) — each hardcodes its own restore boolean internally. PermissionConstants.NotificationCampaigns also defines Execute and Approve, which are not referenced by any action in this controller — likely reserved for a future manual-trigger/approval-gate feature.

Field-level detail — NotificationCampaignEditDto

{
"id": "00000000-0000-0000-0000-000000000000",
"isActive": true,
"displayOrder": 0,
"name": "تذكير انتهاء الاشتراك - يوليو",
"fName": "Subscription expiry reminder - July",
"description": "Reminds tenants whose subscription expires within 7 days.",
"campaignType": 6,
"status": 0,
"priority": 5,
"startDate": "2026-07-10T00:00:00Z",
"endDate": "2026-07-31T23:59:59Z",
"timeZone": "Asia/Riyadh",
"recurrenceType": 2,
"cronExpression": null,
"customIntervalSeconds": null,
"steps": [
{
"stepNumber": 1,
"delay": 0,
"delayUnit": 0,
"eventKey": "SubscriptionExpiryReminder",
"templateId": "9a1b2c3d-0000-0000-0000-000000000010",
"channel": 1,
"trigger": 0,
"continueOnFailure": true,
"stopWorkflow": false,
"priority": 5
},
{
"stepNumber": 2,
"delay": 24,
"delayUnit": 2,
"eventKey": "SubscriptionExpiryReminder",
"templateId": "9a1b2c3d-0000-0000-0000-000000000011",
"channel": 6,
"trigger": 1,
"continueOnFailure": true,
"stopWorkflow": false,
"priority": 5
}
],
"audience": {
"sourceType": 3,
"userIds": null,
"roleNames": null,
"queryExpression": null
}
}

Enum reference:

  • campaignType (CampaignType): OneTime=1, Scheduled=2, Recurring=3, Workflow=4, Drip=5, Reminder=6
  • status (CampaignStatus): Draft=0, Scheduled=1, Running=2, Paused=3, Completed=4, Cancelled=5, Failed=6
  • recurrenceType (RecurrenceType): OneTime=0, Hourly=1, Daily=2, Weekly=3, Monthly=4, Yearly=5, Cron=6, CustomInterval=7
  • delayUnit (DelayUnit): Seconds=0, Minutes=1, Hours=2, Days=3, Weeks=4, Months=5
  • trigger (WorkflowStepTrigger): Always=0, OnDeliverySucceeded=1, OnDeliveryFailed=2, OnRetryExhausted=3, OnCustomEvent=4
  • audience.sourceType (AudienceSourceType): SpecificUsers=0, Roles=1, Subscribers=2, TenantUsers=3, QueryResult=4, DynamicQuery=5

No FluentValidation validator exists for NotificationCampaignEditDto today — step ordering, delay values, and audience configuration are not server-validated beyond what EF Core column constraints enforce.

GET PreviewRecipients/{id} — resolve the audience before running

{
"succeeded": true,
"message": null,
"data": [
{ "userId": "5f1a3b2c-0000-0000-0000-000000000001", "displayName": "Ahmed Al-Otaibi", "email": "ahmed@example.com", "phone": "966549000191" }
]
}

Business notes: this resolves NotificationCampaignAudience.SourceType (e.g. TenantUsers, Roles, QueryResult) into a concrete recipient list via IAudienceResolver, without creating a NotificationCampaignExecution — safe to call repeatedly while authoring a campaign.

GET DryRun/{id} — render without sending

Returns the campaign's steps with templates rendered against a sample data context (no real dispatch, no NotificationDeliveryAttempt rows created) — lets an author verify Scriban template output before scheduling a real run.

PUT Pause/{id} / Resume/{id} / Cancel/{id}

All three act on Status: PausePaused, Resume → back to Scheduled/Running, CancelCancelled (terminal — a cancelled campaign cannot be resumed, only duplicated via Duplicate/{id}).

Angular: a campaign builder wizard — steps + audience configured together, PreviewRecipients/DryRun called before Create/Update is submitted, Pause/Resume/Cancel as toolbar actions on the campaign detail page once it's running. Backend: campaign-scheduler-pipeline (every 1 min) picks up due campaigns; campaign-workflow-executor-pipeline (every 2 min) advances multi-step workflows; campaign-cleanup-pipeline (daily 03:00 UTC) retires old completed/cancelled campaigns. All three run through the platform's Background Jobs Framework — see Chapter 16 — Deployment. Related APIs: Campaign Execution (run history/analytics per campaign), Notification Templates (referenced by steps[].templateId).