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.
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.
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);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
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);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)
}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 resultcurl -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
- Send with an idempotency key.
- On a timeout, a connection error,
429or5xx, retry with the same key and the same body, with exponential backoff. - A replayed success has
Idempotent-Replayed: trueand the original email id. - Don't retry other
4xxerrors unchanged; see Errors.