Documentation StorageHub
Intégrez le stockage sécurisé de fichiers dans n'importe quelle application — site web, app mobile ou backend — en quelques minutes.
Quickstart — 3 étapes
Rendez-vous sur le dashboard ou appelez
POST /v1/accounts/register. Vous recevez instantanément
api_key + hmac_secret — à conserver en lieu sûr.
STORAGE_BASE_URL=https://storagehub.aiforge.world STORAGE_API_KEY=shk_… STORAGE_HMAC_SECRET=…
Choisissez votre plateforme ci-dessous — chaque exemple inclut la signature HMAC complète.
Authentification — signature HMAC-SHA256
Toutes les requêtes /v1/ exigent ces headers. En production,
la signature est obligatoire.
| Header | Description |
|---|---|
| X-API-Key | Votre clé publique shk_… |
| X-Timestamp | Timestamp Unix actuel (tolérance ±30 s, anti-rejeu) |
| X-Signature | HMAC_SHA256(hmac_secret, api_key + ":" + timestamp + ":" + sha256(body)) |
| X-User-Id | ID de l'utilisateur final dans votre système (propriétaire du fichier) |
| X-User-Role | user · admin · kyc_agent |
multipart/form-data,
signez avec un corps vide : sha256(b""). Le body multipart n'est pas inclus dans la signature.Site web / JavaScript
Le front-end ne doit jamais connaître votre hmac_secret.
Pattern recommandé : votre backend génère les headers signés, le navigateur uploade.
Option A — via votre backend (recommandé)
// 1) Votre backend appelle l'API avec HMAC et retourne l'URL publique // 2) Le navigateur n'a besoin d'aucun secret const form = new FormData(); form.append("avatar", fileInput.files[0]); const res = await fetch("/api/my-backend/upload-avatar", { method: "POST", body: form }); const { url } = await res.json(); img.src = url; // https://storagehub.aiforge.world/v1/public/avatars/…
Option B — lire les avatars publics (aucun secret requis)
// Les avatars sont publics — pas d'auth nécessaire <img src="https://storagehub.aiforge.world/v1/public/avatars/{file_id}" alt="avatar" />
Application mobile
Même règle : le secret HMAC vit sur votre serveur, pas dans l'APK. L'app appelle votre backend, qui relaie vers StorageHub.
React Native — upload via votre backend
const data = new FormData(); data.append("file", { uri: photo.uri, // camera roll / camera name: "avatar.jpg", type: "image/jpeg", }); data.append("category", "avatars"); await fetch("https://api.votreapp.com/storage/upload", { method: "POST", body: data, headers: { "Content-Type": "multipart/form-data" }, });
Flutter (Dart)
final req = http.MultipartRequest( 'POST', Uri.parse('https://api.votreapp.com/storage/upload')); req.files.add(await http.MultipartFile.fromPath('file', path)); req.fields['category'] = 'avatars'; final res = await req.send();
GET /v1/files/{id}/signed), puis chargez
directement le lien — il expire automatiquement.Backend serveur
Python — signature + upload complet
import hashlib, hmac, time, requests BASE = "https://storagehub.aiforge.world" API_KEY = "shk_…" HMAC_SECRET = "…" def headers(user_id="user-1", role="user", body=b""): ts = str(int(time.time())) msg = f"{API_KEY}:{ts}:" + hashlib.sha256(body).hexdigest() sig = hmac.new(HMAC_SECRET.encode(), msg.encode(), hashlib.sha256).hexdigest() return {"X-API-Key": API_KEY, "X-Timestamp": ts, "X-Signature": sig, "X-User-Id": user_id, "X-User-Role": role} # Upload (body signé = sha256(b"") pour multipart) r = requests.post(BASE + "/v1/files", headers=headers(), files={"file": ("cni.pdf", open("cni.pdf", "rb"))}, data={"category": "kyc"}) file_id = r.json()["file_id"] # URL signée temporaire r = requests.get(BASE + f"/v1/files/{file_id}/signed", headers=headers(role="admin")) signed_url = r.json()["signed_url"] # expire selon le plan
Node.js
const crypto = require("crypto"); const BASE = "https://storagehub.aiforge.world"; function headers(userId, role = "user", body = Buffer.alloc(0)) { const ts = Math.floor(Date.now()/1000).toString(); const bodyHash = crypto.createHash("sha256").update(body).digest("hex"); const sig = crypto.createHmac("sha256", process.env.STORAGE_HMAC_SECRET) .update(`${process.env.STORAGE_API_KEY}:${ts}:${bodyHash}`).digest("hex"); return { "X-API-Key": process.env.STORAGE_API_KEY, "X-Timestamp": ts, "X-Signature": sig, "X-User-Id": userId, "X-User-Role": role }; }
cURL — test rapide
# calculez sig = HMAC(secret, "KEY:TS:sha256(body)") curl -X POST https://storagehub.aiforge.world/v1/files \ -H "X-API-Key: shk_…" -H "X-Timestamp: $TS" \ -H "X-Signature: $SIG" -H "X-User-Id: user-1" \ -F "file=@avatar.png" -F "category=avatars"
SDK Django clé-en-main
Le module django_integration/storage_client.py gère la signature
automatiquement :
from django_integration.storage_client import StorageClient client = StorageClient( base_url="https://storagehub.aiforge.world", api_key=settings.STORAGE_API_KEY, hmac_secret=settings.STORAGE_HMAC_SECRET, ) # Dans une vue res = client.upload_avatar(request.FILES["avatar"], owner_user_id=str(request.user.id)) profile.avatar_url = res["url"] # Document sensible → lien temporaire url = client.get_file_url(file_id, user_id=str(request.user.id), ttl=60)
Codes d'erreur
| HTTP | Cas | Solution |
|---|---|---|
| 400 | Fichier vide, MIME non autorisé, exécutable, catégorie invalide | Vérifiez format et catégorie |
| 401 | Header X-API-Key manquant | Ajoutez la clé |
| 403 | Clé invalide/révoquée, signature HMAC incorrecte, accès refusé | Vérifiez signature & propriété du fichier |
| 404 | Fichier introuvable ou supprimé | Vérifiez le file_id |
| 413 | Fichier trop grand ou quota plan dépassé | Réduisez la taille ou changez de plan |
| 422 | Validation du formulaire | Champs file + category requis |
Référence des endpoints
| Méthode | Route | Description |
|---|---|---|
| POST | /v1/accounts/register | Créer un compte → credentials API |
| POST | /v1/accounts/login | Session JWT dashboard |
| GET | /v1/accounts/me | Profil + usage + clés |
| POST | /v1/accounts/keys | Nouvelle clé API |
| DELETE | /v1/accounts/keys/{id} | Révoquer une clé |
| POST | /v1/files | Upload (multipart: file, category, owner_user_id?) |
| GET | /v1/files | Lister les fichiers |
| GET | /v1/files/{id} | Stream authentifié |
| GET | /v1/files/{id}/meta | Métadonnées JSON |
| GET | /v1/files/{id}/signed | Générer une URL signée |
| GET | /v1/files/{id}/download | Download via URL signée |
| DELETE | /v1/files/{id} | Suppression logique |
| GET | /v1/public/avatars/{id} | Avatar public (sans auth) |
| GET | /health · /v1/metrics | Monitoring |
Limites par plan
| Plan | Stockage | Clés API | TTL URL signée max |
|---|---|---|---|
| Gratuit | 500 MB | 1 | 60 s |
| Pro — 19€/mois | 50 GB | 3 | 5 min |
| Business — 79€/mois | 500 GB | 10 | 1 h |
POST /v1/accounts/plan ou le dashboard.