Rovela's API is a single JSON endpoint, so sending from Python is a straightforward HTTP request. This guide walks through a minimal send, then adds retries with idempotency keys so a network blip never produces a duplicate message.
Prerequisites
You need a Rovela account (free, no card), an API key from the dashboard, and a verified sending domain. Python 3.7 or later is assumed throughout, with the requests library installed. The same approach works with urllib from the standard library, or with httpx for async, by swapping the HTTP call.
import os
import requests
res = requests.post(
"https://api.rovela.dev/emails",
headers={
"Authorization": f"Bearer {os.environ['ROVELA_API_KEY']}",
"Content-Type": "application/json",
},
json={
"from": "hello@yourdomain.com",
"to": "user@example.com",
"subject": "Welcome aboard",
"html": "<p>Thanks for signing up. Your account is ready.</p>",
},
)
print("Sent:", res.json()["id"])Send your first email
Import requests and os, read your API key from an environment variable, and POST a JSON body to the /emails endpoint. The from address must be on a domain you have verified. The response includes an id you can use to look up the delivery status later.
Retries with idempotency keys
Network requests fail, and the safe response to a timeout is to retry. But a retry can produce a second message if the first one actually went through and only the response was lost. An idempotency key fixes this: send the same key on every attempt, and Rovela returns the original response without dispatching a duplicate. Generate the key once before the retry loop, not inside it.
import os
import time
import uuid
import requests
def send_with_retry(payload, retries=3):
key = str(uuid.uuid4())
for i in range(retries + 1):
res = requests.post(
"https://api.rovela.dev/emails",
headers={
"Authorization": f"Bearer {os.environ['ROVELA_API_KEY']}",
"Content-Type": "application/json",
"Idempotency-Key": key, # same key on every attempt
},
json=payload,
)
if res.status_code < 500:
return res # 2xx or 4xx — stop
time.sleep(min(1000 * 2 ** i, 8000) / 1000) # backoff, capped
raise RuntimeError(f"send failed after {retries} retries")Environment variables
Never put an API key in source code. Use os.environ and load it from a .env file with python-dotenv in development, or from your deployment platform's secret store in production. Rotate the key from the Rovela dashboard if it is ever exposed; the new key takes effect immediately and the old one stops working.
Common questions
- Do I need an SDK, or can I use plain requests?
- Plain requests works fine. Rovela's API is a single JSON endpoint; there is no SDK required. An SDK can reduce boilerplate if you are integrating from a larger codebase, but it is optional.
- How do I send to multiple recipients?
- Pass a list to the 'to' field: ["alice@example.com", "bob@example.com"]. Each address receives an individual copy of the message; recipients do not see each other's addresses. The to, cc, and bcc fields all accept strings or lists.
- How do I send HTML and a plain-text fallback?
- Include both the 'html' and 'text' fields in the request body. Mail clients that cannot render HTML display the text version. Rovela sends both parts in a multipart/alternative message automatically.
- Can I use httpx or aiohttp for async sending?
- Yes. Replace requests.post with the equivalent httpx or aiohttp call. The request body and headers are identical; only the HTTP client changes.