Skip to content

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.

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-key permet à un reverse proxy de logger sans exposer la clé.

Format de clé

ChampValeur
Préfixespk_ (StormeoOS Public Key)
Corps48 caractères hex (24 bytes random via crypto.randomBytes)
StockageSHA-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éationspk_<8 premiers hex> (ex: spk_a1b2c3d4)

Source : server/publicApi/auth.ts:24-47

Génération d'une clé

Via l'UI (recommandé)

  1. Compte → Intégrations → API publique
  2. Cliquer sur Créer une clé
  3. Nommer la clé, choisir les scopes, optionnellement IP whitelist + date d'expiration
  4. Copier la clé immédiatement : elle ne sera plus jamais affichée

Via l'API admin (auth session)

bash
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 :

json
{
  "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.

GroupeScopes
CRMclients:read, clients:write, clients:delete, contacts:read, contacts:write, contacts:delete, websites:read, websites:write, websites:delete
Ticketingtickets:read, tickets:write, tickets:delete
Facturationinvoices:read, invoices:write, quotes:read, quotes:write
Planningtasks:read, tasks:write, tasks:delete, events:read, events:write, events:delete
Webhookswebhooks:manage
Veillewatches: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_DENIED avec un champ required indiquant 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, responseTimeMs
  • apiKeyId, agencyId, ipAddress, userAgent
  • errorCode si 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 :

  1. Créer une nouvelle clé avec les mêmes scopes
  2. Mettre à jour le client externe
  3. Vérifier dans les logs que l'ancienne clé n'est plus utilisée
  4. Révoquer l'ancienne

Sécurité — bonnes pratiques

  • Ne jamais committer une clé dans git (utiliser .env ou 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.

bash
curl -H "x-api-key: spk_..." https://beta.stormeo.io/api/public/v1/me

Réponse :

json
{
  "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"
  }
}

StormeoOS API