Files
monica_site/site/messaging
westfarn d830f07757
Deploy Beta / unit-tests (push) Successful in 9s
Deploy Beta / docker (push) Successful in 15s
Deploy Beta / deploy-beta (push) Successful in 1m36s
Send branded HTML emails and harden SMTP2GO webhooks.
Fix campaign stuck on sending under async queue, and stop UUID ValidationError when SMTP2GO tests send "Headers Unavailable".
2026-08-09 06:37:20 -05:00
..

Messaging

Campaign compose/send, SMTP2GO email + SMS, PCM Integrations postcards, and delivery webhooks.

SMTP2GO webhook setup

Campaign report page polls provider events every 10s. Create two webhooks in SMTP2GO → Settings → Webhooks (email and SMS stay separate).

Auth (SMTP2GO_WEBHOOK_SECRET)

  1. Set SMTP2GO_WEBHOOK_SECRET in .env / prod env (long random string).
  2. In SMTP2GO, set Authorization header to Bearer and paste that same secret (do not leave it as “None”).
  3. Fallback: ?token=<SMTP2GO_WEBHOOK_SECRET> on the webhook URL also works.

Email webhook

Field Value
URL https://mkdrealtor.com/portal/messaging/webhooks/email/
Authorization header Bearer + SMTP2GO_WEBHOOK_SECRET
Output type JSON
Email events processed, bounced, rejected, spam, delivered, unsub/resub, opened, clicked
Email headers X-Monica-Message-Id
SMS events leave unchecked

X-Monica-Message-Id is set on every campaign email send and is required so webhook events match the correct recipient row. Invalid / missing header values no longer 500 the endpoint (SMTP2GO “Test this webhook” often sends a sample non-UUID).

Beta / other hosts: swap the hostname, keep the path.

SMS webhook (separate)

Field Value
URL https://mkdrealtor.com/portal/messaging/webhooks/sms/
Authorization header Bearer + same SMTP2GO_WEBHOOK_SECRET
Output type JSON
Email events leave unchecked
SMS events Submitted, Sending, Delivered, Failed, Rejected, Opt-out

This endpoint also accepts inbound reply POSTs (text=STOP, from=…) and opts the contact out of SMS.

PCM Integrations (postcards)

Default postcard provider. Designer embeds PCMs editor; orders use DirectMail API v3.

Env

Var Purpose
PCM_API_KEY API key from PCM portal (My Account → API Keys)
PCM_API_SECRET Matching API secret; used with key on POST /auth/login
PCM_CHILD_REF_NBR Optional child-app ref for multi-account
PCM_WEBHOOK_SECRETS Comma-separated signature secrets (one per PCM subscription)
PCM_WEBHOOK_SECRET Optional single-secret alias (merged into the list above)
PCM_RETURN_ADDRESS JSON return address on orders
POSTCARD_PROVIDER pcm (default)

Auth flow: POST /auth/login with {apiKey, apiSecret} → short-lived token used as Authorization: Bearer … on design/order calls (PCM Logging In).

Designer

Portal → Postcard design: create/list designs via API, edit in iframe (POST /design/custom, GET /design/{id}/edit?mode=embed). Save as a MessageTemplate (stores design_id) then pick it when composing a postcard campaign.

Postcard webhook

PCM allows one event per subscription, and each subscription gets its own signature secret (copy-only in the UI). Create one subscription per status you care about; point them all at the same URL and paste every secret into env.

Field Value
URL https://mkdrealtor.com/portal/messaging/webhooks/postcard/
Events One subscription each: Pending, Processing, Processed, Delivered, Undeliverable, Canceled (skip QrCodeScan unless needed)
Environments Sandbox and/or Production as needed
Secrets Copy each subscription signature → PCM_WEBHOOK_SECRETS=sec1,sec2,…

We accept Bearer, ?token=, or common signature headers (raw secret or HMAC-SHA256 of body) matching any listed secret.

Correlation: we send extRefNbr=<Message.uuid> on each recipient; webhooks should echo that (or orderID, matched to Message.provider_message_id).

Campaign completion email

When a campaign reaches completed (email, SMS, or postcard), one summary email goes to campaign.created_by.email, else CONTACT_EMAIL. Guarded by Campaign.notify_sent_at so it only sends once.

Local development

SMTP2GO / PCM cannot reach localhost. Use a tunnel (Cloudflare Tunnel / ngrok) to :8000, or test webhooks against beta/prod.

For real SMTP delivery locally (not console logs):

# in .env
EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend
EMAIL_HOST_USER=EMAIL_HOST_PASSWORD=SMTP2GO_WEBHOOK_SECRET=SMTP2GO_SMS_API_KEY=# SMS sends only
PCM_API_KEY=PCM_API_SECRET=PCM_WEBHOOK_SECRETS=sec1,sec2,…
PCM_RETURN_ADDRESS={}

Endpoints (app)

Path Purpose
POST /portal/messaging/webhooks/email/ Email delivery / open / click / bounce / …
POST /portal/messaging/webhooks/sms/ SMS delivery events + inbound STOP
POST /portal/messaging/webhooks/postcard/ PCM order / mail tracking events
GET /portal/messaging/campaigns/<id>/status.json Live stats for the campaign report UI
GET /portal/messaging/postcard/ PCM designer iframe

Code: webhooks.py, providers/postcard/pcm.py, views.py.