Sending email

How to compose and send a message from the dashboard or the API.

Rovela sends email through the API. The dashboard gives you a log and monitoring view, but the actual sending happens through an API call — either from your own code or from the quick-send form in the dashboard.

Before you send

Two things must be in place before any message can go out:

  • A verified sending domain. The from address must be on a domain you have added and verified under Domains. Sends from unverified domains are refused at the API with a 400 naming the domain.
  • An API key. Create one under API Keys; use sending_access for anything in production.

Sending from the API

The minimum required fields are from, to, subject, and one ofhtml or text:

curl -X POST https://api.rovela.dev/emails \
  -H "Authorization: Bearer re_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "Acme <hello@yourdomain.com>",
    "to": "user@example.com",
    "subject": "Your receipt",
    "html": "<p>Thanks for your order.</p>"
  }'

A successful request returns the message id:

{"object": "email", "id": "9f1c2e40-..."}

The to field accepts a single address string or an array of up to 50 addresses. cc and bcc work the same way. The name part of an address — Acme in the example above — is optional.

Adding attachments

Attachments are base64-encoded and sent inline in the request body:

{
  "from": "billing@yourdomain.com",
  "to": "user@example.com",
  "subject": "Your invoice",
  "html": "<p>Please find your invoice attached.</p>",
  "attachments": [
    {
      "filename": "invoice-2026-10.pdf",
      "content": "<base64-encoded content>",
      "content_type": "application/pdf"
    }
  ]
}
The total request body must stay under 40 MB including all attachments. For larger files, link to them from the email body rather than attaching them.

Scheduling a send

Add a scheduled_at field with an ISO 8601 timestamp to hold a message for future delivery. The timestamp must be at least 5 minutes in the future:

{
  "from": "newsletter@yourdomain.com",
  "to": "subscriber@example.com",
  "subject": "This week's update",
  "html": "...",
  "scheduled_at": "2026-11-01T09:00:00Z"
}

A scheduled message can be rescheduled or cancelled with PATCH /emails/:id before its send time arrives. Once delivered it cannot be recalled.

Using a template

If you have saved a template under Templates, reference it by slug and pass variable values at send time. The html field is not needed when using a template — the body comes from the template:

{
  "from": "hello@yourdomain.com",
  "to": "user@example.com",
  "subject": "Welcome, {{first_name}}",
  "template": "welcome-email",
  "variables": {
    "first_name": "Alex",
    "plan": "Pro"
  }
}

Checking delivery status

A 200 response means the message was accepted, not that it arrived. To check status after sending:

  • GET /emails/:id — the message's current status field.
  • GET /emails/:id/events — the full delivery timeline with timestamps.

For production use, set up a webhook instead of polling. The dashboard view under Emails shows the same timeline with no API call needed.

Suppressed sends

Sending to an address on the suppression list is accepted by the API and returns an id, but the message is never delivered. Its status becomes suppressed. Check the Suppressions page if a message you expected to arrive didn't show up. See Dashboard overview for what goes on the suppression list automatically.