API

Scanner en CI/CD

Un endpoint pour lancer un scan et récupérer le résultat en une seule requête — pensé pour bloquer un déploiement si le score de sécurité n'est pas suffisant, avant que ça parte en prod.

Authentification

Une clé API est requise (toujours pas de compte utilisateur — une clé est juste rattachée à un e-mail, comme le reste de Vetora). Génère-en une. organization et project sont des labels libres et optionnels pour grouper tes clés ; expiresInDays est optionnel aussi (omets-le pour une clé qui n'expire jamais) :

curl -s -X POST https://www.vetora.site/api/api-keys \
  -H "Content-Type: application/json" \
  -d '{"email":"toi@exemple.com","label":"CI GitHub Actions","organization":"acme-inc","project":"web-app","expiresInDays":90}'

# → { "apiKey": "vs_live_...", "email": "toi@exemple.com", ... }

La clé n'est affichée qu'une seule fois — enregistre-la immédiatement (ex. comme secret GitHub Actions). Envoie-la ensuite sur chaque requête :

Authorization: Bearer vs_live_xxx

Chaque création envoie aussi par email un lien de gestion pour cette adresse — liste toutes tes clés, révoque-en une, ou régénère-en une (remplace sa valeur secrète en gardant la même identité de clé et le même historique d'utilisation) sans passer par le support.

Endpoints

  • POST /api/api-keys — créer une clé (pas d'authentification requise — voir ci-dessus).
  • POST /api/v1/scan — lancer un scan et récupérer le résultat (clé bearer requise).

Endpoint

Limité à 5 scans par clé toutes les 10 minutes (et non plus par IP — changer de réseau ne réinitialise plus la limite).

POST https://www.vetora.site/api/v1/scan
Content-Type: application/json
Authorization: Bearer vs_live_xxx

{
  "url": "https://ton-app.vercel.app",
  "minScore": "B"
}

minScore est optionnel (A, B, C, D ou F). Si présent, la réponse indique si le score obtenu l'atteint via passed. Sans lui, passed vaut null — la requête reste purement informative.

Réponse

{
  "scanId": "uuid",
  "url": "https://ton-app.vercel.app",
  "score": "B",
  "minScore": "B",
  "passed": true,
  "detectedBuilder": "lovable",
  "reportUrl": "https://www.vetora.site/scan/uuid",
  "findings": {
    "base": [
      {
        "title": "Header de sécurité manquant : ...",
        "severity": "medium",
        "description": "...",
        "fixInstructions": "...",
        "evidence": "HTTP 200\ncontent-type: text/html\ncontent-security-policy: NOT PRESENT",
        "confidence": "confirmed"
      }
    ],
    "premium": {
      "stripeAndSupabaseCount": 2,
      "vibeCodingCount": 1,
      "infrastructureCount": 1,
      "unlockUrl": "https://www.vetora.site/scan/uuid/unlock"
    }
  }
}

Les failles de la catégorie "Sécurité de base" (headers, HTTPS, cookies, CORS) sont renvoyées en détail — c'est la partie gratuite du scan, comme sur le site. Les failles Stripe & Supabase et vibe coding sont comptées mais pas détaillées : même règle que pour un scan non payé sur le site, l'API ne contourne pas le rapport complet à 49€.

detectedBuilder vaut "bolt", "lovable" ou null — détection automatique et informative (pas une faille), basée sur le badge d'attribution public de ces plateformes. v0 et Cursor ne sont pas détectés : aucun badge équivalent à chercher.

Exemple : bloquer un déploiement (bash)

SCORE=$(curl -s -X POST https://www.vetora.site/api/v1/scan \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $VETORA_API_KEY" \
  -d '{"url":"https://ton-app.vercel.app","minScore":"B"}' \
  | jq -r '.passed')

if [ "$SCORE" != "true" ]; then
  echo "Score de sécurité insuffisant — déploiement bloqué."
  exit 1
fi

Exemple : GitHub Actions

- name: Vetora security gate
  env:
    VETORA_API_KEY: ${{ secrets.VETORA_API_KEY }}
  run: |
    RESULT=$(curl -s -X POST https://www.vetora.site/api/v1/scan \
      -H "Content-Type: application/json" \
      -H "Authorization: Bearer $VETORA_API_KEY" \
      -d '{"url":"${{ steps.deploy.outputs.url }}","minScore":"C"}')
    echo "$RESULT" | jq .
    echo "$RESULT" | jq -e '.passed == true' > /dev/null

Limites à connaître

  • Un scan prend en général quelques secondes à ~30 secondes selon le site — la requête bloque jusqu'à la fin, prévois un timeout généreux côté CI.
  • Le site scanné doit être accessible publiquement au moment de l'appel — utile après un déploiement preview/staging, pas avant.
  • Pas de quota dédié par organisation — seulement la limite par clé ci-dessous. Si ton usage grandit, contacte-nous.

Erreurs

Chaque réponse d'erreur a la même forme : { "error": "message lisible" }. Codes de statut :

  • 400 — requête malformée (url manquante/invalide, minScore pas parmi A/B/C/D/F, email/label invalide à la création d'une clé).
  • 401 — en-tête Authorization manquant/mal formé, ou clé invalide, révoquée ou expirée.
  • 409 — création de clé uniquement : déjà 5 clés actives pour cet e-mail. Révoque-en une d'abord.
  • 429 — limite de fréquence dépassée (voir ci-dessous).
  • 500 — erreur interne, pas de ta faute. Peut être retentée sans risque.
  • 502 — scan uniquement : site cible inaccessible ou timeout. Pas une panne Vetora — vérifie que l'URL est bien accessible publiquement.

Rate limits

  • POST /api/v1/scan — 5 scans par clé API toutes les 10 minutes glissantes.
  • POST /api/api-keys — 3 créations de clé par adresse IP toutes les 24 heures glissantes.

Une requête limitée retourne 429 sans en-tête Retry-After pour l'instant — patiente le temps de la fenêtre indiquée avant de retenter.

Webhooks

Vetora n'appelle pas de webhook pour un résultat de scan — l'API elle-même est synchrone. Le webhook qui existe va dans l'autre sens : un abonnement surveillé (surveillance continue) peut configurer un webhook générique sur sa page de gestion pour recevoir une alerte quand un rescan détecte une baisse de score, une régression, ou une nouvelle faille critique/élevée — en plus (ou à la place) de l'email et de Slack.

Chaque envoi est un POST signé :

POST <ton URL>
Content-Type: application/json
X-Vetora-Signature: <HMAC-SHA256 hex du corps brut>

{
  "event": "security_alert",
  "subscriptionId": "uuid",
  "url": "https://ton-app.vercel.app",
  "reportUrl": "https://www.vetora.site/scan/uuid/report",
  "previousScore": "B",
  "currentScore": "D",
  "scoreDropped": true,
  "regressedFindings": [{ "title": "...", "severity": "critical" }],
  "newCriticalFindings": [],
  "fixedCount": 0,
  "timestamp": "2026-08-21T12:00:00.000Z"
}

Le secret de signature n'est affiché qu'une seule fois, à la configuration du webhook — vérifie chaque envoi avec (exemple Node.js) :

const crypto = require('crypto');

function isValidSignature(rawBody, signatureHeader, secret) {
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader));
}

Envoi best-effort, pas encore de file de réessai : traite un envoi manqué comme un email manqué — le rapport lui-même (reportUrl) reste toujours la source de vérité.

OpenAPI

Spec machine-lisible complète (requêtes, réponses, schémas d'erreur) pour les deux endpoints ci-dessus, importable dans Postman, Insomnia, ou tout générateur de client basé sur OpenAPI :

curl -s https://www.vetora.site/openapi.yaml

/openapi.yaml