Skip to main content

Transactional email

Send one-off transactional messages from your backend using a project API key. Content can be inline HTML or a dashboard-managed template. Sends are recorded for delivery analytics, and optional guardrails can block recipients with poor reputation signals.

Prerequisites

  1. Verified sender domain — The From address must use a domain (or a subdomain of a domain) your organization has verified in the dashboard (unless your deployment allows platform-owned domains via EMAIL_PLATFORM_ALLOWED_FROM_DOMAINS). For example, verifying acme.com also allows sending from addresses at mail.acme.com, news.acme.com, etc.
  2. Provider configuration — The deployment must have a mail provider configured (Resend or Amazon SES). See Environment variables.
  3. Secret API key — Create a project API key with the email:send scope. Publishable keys cannot call the send endpoint.

In the dashboard: Engage → Transactional has a setup checklist, test send, and API reference.

Send email

POST /api/v1/emails/send

Use the same host as your Syntropy web app (cloud or self-hosted), for example:

https://your-domain.com/api/v1/emails/send

Authentication

HeaderValue
AuthorizationBearer syn_sk_...
or x-api-keysyn_sk_...

The key must include the email:send scope (or project:admin). The organization is inferred from the key; do not send organizationId in the body.

Request body

FieldTypeRequiredDescription
tostringyesRecipient email
subjectstringconditionalRequired if not using a template with a subject (max 500 characters)
htmlstringconditionalHTML body; required unless templateId is set
templateIdstring (UUID)conditionalDashboard email template for this organization; supplies cached HTML and default subject
variablesobjectnoString map substituted into html / template body (see Templating)
fromstringnoOverrides default; domain (or any subdomain of a verified domain) must be allowed for the org
replyTostring (email)noReply-To address; overrides the dashboard default when set

Provide either html or templateId. If both are omitted, or subject cannot be resolved, the API returns 400.

Reply-To defaults

Transactional sends omit a Reply-To header unless (1) the request includes replyTo, or (2) your organization has saved a default in the dashboard under Engage → Transactional → Settings. Clearing that default means only per-request replyTo values add a header.

Templating

Placeholders use double curly braces with optional spaces: {{name}} or {{ order_id }}. Keys must match [a-zA-Z0-9_.-]+. Missing keys are left as the original placeholder in the output.

Responses

202 — Accepted (sent to provider)

{
"id": "<send-id>",
"providerId": "<provider-message-id>"
}

providerId may be null depending on the adapter.

400 / 404 — Validation

{ "error": "<message>" }

Examples: invalid JSON, invalid email, missing content/subject, template not found, sender domain not allowed.

202 — Blocked by guardrails

When deliverability rules prevent the send, the API still returns 202 with a body that marks the block (the send row is recorded as blocked):

{
"id": "<send-id>",
"blocked": true,
"reasonCode": "<code>",
"reason": "<human-readable explanation>"
}

reasonCode may be one of: subscriber_unsubscribed, subscriber_bounced, subscriber_complained, suppressed_email, suppressed_domain, org_bounce_rate, org_complaint_rate.

502 — Provider failure

{
"id": "<send-id>",
"error": "<message>"
}

500 — Unexpected server error

{ "error": "Failed to send email" }

Example

curl -X POST 'https://your-domain.com/api/v1/emails/send' \
-H 'Authorization: Bearer syn_sk_...' \
-H 'Content-Type: application/json' \
-d '{
"to": "user@example.com",
"subject": "Order confirmed",
"html": "<p>Hi {{name}}, your order is on the way.</p>",
"variables": { "name": "Alex" }
}'

Pipeline (summary)

  1. Resolve HTML and subject from the body or template.
  2. Enforce sender domain policy.
  3. Insert a pending email_sends row.
  4. Run deliverability guardrails (suppression list, subscriber status, optional org bounce/complaint thresholds).
  5. Send via the configured provider (Resend or SES).
  6. Update the send row with success or failure.

Environment variables

Configure these on the web deployment that handles /api/v1/emails/send:

VariablePurpose
EMAIL_PROVIDERresend (default) or ses.
RESEND_SECRETResend API key when using Resend.
AWS_REGIONRegion for SES.
AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEYSES credentials (optional if using an IAM role).
EMAIL_FROM_ADDRESSDefault From when the request omits from.
EMAIL_PLATFORM_ALLOWED_FROM_DOMAINSComma-separated domains that skip per-org verification (platform-operated).
EMAIL_REPUTATION_GUARDRAILS_ENABLEDEnable bounce/complaint guardrails (default: true).
EMAIL_REPUTATION_BOUNCE_RATE_THRESHOLDRolling bounce rate threshold (default 0.08).
EMAIL_REPUTATION_COMPLAINT_RATE_THRESHOLDRolling complaint rate threshold (default 0.02).
EMAIL_REPUTATION_MIN_SENT_COUNTMinimum sends before org-level rate guardrails apply (default 50).
EMAIL_REPUTATION_WINDOW_HOURSRolling window for reputation stats (default 24).
EMAIL_SUPPRESSION_EMAILSComma-separated addresses always blocked.
EMAIL_SUPPRESSION_DOMAINSComma-separated domains always blocked.
RESEND_WEBHOOK_SECRETSvix secret for Resend webhooks (delivery events).
SES_WEBHOOK_AUTH_TOKENBearer token for the SES webhook endpoint.
POSTMARK_WEBHOOK_TOKENBearer token for the Postmark webhook endpoint.

SES provisioning can attach a per-organization configuration set so bounces and complaints route to your SNS topic; see infrastructure docs for SES setup.

Pre-send Eligibility Check

Before sending to a customer record managed in Syntropy, call the customer eligibility API to check opt-in status, active suppressions, and optional segment membership. The email send pipeline runs its own deliverability guardrails (bounce rate, suppression list), but the eligibility API lets you gate sends earlier and surface a reason to the user before a request is even made.