Sendr365 Developer Documentation
Welcome
Sendr365 Developer Docs
Sendr365 is a multi-channel email infrastructure platform — transactional API, SMTP relay, marketing campaigns, contact management, and webhook events. This guide walks you from a first API call to production sending.
Every endpoint accepts JSON over HTTPS. Authentication is via a tenant API key (X-API-Key header) — generated in your dashboard. All paths are versioned under https://sendr365.com/api/v1.
What's in here
Quick start
Send your first email in 5 minutes
Three steps: sign up, generate an API key, send a test email. No credit card required — every new account gets 100 free emails for 30 days — 50 transactional + 50 marketing.
Create an account
Sign up at sendr365.com/signup — verify your email, you're in. Your account starts with 100 credits each in transactional and marketing pools.
Generate an API key
Open Settings → API keys, click Generate, and copy the key. It's shown once; store it in your secrets manager.
Send a test email
Use your key in the X-API-Key header. The example below sends a plain HTML email to one recipient.
curl -X POST 'https://sendr365.com/api/v1/email/send' \
-H 'X-API-Key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"to": "[email protected]",
"subject": "Hello from Sendr365",
"html": "<h1>It works!</h1><p>This is your first email.</p>"
}'Response
{
"sent": 1,
"skipped": 0,
"failed": 0,
"results": [
{ "to": "[email protected]", "messageId": "msg_2K9xQz3vN8mLp4yF", "status": "sent" }
],
"creditsRemaining": 99
}Auth
Authentication
Tenant endpoints are authenticated with an API key in the X-API-Keyheader. Each key is scoped to one account — keep keys server-side, never embed in browser code.
API key scopes
Every key has a set of scopes. Pick the minimum scope your integration needs — narrower scopes reduce blast radius if a key leaks.
| Scope | What it allows |
|---|---|
transactional:send | Send via /email/send (debits TXN credits) |
campaigns:read | Read campaign metadata + reports |
campaigns:write | Create/schedule/cancel campaigns |
contacts:read | Read contacts + lists |
contacts:write | Create/update/delete contacts + lists |
events:read | Read send/open/click events for analytics |
Header format
X-API-Key: sk_live_AbCdEf123456...Rate limits
Default rate limit: 10 requests/sec per key, with burst tolerance. Hit a limit → 429 Too Many Requests with a Retry-Afterheader. Upgrade plans raise the ceiling.
Transactional
Send a single email
The POST /email/send endpoint sends one transactional email synchronously. Use it for OTP codes, password resets, receipts, alerts — anything triggered by a single user action.
When to use this
- One-off, system-generated emails to a single recipient
- Time-sensitive sends (login codes, order confirmations)
- Emails triggered programmatically from your backend
Request body
| Field | Type | Required | Description |
|---|---|---|---|
to | string | yes | Recipient email address |
subject | string | yes | Email subject (max 300 chars) |
html | string | yes | HTML body (sanitized server-side) |
text | string | no | Plaintext fallback |
fromEmail | string | no | Override the default sender. Must be on a verified domain. |
fromName | string | no | Override the sender display name |
replyTo | string | no | Reply-To address |
variables | object | no | Personalization values (e.g. {"name":"Alice"}) — referenced as {{name}} in body |
headers | object | no | Custom headers (e.g. X-Campaign-Id) |
Code examples
curl -X POST 'https://sendr365.com/api/v1/email/send' \
-H 'X-API-Key: $SENDR365_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"to": "[email protected]",
"subject": "Your order #1234 is confirmed",
"html": "<h1>Thanks Alice!</h1><p>Your order ships tomorrow.</p>",
"fromEmail": "[email protected]",
"fromName": "Acme Store"
}'Response
{
"sent": 1,
"skipped": 0,
"failed": 0,
"results": [
{ "to": "[email protected]", "messageId": "msg_2K9xQz3vN8mLp4yF", "status": "sent" }
],
"creditsRemaining": 99
}Batch sending
To send many transactional emails in one request, post { "messages": [...] } with up to 500 message objects, each with the same fields shown above. Credits are debited atomically for the whole batch up-front.
Marketing
Send a campaign to a list
Campaigns send the same email to many recipients with throttling, suppression filtering, and per-recipient tracking. Use these for newsletters, broadcasts, and re-engagement flows.
Lifecycle
- Create a draft (subject, sender, body)
- Schedule it against one or more lists
- Worker expands the audience and dispatches at your configured rate
- Each recipient deducts 1 marketing credit
Create + schedule
# 1. Create draft
curl -X POST 'https://sendr365.com/api/v1/campaigns' \
-H 'X-API-Key: $SENDR365_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"name": "April newsletter",
"subject": "What we shipped this month",
"fromEmail": "[email protected]",
"fromName": "Acme Team",
"bodyHtml": "<h1>Hello!</h1><p>...</p>"
}'
# 2. Schedule it (returns campaignId)
curl -X POST 'https://sendr365.com/api/v1/campaigns/CAMPAIGN_ID/schedule' \
-H 'X-API-Key: $SENDR365_API_KEY' \
-H 'Content-Type: application/json' \
-d '{ "listIds": ["list_abc"], "scheduledFor": "2026-05-01T10:00:00Z" }'Audience
Manage contacts and lists
Contacts are people you can send to. Lists are groups of contacts. Suppressions are global — a contact who unsubscribes won't receive any future marketing send from your account.
Add contacts to a list
curl -X POST 'https://sendr365.com/api/v1/contacts' \
-H 'X-API-Key: $SENDR365_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"email": "[email protected]",
"firstName": "Alice",
"lastName": "Smith",
"tags": ["vip"],
"listIds": ["list_abc"]
}'Setup
Verify a sending domain
Add a domain you own, publish 3 DNS records (SPF, DKIM, DMARC), and Sendr365 auto-verifies within ~5 minutes. The default [email protected] identity is provisioned the moment verification completes.
Add domain
curl -X POST 'https://sendr365.com/api/v1/sending-domains' \
-H 'X-API-Key: $SENDR365_API_KEY' \
-H 'Content-Type: application/json' \
-d '{ "domain": "yourdomain.com", "selector": "mf1" }'Response
{
"id": "dom_2KP4...",
"domain": "yourdomain.com",
"spfStatus": "PENDING",
"dkimStatus": "PENDING",
"dmarcStatus": "PENDING",
"dnsRecords": {
"spf": { "host": "yourdomain.com", "type": "TXT", "value": "v=spf1 include:..." },
"dkim": { "host": "mf1._domainkey...", "type": "TXT", "value": "..." },
"dmarc": { "host": "_dmarc.yourdomain.com", "type": "TXT", "value": "v=DMARC1; p=none; ..." }
}
}Publish the records at your registrar, then poll GET /sending-domains/:id— or just wait, the platform auto-checks every 3 minutes. Status flips through PENDING → CHECKING_DNS → VERIFYING_PROVIDER → ACTIVE.
Integrations
Connect via SMTP
Already using a tool that speaks SMTP? Point it at our relay and you're done — no API integration needed. Works with WordPress, Shopify, n8n, Zapier, and any custom backend.
| Setting | Value |
|---|---|
Host | smtp.sendr365.com |
Port | 587 (STARTTLS), 465 (Implicit TLS), or 2525 (alt-submission) |
Username | Generated in /app/settings/smtp |
Password | Generated alongside username, shown once |
Auth method | PLAIN or LOGIN |
Encryption | STARTTLS required (server rejects AUTH on plaintext) |
Max message | 25 MB |
Generate credentials
Open SMTP settings in your dashboard, click Generate credentials, and copy both the username + password. Each credential is independent — you can have one per integration.
Node.js (Nodemailer)
import nodemailer from 'nodemailer';
const transporter = nodemailer.createTransport({
host: 'smtp.sendr365.com',
port: 587,
secure: false, // STARTTLS upgrades the connection
requireTLS: true,
auth: {
user: process.env.SENDR_SMTP_USER,
pass: process.env.SENDR_SMTP_PASS,
},
});
await transporter.sendMail({
from: '"Acme" <[email protected]>',
to: '[email protected]',
subject: 'Order confirmed',
html: '<p>Thanks!</p>',
});Events
Webhooks
Outbound webhooks are on the roadmap — Sendr365 will POST real-time events (opens, clicks, bounces, complaints, unsubscribes) to a URL you control. Until that's live, use the polling endpoints below or the dashboard Metrics page.
Today: see events in the dashboard
Events are tracked internally and surfaced in the Metrics dashboard with per-campaign breakdowns. A read API + push webhooks are next on the roadmap.
Event types tracked
| Type | Fires when |
|---|---|
SENT | Email accepted by upstream MTA |
DELIVERED | Recipient mailbox confirmed delivery |
OPEN | Recipient opened (tracking pixel loaded) |
CLICK | Recipient clicked a tracked link |
BOUNCE | Mailbox rejected the email |
COMPLAINT | Recipient marked as spam |
UNSUBSCRIBE | Recipient hit the one-click unsubscribe link |
DEFERRED | Temporary delivery delay (recipient mailbox queue full, etc.) |
What's coming
- POST your endpoint a JSON payload per event with HMAC-SHA-256 signature
- Configurable retry policy (exponential backoff, 24-hour give-up)
- Dashboard UI to register endpoints and inspect delivery history
- Per-event-type filtering so you only get the events you care about
Want webhooks now? Open a ticket at /app/support — we prioritize based on user demand.
Reference
Errors & rate limits
Errors return JSON with a stable error code your client can branch on. Always check status codes — 2xx = success, 4xx = your fault, 5xx = ours.
Error response format
{
"statusCode": 400,
"error": "INSUFFICIENT_CREDITS",
"message": "Need 50 marketing credits; have 12 free + 0 paid",
"have": { "free": 12, "paid": 0 },
"category": "PROMO"
}Common error codes
Codes in bold are stable — your client can safely branch on them. Others come from the framework's default HTTP error mapping.
| HTTP | Code | Meaning |
|---|---|---|
400 | INSUFFICIENT_CREDITS | Account ran out of credits in the relevant pool |
400 | DOMAIN_REQUIRED | Bulk send blocked — verified domain not yet set up |
400 | SENDING_BLOCKED | Free trial expired or account suspended/canceled |
401 | Unauthorized | Missing or invalid X-API-Key header |
403 | Forbidden | API key lacks the required scope |
404 | Not Found | Resource doesn't exist on this account |
409 | DOMAIN_LIMIT_REACHED | Single-domain rule — delete current domain to add another |
422 | VALIDATION_ERROR | Request body failed schema validation |
429 | Too Many Requests | Rate limit hit — see x-ratelimit-* response headers + Retry-After |
503 | Service Unavailable | Upstream provider temporarily unavailable |
Rate limit windows
| Window | Limit | Header |
|---|---|---|
1 second | 10 requests | x-ratelimit-limit-short |
1 minute | 60 requests | x-ratelimit-limit-medium |
1 hour | 500 requests | x-ratelimit-limit-long |
X-Request-Id header. Include this in any support ticket — it lets us pull the full server-side log instantly.