Z
Zen Mail
SES-powered email marketing

Sending and monitoring emails in one simple platform.

Zen Mail is built for both tech and non-tech teams: minimal markup friction compared to raw SES, fast template workflows, and a clear queue + deliverability view.

How it works

Keep SES as your sending provider, then let Zen Mail handle templates, scheduling, retries, and monitoring.

1

Register your SES sender

Add (and verify) your sender email or domain in AWS SES, then save your SES credentials in Zen Mail.
2

Build templates & audiences

Create templates with variables, preview them, and manage people + tags in one place.
3

Schedule sends & monitor delivery

Schedule email jobs via UI or API. Zen Mail handles queueing, retries, and records delivery events.
Under the hood
API → Firestore → Queue → Worker → SES

Core features

Templates + preview

Liquid templates, light/dark previews, and built-in system.* fields (see docs below).

People management

Contacts, tags, and custom columns per tenant.

Queue + retries

Idempotency keys, retry/backoff, and a full send log.

Deliverability webhooks

Record delivered, bounced, and complaint events.

Tracking + unsubscribe

Open tracking pixel, signed one-click unsubscribe links, and per-sender opt-out stored on each person.

Assets + API keys

Foldered assets for email images and S2S API keys.

Template variables (Liquid)

Every send (template or raw HTML/text) merges a system object into your Liquid scope. You do not need to pass these from the API for real mail; use Sample data in the template editor only so the preview can resolve the same paths.

Built-in fields
  • {{ system.unsubscribe }} — signed URL for one-click unsubscribe from the specific From address used for that send.
  • {{ system.people.first_name }}, {{ system.people.last_name }}, {{ system.people.email }} — resolved from the recipient when they exist in People (or from job variables).

Set PUBLIC_BASE_URL on the server so system.unsubscribe is a real link in production. Optional person_id on API jobs pins the CRM row for unsubscribe and merge fields.

Example (HTML footer)
<p style="font-size:12px;color:#6b7280">
  <a href="{{ system.unsubscribe }}">Unsubscribe</a>
  · sent to {{ system.people.email }}
</p>
Example (subject line)
Hi {{ system.people.first_name }}, here's your update

For developers

Scheduling an email is a single API call. Zen Mail stores the job, queues it, and sends it at the scheduled time.

Create a scheduled job
POST /api/email-jobs
curl -X POST "$BASE_URL/api/email-jobs" \
  -H "Authorization: Bearer $ID_TOKEN" \
  -H "x-tenant-id: $TENANT_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "template",
    "template_id": "welcome_email",
    "to": ["person@example.com"],
    "subject": "Welcome to Zen Mail",
    "variables": { "firstName": "Ada" },
    "scheduled_at": 1760000000000,
    "idempotency_key": "welcome_email_person@example.com_1760000000000",
    "max_retries": 3
  }'
  • Idempotency built-in: repeated calls with the same idempotency_key return the same job.
  • Schedule in epoch ms: set scheduled_at (or omit it to send immediately).