Návod
Idempotentní požadavky
Hlavičkou Idempotency-Key bezpečně opakujte požadavky na odeslání a přeposlání. Formát klíče, 24hodinové okno, opakované odpovědi, odpovědi 409 a 503 a volba klíčů.
Sítě selhávají. Když u požadavku na odeslání vyprší časový limit, nevíte, jestli ho Emailit přijal, a když ho odešlete znovu, může váš zákazník dostat e-mail dvakrát. Hlavička Idempotency-Key dělá opakování bezpečným: Emailit zpracuje první požadavek a na každé opakování se stejným klíčem vrátí stejnou odpověď.
Jak to funguje
Přidejte hlavičku Idempotency-Key k POST /emails nebo POST /emails/{id}/forward.
- První požadavek. Emailit klíč pro váš workspace zarezervuje a požadavek zpracuje.
- Úspěch. Emailit uloží odpověď na 24 hodin. Každý požadavek se stejným klíčem v tomto okně dostane uloženou odpověď s
200a žádný nový e-mail se nevytvoří. - Selhání. Pokud požadavek selže, například s
400nebo402, Emailit klíč uvolní. Opravte problém a požadavek zopakujte se stejným klíčem. - Překryv. Pokud dorazí druhý požadavek, zatímco první ještě probíhá, dostane
409a nic se neodešle. Za chvíli ho zopakujte se stejným klíčem.
Klíče platí v rámci vašeho workspace, takže dva workspace mohou použít stejný klíč bez konfliktu.
Formát klíče
| Pravidlo | Hodnota |
|---|---|
| Délka | 1 až 256 znaků |
| Znaky | Písmena A–Z a a–z, číslice 0–9, spojovník - a podtržítko _ |
| Platnost | Pro každý workspace zvlášť |
| Okno | 24 hodin od první úspěšné odpovědi |
Klíč s jinými znaky, například : nebo /, se odmítne s 400 Invalid Idempotency-Key.
Zvolte klíč
Odvoďte klíč od události, která e-mail způsobuje, aby každá cesta opakování vytvořila stejný klíč:
| Příklad klíče | |
|---|---|
| Potvrzení objednávky | order-1042-receipt |
| Obnovení hesla | password-reset-7f3c9a1e (ID tokenu pro obnovení) |
| Týdenní přehled | digest-user-881-2026-w40 |
| Úloha na pozadí | ID úlohy, nebo UUID, které vygenerujete při zařazení úlohy do fronty a uložíte s ní |
Vyhněte se klíčům, které se mezi pokusy mění, například časovým razítkům nebo UUID generovanému uvnitř smyčky opakování. Kvůli nim vypadá každé opakování jako nový požadavek.
Odešlete e-mail s idempotenčním klíčem
Příklady pro Node.js, Python a PHP opakují požadavek při chybách sítě a při odpovědích 409, 429 a 5xx a pokaždé použijí stejný klíč. Příklad pro cURL využívá vestavěné opakování curl, které pokrývá vypršení časového limitu, 429 a většinu odpovědí 5xx.
curl https://api.emailit.com/v2/emails \
-H "Authorization: Bearer $EMAILIT_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-1042-receipt" \
--retry 3 \
-d '{
"from": "Acme <orders@acme.com>",
"to": "ada@example.com",
"subject": "Receipt for order 1042",
"text": "Thanks for your order."
}'async function sendOnce(payload, key, attempts = 4) {
for (let attempt = 1; attempt <= attempts; attempt++) {
try {
const res = await fetch('https://api.emailit.com/v2/emails', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.EMAILIT_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': key,
},
body: JSON.stringify(payload),
});
if (res.ok) return res.json();
if (![409, 429].includes(res.status) && res.status < 500) {
throw new Error(`Send failed: ${res.status} ${await res.text()}`);
}
const wait = Number(res.headers.get('retry-after')) || attempt * 2;
await new Promise((r) => setTimeout(r, wait * 1000));
} catch (err) {
if (err.message.startsWith('Send failed') || attempt === attempts) throw err;
await new Promise((r) => setTimeout(r, attempt * 2000));
}
}
throw new Error('Send failed after retries');
}
const email = await sendOnce(
{
from: 'Acme <orders@acme.com>',
to: 'ada@example.com',
subject: 'Receipt for order 1042',
text: 'Thanks for your order.',
},
'order-1042-receipt',
);import os
import time
import requests
def send_once(payload, key, attempts=4):
for attempt in range(1, attempts + 1):
try:
res = requests.post(
"https://api.emailit.com/v2/emails",
headers={
"Authorization": f"Bearer {os.environ['EMAILIT_API_KEY']}",
"Idempotency-Key": key,
},
json=payload,
timeout=30,
)
except requests.RequestException:
if attempt == attempts:
raise
time.sleep(attempt * 2)
continue
if res.ok:
return res.json()
if res.status_code not in (409, 429) and res.status_code < 500:
res.raise_for_status()
time.sleep(int(res.headers.get("retry-after", attempt * 2)))
raise RuntimeError("Send failed after retries")
email = send_once(
{
"from": "Acme <orders@acme.com>",
"to": "ada@example.com",
"subject": "Receipt for order 1042",
"text": "Thanks for your order.",
},
"order-1042-receipt",
)function sendOnce(array $payload, string $key, int $attempts = 4): array
{
for ($attempt = 1; $attempt <= $attempts; $attempt++) {
$ch = curl_init('https://api.emailit.com/v2/emails');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('EMAILIT_API_KEY'),
'Content-Type: application/json',
'Idempotency-Key: ' . $key,
],
CURLOPT_POSTFIELDS => json_encode($payload),
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
if ($body !== false && $status >= 200 && $status < 300) {
return json_decode($body, true);
}
if ($body !== false && !in_array($status, [409, 429]) && $status < 500) {
throw new RuntimeException("Send failed: $status $body");
}
sleep($attempt * 2);
}
throw new RuntimeException('Send failed after retries');
}
$email = sendOnce([
'from' => 'Acme <orders@acme.com>',
'to' => 'ada@example.com',
'subject' => 'Receipt for order 1042',
'text' => 'Thanks for your order.',
], 'order-1042-receipt');Odpovědi
| Stav | Kdy | Co dělat |
|---|---|---|
200 |
První úspěšný požadavek nebo jeho opakování do 24 hodin | Použijte odpověď. Opakovaná odpověď má stejné tělo včetně stejného id. |
400 Invalid Idempotency-Key |
Klíč je prázdný, příliš dlouhý, nebo obsahuje neplatné znaky | Opravte klíč. |
409 Idempotency key in progress |
Požadavek se stejným klíčem se ještě zpracovává | Chvíli počkejte a pak požadavek zopakujte se stejným klíčem. |
503 Idempotency unavailable |
Emailit se nemohl spojit se svým úložištěm idempotenčních klíčů, a tak požadavek raději odmítl, než aby riskoval duplicitu | Zopakujte požadavek se stejným klíčem. |
| Jakákoli jiná chyba | Požadavek selhal a klíč se uvolnil | Odstraňte příčinu a zopakujte požadavek se stejným klíčem. |
Limity rychlosti se kontrolují před klíčem, takže i opakování může dostat 429. Počkejte podle hlavičky retry-after a odešlete stejný klíč znovu.