Platform
Mail
Overview

Mail

Base URL: https://mail-api.pcampus.co

Overview

Mail is the email delivery service responsible for sending every type of email in the Pcampus ecosystem — both transactional email (registration confirmation, password reset, invoice) and marketing email (newsletters, campaigns, promotions) — through a single provider.

The Notification service uses the Mail service for its email channel — no service sends SMTP directly on its own.

Who uses this

PersonaWhat They Do
Internal serviceNotification, PIP call the API to send transactional emails automatically
Developer (customer)Send transactional emails to their own users (order confirmation, OTP, etc.)
Marketing ManagerCreate and send email campaigns / newsletters to subscriber lists
AdminView delivery analytics, manage domain settings, handle bounces

Key features

  • Transactional email — Sent immediately when an event occurs (user signup, payment, etc.)
  • Marketing campaigns — Create campaign emails with a template editor, scheduling, and A/B testing
  • Template engine — HTML templates with variable substitution ({{user.name}})
  • Domain authentication — SPF, DKIM, DMARC setup per organization
  • Deliverability tracking — Tracks delivered, opened, clicked, bounced, and unsubscribed per email
  • Bounce & spam management — Handles hard/soft bounces, suppresses unsubscribed addresses
  • Subscriber lists — Manage lists, tags, and segments for marketing campaigns

User stories

Internal service:

"When an invoice is created, we need to send a PDF invoice to the org admin via email"

→ Call Notification → Notification calls POST /mail/send with templateId: invoice, PDF attachment

Developer (customer):

"I want to send an OTP email to users when they log in with a phone number"

→ Console → add domain → create OTP template → call POST /mail/send

Marketing Manager:

"I want to send a newsletter to all my subscribers every week"

→ Create subscriber list → design template → schedule campaign → view open rate after sending

How it works

Data model

Integration

  • Notification — Mail acts as the email delivery layer for Notification
  • PIP — Email verification and password reset use Mail

Domain setup

Every organization must verify their domain before sending email with their own from address.

DNS Records required:
  TXT  @ "v=spf1 include:pcampus-mail.co ~all"
  TXT  pcampus._domainkey "v=DKIM1; k=rsa; p=..."
  TXT  _dmarc "v=DMARC1; p=quarantine;"

API overview

MethodEndpointDescription
POST/mail/sendSend single email
POST/mail/send-batchSend to multiple recipients
GET/mail/:id/statusGet delivery status
GET/templatesList email templates
POST/templatesCreate template
PATCH/templates/:idUpdate template
GET/subscribersList subscribers
POST/subscribersAdd subscriber
DELETE/subscribers/:emailUnsubscribe
GET/campaignsList campaigns
POST/campaignsCreate campaign
POST/campaigns/:id/sendSend campaign
GET/campaigns/:id/analyticsGet campaign analytics
GET/domainsList verified domains
POST/domainsAdd domain for verification

Send email payload

{
  "to": "user@example.com",
  "templateId": "tmpl_xxxx",
  "from": "noreply@yourdomain.com",
  "data": {
    "user": { "name": "Somchai" },
    "otp": "123456"
  }
}

Visual Email Block Builder

Mail includes a block-based email builder in Console — the same concept as the Flex Message builder, but for email instead of LINE.

Instead of writing HTML directly, users drag blocks into place and the system automatically renders responsive HTML.

Block types

BlockDescriptionAdjustable Properties
HeaderLogo + brand name at the topLogo URL, background color, alignment
Hero ImageLarge clickable imageImage URL, link URL, alt text
TextText paragraphFont size, color, alignment, variable slots
ButtonCTA buttonLabel, URL, style (filled/outline), color
Product CardImage + name + price + buttonAll fields support {{variable}}
DividerSeparator lineColor, margin
Columns2–3 column layoutProportions, content in each column
SocialSocial link iconsFacebook, LINE, Instagram, Website
FooterUnsubscribe link + copyrightText, links

Block Structure Example (JSON)

