Quickstart

OctusRelay provides two ways to send email: SMTP relay and REST API. Both require an API key for authentication and a verified sending domain.

The fastest way to get started is with the REST API. Here is a complete example that sends an email using cURL:

curl -X POST https://api.octusrelay.com/v1/send \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "hello@yourdomain.com",
    "to": "recipient@example.com",
    "subject": "Your first email via OctusRelay",
    "html": "<h1>It works!</h1><p>Sent via OctusRelay API.</p>"
  }'
import requests

response = requests.post(
    "https://api.octusrelay.com/v1/send",
    headers={
        "Authorization": "Bearer YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={
        "from": "hello@yourdomain.com",
        "to": "recipient@example.com",
        "subject": "Your first email via OctusRelay",
        "html": "<h1>It works!</h1>",
    },
)

print(response.json())
# {"id": "msg_a1b2c3d4e5f6", "status": "queued"}
const response = await fetch("https://api.octusrelay.com/v1/send", {
  method: "POST",
  headers: {
    "Authorization": "Bearer YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    from: "hello@yourdomain.com",
    to: "recipient@example.com",
    subject: "Your first email via OctusRelay",
    html: "<h1>It works!</h1>",
  }),
});

const data = await response.json();
console.log(data);
// { id: "msg_a1b2c3d4e5f6", status: "queued" }
<?php

$ch = curl_init("https://api.octusrelay.com/v1/send");

curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => [
        "Authorization: Bearer YOUR_API_KEY",
        "Content-Type: application/json",
    ],
    CURLOPT_POSTFIELDS     => json_encode([
        "from"    => "hello@yourdomain.com",
        "to"      => "recipient@example.com",
        "subject" => "Your first email via OctusRelay",
        "html"    => "<h1>It works!</h1>",
    ]),
]);

$response = curl_exec($ch);
curl_close($ch);

echo $response;
// {"id": "msg_a1b2c3d4e5f6", "status": "queued"}

Authentication

All API requests require a valid API key passed in the Authorization header using the Bearer scheme. API keys are generated in your dashboard and can be scoped to specific domains.

Authorization: Bearer or_live_a1b2c3d4e5f6g7h8i9j0

API keys use the prefix or_live_ for production keys and or_test_ for test keys. Test keys route email to a sandbox environment and do not count toward your monthly quota.

Base URL

All REST API requests should be made to the following base URL:

https://api.octusrelay.com/v1

All responses are returned as JSON with appropriate HTTP status codes. The API supports both application/json and multipart/form-data content types.

SMTP Setup

To send email via SMTP, use the following connection settings in your application or email client:

Host:     smtp.octusrelay.com
Port:     587 (STARTTLS) or 465 (Implicit TLS)
Username: your-api-key
Password: your-api-key
Auth:     PLAIN or LOGIN
TLS:      Required
# .env
MAIL_MAILER=smtp
MAIL_HOST=smtp.octusrelay.com
MAIL_PORT=587
MAIL_USERNAME=your-api-key
MAIL_PASSWORD=your-api-key
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=hello@yourdomain.com
MAIL_FROM_NAME="Your App"
# settings.py
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "smtp.octusrelay.com"
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = "your-api-key"
EMAIL_HOST_PASSWORD = "your-api-key"
DEFAULT_FROM_EMAIL = "hello@yourdomain.com"
// wp-config.php (with WP Mail SMTP plugin)
define('WPMS_ON', true);
define('WPMS_SMTP_HOST', 'smtp.octusrelay.com');
define('WPMS_SMTP_PORT', 587);
define('WPMS_SSL', 'tls');
define('WPMS_SMTP_AUTH', true);
define('WPMS_SMTP_USER', 'your-api-key');
define('WPMS_SMTP_PASS', 'your-api-key');
define('WPMS_MAIL_FROM', 'hello@yourdomain.com');
define('WPMS_MAIL_FROM_NAME', 'Your Site');

API Reference

The following endpoints are available in the OctusRelay REST API. All endpoints require authentication via the Authorization header.

Email

POST
/v1/send
Send a single email or batch of up to 1,000 recipients
GET
/v1/emails/{id}
Retrieve the status and metadata of a sent email
GET
/v1/emails
List sent emails with filtering by date, domain, and status

Domains

POST
/v1/domains
Add a new sending domain for verification
GET
/v1/domains
List all domains and their verification status
GET
/v1/domains/{domain}
Get DNS records and verification details for a domain
POST
/v1/domains/{domain}/verify
Trigger DNS verification check for a domain
DELETE
/v1/domains/{domain}
Remove a sending domain and its DKIM keys

Analytics

GET
/v1/analytics/summary
Aggregate delivery statistics for a date range
GET
/v1/analytics/bounces
List bounce events with categorisation and detail

API Keys

GET
/v1/keys
List all API keys for your account
POST
/v1/keys
Create a new API key with optional domain scoping
DELETE
/v1/keys/{id}
Revoke an API key immediately

Sending Email

The POST /v1/send endpoint accepts a JSON payload with the following fields:

POST /v1/send

{
  "from": "notifications@yourdomain.com",
  "from_name": "Your App",
  "to": "recipient@example.com",
  "subject": "Order Confirmation #12345",
  "html": "<h1>Order Confirmed</h1><p>Thank you for your order.</p>",
  "text": "Order Confirmed\n\nThank you for your order.",
  "reply_to": "support@yourdomain.com",
  "headers": {
    "X-Custom-Tag": "order-confirmation"
  },
  "tags": ["transactional", "orders"]
}
POST /v1/send

