Moving from Resend
Duta’s API follows Resend’s, so an application using a Resend SDK moves by changing configuration. The code stays as it is.
Point the SDK at Duta
Section titled “Point the SDK at Duta”Set the API key to a Duta key. Each SDK then takes Duta’s address in its own way:
RESEND_API_KEY=duta_xxxxxxxxxxxxxxxxRESEND_BASE_URL=https://api.duta.indra.shOr in code: new Resend(key, { baseUrl: 'https://api.duta.indra.sh' }).
RESEND_API_KEY=duta_xxxxxxxxxxxxxxxxRESEND_BASE_URL=https://api.duta.indra.shThe Python SDK reads a different variable name:
RESEND_API_KEY=duta_xxxxxxxxxxxxxxxxRESEND_API_URL=https://api.duta.indra.shOr in code: resend.api_url = "https://api.duta.indra.sh".
RESEND_API_KEY=duta_xxxxxxxxxxxxxxxxRESEND_BASE_URL=https://api.duta.indra.sh/Or on the client: client.BaseURL, _ = url.Parse("https://api.duta.indra.sh/").
builder.Services.AddResend(o =>{ o.ApiToken = Environment.GetEnvironmentVariable("RESEND_API_KEY") ?? Environment.GetEnvironmentVariable("DUTA_API_KEY")!; o.ApiUrl = "https://api.duta.indra.sh";});Before you switch
Section titled “Before you switch”-
Add your domain in Duta and publish its two records. Duta uses the
duta.label, not Resend’ssend., so your Resend records stay live while you move and nothing breaks in between. See Domains. -
Create a Duta API key. A key is send only unless you ask for
full_access. Ask for it if the application also manages domains, keys or webhooks. -
Point your webhook at Duta if you use one. Deliveries are signed the way Resend signs them (Svix headers,
whsec_secret), soresend.webhooks.verify(...)and the Svix libraries verify them unchanged. Use the secret Duta shows when you add the endpoint.
What works the same
Section titled “What works the same”emails.send, includingtags,cc,bcc,replyTo(mapped toreply_toon the wire),headersand attachments given as base64, aBufferor apathURL. A missing content type is inferred from the filename. React Email (react: <Component />) works in the Node SDK because the SDK renders HTML client-side before sending.batch.send, strict by default: one invalid email and none are sent.batchValidation: 'permissive'(or thex-batch-validation: permissiveheader) sends the valid ones and lists the rest inerrorsby index.emails.get,emails.list,domains.list,domains.get,domains.create,domains.verify,domains.remove,apiKeys.list,apiKeys.create,apiKeys.remove,webhooks.createandwebhooks.list.idempotencyKey, for 24 hours per account.- Errors carry Resend’s
name,statusCodeandmessage, so code that branches onerror.namekeeps working. Duta adds a finer-grainedcodeandrequest_id, which finds the request on the Logs screen. - Webhook event names:
email.sent,email.delivered,email.bounced,email.complained,email.delivery_delayedandemail.failed.
What is different
Section titled “What is different”- Refused plainly rather than ignored:
scheduledAt(scheduled_at),template,topicId(topic_id) and API keys limited to one domain (domain_id). Duta answers 422 naming the field, so nothing goes out at the wrong time or with a wider key than you asked for. - Not in Duta: audiences, contacts, broadcasts, segments, templates, receiving and SMTP. Duta is transactional sending over the API.
- Region: every domain sends from
ap-southeast-1. Aregiongiven when adding a domain is accepted and ignored, and the response says which region is used. - Tracking: Duta does not track opens or clicks, so
email.openedandemail.clickedare accepted on a webhook but never sent. - Send status:
POST /emailsanswers202with{ id, status }. The SDKs treat it as success like Resend’s200. Only raw HTTP code that checks for exactly200needs a change. - Attachments in a batch: inline content only.
pathis refused there. - Attachment size and types: Free sends take no attachments. Starter allows up to 1 MB of attachments per message, Pro allows 2 MB, and Custom allows 5 MB, with an overall 10 MB message ceiling. Files must be on the allowed types list (PDF, images, CSV, text, Office documents).
- Free plan: sends only from your sandbox address to recipients who confirmed, 10 a day, without attachments. Any paid plan sends from your own domain.
- Timestamps: every timestamp the API returns is an ISO 8601 string, as Resend’s are.