Quick Start

From an empty account to a delivered message, in five steps.

This walks through the shortest path to a real email arriving in a real inbox. It assumes nothing about how the API works, and it ends with a message you can confirm was delivered rather than merely accepted.

The one step people skip is the second one. You cannot send from a domain you have not verified, and a send from an unverified domain is refused — see Domain verification for what to do when it is.

1. Create an account

Sign up at rovela.dev/signup. You land on the dashboard with a free plan, which is enough to send real mail — there is no separate sandbox mode and no test key that behaves differently from a live one.

2. Add and verify a sending domain

Open Domains, add the domain you want to send from, and publish the DNS records the dashboard shows you. There are four, and they do different jobs: SPF says which servers may send for the domain, DKIM signs the messages so a recipient can tell they were not altered, DMARC tells receivers what to do when the first two fail, and the return-path record lets us receive bounces for you.

Click Verify once the records are in. DNS changes take time to propagate, so a failure immediately after adding them usually means "not yet" rather than "wrong" — re-check after a few minutes before changing anything.

3. Create an API key

Open API Keys and create one. The full key is shown once, at the moment it is created, and never again — store it somewhere you can read it later. Keys look like re_… and are sent as a bearer token.

A key created with sending_access can send mail and nothing else. Use that for anything running in production; full_access is for tooling that needs to manage domains, templates and contacts too.

4. Send a message

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": "you@example.com",
    "subject": "Hello from Rovela",
    "html": "<p>It worked.</p>"
  }'

The response gives you the id of the queued message:

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

The from address has to be on a domain you have verified. Anything else is refused with a 400 naming the domain, rather than being accepted and then silently dropped.

5. Confirm it was delivered

A 200 means the message was accepted for delivery, not that it arrived. Delivery is asynchronous, so the message moves through states after the API has answered you. Two ways to watch it:

  • GET /emails/:id to read the message's current state.
  • GET /emails/:id/events for the timeline — sent, delivered, opened, clicked, bounced, and so on.

For anything in production, use a webhook instead of polling. See Webhooks.

Errors you will actually hit

Every error has the same shape, so one piece of handling covers all of them:

{
  "statusCode": 400,
  "name": "Bad Request",
  "message": "html or text body is required"
}

The message says what was wrong with the request rather than restating the status code, so it is worth surfacing in your own logs as-is.

Where next