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
| Persona | What They Do |
|---|---|
| Internal service | Notification, Billing, PIP call the API to send transactional emails automatically |
| Developer (customer) | Send transactional emails to their own users (order confirmation, OTP, etc.) |
| Marketing Manager | Create and send email campaigns / newsletters to subscriber lists |
| Admin | View 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
Billing service (internal):
"When an invoice is created, we need to send a PDF invoice to the org admin via email"
→ Billing calls 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
- Billing — Invoice and payment receipts are sent directly through Mail
- 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
| Method | Endpoint | Description |
|---|---|---|
POST | /mail/send | Send single email |
POST | /mail/send-batch | Send to multiple recipients |
GET | /mail/:id/status | Get delivery status |
GET | /templates | List email templates |
POST | /templates | Create template |
PATCH | /templates/:id | Update template |
GET | /subscribers | List subscribers |
POST | /subscribers | Add subscriber |
DELETE | /subscribers/:email | Unsubscribe |
GET | /campaigns | List campaigns |
POST | /campaigns | Create campaign |
POST | /campaigns/:id/send | Send campaign |
GET | /campaigns/:id/analytics | Get campaign analytics |
GET | /domains | List verified domains |
POST | /domains | Add 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
| Block | Description | Adjustable Properties |
|---|---|---|
| Header | Logo + brand name at the top | Logo URL, background color, alignment |
| Hero Image | Large clickable image | Image URL, link URL, alt text |
| Text | Text paragraph | Font size, color, alignment, variable slots |
| Button | CTA button | Label, URL, style (filled/outline), color |
| Product Card | Image + name + price + button | All fields support {{variable}} |
| Divider | Separator line | Color, margin |
| Columns | 2–3 column layout | Proportions, content in each column |
| Social | Social link icons | Facebook, LINE, Instagram, Website |
| Footer | Unsubscribe link + copyright | Text, 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
- Console → Mail → Templates → New Template → Visual Builder
- Choose a layout preset — Newsletter, Promotion, Order Confirmation, Win-back, Receipt
- Drag blocks from the left panel into the design area
- Click a block to edit content — type
{{to insert a variable - Preview — view results on both Desktop and Mobile (320px) to verify responsive behavior
- 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
| Mode | Description | Example |
|---|---|---|
| Personal digest | Content bound to each person's Customer Story | "Your monthly balance, Somchai", "Purchases in the last 30 days" |
| Group digest | Segment summary — sent to admin or manager | "Weekly VIP customer report", "Bang Na branch sales summary" |
| Activity digest | Events 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:
- Choose Digest type — Personal / Group / Activity
- Define Data source — Customer Story fields or segment metrics
- Design the template — same block builder, with extra blocks:
stat_row,product_list,table - Define Recipients — subscriber list or segment from Intelligence
- Set Schedule — daily / weekly / monthly — see also Scheduler
- 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:
- Choose channel — Email / LINE / Push / SMS
- Choose template — select from templates already created
- Define recipients — subscriber list, segment from Intelligence, or customer ID list
- Set send time — date + time (timezone: Asia/Bangkok) or set recurrence
- Preview — view a sample email/message before scheduling
- Confirm — broadcast appears on the calendar immediately
Recurrence
| Pattern | Example |
|---|---|
| One-time | Send once on 16 Jul 09:00 |
| Daily | Every day at 08:00 (daily digest) |
| Weekly | Every Monday 09:00 (VIP weekly report) |
| Monthly | 1st of every month at 10:00 (monthly summary) |
| Custom cron | 0 9 * * 1 — every Monday 09:00 |
List view
In addition to the calendar, there is a filterable/sortable list view:
| Field | Filter options |
|---|---|
| Channel | Email, LINE, Push, SMS |
| Status | Scheduled, Sent, Failed, Cancelled |
| Date range | Scheduled send date |
| Template | Filter by template ID |
| Recipient count | Low to high |
See Notification API → Scheduler and Mail API → Scheduler for REST endpoints