Idempotence
Une requête réseau peut échouer après avoir été traitée : votre client ne sait alors pas si l’adresse, la règle ou le canal a été créé. L’en-tête Idempotency-Key permet de relancer la même requête sans risque de doublon.
Utilisation#
Ajoutez à chaque POST une valeur unique, générée par votre client (un UUID est recommandé), et réutilisez-la pour chaque nouvelle tentative de la même opération :
curl -X POST https://aicscan.io/v1/addresses \
-H "Authorization: Bearer $AICSCAN_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6f1c2b8e-3d4a-4f0e-9b1a-2c3d4e5f6a7b" \
-d '{"chain_id":56,"address":"0x8894E0a0c962CB723c1976a4421c95949bE2D4E3","label":"Treasury"}'- Valeur : 1 à 255 caractères ASCII visibles. Sinon :
400 invalid_idempotency_key. - Portée : la clé d’idempotence est propre à votre espace ; deux espaces peuvent utiliser la même valeur sans interférence.
- Durée : la réponse est mémorisée 24 heures.
- Méthodes :
POSTseulement.PATCHetDELETEsont naturellement idempotents ; l’en-tête y est ignoré.
Comportement#
| Situation | Réponse |
|---|---|
| Première requête | Traitée normalement ; la réponse (statut, corps, en-tête Location) est mémorisée. |
| Même valeur, même requête | La réponse d’origine est renvoyée telle quelle, avec Idempotent-Replayed: true. Rien n’est recréé. |
| Même valeur, requête différente | 422 idempotency_key_reused. |
| Requête d’origine encore en cours | 409 idempotency_in_progress avec Retry-After: 1 : réessayez. |
| Erreur du serveur (5xx) | Rien n’est mémorisé : réessayez avec la même valeur. |
« Même requête » signifie même méthode, même chemin avec ses paramètres, et même corps à l’octet près. Une erreur 4xx (par exemple 422 validation_failed) est mémorisée comme une réussite : corrigez la requête et utilisez une nouvelle valeur.
Le corps d’une requête accompagnée d’une Idempotency-Key est limité à 1 Mio (413 payload_too_large au-delà).
Secrets affichés une seule fois#
Le secret de signature d’un webhook (webhook_secret) n’est jamais mémorisé. Le rejeu d’une création de webhook renvoie le même canal avec "webhook_secret": null : si vous avez perdu la première réponse, supprimez ce canal et créez-en un autre.
Exemple de relance#
import { randomUUID } from 'node:crypto';
async function createRule(rule) {
const key = randomUUID(); // la même pour toutes les tentatives
for (let attempt = 0; ; attempt++) {
try {
const res = await fetch('https://aicscan.io/v1/rules', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.AICSCAN_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': key,
},
body: JSON.stringify(rule),
});
if (res.status >= 500 || res.status === 409 || res.status === 429) throw new Error(String(res.status));
return await res.json();
} catch (e) {
if (attempt >= 4) throw e;
await new Promise((r) => setTimeout(r, 2 ** attempt * 500));
}
}
}import os, time, uuid
import requests
def create_rule(rule):
key = str(uuid.uuid4()) # la même pour toutes les tentatives
headers = {"Authorization": f"Bearer {os.environ['AICSCAN_API_KEY']}", "Idempotency-Key": key}
for attempt in range(5):
try:
resp = requests.post("https://aicscan.io/v1/rules", json=rule, headers=headers, timeout=10)
if resp.status_code < 500 and resp.status_code not in (409, 429):
return resp.json()
except requests.RequestException:
pass
time.sleep(2 ** attempt * 0.5)
raise RuntimeError("Aicscan API unavailable")