Signer les requêtes
Toutes les requêtes portent votre clé : Authorization: Bearer tano_sandbox_…. Les créations
(POST d’un dossier, d’une session, d’un webhook…) demandent en plus trois en-têtes :
| En-tête | Valeur |
|---|---|
Idempotency-Key |
un identifiant unique par intention (un UUID) |
X-Tano-Timestamp |
l’heure actuelle, en secondes Unix |
X-Tano-Signature |
v2= suivi de la signature, en hexadécimal |
Calculer la signature
Section intitulée « Calculer la signature »message = horodatage + "." + MÉTHODE + "." + cible + "." + corpssignature = "v2=" + hex(HMAC-SHA256(secret, message))- secret : la dernière partie de votre clé (
tano_sandbox_<identifiant>_<secret>). - MÉTHODE : en majuscules (
POST). - cible : le chemin tel qu’envoyé, chaîne de requête comprise (
/v1/cases). - corps : les octets exacts que vous envoyez.
import hashlib, hmac, json, time, uuid, urllib.request
CLE = "tano_sandbox_…"SECRET = CLE.rsplit("_", 1)[1]
def creer(cible: str, donnees: dict) -> dict: corps = json.dumps(donnees).encode() horodatage = str(int(time.time())) message = f"{horodatage}.POST.{cible}.".encode() + corps signature = "v2=" + hmac.new(SECRET.encode(), message, hashlib.sha256).hexdigest() requete = urllib.request.Request( "https://api.tano.africa" + cible, data=corps, method="POST", headers={ "Authorization": f"Bearer {CLE}", "Content-Type": "application/json", "Idempotency-Key": str(uuid.uuid4()), "X-Tano-Timestamp": horodatage, "X-Tano-Signature": signature, }, ) with urllib.request.urlopen(requete) as reponse: return json.load(reponse)
dossier = creer("/v1/cases", {"flow_name": "identite_ci", "country": "CI"})import { createHmac, randomUUID } from "node:crypto";
const CLE = process.env.TANO_API_KEY;const SECRET = CLE.slice(CLE.lastIndexOf("_") + 1);
async function creer(cible, donnees) { const corps = JSON.stringify(donnees); const horodatage = String(Math.floor(Date.now() / 1000)); const signature = "v2=" + createHmac("sha256", SECRET).update(`${horodatage}.POST.${cible}.${corps}`).digest("hex"); const reponse = await fetch(`https://api.tano.africa${cible}`, { method: "POST", body: corps, // la chaîne signée, telle quelle headers: { Authorization: `Bearer ${CLE}`, "Content-Type": "application/json", "Idempotency-Key": randomUUID(), "X-Tano-Timestamp": horodatage, "X-Tano-Signature": signature, }, }); if (!reponse.ok) throw new Error(JSON.stringify(await reponse.json())); return reponse.json();}
const dossier = await creer("/v1/cases", { flow_name: "identite_ci", country: "CI" });L’horodatage
Section intitulée « L’horodatage »La requête doit arriver dans une fenêtre de cinq minutes autour de l’heure de Tano. Au-delà :
signature_expired. Gardez l’horloge de vos serveurs synchronisée
(NTP). Une même signature ne se rejoue pas : signature_replayed.
L’idempotence
Section intitulée « L’idempotence »Un réseau coupe parfois après que Tano a reçu la requête : vous ne savez pas si le dossier
existe. Réessayez avec la même Idempotency-Key : Tano rend la même réponse que la première
fois, sans créer de doublon. Réutiliser une clé pour une autre requête est refusé
(idempotency_key_reused).
Règle simple : une clé d’idempotence par intention (« ouvrir le dossier du client 4821 »), conservée tant que vous réessayez cette intention.
© 2026 Qalebasse · Tano, plateforme de confiance numériquetano.africaDemander un accès[email protected]