Skip to content
Pidgely Docs

Get started

Idempotency

Send an Idempotency-Key with POST /emails and /emails/batch to retry safely. The response is replayed for 24 hours; a different body with the same key gets 409.

View as Markdown

Networks fail. If a send times out you can't know whether Pidgely received it, and retrying blindly can deliver the same email twice. An idempotency key makes the retry safe: Pidgely stores the first response and replays it for every retry with the same key.

Node.js
import { Pidgely } from 'pidgely';

const pidgely = new Pidgely(process.env.PIDGELY_API_KEY);

const { data, error } = await pidgely.emails.send({
  from: 'Acme <orders@acme.com>',
  to: ['steve@example.com'],
  subject: 'Order confirmed',
  html: '<p>Thanks!</p>',
}, {
  idempotencyKey: 'order-1234/confirmation',
});
console.log(data ?? error);
Python
import os
import pidgely

pidgely.api_key = os.environ["PIDGELY_API_KEY"]

result = pidgely.Emails.send({
    "from": "Acme <orders@acme.com>",
    "to": ["steve@example.com"],
    "subject": "Order confirmed",
    "html": "<p>Thanks!</p>",
}, {
    "idempotency_key": "order-1234/confirmation",
})
print(result)
PHP
<?php

require __DIR__ . '/vendor/autoload.php';

$pidgely = Pidgely::client(getenv('PIDGELY_API_KEY'));

$result = $pidgely->emails->send([
    'from' => 'Acme <orders@acme.com>',
    'to' => ['steve@example.com'],
    'subject' => 'Order confirmed',
    'html' => '<p>Thanks!</p>',
], [
    'idempotency_key' => 'order-1234/confirmation',
]);
var_dump($result);
Go
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/pidgely/pidgely-go"
)

func main() {
	ctx := context.Background()
	client := pidgely.NewClient(os.Getenv("PIDGELY_API_KEY"))

	result, err := client.Emails.Send(ctx, &pidgely.SendEmailRequest{
		From:    "Acme <orders@acme.com>",
		To:      []string{"steve@example.com"},
		Subject: "Order confirmed",
		Html:    "<p>Thanks!</p>",
	}, pidgely.WithIdempotencyKey("order-1234/confirmation"))
	if err != nil {
		panic(err)
	}
	fmt.Println(result)
}
Ruby
require "pidgely"

Pidgely.api_key = ENV["PIDGELY_API_KEY"]

result = Pidgely::Emails.send({
  from: "Acme <orders@acme.com>",
  to: ["steve@example.com"],
  subject: "Order confirmed",
  html: "<p>Thanks!</p>"
}, {
  idempotency_key: "order-1234/confirmation"
})
puts result
cURL
curl -X POST 'https://api.pidgely.com/emails' \
  -H "Authorization: Bearer $PIDGELY_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: order-1234/confirmation' \
  -d '{
  "from": "Acme <orders@acme.com>",
  "to": [
    "steve@example.com"
  ],
  "subject": "Order confirmed",
  "html": "<p>Thanks!</p>"
}'

With the Node.js SDK, pass it as an option: pidgely.emails.send(params, { idempotencyKey: 'order-1234/confirmation' }). The SDK then also retries failed sends automatically.

How it works

Behaviour
Endpoints POST /emails, POST /emails/batch (and /v1/emails, /v1/emails/batch). Over SMTP, use the Pidgely-Idempotency-Key header.
Key 1 to 256 characters; longer keys get 400 invalid_idempotency_key. An empty header means no idempotency.
Scope Per account: two API keys of the same account share the key space.
Lifetime 24 hours from the first request.
Same key, same request The stored status code and body are returned, with the header Idempotent-Replayed: true. Nothing is sent again.
Same key, different request 409 invalid_idempotent_request (Idempotency-Key was used with a different request). The request is compared by method, path and exact body.
Errors 4xx responses are stored and replayed like successes. 5xx responses are not stored, so a retry runs the request again.

Choosing keys

Derive the key from the business event that triggers the email, not from the attempt:

  • user-42/welcome, order-1234/confirmation, invoice-2026-10/acme: retries of the same event reuse the key, a different event gets a different key.
  • A random UUID generated once per event and stored with it works too. A new UUID per retry defeats the purpose.

Retry recipe

  1. Send with an idempotency key.
  2. On a timeout, a connection error, 429 or 5xx, retry with the same key and the same body, with exponential backoff.
  3. A replayed success has Idempotent-Replayed: true and the original email id.
  4. Don't retry other 4xx errors unchanged; see Errors.