Documentation
The ridu API is compatible with Resend: same paths, fields, errors and webhook signatures.
In three steps
- Create an account and copy the API key.
- Add a domain and set the two DNS records.
- Send with the Resend SDK or curl.
Authentication
Every call carries an API key as bearer token. Live keys (rd_live_) deliver, test keys (rd_test_) run everything but never deliver. Limit: 10 requests per second per organization.
curl https://api.ridu.ai/domains \
-H "Authorization: Bearer rd_live_…"Sending email
The idempotency key makes retries safe: the same request within 24 hours creates no second email.
curl -X POST https://api.ridu.ai/emails \
-H "Authorization: Bearer rd_live_…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-4711" \
-d '{
"from": "Acme <hallo@mail.acme.de>",
"to": ["kunde@example.com"],
"subject": "Deine Bestellung",
"html": "<p>Danke!</p>",
"tags": [{"name": "kind", "value": "order"}],
"scheduled_at": "in 1 hour"
}'More fields: cc, bcc, reply_to, text (otherwise derived from html), headers, attachments (content as base64 or path as URL, up to 40 MB), template, topic_id. Up to 100 emails at once via POST /emails/batch.
SMTP
For apps without API integration. Bcc recipients come from the SMTP envelope, the message stays as your app builds it.
Host: smtp.ridu.ai
Port: 465 (TLS) oder 587 (STARTTLS)
User: ridu
Passwort: rd_live_…
Optionale Header: X-Ridu-Tags: kind=order, Ridu-Idempotency-Key: …Webhooks
Webhooks are signed like Svix. Verify them with the svix library and the signing secret from the dashboard. Failed deliveries are retried 7 times over 32 hours.
import { Webhook } from "svix";
const event = new Webhook(process.env.RIDU_WEBHOOK_SECRET)
.verify(rawBody, {
"svix-id": req.headers["svix-id"],
"svix-timestamp": req.headers["svix-timestamp"],
"svix-signature": req.headers["svix-signature"],
});Event types: email.sent email.scheduled email.delivered email.delivery_delayed email.bounced email.complained email.opened email.clicked email.failed email.suppressed email.canceled domain.created domain.updated domain.deleted contact.created contact.updated contact.deleted contact.topics.updated suppression.added suppression.removed
Testing
These addresses produce the matching event without delivering anything or using quota:
delivered@test.ridu.aibounced@test.ridu.aicomplained@test.ridu.aidelayed@test.ridu.aisuppressed@test.ridu.ai
Moving from Resend
The official Resend SDKs work unchanged. Set the key and the base URL; verify your domains once more because the DKIM key now lives at ridu.
RESEND_API_KEY=rd_live_…
RESEND_BASE_URL=https://api.ridu.ai # Node, PHP
RESEND_API_URL=https://api.ridu.ai # PythonErrors
Errors have the same shape and names as in Resend, for example validation_error, rate_limit_exceeded or daily_quota_exceeded.
{
"statusCode": 422,
"name": "missing_required_field",
"message": "Missing `subject` field."
}All endpoints
This list comes straight from the router, so it is always complete.
| Method | Path | Key |
|---|---|---|
| POST | /emails | Sending only |
| POST | /emails/batch | Sending only |
| GET | /emails | Full access |
| GET | /emails/{id} | Full access |
| PATCH | /emails/{id} | Full access |
| POST | /emails/{id}/cancel | Full access |
| POST | /domains | Full access |
| GET | /domains | Full access |
| GET | /domains/{id} | Full access |
| PATCH | /domains/{id} | Full access |
| DELETE | /domains/{id} | Full access |
| POST | /domains/{id}/verify | Full access |
| GET | /suppressions | Full access |
| POST | /suppressions | Full access |
| DELETE | /suppressions/{email} | Full access |
| POST | /templates | Full access |
| GET | /templates | Full access |
| GET | /templates/{id} | Full access |
| PATCH | /templates/{id} | Full access |
| DELETE | /templates/{id} | Full access |
| POST | /templates/{id}/publish | Full access |
| POST | /contacts | Full access |
| GET | /contacts | Full access |
| GET | /contacts/{id} | Full access |
| PATCH | /contacts/{id} | Full access |
| DELETE | /contacts/{id} | Full access |
| GET | /contacts/{id}/consents | Full access |
| GET | /contacts/{id}/segments | Full access |
| POST | /contacts/{id}/segments/{segment} | Full access |
| DELETE | /contacts/{id}/segments/{segment} | Full access |
| GET | /contacts/{id}/topics | Full access |
| PATCH | /contacts/{id}/topics | Full access |
| POST | /segments | Full access |
| GET | /segments | Full access |
| GET | /segments/{id} | Full access |
| PATCH | /segments/{id} | Full access |
| DELETE | /segments/{id} | Full access |
| GET | /segments/{id}/contacts | Full access |
| POST | /audiences | Full access |
| GET | /audiences | Full access |
| GET | /audiences/{id} | Full access |
| DELETE | /audiences/{id} | Full access |
| POST | /audiences/{id}/contacts | Full access |
| GET | /audiences/{id}/contacts | Full access |
| GET | /audiences/{aud}/contacts/{id} | Full access |
| PATCH | /audiences/{aud}/contacts/{id} | Full access |
| DELETE | /audiences/{aud}/contacts/{id} | Full access |
| POST | /topics | Full access |
| GET | /topics | Full access |
| GET | /topics/{id} | Full access |
| PATCH | /topics/{id} | Full access |
| DELETE | /topics/{id} | Full access |
| POST | /broadcasts | Full access |
| GET | /broadcasts | Full access |
| GET | /broadcasts/{id} | Full access |
| PATCH | /broadcasts/{id} | Full access |
| DELETE | /broadcasts/{id} | Full access |
| POST | /broadcasts/{id}/send | Full access |
| POST | /broadcasts/{id}/cancel | Full access |
| POST | /webhooks | Full access |
| GET | /webhooks | Full access |
| GET | /webhooks/{id} | Full access |
| PATCH | /webhooks/{id} | Full access |
| DELETE | /webhooks/{id} | Full access |
| POST | /webhooks/{id}/test | Full access |
| POST | /api-keys | Full access |
| GET | /api-keys | Full access |
| DELETE | /api-keys/{id} | Full access |