Skip to main content
Resend Docs

Search documentation

Type to search this documentation.

On this pageOverview

Idempotency Keys

Include an idempotency key in any email requests to ensure that the same email request is processed only once, even if it's sent multiple times.

When you send an email with an idempotency key, Resend checks whether an email with the same idempotency key has already been sent in the last 24 hours. This is an optional feature that simplifies managing retries on your side.

This makes it safe to retry requests that send an email. You don't have to worry about checking if the original request was sent. Instead, you can make the same request and our API will give the same response, without actually sending the email again.

Idempotency keys can be up to 256 characters and must be unique per API request.

We recommend that you use a UUID or other string that uniquely identifies that specific email.

Send the key in the Idempotency-Key HTTP header in your API requests. Our SDKs also provide a convenient way to set this header. If you're using SMTP, you can set the Resend-Idempotency-Key email header instead.

Idempotency keys are kept in the system for 24 hours. This gives you an ample window to retry any failed processes on your end without having to keep track of the sent status.

Node.js
import { Resend } from 'resend';const resend = new Resend('re_xxxxxxxxx');await resend.emails.send(  {    from: 'Acme <onboarding@resend.dev>',    to: ['delivered@resend.dev'],    subject: 'hello world',    html: '<p>it works!</p>',  },  {    idempotencyKey: 'welcome-user/123456789',  },);
PHP
$resend = Resend::client('re_xxxxxxxxx');$resend->emails->send([  'from' => 'Acme <onboarding@resend.dev>',  'to' => ['delivered@resend.dev'],  'subject' => 'hello world',  'html' => '<p>it works!</p>',], [  'idempotency_key' => 'welcome-user/123456789',]);
Python
import resendresend.api_key = "re_xxxxxxxxx"params: resend.Emails.SendParams = {  "from": "Acme <onboarding@resend.dev>",  "to": ["delivered@resend.dev"],  "subject": "hello world",  "html": "<p>it works!</p>"}options: resend.Emails.SendOptions = {  "idempotency_key": "welcome-user/123456789",}resend.Emails.send(params, options)
Ruby
require "resend"Resend.api_key = "re_xxxxxxxxx"params = {  "from": "Acme <onboarding@resend.dev>",  "to": ["delivered@resend.dev"],  "subject": "hello world",  "html": "<p>it works!</p>"}Resend::Emails.send(  params,  options: { idempotency_key: "welcome-user/123456789" })
Go
package mainimport (	"context"	"github.com/resend/resend-go/v4")func main() {	ctx := context.TODO()	client := resend.NewClient("re_xxxxxxxxx")	params := &resend.SendEmailRequest{		From:    "onboarding@resend.dev",		To:      []string{"delivered@resend.dev"},		Subject: "hello world",		Html:    "<p>it works!</p>",	}	options := &resend.SendEmailOptions{		IdempotencyKey: "welcome-user/123456789",	}	_, err := client.Emails.SendWithOptions(ctx, params, options)	if err != nil {		panic(err)	}}
Rust
use resend_rs::types::CreateEmailBaseOptions;use resend_rs::{Resend, Result};#[tokio::main]async fn main() -> Result<()> {  let resend = Resend::new("re_xxxxxxxxx");  let from = "Acme <onboarding@resend.dev>";  let to = ["delivered@resend.dev"];  let subject = "Hello World";  let email = CreateEmailBaseOptions::new(from, to, subject)    .with_html("<p>it works!</p>")    .with_idempotency_key("welcome-user/123456789");  let _email = resend.emails.send(email).await?;  Ok(())}
Java
import com.resend.*;import com.resend.core.net.RequestOptions;import com.resend.services.emails.model.CreateEmailOptions;import com.resend.services.emails.model.CreateEmailResponse;Resend resend = new Resend("re_xxxxxxxxx");CreateEmailOptions params = CreateEmailOptions.builder()  .from("Acme <onboarding@resend.dev>")  .to("delivered@resend.dev")  .subject("hello world")  .html("<p>it works!</p>")  .build();RequestOptions options = RequestOptions.builder()  .setIdempotencyKey("welcome-user/123456789").build();CreateEmailResponse data = resend.emails().send(params, options);
C#
using Resend;IResend resend = ResendClient.Create( "re_xxxxxxxxx" ); // Or from DIvar key = IdempotencyKey.New<int>( "welcome-user", 123456789 );var resp = await resend.EmailSendAsync(key, new EmailMessage(){    From = "Acme <onboarding@resend.dev>",    To = "delivered@resend.dev",    Subject = "hello world",    HtmlBody = "<p>it works!</p>",} );Console.WriteLine( "Email Id={0}", resp.Content );
cURL
curl -X POST 'https://api.resend.com/emails' \     -H 'Authorization: Bearer re_xxxxxxxxx' \     -H 'Content-Type: application/json' \     -H 'Idempotency-Key: welcome-user/123456789' \     -d $'{  "from": "Acme <onboarding@resend.dev>",  "to": ["delivered@resend.dev"],  "subject": "hello world",  "html": "<p>it works!</p>"}'
CLI
resend emails send \  --from "Acme <onboarding@resend.dev>" \  --to delivered@resend.dev \  --subject "hello world" \  --html "<p>it works!</p>" \  --idempotency-key "welcome-user/123456789"
SMTP
From: Acme <onboarding@resend.dev>To: delivered@resend.devSubject: hello worldResend-Idempotency-Key: welcome-user/123456789<p>it works!</p>
Node.js
import { Resend } from 'resend';const resend = new Resend('re_xxxxxxxxx');await resend.batch.send(  [    {      from: 'Acme <onboarding@resend.dev>',      to: ['foo@gmail.com'],      subject: 'hello world',      html: '<h1>it works!</h1>',    },    {      from: 'Acme <onboarding@resend.dev>',      to: ['bar@outlook.com'],      subject: 'world hello',      html: '<p>it works!</p>',    },  ],  {    idempotencyKey: 'team-quota/123456789',  },);
PHP
$resend = Resend::client('re_xxxxxxxxx');$resend->batch->send(  [    [      'from' => 'Acme <onboarding@resend.dev>',      'to' => ['foo@gmail.com'],      'subject' => 'hello world',      'html' => '<h1>it works!</h1>',    ],    [      'from' => 'Acme <onboarding@resend.dev>',      'to' => ['bar@outlook.com'],      'subject' => 'world hello',      'html' => '<p>it works!</p>',    ]  ],  [    'idempotency_key' => 'team-quota/123456789',  ]);
Python
import resendfrom typing import Listresend.api_key = "re_xxxxxxxxx"params: List[resend.Emails.SendParams] = [  {    "from": "Acme <onboarding@resend.dev>",    "to": ["foo@gmail.com"],    "subject": "hello world",    "html": "<h1>it works!</h1>",  },  {    "from": "Acme <onboarding@resend.dev>",    "to": ["bar@outlook.com"],    "subject": "world hello",    "html": "<p>it works!</p>",  }]options: resend.Batch.SendOptions = {  "idempotency_key": "team-quota/123456789",}resend.Batch.send(params, options)
Ruby
require "resend"Resend.api_key = 're_xxxxxxxxx'params = [  {    "from": "Acme <onboarding@resend.dev>",    "to": ["foo@gmail.com"],    "subject": "hello world",    "html": "<h1>it works!</h1>",  },  {    "from": "Acme <onboarding@resend.dev>",    "to": ["bar@outlook.com"],    "subject": "world hello",    "html": "<p>it works!</p>",  }]Resend::Batch.send(  params,  options: { idempotency_key: "team-quota/123456789" })
Go
package mainimport (	"context"	"fmt"	"github.com/resend/resend-go/v4")func main() {  ctx := context.TODO()  client := resend.NewClient("re_xxxxxxxxx")  var batchEmails = []*resend.SendEmailRequest{    {      From:    "Acme <onboarding@resend.dev>",      To:      []string{"foo@gmail.com"},      Subject: "hello world",      Html:    "<h1>it works!</h1>",    },    {      From:    "Acme <onboarding@resend.dev>",      To:      []string{"bar@outlook.com"},      Subject: "world hello",      Html:    "<p>it works!</p>",    },  }  options := &resend.BatchSendEmailOptions{    IdempotencyKey: "team-quota/123456789",  }  sent, err := client.Batch.SendWithOptions(ctx, batchEmails, options)  if err != nil {    panic(err)  }  fmt.Println(sent.Data)}
Rust
use resend_rs::{idempotent::IdempotentTrait, types::CreateEmailBaseOptions};use resend_rs::{Resend, Result};#[tokio::main]async fn main() -> Result<()> {  let resend = Resend::new("re_xxxxxxxxx");  let emails = vec![    CreateEmailBaseOptions::new(      "Acme <onboarding@resend.dev>",      vec!["foo@gmail.com"],      "hello world",    )    .with_html("<h1>it works!</h1>"),    CreateEmailBaseOptions::new(      "Acme <onboarding@resend.dev>",      vec!["bar@outlook.com"],      "world hello",    )    .with_html("<p>it works!</p>"),  ]  .with_idempotency_key("team-quota/123456789");  let _emails = resend.batch.send(emails).await?;  Ok(())}
Java
import com.resend.*;import com.resend.core.exception.ResendException;import com.resend.core.net.RequestOptions;import com.resend.services.batch.model.CreateBatchEmailsResponse;import com.resend.services.emails.model.CreateEmailOptions;import java.util.Arrays;public class Main {    public static void main(String[] args) throws ResendException {        Resend resend = new Resend("re_xxxxxxxxx");        CreateEmailOptions firstEmail = CreateEmailOptions.builder()            .from("Acme <onboarding@resend.dev>")            .to("foo@gmail.com")            .subject("hello world")            .html("<h1>it works!</h1>")            .build();        CreateEmailOptions secondEmail = CreateEmailOptions.builder()            .from("Acme <onboarding@resend.dev>")            .to("bar@outlook.com")            .subject("world hello")            .html("<p>it works!</p>")            .build();        CreateBatchEmailsResponse data = resend.batch().send(            Arrays.asList(firstEmail, secondEmail),            RequestOptions.builder().setIdempotencyKey("team-quota/123456789").build()        );    }}
.NET
using Resend;IResend resend = ResendClient.Create( "re_xxxxxxxxx" ); // Or from DIvar key = IdempotencyKey.New<int>( "team-quota", 123456789 );var mail1 = new EmailMessage(){    From = "Acme <onboarding@resend.dev>",    To = "foo@gmail.com",    Subject = "hello world",    HtmlBody = "<p>it works!</p>",};var mail2 = new EmailMessage(){    From = "Acme <onboarding@resend.dev>",    To = "bar@outlook.com",    Subject = "hello world",    HtmlBody = "<p>it works!</p>",};var resp = await resend.EmailBatchAsync(key, [ mail1, mail2 ] );Console.WriteLine( "Nr Emails={0}", resp.Content.Count );
cURL
curl -X POST 'https://api.resend.com/emails/batch' \     -H 'Authorization: Bearer re_xxxxxxxxx' \     -H 'Content-Type: application/json' \     -H 'Idempotency-Key: team-quota/123456789' \     -d $'[  {    "from": "Acme <onboarding@resend.dev>",    "to": ["foo@gmail.com"],    "subject": "hello world",    "html": "<h1>it works!</h1>"  },  {    "from": "Acme <onboarding@resend.dev>",    "to": ["bar@outlook.com"],    "subject": "world hello",    "html": "<p>it works!</p>"  }]'

After checking if an email with the same idempotency key has already been sent, Resend returns one of the following responses:

  • Successful responses will return the email ID of the sent email.
  • Error responses will return one of the following errors:
    • 400: invalid_idempotency_key - the idempotency key has to be between 1–256 characters. You can retry with a valid key or without supplying an idempotency key.
    • 409: invalid_idempotent_request - this idempotency key has already been used on a request that had a different payload. Retrying this request is useless without changing the idempotency key or payload.
    • 409: concurrent_idempotent_requests - another request with the same idempotency key is in progress. As it isn't finished yet, Resend can't return its original response, but it is safe to retry this request later if needed.
Suggest an edit

Propose a replacement for this page. The site team reviews it before applying any changes.

Export
Documentation menu