SDKs
Every Duta SDK covers the whole API and behaves the same way:
- Safe retries. Every send carries an idempotency key, made for you when you give none, so a retry can never send twice. Rate limits are retried after
Retry-After. Server errors are retried only for requests that are safe to repeat. - Duta’s errors. Each error carries Duta’s error code and the
request_idthat finds the request on the Logs screen. - Paging.
listAllon emails, logs and suppressions walks every page for you. - Webhook verification, for the signature on every webhook delivery.
Each one is tested against the same API spec and the same signed webhook delivery, so they cannot drift apart.
Install and send
Section titled “Install and send”npm install @duta/sdkimport { Duta } from '@duta/sdk';
const duta = new Duta(process.env.DUTA_API_KEY);
const { data, error } = await duta.emails.send({ from: 'Kedai <resit@kedai.my>', to: 'siti@example.com', subject: 'Resit #1042', html: '<p>Terima kasih.</p>',});
if (error) console.error(error.code, error.message, error.requestId);Node 20+, Bun, Deno and Cloudflare Workers, with no dependencies. Published with signed provenance. github.com/indra-sh/duta-js
composer require duta/duta-php$duta = new Duta\Duta(getenv('DUTA_API_KEY'));
$sent = $duta->emails->send([ 'from' => 'Kedai <resit@kedai.my>', 'to' => 'siti@example.com', 'subject' => 'Resit #1042', 'html' => '<p>Terima kasih.</p>',]);PHP 8.1+. Errors throw Duta\DutaException. Using Laravel? Set
MAIL_MAILER=duta and your Mailables send through Duta: see
Laravel.
github.com/indra-sh/duta-php
go get github.com/indra-sh/duta-goclient := duta.NewClient(os.Getenv("DUTA_API_KEY"))
sent, err := client.Emails.Send(ctx, &duta.SendEmailRequest{ From: "Kedai <resit@kedai.my>", To: []string{"siti@example.com"}, Subject: "Resit #1042", HTML: "<p>Terima kasih.</p>",})Go 1.23+, standard library only. Errors are *duta.Error.
github.com/indra-sh/duta-go
dotnet add package Duta.Netbuilder.Services.AddDuta(o => o.ApiKey = builder.Configuration["Duta:ApiKey"]);
// then, wherever DutaClient is injected:var sent = await duta.Emails.SendAsync(new SendEmailRequest{ From = "Kedai <resit@kedai.my>", To = ["siti@example.com"], Subject = "Resit #1042", Html = "<p>Terima kasih.</p>",});.NET 8+. Errors throw DutaException.
github.com/indra-sh/duta-dotnet
pip install duta-sdkfrom duta import Duta
duta = Duta() # reads DUTA_API_KEY
sent = duta.emails.send({ "from": "Kedai <resit@kedai.my>", "to": "siti@example.com", "subject": "Resit #1042", "html": "<p>Terima kasih.</p>",})Python 3.9+, with AsyncDuta for asyncio. Errors raise duta.DutaError.
Published with attestations.
github.com/indra-sh/duta-python
Coming from Resend
Section titled “Coming from Resend”Duta’s API follows Resend’s, so Resend’s official SDKs work with Duta too, once they have a Duta API key and Duta’s address. That makes moving over a configuration change: see Moving from Resend.
Set RESEND_BASE_URL=https://api.duta.indra.sh, or
new Resend(key, { baseUrl: 'https://api.duta.indra.sh' }).
Set RESEND_BASE_URL=https://api.duta.indra.sh.
Set RESEND_BASE_URL=https://api.duta.indra.sh/, or
client.BaseURL, _ = url.Parse("https://api.duta.indra.sh/").
Set o.ApiUrl = "https://api.duta.indra.sh" in AddResend(...).
pip install resendimport osimport resend
resend.api_key = os.environ["DUTA_API_KEY"]resend.api_url = "https://api.duta.indra.sh"
resend.Emails.send({ "from": "Kedai <resit@kedai.my>", "to": "siti@example.com", "subject": "Resit #1042", "html": "<p>Terima kasih.</p>",})Or set RESEND_API_URL. The Python SDK reads that name, not RESEND_BASE_URL.