diff --git a/mintlify/docs/api-reference/mail/send.mdx b/mintlify/docs/api-reference/mail/send.mdx
index 4d8910f1..02d01c9a 100644
--- a/mintlify/docs/api-reference/mail/send.mdx
+++ b/mintlify/docs/api-reference/mail/send.mdx
@@ -7,6 +7,10 @@ description: "Send transactional email with direct content or a named template."
Sends transactional email from your project.
+
+ For the complete Mail Platform documentation (BYOK, batch sending, logs, audiences, contacts, broadcasts, webhooks, and dashboard workflows), see Mail Platform guide.
+
+
This endpoint requires a **Secret Key** (`sk_live_...`). Do not call it from browser/client code.
diff --git a/mintlify/docs/docs.json b/mintlify/docs/docs.json
index f6721d50..3647134e 100644
--- a/mintlify/docs/docs.json
+++ b/mintlify/docs/docs.json
@@ -44,16 +44,17 @@
"concepts/row-level-security"
]
},
- {
- "group": "Guides",
- "pages": [
- "guides/authentication",
- "guides/social-auth",
- "guides/database",
- "guides/storage",
- "guides/webhooks"
- ]
- },
+ {
+ "group": "Guides",
+ "pages": [
+ "guides/authentication",
+ "guides/social-auth",
+ "guides/mail-platform",
+ "guides/database",
+ "guides/storage",
+ "guides/webhooks"
+ ]
+ },
{
"group": "SDK",
"pages": [
diff --git a/mintlify/docs/guides/mail-platform.mdx b/mintlify/docs/guides/mail-platform.mdx
new file mode 100644
index 00000000..2f292989
--- /dev/null
+++ b/mintlify/docs/guides/mail-platform.mdx
@@ -0,0 +1,618 @@
+---
+title: "Mail Platform"
+description: "Comprehensive developer guide for urBackend Mail Platform: BYOK, batch delivery, logs, audiences, contacts, broadcasts, and webhook processing."
+---
+
+## Overview
+
+urBackend Mail Platform extends transactional sending into a full delivery workflow with:
+
+- async queue-backed single sending (`/api/mail/send`)
+- direct provider batch sending (`/api/mail/send-batch`)
+- BYOK (Bring Your Own Key) with encrypted project-level Resend keys
+- delivery tracking via persistent `MailLog`
+- audience/contact management (BYOK-gated)
+- marketing broadcasts (BYOK + Pro gated)
+- webhook-driven status updates with Svix verification
+
+Implementation references:
+
+- [Mail Platform implementation PR #172](https://github.com/geturbackend/urBackend/pull/172)
+- [Mail security fixes PR #175](https://github.com/geturbackend/urBackend/pull/175)
+
+### How Mail Platform fits urBackend
+
+- **Public API** (`/api/mail/*`): app/runtime sending, logs, live status, webhook receiver.
+- **Dashboard API** (`/api/projects/:projectId/mail/*`): operator/admin workflows (logs, live checks, audiences, contacts, broadcasts).
+- **Dashboard UI** (`/project/:projectId/mail`): unified Mail Platform control plane.
+
+### Architecture
+
+```mermaid
+flowchart LR
+ A1[Client request\n/api/mail/send] --> B[Public API]
+ A2[Client request\n/api/mail/send-batch] --> B
+ B --> C[Quota reservation + BYOK/default provider resolve]
+ C --> D[BullMQ mail queue (single-send path)]
+ D --> E[Resend API]
+ C --> E[Resend API (batch-send direct path)]
+ E --> F[Delivery events webhook]
+ F --> G[POST /api/mail/webhook\nSvix verify]
+ G --> H[(MailLog)]
+ H --> I[Dashboard/Public log queries]
+```
+
+### Feature gating matrix
+
+| Capability | Free (shared key) | Free + BYOK | Pro + BYOK |
+|---|---:|---:|---:|
+| Transactional send (`/api/mail/send`) | ✅ | ✅ | ✅ |
+| Batch send (`/api/mail/send-batch`, max 100 items) | ✅ | ✅ | ✅ |
+| Delivery logs + live status | ✅ | ✅ | ✅ |
+| Audiences & Contacts | ❌ | ✅ | ✅ |
+| Marketing Broadcasts | ❌ | ❌ | ✅ |
+
+## Getting started
+
+### Prerequisites
+
+- Resend account
+- Valid Resend key with format: `re_[A-Za-z0-9_]+`
+- urBackend project with a **Secret Key** (`sk_live_...`) for public API calls
+
+### Configure BYOK in Dashboard
+
+1. Open **Project Settings** for your project.
+2. Set `resendApiKey` with your Resend key (`re_...`).
+3. Optionally set `resendFromEmail`.
+4. Save. urBackend stores this key encrypted at rest.
+
+### Required server environment
+
+| Variable | Required | Purpose |
+|---|---:|---|
+| `RESEND_API_KEY` | ✅ (fallback) | Default/shared provider key |
+| `RESEND_API_KEY_2` | Optional | Higher-priority fallback key |
+| `RESEND_WEBHOOK_SECRET` | ✅ for webhook verification | Svix secret for `/api/mail/webhook` |
+| `EMAIL_FROM` | ✅ recommended | Default sender fallback |
+
+### Register webhook in Resend
+
+In Resend dashboard, configure webhook URL:
+
+`POST https:///api/mail/webhook`
+
+Enable relevant events at minimum:
+
+- `email.sent`
+- `email.delivered`
+- `email.bounced`
+- `email.complained`
+
+## Sending emails
+
+
+All `/api/mail/*` send endpoints require your **Secret Key** (`sk_live_...`) in `x-api-key`.
+
+
+### POST `/api/mail/send` (single)
+
+#### Request schema
+
+| Field | Type | Required | Notes |
+|---|---|---:|---|
+| `to` | `string` | ✅ | recipient email |
+| `subject` | `string` | Direct mode ✅ | required when not using template fields |
+| `html` | `string` | Direct mode one of `html/text` | HTML body |
+| `text` | `string` | Direct mode one of `html/text` | Text body |
+| `templateId` | `string` | Template mode one of `templateId/templateName` | 24-char ObjectId |
+| `templateName` | `string` | Template mode one of `templateId/templateName` | key/name lookup |
+| `variables` | `object` | Optional | template vars |
+
+#### Quota and limits
+
+- per-request monthly quota slot is reserved in Redis
+- on terminal failure, slot is refunded
+- over-limit returns HTTP `429`
+
+#### Example
+
+
+
+```bash curl
+curl -X POST "https://api.ub.bitbros.in/api/mail/send" \
+ -H "Content-Type: application/json" \
+ -H "x-api-key: sk_live_YOUR_SECRET_KEY" \
+ -d '{
+ "to": "user@example.com",
+ "subject": "Welcome",
+ "html": "Hello
"
+ }'
+```
+
+```ts JavaScript SDK
+import { UrBackendClient } from 'urbackend-sdk';
+
+const client = new UrBackendClient({ apiKey: process.env.URB_SECRET_KEY! });
+const result = await client.mail.send({
+ to: 'user@example.com',
+ subject: 'Welcome',
+ html: 'Hello
'
+});
+console.log(result.id, result.provider);
+```
+
+
+
+#### Success envelope
+
+```json
+{
+ "success": true,
+ "data": {
+ "id": "",
+ "provider": "byok",
+ "monthlyUsage": 12,
+ "monthlyLimit": 100
+ },
+ "message": "Mail queued successfully."
+}
+```
+
+### POST `/api/mail/send-batch` (max 100)
+
+#### Request schema
+
+Array of 1..100 items:
+
+```json
+[
+ {
+ "to": "u1@example.com",
+ "subject": "Campaign",
+ "html": "Hello
"
+ }
+]
+```
+
+Each item supports `to`, `subject`, `html?`, `text?`.
+
+#### Quota behavior
+
+- reserves one quota slot per batch item before provider call
+- if provider call fails, each reserved slot is refunded
+- response includes per-item provider result objects
+
+#### Partial success
+
+`data` returns per-recipient/provider results. Treat each item independently in your caller logic.
+
+#### Example
+
+
+
+```bash curl
+curl -X POST "https://api.ub.bitbros.in/api/mail/send-batch" \
+ -H "Content-Type: application/json" \
+ -H "x-api-key: sk_live_YOUR_SECRET_KEY" \
+ -d '[
+ {"to":"a@example.com","subject":"Hi A","html":"A
"},
+ {"to":"b@example.com","subject":"Hi B","html":"B
"}
+ ]'
+```
+
+```ts JavaScript SDK
+import { UrBackendClient } from 'urbackend-sdk';
+
+const client = new UrBackendClient({ apiKey: process.env.URB_SECRET_KEY! });
+const result = await client.request('POST', '/api/mail/send-batch', {
+ body: [
+ { to: 'a@example.com', subject: 'Hi A', html: 'A
' },
+ { to: 'b@example.com', subject: 'Hi B', html: 'B
' }
+ ]
+});
+console.log(result);
+```
+
+
+
+## Mail logs
+
+### GET `/api/mail/logs` (public) and GET `/api/projects/:projectId/mail/logs` (dashboard)
+
+#### `MailLog` fields
+
+- `resendEmailId`
+- `to`
+- `subject`
+- `status`
+- `usingByok`
+- `templateUsed`
+- `sentAt`
+
+Status enum:
+
+`queued | sent | delivered | bounced | complained | failed`
+
+Sorting/pagination behavior:
+
+- current implementation returns latest 50, sorted by `sentAt DESC`
+
+
+
+```bash curl
+curl "https://api.ub.bitbros.in/api/mail/logs" \
+ -H "x-api-key: sk_live_YOUR_SECRET_KEY"
+```
+
+```ts JavaScript SDK
+const logs = await client.request('GET', '/api/mail/logs');
+console.log(logs);
+```
+
+
+
+Dashboard API variant (bearer auth):
+
+```bash
+curl "https://dashboard-api.ub.bitbros.in/api/projects//mail/logs" \
+ -H "Authorization: Bearer "
+```
+
+### Live status
+
+- Public: `GET /api/mail/logs/:resendId`
+- Dashboard: `GET /api/projects/:projectId/mail/logs/:resendId/live`
+
+Use live status when you need real-time provider status. Use stored logs for analytics/history and low-latency UI lists.
+
+- `404` if log entry does not belong to the current project (cross-project isolation)
+
+
+
+```bash curl
+curl "https://api.ub.bitbros.in/api/mail/logs/re_123" \
+ -H "x-api-key: sk_live_YOUR_SECRET_KEY"
+```
+
+```ts JavaScript SDK
+const status = await client.request('GET', '/api/mail/logs/re_123');
+console.log(status.dbLog, status.resendStatus);
+```
+
+
+
+Dashboard API live check:
+
+```bash
+curl "https://dashboard-api.ub.bitbros.in/api/projects//mail/logs/re_123/live" \
+ -H "Authorization: Bearer "
+```
+
+## Audiences & Contacts (BYOK-gated)
+
+These endpoints proxy Resend API and require a valid BYOK key on the project.
+
+Error status/message semantics follow upstream Resend responses where applicable.
+
+### Audiences endpoints
+
+- `GET /api/mail/audiences`
+- `POST /api/mail/audiences` body: `{ "name": "VIP Customers" }`
+- `DELETE /api/mail/audiences/:audienceId`
+
+
+
+```bash curl
+curl "https://api.ub.bitbros.in/api/mail/audiences" \
+ -H "x-api-key: sk_live_YOUR_SECRET_KEY"
+
+curl -X POST "https://api.ub.bitbros.in/api/mail/audiences" \
+ -H "Content-Type: application/json" \
+ -H "x-api-key: sk_live_YOUR_SECRET_KEY" \
+ -d '{"name":"VIP Customers"}'
+
+curl -X DELETE "https://api.ub.bitbros.in/api/mail/audiences/aud_123" \
+ -H "x-api-key: sk_live_YOUR_SECRET_KEY"
+```
+
+```ts JavaScript SDK
+await client.request('GET', '/api/mail/audiences');
+await client.request('POST', '/api/mail/audiences', { body: { name: 'VIP Customers' } });
+await client.request('DELETE', '/api/mail/audiences/aud_123');
+```
+
+
+
+### Contacts endpoints
+
+- `GET /api/mail/audiences/:audienceId/contacts`
+- `POST /api/mail/audiences/:audienceId/contacts`
+- `PATCH /api/mail/audiences/:audienceId/contacts/:contactId`
+- `DELETE /api/mail/audiences/:audienceId/contacts/:contactId`
+
+Contact payload fields:
+
+`email`, `firstName`, `lastName`, `unsubscribed`
+
+
+
+```bash curl
+curl -X POST "https://api.ub.bitbros.in/api/mail/audiences/aud_123/contacts" \
+ -H "Content-Type: application/json" \
+ -H "x-api-key: sk_live_YOUR_SECRET_KEY" \
+ -d '{
+ "email": "contact@example.com",
+ "firstName": "Ada",
+ "lastName": "Lovelace",
+ "unsubscribed": false
+ }'
+```
+
+```ts JavaScript SDK
+const contact = await client.request('POST', '/api/mail/audiences/aud_123/contacts', {
+ body: {
+ email: 'contact@example.com',
+ firstName: 'Ada',
+ lastName: 'Lovelace',
+ unsubscribed: false
+ }
+});
+console.log(contact);
+```
+
+
+
+For the remaining contact endpoints:
+
+
+
+```bash curl
+curl "https://api.ub.bitbros.in/api/mail/audiences/aud_123/contacts" \
+ -H "x-api-key: sk_live_YOUR_SECRET_KEY"
+
+curl -X PATCH "https://api.ub.bitbros.in/api/mail/audiences/aud_123/contacts/ct_123" \
+ -H "Content-Type: application/json" \
+ -H "x-api-key: sk_live_YOUR_SECRET_KEY" \
+ -d '{"firstName":"Ada","unsubscribed":true}'
+
+curl -X DELETE "https://api.ub.bitbros.in/api/mail/audiences/aud_123/contacts/ct_123" \
+ -H "x-api-key: sk_live_YOUR_SECRET_KEY"
+```
+
+```ts JavaScript SDK
+await client.request('GET', '/api/mail/audiences/aud_123/contacts');
+await client.request('PATCH', '/api/mail/audiences/aud_123/contacts/ct_123', {
+ body: { firstName: 'Ada', unsubscribed: true }
+});
+await client.request('DELETE', '/api/mail/audiences/aud_123/contacts/ct_123');
+```
+
+
+
+## Marketing broadcasts (BYOK + Pro)
+
+Endpoints:
+
+- `POST /api/mail/broadcasts`
+- `POST /api/mail/broadcasts/:id/send`
+- `GET /api/mail/broadcasts`
+- `GET /api/mail/broadcasts/:id`
+- `DELETE /api/mail/broadcasts/:id`
+
+### Two-step flow
+
+1. **Create** broadcast (draft/scheduled payload)
+2. **Send** broadcast by id
+
+### `from` resolution order
+
+1. `from` from request body
+2. `project.resendFromEmail`
+3. `EMAIL_FROM` env fallback
+
+### Quota checks
+
+Broadcast create/send/list/detail/delete paths run behind mail usage gating middleware and plan checks.
+
+
+
+```bash curl
+# 1) Create
+curl -X POST "https://api.ub.bitbros.in/api/mail/broadcasts" \
+ -H "Content-Type: application/json" \
+ -H "x-api-key: sk_live_YOUR_SECRET_KEY" \
+ -d '{
+ "audienceId": "aud_123",
+ "subject": "May launch",
+ "html": "We shipped
",
+ "scheduledAt": "2026-05-20T10:00:00.000Z"
+ }'
+
+# 2) Send
+curl -X POST "https://api.ub.bitbros.in/api/mail/broadcasts/brd_123/send" \
+ -H "x-api-key: sk_live_YOUR_SECRET_KEY"
+```
+
+```ts JavaScript SDK
+const created = await client.request('POST', '/api/mail/broadcasts', {
+ body: {
+ audienceId: 'aud_123',
+ subject: 'May launch',
+ html: 'We shipped
'
+ }
+});
+
+await client.request('POST', `/api/mail/broadcasts/${created.id}/send`);
+```
+
+
+
+Other broadcast endpoints:
+
+
+
+```bash curl
+curl "https://api.ub.bitbros.in/api/mail/broadcasts" \
+ -H "x-api-key: sk_live_YOUR_SECRET_KEY"
+
+curl "https://api.ub.bitbros.in/api/mail/broadcasts/brd_123" \
+ -H "x-api-key: sk_live_YOUR_SECRET_KEY"
+
+curl -X DELETE "https://api.ub.bitbros.in/api/mail/broadcasts/brd_123" \
+ -H "x-api-key: sk_live_YOUR_SECRET_KEY"
+```
+
+```ts JavaScript SDK
+await client.request('GET', '/api/mail/broadcasts');
+await client.request('GET', '/api/mail/broadcasts/brd_123');
+await client.request('DELETE', '/api/mail/broadcasts/brd_123');
+```
+
+
+
+## Webhook integration
+
+Endpoint: `POST /api/mail/webhook`
+
+### Why raw-body parsing is required
+
+Svix signatures are computed over raw payload bytes. `express.raw({ type: 'application/json' })` must run before JSON parser on this path.
+
+### Secret configuration
+
+Set `RESEND_WEBHOOK_SECRET` in server env. Webhook signature verification and event processing only occur when this secret is configured correctly.
+
+- Current behavior when missing/misconfigured: the handler returns HTTP `200` with `{"success":true,"message":"Webhook ignored: secret not configured."}` and skips verification/processing.
+
+### Event mapping to `MailLog.status`
+
+| Resend event type | MailLog status |
+|---|---|
+| `email.sent` | `sent` |
+| `email.delivered` | `delivered` |
+| `email.bounced` | `bounced` |
+| `email.complained` | `complained` |
+| `email.delivery_delayed` | `queued` |
+
+`email.sent` represents provider acceptance (intermediate state), not final inbox delivery.
+
+### Retry behavior
+
+Resend controls webhook retries for non-2xx/timeout outcomes. Keep endpoint idempotent and safe for repeated delivery attempts.
+
+
+
+```bash curl
+curl -X POST "https://api.ub.bitbros.in/api/mail/webhook" \
+ -H "Content-Type: application/json" \
+ -H "svix-id: " \
+ -H "svix-timestamp: " \
+ -H "svix-signature: " \
+ -d '{"type":"email.delivered","data":{"email_id":"re_123"}}'
+```
+
+```ts JavaScript (server webhook simulator)
+await fetch('https://api.ub.bitbros.in/api/mail/webhook', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'svix-id': '',
+ 'svix-timestamp': '',
+ 'svix-signature': ''
+ },
+ body: JSON.stringify({
+ type: 'email.delivered',
+ data: { email_id: 're_123' }
+ })
+});
+```
+
+
+
+## Dashboard UI guide
+
+Route: `/project/:projectId/mail`
+
+### Delivery Logs tab
+
+- status badge (`queued/sent/delivered/bounced/complained/failed`)
+- subject, recipient(s), provider id, sent time
+- live status modal fetches provider status by resend id
+
+### Audiences & Contacts tab
+
+- create/delete audiences
+- add/remove contacts
+- locked if BYOK key is not configured
+
+### Marketing Broadcasts tab
+
+- compose audience + subject + html
+- send campaign via broadcast API
+- locked unless BYOK is configured and account is Pro
+
+
+Add product screenshots/annotated walkthrough images in this section if your docs deployment supports hosted image assets.
+
+
+## Security notes
+
+- BYOK format validation: `^re_[A-Za-z0-9_]+$`
+- key encryption at rest uses shared `encrypt`/`decrypt` helpers (AES)
+- rotate/clear BYOK through project update workflows (`PATCH /api/projects/:projectId` with `resendApiKey: null`, then set a new `re_...` key)
+- live status endpoints validate project ownership before provider lookups
+- webhook authenticity is checked with Svix verification
+
+## Error reference
+
+| Endpoint | Status | Typical message |
+|---|---:|---|
+| `POST /api/mail/send` | `400` | Invalid mail payload / template not found / subject required |
+| `POST /api/mail/send` | `403` | Secret key required |
+| `POST /api/mail/send` | `429` | Monthly mail limit exceeded |
+| `POST /api/mail/send-batch` | `400` | Invalid batch mail payload |
+| `POST /api/mail/send-batch` | `403` | Secret key required |
+| `POST /api/mail/send-batch` | `429` | Monthly mail limit exceeded |
+| `GET /api/mail/logs/:resendId` | `400` | Invalid resendId format |
+| `GET /api/mail/logs/:resendId` | `404` | Mail log entry not found for this project |
+| `GET /api/mail/audiences` + audience mutations | `403` | This feature requires a BYOK Resend key |
+| `GET/PATCH/DELETE /api/mail/audiences/:id/contacts/*` | `400` | Invalid audience/contact id format |
+| `GET/PATCH/DELETE /api/mail/audiences/:id/contacts/*` | `403` | Contacts require a custom Resend API Key (BYOK) |
+| `POST /api/mail/broadcasts*` | `400` | audienceId, subject, and html are required |
+| `POST /api/mail/broadcasts*` | `403` | Broadcasts require both BYOK and Pro plan |
+| `POST /api/mail/webhook` | `400` | Webhook signature verification failed |
+| Provider-proxied endpoints | passthrough | Upstream Resend status/message are surfaced |
+
+Most endpoints follow urBackend envelope shape:
+
+```json
+{
+ "success": false,
+ "data": {},
+ "message": "Human-readable error"
+}
+```
+
+Webhook signature failures currently return:
+
+```json
+{
+ "success": false,
+ "message": "Webhook signature verification failed."
+}
+```
+
+## Changelog & migration notes
+
+Compared to legacy single-send usage:
+
+- `/api/mail/send` is now asynchronous queue-backed
+- `/api/mail/send-batch` adds bulk dispatch (up to 100 items/request)
+- `MailLog` is first-class for auditability and dashboard visibility
+- new BYOK-gated resources: audiences, contacts
+- new BYOK+Pro resource: broadcasts
+- webhook path requires raw-body middleware placement before JSON parser
+- new env dependencies for production-grade mail processing (`RESEND_WEBHOOK_SECRET`, sender defaults)
+
+For release-level change history, see [May 2026 changelog](/changelog/may-2026).