Documentation

The ridu API is compatible with Resend: same paths, fields, errors and webhook signatures.

In three steps

  1. Create an account and copy the API key.
  2. Add a domain and set the two DNS records.
  3. 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.ai
  • bounced@test.ridu.ai
  • complained@test.ridu.ai
  • delayed@test.ridu.ai
  • suppressed@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    # Python

Errors

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.

MethodPathKey
POST/emailsSending only
POST/emails/batchSending only
GET/emailsFull access
GET/emails/{id}Full access
PATCH/emails/{id}Full access
POST/emails/{id}/cancelFull access
POST/domainsFull access
GET/domainsFull access
GET/domains/{id}Full access
PATCH/domains/{id}Full access
DELETE/domains/{id}Full access
POST/domains/{id}/verifyFull access
GET/suppressionsFull access
POST/suppressionsFull access
DELETE/suppressions/{email}Full access
POST/templatesFull access
GET/templatesFull access
GET/templates/{id}Full access
PATCH/templates/{id}Full access
DELETE/templates/{id}Full access
POST/templates/{id}/publishFull access
POST/contactsFull access
GET/contactsFull access
GET/contacts/{id}Full access
PATCH/contacts/{id}Full access
DELETE/contacts/{id}Full access
GET/contacts/{id}/consentsFull access
GET/contacts/{id}/segmentsFull access
POST/contacts/{id}/segments/{segment}Full access
DELETE/contacts/{id}/segments/{segment}Full access
GET/contacts/{id}/topicsFull access
PATCH/contacts/{id}/topicsFull access
POST/segmentsFull access
GET/segmentsFull access
GET/segments/{id}Full access
PATCH/segments/{id}Full access
DELETE/segments/{id}Full access
GET/segments/{id}/contactsFull access
POST/audiencesFull access
GET/audiencesFull access
GET/audiences/{id}Full access
DELETE/audiences/{id}Full access
POST/audiences/{id}/contactsFull access
GET/audiences/{id}/contactsFull access
GET/audiences/{aud}/contacts/{id}Full access
PATCH/audiences/{aud}/contacts/{id}Full access
DELETE/audiences/{aud}/contacts/{id}Full access
POST/topicsFull access
GET/topicsFull access
GET/topics/{id}Full access
PATCH/topics/{id}Full access
DELETE/topics/{id}Full access
POST/broadcastsFull access
GET/broadcastsFull access
GET/broadcasts/{id}Full access
PATCH/broadcasts/{id}Full access
DELETE/broadcasts/{id}Full access
POST/broadcasts/{id}/sendFull access
POST/broadcasts/{id}/cancelFull access
POST/webhooksFull access
GET/webhooksFull access
GET/webhooks/{id}Full access
PATCH/webhooks/{id}Full access
DELETE/webhooks/{id}Full access
POST/webhooks/{id}/testFull access
POST/api-keysFull access
GET/api-keysFull access
DELETE/api-keys/{id}Full access