Skip to main content

Marketing email (Azure Communication Services)

Newsletters, event announcements and prayer letters are sent from the Admin CRM through Azure Communication Services Email (replacing Mailchimp). Transactional mail uses a different path (Microsoft Graph).

Codecampaigns/ (acs.py client, models.py, rendering.py, designs.py, ai_templates.py, audience.py, services.py, tasks.py, reporting.py, unsubscribe_views.py, webhook_views.py, events.py)
Runbookcampaigns/README.md (Event Grid setup, ops checklist for Azure portal + Cloudflare DNS, privacy)
EnvACS_CONNECTION_STRING, ACS_SENDER_ADDRESS, ACS_SENDER_NAME, ACS_REPLY_TO, ACS_EVENTGRID_WEBHOOK_KEY, CAMPAIGN_SENDING_ENABLED, CAMPAIGN_SEND_ALLOWED_GROUP_IDS, CAMPAIGN_SEND_RATE_PER_MINUTE, CAMPAIGN_BATCH_SIZE, CAMPAIGN_LARGE_THRESHOLD, CAMPAIGN_REQUIRE_TEST_SEND, PUBLIC_BASE_URL, WEBSITE_BASE_URL, CAMPAIGN_POSTAL_ADDRESS, AI_TEMPLATE_ENABLED, AI_TEMPLATE_MODEL

Lifecycle​

  1. Template: an EmailTemplate built in the admin app's block designer, rich-text editor or raw HTML; optional AI draft (generate_ai_template_draft); images uploaded as EmailImage; variables such as {{ registration_url }}.
  2. Audience: a pydantic AudienceDefinition (groups, tags, regions/areas, saved map shapes, event proximity) previewed through /rest/campaigns/audience/preview/.
  3. Prepare: recipients are materialised as CampaignRecipient, suppression and marketing status applied, a test send is required if CAMPAIGN_REQUIRE_TEST_SEND.
  4. Send: start_campaign → chained send_campaign_batch (CAMPAIGN_BATCH_SIZE at CAMPAIGN_SEND_RATE_PER_MINUTE) → finalize_campaign. Large sends above CAMPAIGN_LARGE_THRESHOLD get an extra confirmation in the UI.
  5. Events: ACS publishes delivery, bounce, open and click events to Event Grid, which calls POST /rest/campaigns/acs-events/?key=<ACS_EVENTGRID_WEBHOOK_KEY>; they update recipients and the campaign report.
  6. Unsubscribe: one-click link to /unsubscribe/<token>/ (outside /rest/), which sets the contact's marketing status.

Dev safety​

The dev database holds real people. Two guards:

  • Backend: sending is off unless CAMPAIGN_SENDING_ENABLED=1. If it is on, CAMPAIGN_SEND_ALLOWED_GROUP_IDS (set to 36, the "Email Testing" group, on dev) makes send/schedule refuse recipients outside those groups.
  • Admin CRM: against the dev backend it shows a banner and disables the send button unless the audience is limited to group 36 (VITE_DEV_SEND_GROUP_IDS).

Keep both. Test with manage.py acs_send_test and the template send test action, which goes to an address you type.

Setup (once per environment)​

In the Azure portal: an ACS resource with an Email Communication Service and a verified sender domain; DNS records (SPF, DKIM, DMARC) in Cloudflare; an Event Grid subscription on the ACS resource pointing at the webhook URL with the key. The ops checklist in the README walks through it.