Authentification — Public API
L'API publique utilise une clé API stockée hashée (SHA-256) côté serveur. La clé en clair n'est jamais persistée.
Header
Toutes les requêtes doivent inclure :
x-api-key: spk_<48 caractères hex>Exemple : spk_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4
Note : pas de schéma
Authorization: Bearer. Le header dédiéx-api-keypermet à un reverse proxy de logger sans exposer la clé.
Format de clé
| Champ | Valeur |
|---|---|
| Préfixe | spk_ (StormeoOS Public Key) |
| Corps | 48 caractères hex (24 bytes random via crypto.randomBytes) |
| Stockage | SHA-256 (keyHash), la clé en clair n'est affichée qu'une seule fois après création |
| Préfixe affiché en UI après création | spk_<8 premiers hex> (ex: spk_a1b2c3d4) |
Source : server/publicApi/auth.ts:24-47
Génération d'une clé
Via l'UI (recommandé)
- Compte → Intégrations → API publique
- Cliquer sur Créer une clé
- Nommer la clé, choisir les scopes, optionnellement IP whitelist + date d'expiration
- Copier la clé immédiatement : elle ne sera plus jamais affichée
Via l'API admin (auth session)
POST /api/public-api/admin/keys
Cookie: connect.sid=<session>
Content-Type: application/json
{
"name": "Intégration Zapier prod",
"scopes": ["clients:read", "invoices:write"],
"rateLimitPerMinute": 60,
"rateLimitPerDay": 10000,
"ipWhitelist": ["54.12.34.56"],
"expiresAt": "2026-12-31T23:59:59.000Z"
}Réponse :
{
"key": "spk_a1b2...e3f4", // ← AFFICHÉE UNE SEULE FOIS
"id": 42,
"name": "Intégration Zapier prod",
"prefix": "spk_a1b2c3d4",
"scopes": [...],
"createdAt": "2026-04-27T10:00:00.000Z"
}Scopes
Les scopes définissent quelles ressources / actions la clé peut consommer. Format : ressource:action.
| Groupe | Scopes |
|---|---|
| CRM | clients:read, clients:write, clients:delete, contacts:read, contacts:write, contacts:delete, websites:read, websites:write, websites:delete |
| Ticketing | tickets:read, tickets:write, tickets:delete |
| Facturation | invoices:read, invoices:write, quotes:read, quotes:write |
| Planning | tasks:read, tasks:write, tasks:delete, events:read, events:write, events:delete |
| Webhooks | webhooks:manage |
| Veille | watches:read, watches:write |
| Wildcard admin | * (tous les scopes — opt-in explicite uniquement) |
Catalogue complet et à jour : GET /api/public/v1/scopes ou server/publicApi/scopes.ts:11-51.
Comportement
- Une clé sans le scope requis reçoit
403 PERMISSION_DENIEDavec un champrequiredindiquant le scope manquant. - Le scope
*accorde tous les scopes — réservé aux clés admin internes. - Les scopes ne sont pas modifiables après création : générer une nouvelle clé pour changer.
Restrictions optionnelles
IP whitelist
Si ipWhitelist est non vide, seules les requêtes provenant d'une IP listée passent (sinon 403 IP_NOT_ALLOWED). L'IP est lue via req.ip (respecte trust proxy configuré).
Expiration
Si expiresAt est défini et passé, toute requête est rejetée en 401 EXPIRED_API_KEY. Pas de période de grâce.
Révocation
Une clé peut être révoquée à tout moment via l'UI ou DELETE /api/public-api/admin/keys/:id. Toute requête ultérieure renvoie 401 REVOKED_API_KEY. La révocation est immédiate (pas de cache).
Plan d'agence requis
Le plan tarifaire de l'agence doit avoir le feature flag api_access = true. Sinon : 403 PLAN_UPGRADE_REQUIRED avec upgradeUrl: "/pricing". Voir server/helpers/planFeatures.ts.
Logging
Chaque requête est loggée dans la table public_api_logs :
endpoint,method,statusCode,responseTimeMsapiKeyId,agencyId,ipAddress,userAgenterrorCodesi statut ≥ 400
Logs accessibles via GET /api/public-api/admin/logs?keyId=...&since=... (auth session admin).
Renouvellement (rotation)
Pas de mécanisme de rotation automatique. Procédure recommandée :
- Créer une nouvelle clé avec les mêmes scopes
- Mettre à jour le client externe
- Vérifier dans les logs que l'ancienne clé n'est plus utilisée
- Révoquer l'ancienne
Sécurité — bonnes pratiques
- Ne jamais committer une clé dans git (utiliser
.envou un secret manager) - Restreindre par IP quand le client est un serveur fixe
- Définir une expiration pour les clés temporaires
- Scopes minimaux : ne donner que les permissions strictement nécessaires
- Tourner les clés régulièrement (90 jours suggérés pour les intégrations critiques)
- Headers HTTPS uniquement : le serveur StormeoOS n'accepte que TLS
Endpoint debug : /me
Renvoie les métadonnées de la clé courante + utilisation rate limit en temps réel.
curl -H "x-api-key: spk_..." https://beta.stormeo.io/api/public/v1/meRéponse :
{
"id": 42,
"agencyId": 7,
"scopes": ["clients:read", "invoices:write"],
"rateLimitPerMinute": 60,
"rateLimitPerDay": 10000,
"usage": {
"minuteCount": 3,
"minuteResetAt": "2026-04-27T10:01:00.000Z",
"dayCount": 142,
"dayResetAt": "2026-04-28T00:00:00.000Z"
}
}