{
  "templateId": "tmpl_winback",
  "name": "Win-back Campaign",
  "subject": "คิดถึงคุณ {{customerName}} ครับ",
  "blocks": [
    {
      "type": "header",
      "logo": "https://cdn.pcampus.co/pharmacy/logo.png",
      "backgroundColor": "#2563eb",
      "textColor": "#ffffff",
      "brandName": "Pharmacy App"
    },
    {
      "type": "hero_image",
      "imageUrl": "https://cdn.pcampus.co/pharmacy/winback-banner.jpg",
      "linkUrl": "{{shopUrl}}",
      "altText": "กลับมาซื้อยากับเราได้เลยครับ"
    },
    {
      "type": "text",
      "content": "สวัสดีครับ คุณ {{customerName}}\n\nเราสังเกตว่ายา {{lastProduct}} ของคุณน่าจะใกล้หมดแล้วนะครับ ไม่อยากให้ขาดยาครับ",
      "fontSize": "16px",
      "color": "#333333"
    },
    {
      "type": "button",
      "label": "สั่งยาซ้ำเลยครับ",
      "url": "{{reorderUrl}}",
      "style": "filled",
      "color": "#2563eb"
    },
    {
      "type": "text",
      "content": "ใช้โค้ด **{{couponCode}}** รับส่วนลด {{discountPercent}}% ครับ",
      "fontSize": "14px",
      "color": "#666666"
    },
    {
      "type": "divider"
    },
    {
      "type": "footer",
      "unsubscribeText": "ไม่ต้องการรับอีเมลนี้? {{unsubscribeLink}}",
      "copyright": "© 2026 Pharmacy App. สงวนลิขสิทธิ์"
    }
  ]
}

