Authentication Required
All endpoints require a Bearer token in the Authorization header.
Authorization: Bearer orly_xxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
API keys are issued per-project from the admin panel. Keys follow the format orly_<prefix>.<secret> and are shown only once at creation.
Simple API
Single-tenant endpoints that use the server's global SMTP configuration. Authenticated with the global API key.
/api/health
Check API health status
Request:
curl -X GET https://email.getorly.com/api/health \
-H "Authorization: Bearer $API_KEY"
Response:
{
"status": "ok",
"timestamp": "2025-11-09T10:46:25+00:00",
"service": "Orly Email API",
"version": "1.0.0"
}
/api/send-email
Send an email synchronously
Parameters
- to
- Required. Recipient email address
- subject
- Required. Email subject (max 255 characters)
- body
- Required. Email body content (plain text or HTML)
- priority optional
high,normal(default), orlow. Sets X-Priority headers.- html optional
- Boolean. Set to
trueto send body as HTML with an auto-generated plain-text fallback.
Examples
Plain text
curl -X POST https://email.getorly.com/api/send-email \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "recipient@example.com",
"subject": "Hello",
"body": "This is a plain text email."
}'
Raw HTML
Pass any HTML string in body and set html: true.
curl -X POST https://email.getorly.com/api/send-email \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "recipient@example.com",
"subject": "Welcome!",
"body": "<!DOCTYPE html><html><body><h1>Welcome</h1><p>Thanks for signing up.</p></body></html>",
"html": true,
"priority": "high"
}'
React Email
Use React Email to design your template, render it with @react-email/render, then pass the output as body.
npm install @react-email/render @react-email/components
1. Create your template:
// emails/WelcomeEmail.tsx
import { Html, Heading, Text, Button } from '@react-email/components';
export default function WelcomeEmail({ name }: { name: string }) {
return (
<Html>
<Heading>Welcome, {name}!</Heading>
<Text>Thanks for joining us.</Text>
<Button href="https://email.getorly.com">Get started</Button>
</Html>
);
}
2. Render and send:
import { render } from '@react-email/render';
import WelcomeEmail from './emails/WelcomeEmail';
const html = await render(<WelcomeEmail name="John" />);
await fetch('https://email.getorly.com/api/send-email', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.API_KEY}`,
},
body: JSON.stringify({
to: 'john@example.com',
subject: 'Welcome!',
body: html,
html: true,
priority: 'high',
}),
});
Success Response (200)
{
"success": true,
"message": "Email sent successfully",
"email_id": "3d1590af-3e5c-43a3-a467-4fda829b4ab7"
}
Validation Error (422)
{
"success": false,
"message": "Validation failed",
"errors": {
"to": ["The to field must be a valid email address."],
"subject": ["The subject field is required."],
"body": ["The body field is required."]
}
}
Multi-tenant API (/api/v1)
Per-project endpoints with isolated SMTP settings, queued delivery, and email logs. Authenticated with a project-scoped API key.
API Key Scopes
| Scope | Grants access to |
|---|---|
email:send |
POST /api/v1/emails |
email:read |
GET /api/v1/emails, GET /api/v1/emails/{id}, GET /api/v1/health, GET /api/v1/templates/* |
email:admin |
POST /api/v1/templates, PUT /api/v1/templates/{id} |
/api/v1/emails
scope: email:sendQueue an email for delivery using a project sender address and optional template
Parameters
- email_id
- Required. UUID of the sender address registered for this project
- to
- Required. Recipient email address
- subject
- Required. Email subject (max 255 characters)
- type
- Required.
otp,ticket, orgeneric - data
- Required. Object of template variables (keys vary by type — see below)
Data fields by type
| Type | Required fields | Optional fields |
|---|---|---|
otp |
otp, expiresIn |
recipientName, message |
ticket |
eventName, eventDate, ticketNumber, totalPrice, currency |
recipientName, message |
generic |
— | recipientName, message, htmlContent |
OTP email
curl -X POST https://email.getorly.com/api/v1/emails \
-H "Authorization: Bearer $PROJECT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"email_id": "550e8400-e29b-41d4-a716-446655440000",
"to": "user@example.com",
"subject": "Your verification code",
"type": "otp",
"data": {
"recipientName": "Jane",
"otp": "482910",
"expiresIn": "10 minutes"
}
}'
Success Response (202 Accepted)
{
"success": true,
"message": "Email queued for sending",
"email_id": "018e1b2c-3d4e-5f6a-7b8c-9d0e1f2a3b4c",
"status": "queued",
"recipient": "user@example.com",
"project": "my-project"
}
/api/v1/emails/{email_id}
scope: email:readGet the delivery status of a queued email
curl https://email.getorly.com/api/v1/emails/018e1b2c-3d4e-5f6a-7b8c-9d0e1f2a3b4c \
-H "Authorization: Bearer $PROJECT_API_KEY"
{
"email_id": "018e1b2c-3d4e-5f6a-7b8c-9d0e1f2a3b4c",
"to": "user@example.com",
"subject": "Your verification code",
"type": "otp",
"status": "sent",
"sent_at": "2025-11-09T10:46:30+00:00",
"error_message": null,
"retry_count": 0,
"created_at": "2025-11-09T10:46:25+00:00"
}
Status values: queued → sent or failed
/api/v1/emails
scope: email:readList email logs for this project with optional filters
Query Parameters
- status optional
- Filter by
queued,sent, orfailed - type optional
- Filter by email type:
otp,ticket, orgeneric - recipient optional
- Filter by recipient email address
- per_page optional
- Results per page (default: 50, max: 100)
curl "https://email.getorly.com/api/v1/emails?status=failed&per_page=20" \
-H "Authorization: Bearer $PROJECT_API_KEY"
/api/v1/health
scope: email:readProject-scoped health check with email delivery statistics
curl https://email.getorly.com/api/v1/health \
-H "Authorization: Bearer $PROJECT_API_KEY"
{
"status": "ok",
"timestamp": "2025-11-09T10:46:25+00:00",
"project": "my-project",
"database": "connected",
"stats": {
"queued": 2,
"sent": 1048,
"failed": 3,
"total": 1053
}
}