{
  "from": "newsletter@yourdomain.com",
  "from_name": "Weekly Digest",
  "to": [
    "alice@example.com",
    "bob@example.com",
    "charlie@example.com"
  ],
  "subject": "Your weekly digest",
  "html": "<h1>This week's highlights</h1>",
  "tags": ["marketing", "newsletter"]
}
// 200 OK - Single recipient
{
  "id": "msg_a1b2c3d4e5f6",
  "status": "queued",
  "created_at": "2026-02-27T10:30:00Z"
}

// 200 OK - Batch recipients
{
  "batch_id": "batch_x1y2z3",
  "total": 3,
  "queued": 3,
  "failed": 0
}

Domain Verification

Before sending email from a domain, you must verify ownership by adding DNS records. OctusRelay requires three records: SPF, DKIM, and an optional but recommended DMARC policy.

Required DNS Records

Type:  TXT
Host:  @ (or yourdomain.com)
Value: v=spf1 include:spf.octusrelay.com ~all
TTL:   3600

# If you have an existing SPF record, add the include:
# v=spf1 include:_spf.google.com include:spf.octusrelay.com ~all
Type:  TXT
Host:  octusrelay._domainkey
Value: v=DKIM1; k=rsa; p=MIGfMA0GCSqGSI... (provided in dashboard)
TTL:   3600
Type:  TXT
Host:  _dmarc
Value: v=DMARC1; p=quarantine; rua=mailto:dmarc@yourdomain.com; pct=100
TTL:   3600

# Start with p=none if you are testing, then move to quarantine or reject

After adding the DNS records, call the verification endpoint or click "Verify" in your dashboard. DNS propagation typically takes between 5 minutes and 24 hours depending on your registrar.

DKIM Setup

OctusRelay automatically generates a 2048-bit DKIM key pair when you add a domain. The public key is provided as a DNS TXT record value in your domain settings. Once the record is published and verified, all outbound messages from that domain are signed automatically.

No code changes are needed. DKIM signing happens transparently for both SMTP and API-sent messages.

Webhooks

Webhooks deliver real-time event notifications to your server via HTTP POST. Configure your endpoint URL in the dashboard and select which events you want to receive.

Available Events

  • email.delivered -- message accepted by the recipient server
  • email.bounced -- message permanently rejected (hard bounce)
  • email.deferred -- temporary delivery failure, will retry
  • email.opened -- recipient opened the message (requires tracking pixel)
  • email.clicked -- recipient clicked a tracked link
  • email.complained -- recipient marked the message as spam
  • email.unsubscribed -- recipient used the list-unsubscribe header

Webhook Payload

POST https://your-server.com/webhooks/octusrelay

Headers:
  Content-Type: application/json
  X-OctusRelay-Signature: sha256=a1b2c3d4...
  X-OctusRelay-Timestamp: 1709042400

Body:
{
  "event": "email.delivered",
  "timestamp": "2026-02-27T10:30:00Z",
  "data": {
    "id": "msg_a1b2c3d4e5f6",
    "from": "hello@yourdomain.com",
    "to": "recipient@example.com",
    "subject": "Order Confirmation",
    "mx_host": "mx.example.com",
    "smtp_response": "250 2.0.0 OK"
  }
}
# Verify the webhook signature in Python
import hmac
import hashlib

def verify_signature(payload, signature, secret):
    expected = hmac.new(
        secret.encode("utf-8"),
        payload.encode("utf-8"),
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(
        f"sha256={expected}",
        signature
    )

SDKs

Official SDKs are available for popular languages. Each SDK wraps the REST API with idiomatic methods and handles authentication, retries, and error parsing.

# Install
pip install octusrelay

# Usage
from octusrelay import OctusRelay

client = OctusRelay(api_key="or_live_your_api_key")

response = client.send(
    from_email="hello@yourdomain.com",
    to="recipient@example.com",
    subject="Hello from OctusRelay",
    html="<p>Sent via the Python SDK.</p>",
)

print(response.id)      # msg_a1b2c3d4e5f6
print(response.status)  # queued
// Install
// npm install @octusrelay/sdk

import { OctusRelay } from "@octusrelay/sdk";

const client = new OctusRelay({ apiKey: "or_live_your_api_key" });

const response = await client.send({
  from: "hello@yourdomain.com",
  to: "recipient@example.com",
  subject: "Hello from OctusRelay",
  html: "<p>Sent via the Node.js SDK.</p>",
});

console.log(response.id);     // msg_a1b2c3d4e5f6
console.log(response.status); // queued
// Install
// composer require octusrelay/sdk

use OctusRelay\Client;

$client = new Client("or_live_your_api_key");

$response = $client->send([
    "from"    => "hello@yourdomain.com",
    "to"      => "recipient@example.com",
    "subject" => "Hello from OctusRelay",
    "html"    => "<p>Sent via the PHP SDK.</p>",
]);

echo $response->id;     // msg_a1b2c3d4e5f6
echo $response->status; // queued

Rate Limits

API requests are rate-limited per API key. The current limits are returned in the response headers:

X-RateLimit-Limit: 100        # Max requests per window
X-RateLimit-Remaining: 97    # Remaining requests
X-RateLimit-Reset: 1709042460 # Window reset (Unix timestamp)

If you exceed the limit, the API returns 429 Too Many Requests with a Retry-After header indicating when you can retry. The SDKs handle retries automatically with exponential backoff.

Error Handling

The API returns standard HTTP status codes and a JSON error body with a machine-readable code and a human-readable message.

// 422 Unprocessable Entity
{
  "error": {
    "code": "validation_error",
    "message": "The 'to' field must be a valid email address.",
    "field": "to"
  }
}
400 bad_request        # Malformed JSON or missing fields
401 unauthorized        # Invalid or missing API key
403 forbidden           # Key lacks permission for this domain
404 not_found           # Resource does not exist
422 validation_error    # Invalid field value
429 rate_limited        # Too many requests
500 internal_error      # Server error (retry safe)