How to Use the Builder in Console

  1. Console → Mail → Templates → New Template → Visual Builder
  2. Choose a layout preset — Newsletter, Promotion, Order Confirmation, Win-back, Receipt
  3. Drag blocks from the left panel into the design area
  4. Click a block to edit content — type {{ to insert a variable
  5. Preview — view results on both Desktop and Mobile (320px) to verify responsive behavior
  6. Send test — send to a real email address before publishing

The Builder uses MJML as its rendering engine — the generated HTML uses a table-based layout compatible with Gmail, Outlook, and Apple Mail, covering more than 90% of email clients.

Variable Substitution in Blocks

Variables can be used in any text field:

subject:  "คำสั่งซื้อ #{{orderId}} ได้รับแล้วครับ"
body:     "สวัสดีครับ {{customerName}}"
button:   label = "ดูคำสั่งซื้อ", url = "{{orderUrl}}"

For blocks with lists (e.g., order items), use a loop:

{{#each items}}
  {{this.name}} — {{this.qty}} ชิ้น — {{this.price}}
{{/each}}

Rendering via API

The Builder saves block JSON in the template; the system renders it to HTML at send time:

POST https://mail-api.pcampus.co/v1/mail/send
{
  "to": "somchai@example.com",
  "templateId": "tmpl_winback",
  "variables": {
    "customerName": "สมชาย",
    "lastProduct": "Amlodipine 5mg",
    "couponCode": "WINBACK10",
    "discountPercent": "10",
    "reorderUrl": "https://pharmacy-app.co.th/reorder/cust_001",
    "shopUrl": "https://pharmacy-app.co.th"
  }
}

See Mail API → for endpoints for managing block templates via code.


Subscription Digest Email

Digest email is a summary email that the system generates with personalized content from each recipient's Customer Story in PBCP — it is not a single blast template sent to everyone; the content in each email differs based on the individual recipient's data.

Digest modes

ModeDescriptionExample
Personal digestContent bound to each person's Customer Story"Your monthly balance, Somchai", "Purchases in the last 30 days"
Group digestSegment summary — sent to admin or manager"Weekly VIP customer report", "Bang Na branch sales summary"
Activity digestEvents that occurred with customers over a period"Orders pending more than 3 days", "Customers whose membership expires this month"

Personal Digest — Bound to Customer Story

{
  "templateId": "tmpl_monthly_summary",
  "name": "Monthly Customer Digest",
  "type": "digest",
  "subject": "สรุปกิจกรรมของคุณ {{customerName}} — {{month}}",
  "digestConfig": {
    "dataSource": "customer_story",
    "personalized": true,
    "fields": [
      "totalSpend30d",
      "visitCount30d",
      "topProducts",
      "loyaltyPoints",
      "nextReward",
      "recommendations"
    ]
  },
  "blocks": [
    { "type": "header", "brandName": "Pharmacy App" },
    {
      "type": "text",
      "content": "สวัสดีครับ คุณ {{customerName}} นี่คือสรุปกิจกรรมของคุณในเดือน {{month}} ครับ"
    },
    {
      "type": "stat_row",
      "stats": [
        { "label": "ยอดซื้อรวม", "value": "{{totalSpend30d}}", "unit": "บาท" },
        { "label": "จำนวนครั้ง", "value": "{{visitCount30d}}", "unit": "ครั้ง" },
        { "label": "คะแนนสะสม", "value": "{{loyaltyPoints}}", "unit": "แต้ม" }
      ]
    },
    {
      "type": "product_list",
      "title": "สินค้าที่ซื้อบ่อย",
      "items": "{{topProducts}}"
    },
    {
      "type": "callout",
      "content": "อีก {{nextReward.pointsNeeded}} แต้ม คุณจะได้รับ {{nextReward.rewardName}} ครับ"
    },
    { "type": "footer" }
  ]
}

Data in the fields is automatically fetched from each recipient's Customer Story — no need to supply variables at send time.

Group Digest — Segment Summary

{
  "templateId": "tmpl_vip_weekly",
  "name": "VIP Weekly Report",
  "type": "digest",
  "subject": "VIP Weekly Report — สัปดาห์ที่ {{weekNumber}}",
  "digestConfig": {
    "dataSource": "segment",
    "segmentId": "seg_vip",
    "aggregation": "weekly",
    "recipientType": "admin",
    "metrics": [
      "totalRevenue",
      "newVipCount",
      "churnRiskCount",
      "topSpenders"
    ]
  },
  "blocks": [
    { "type": "header", "brandName": "Pharmacy Admin" },
    {
      "type": "stat_row",
      "stats": [
        { "label": "รายได้จาก VIP", "value": "{{totalRevenue}}" },
        { "label": "VIP ใหม่", "value": "{{newVipCount}} คน" },
        { "label": "เสี่ยง Churn", "value": "{{churnRiskCount}} คน" }
      ]
    },
    { "type": "table", "title": "Top Spenders สัปดาห์นี้", "data": "{{topSpenders}}" },
    { "type": "footer" }
  ]
}

Configure Digest in Console

Console → Mail → Digest → New Digest:

  1. Choose Digest type — Personal / Group / Activity
  2. Define Data source — Customer Story fields or segment metrics
  3. Design the template — same block builder, with extra blocks: stat_row, product_list, table
  4. Define Recipients — subscriber list or segment from Intelligence
  5. Set Schedule — daily / weekly / monthly — see also Scheduler
  6. Preview per-recipient — select a customer to see exactly what their email will look like

Broadcast Scheduler

The Scheduler is the calendar view of all scheduled broadcasts — you can see what will be sent on each day, to whom, and through which channel, all creatable and manageable from one place.

Scheduler Page in Console

Console → Mail → Scheduler (for email) or Console → Notification → Scheduler (for LINE / Push / SMS)

┌─────────────────────────────────────────────────────────┐
│  📅  Broadcast Scheduler — กรกฎาคม 2026                │
│  [+ New Broadcast]                  [List] [Calendar]   │
├────────┬────────┬────────┬────────┬────────┬────────────┤
│  จ 14  │  อ 15  │  พ 16  │  พฤ 17 │  ศ 18  │  ส 19      │
├────────┼────────┼────────┼────────┼────────┼────────────┤
│        │ 09:00  │        │ 10:00  │ 08:00  │            │
│        │ 📧 VIP │        │ 📱LINE │ 📧     │            │
│        │ Weekly │        │ Promo  │ Digest │            │
│        │ 2,341  │        │ 5,820  │ 1,204  │            │
│        │ คน     │        │ คน     │ คน     │            │
└────────┴────────┴────────┴────────┴────────┴────────────┘

Each block on the calendar is clickable to view:

  • Broadcast name and template used
  • Number of recipients (total / segment)
  • Status: scheduled | sending | sent | failed
  • Edit / Cancel / Duplicate buttons

Create a Scheduled Broadcast

Console → Scheduler → + New Broadcast:

  1. Choose channel — Email / LINE / Push / SMS
  2. Choose template — select from templates already created
  3. Define recipients — subscriber list, segment from Intelligence, or customer ID list
  4. Set send time — date + time (timezone: Asia/Bangkok) or set recurrence
  5. Preview — view a sample email/message before scheduling
  6. Confirm — broadcast appears on the calendar immediately

Recurrence

PatternExample
One-timeSend once on 16 Jul 09:00
DailyEvery day at 08:00 (daily digest)
WeeklyEvery Monday 09:00 (VIP weekly report)
Monthly1st of every month at 10:00 (monthly summary)
Custom cron0 9 * * 1 — every Monday 09:00

List view

In addition to the calendar, there is a filterable/sortable list view:

FieldFilter options
ChannelEmail, LINE, Push, SMS
StatusScheduled, Sent, Failed, Cancelled
Date rangeScheduled send date
TemplateFilter by template ID
Recipient countLow to high

See Notification API → Scheduler and Mail API → Scheduler for REST endpoints