Werkmail Docs
Contents

API

Drop-in send APIs

Keep your existing client. Change the base URL (and put a Werkmail bearer in the token field). Bodies and success envelopes match the foreign SDK. Gates do not change — content score, consent, suppressions, and stream split still apply. Failures return that vendor’s error shape plus our hint in the message text.

Set BASE=https://werkmail.eu. Authenticate with Authorization: Bearer $TOKEN (wm_team_… or wm_proj_…). A project token already knows the workspace. A session bearer must pass practice_id (query, body, or X-Werkmail-Practice).

Postmark clients may send X-Postmark-Server-Token instead of Authorization — use the same Werkmail token value.

POST /api/practices/{id}/send and POST /api/emails (workspace from the token).

Flexible JSON: text/html or text_body/html_body; to as string, array, or comma-separated list; PascalCase Postmark fields; Resend-style tags: [{name, value}].

Success is 202 with { "id", "message_id", "status", "code", "hint" } — code/hint appear when something is blocked.

Resend SDK

Base URL: https://werkmail.eu/api/compat/resend

SDK call Werkmail path
emails.send POST /emails
emails.send batch POST /emails/batch (max 100)
retrieve GET /emails/{id}

Success is 200 { "id": "…" } (batch: { "data": [{ "id" }] }). Errors use { "statusCode", "name", "message" }.

bash
curl -sS -X POST "$BASE/api/compat/resend/emails" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Idempotency-Key: order-123" \
  -d '{
    "from": "Praxis <noreply@your-domain.example>",
    "to": ["you@example.com"],
    "subject": "Terminbestätigung",
    "html": "<p>Ihr Termin ist bestätigt.</p>",
    "tags": [{"name": "kind", "value": "termin"}]
  }'

scheduled_at accepts an RFC3339 timestamp for future delayed dispatch (e.g. 2026-09-02T16:00:00Z). Werkmail holds the message in queued status until the designated time before running compliance checks and delivering.

Postmark SDK

Base URL: https://werkmail.eu/api/compat/postmark

SDK call Werkmail path
send POST /email
send batch POST /email/batch

Success is 200 { "ErrorCode": 0, "Message": "OK", "MessageID", "To", "SubmittedAt" }. Errors use { "ErrorCode", "Message" }.

MessageStream outbound maps to outgoing; broadcast stays broadcast. To may be a comma-separated string. Headers may be [{ "Name", "Value" }]. Attachments accept Name / Content / ContentType.

Lettermint

Base URL: https://werkmail.eu/api/compat/lettermint

Use this when your app already speaks Lettermint (or a Lettermint-shaped REST client). Put the Werkmail token in the same place you used the Lettermint key.

Call Path
send POST /send
send (emails alias) POST /emails
batch POST /send/batch (max 100)
bash
curl -sS -X POST "$BASE/api/compat/lettermint/send" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Idempotency-Key: order-123" \
  -d '{
    "from": "Praxis <noreply@your-domain.example>",
    "to": ["you@example.com"],
    "subject": "Terminbestätigung",
    "html": "<p>Ihr Termin ist bestätigt.</p>",
    "text": "Ihr Termin ist bestätigt."
  }'

Same flexible body as native send (from, to, subject, html / text). Response is the native envelope (id, message_id, status, plus code / hint when blocked). Templated bulk stays at POST /api/emails/send-bulk (project_id = workspace).

/api/compat/eu/send, /api/compat/eu/send/batch, and /api/compat/lettermind/… (send, emails, send/batch) are aliases of this base if a client was already pointed there. Prefer /api/compat/lettermint.

What is never a silent drop-in

Foreign feature Werkmail behaviour
React Email / server-side JSX Render HTML in your app, send html
Dedicated IP / stream product names Use outgoing vs broadcast
Skipping unsubscribe or DOI Broadcast still requires Impressum + DOI
Provider-account suppression as the only list Our Postgres suppressions still apply

See Errors, Send, and Regulated transactional.

View this page as Markdown · llms.txt · full corpus