Skip to main content

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:

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

Feature gating matrix

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

Register webhook in Resend

In Resend dashboard, configure webhook URL: POST https://<your-public-api-domain>/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

Quota and limits

  • per-request monthly quota slot is reserved in Redis
  • on terminal failure, slot is refunded
  • over-limit returns HTTP 429

Example

Success envelope

POST /api/mail/send-batch (max 100)

Request schema

Array of 1..100 items:
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

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
Dashboard API variant (bearer auth):

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)
Dashboard API live check:

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

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
For the remaining contact endpoints:

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.
Other broadcast endpoints:

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

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.

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

Most endpoints follow urBackend envelope shape:
Webhook signature failures currently return:

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.