L'intégrer à votre application
Signature des requêtes
#Quand l'utiliser
Une application sans backend propre doit embarquer la clé, que quiconque capture son trafic peut alors lire. Activez la signature des requêtes sur cette clé (Console → Clés API → Modifier → Signature des requêtes) : chaque requête doit alors aussi porter une signature produite avec un secret de signature distinct. La clé sk- seule devient inutilisable et une requête capturée ne peut pas être rejouée.
#Les trois en-têtes
Continuez d'envoyer Authorization comme avant et ajoutez trois en-têtes. Le texte signé est l'horodatage, le nonce, la méthode HTTP et le chemin (sans paramètres de requête), joints par des sauts de ligne ; la clé HMAC est le secret sig_… exactement tel qu'affiché dans la console.
X-Timestamp: 1759312345 # unix seconds, within 5 minutes of server time
X-Nonce: 9f2c4e7a1b3d5f60 # 16-128 chars of A-Z a-z 0-9 - _, new for every request
X-Signature: hex(HMAC-SHA256(secret, "1759312345\n9f2c4e7a1b3d5f60\nPOST\n/v1/images/tasks"))#Signer une requête
import 'dart:convert';
import 'dart:math';
import 'package:crypto/crypto.dart';
const apiKey = 'sk-YOUR_KEY';
const signingSecret = 'sig_YOUR_SIGNING_SECRET';
int clockOffset = 0; // server time - device time, from X-Server-Timestamp
Map<String, String> signedHeaders(String method, String path) {
final ts = (DateTime.now().millisecondsSinceEpoch ~/ 1000 + clockOffset).toString();
final random = Random.secure();
final nonce = List.generate(16, (_) => random.nextInt(256).toRadixString(16).padLeft(2, '0')).join();
final signature = Hmac(sha256, utf8.encode(signingSecret))
.convert(utf8.encode('$ts\n$nonce\n$method\n$path'))
.toString();
return {
'Authorization': 'Bearer $apiKey',
'X-Timestamp': ts,
'X-Nonce': nonce,
'X-Signature': signature,
};
}
// headers: signedHeaders('POST', '/v1/images/tasks')
// GET /v1/images/tasks/{id} is signed with its own path, e.g. '/v1/images/tasks/task_123'import crypto from "node:crypto";
const ts = Math.floor(Date.now() / 1000).toString();
const nonce = crypto.randomBytes(16).toString("hex");
const signature = crypto
.createHmac("sha256", process.env.SIGNING_SECRET)
.update(`${ts}\n${nonce}\nPOST\n/v1/images/tasks`)
.digest("hex");
await fetch("https://www.aisplice.store/v1/images/tasks", {
method: "POST",
headers: {
Authorization: "Bearer sk-YOUR_KEY",
"Content-Type": "application/json",
"X-Timestamp": ts,
"X-Nonce": nonce,
"X-Signature": signature,
},
body: JSON.stringify(payload),
});#Erreurs et règles
- Les requêtes rejetées renvoient 401 avec error.code signature_required, signature_expired, signature_invalid ou signature_replayed.
- signature_expired renvoie aussi X-Server-Timestamp. Les téléphones dont l'horloge est déréglée doivent enregistrer l'écart, l'ajouter à chaque horodatage et réessayer une fois avec un nouveau nonce.
- Les nouvelles tentatives nécessitent un nouveau nonce et une nouvelle signature ; un nonce est mémorisé pendant 10 minutes.
- Remplacer le secret dans la console désactive l'ancien en moins de 30 secondes. Désactiver puis réactiver la signature conserve le même secret.
- La signature protège contre le trafic capturé, pas contre quelqu'un qui décompile l'application. Obfusquez le build (Flutter : --obfuscate) et gardez un plafond de quota sur la clé.
Toujours bloqué ?
Envoyez-nous l'ID de requête ou le message d'erreur, et nous examinerons le problème.