Spotlite API

Ajoutez l'analyse IA de candidats à votre ATS en quelques heures — sans infrastructure ML, sans forfait fixe.

-87%
de temps de présélection
0,50€
par candidat analysé
< 2h
d'intégration dans un ATS
3 API
appels pour un dossier complet
🏗️
TalentFlow — Scale-up SaaS RH, 120 employés
Intégré en 1 sprint · en production depuis 3 mois
Cas fictif illustratif
« Avant, un recruteur passait 3h à regarder des vidéos pour shortlister 20 candidats sur 200. Maintenant l'API le fait en 4 minutes, on ne regarde plus que les 15 premiers scores. »
Problème
  • 200 candidatures/semaine avec vidéo
  • 2 recruteurs à temps plein pour trier
  • Délais de réponse : 12 jours
Résultat
  • Présélection automatisée en < 5 min
  • Coût : ~100 €/semaine (vs 2×SMIC RH)
  • Délais ramenés à 2 jours
Pipeline (3 appels)
🎬Vidéo candidatanalyzeVideoApplication
📄Générer CVbuildCvFromVideo
🎯Matcher postematchCandidateToJob
Shortlist autoscore ≥ 75
Voir le code d'intégration (JavaScript)
// 1. Analyser la vidéo
const analysis = await fetch('https://europe-west6-spotlite-9da69.cloudfunctions.net/apiAnalyzeVideoApplication', {
  method: 'POST',
  headers: { 'X-API-Key': API_KEY, 'Content-Type': 'application/json' },
  body: JSON.stringify({ videoUrl: candidate.videoUrl, candidateId: candidate.id, jobDescription: job.description })
}).then(r => r.json());

// 2. Générer le CV structuré
const cv = await fetch('https://europe-west6-spotlite-9da69.cloudfunctions.net/apiBuildCvFromVideo', {
  method: 'POST',
  headers: { 'X-API-Key': API_KEY, 'Content-Type': 'application/json' },
  body: JSON.stringify({ videoUrl: candidate.videoUrl })
}).then(r => r.json());

// 3. Matcher au poste
const match = await fetch('https://europe-west6-spotlite-9da69.cloudfunctions.net/apiMatchCandidateToJob', {
  method: 'POST',
  headers: { 'X-API-Key': API_KEY, 'Content-Type': 'application/json' },
  body: JSON.stringify({ cvData: cv.result, jobDescription: job.description })
}).then(r => r.json());

if (match.matchScore >= 75) addToShortlist(candidate); // ~0,50 € / candidat
👔
TalentESN — Cabinet de recrutement IT, 25 consultants
Utilisé pour sourcer & qualifier des profils LinkedIn · 500 profils/mois
Cas fictif illustratif
« On récupère le profil LinkedIn d'un candidat, on génère son CV Spotlite, on le matche sur 3 postes ouverts et on lance le coach pour préparer l'entretien — en 30 secondes. Avant, c'était 45 minutes de travail manuel. »
Problème
  • Qualification manuelle de 500 profils/mois
  • 45 min par profil pour rédiger un dossier
  • Perte de candidats faute de réactivité
Résultat
  • Dossier complet généré en 30 secondes
  • Coût : ~0,12 € par profil qualifié
  • Taux de placement +34 %
Pipeline (4 appels)
🔗URL LinkedInextractProfileFromUrl
📄Générer CVbuildCvFromVideo
🎯Matcher postematchCandidateToJob
🤖Coach IAcareerCoachEmbed
Voir le code d'intégration (JavaScript)
// 1. Extraire le profil depuis LinkedIn
const profile = await fetch('https://europe-west6-spotlite-9da69.cloudfunctions.net/apiExtractProfileFromUrl', {
  method: 'POST',
  headers: { 'X-API-Key': API_KEY, 'Content-Type': 'application/json' },
  body: JSON.stringify({ profileUrl: 'https://linkedin.com/in/marie-dupont', language: 'fr' })
}).then(r => r.json());

// 2. Matcher sur un poste
const match = await fetch('https://europe-west6-spotlite-9da69.cloudfunctions.net/apiMatchCandidateToJob', {
  method: 'POST',
  headers: { 'X-API-Key': API_KEY, 'Content-Type': 'application/json' },
  body: JSON.stringify({ cvData: profile, jobDescription: job.description })
}).then(r => r.json());

// 3. Préparer l'entretien avec le coach IA (SSE streaming)
const coachRes = await fetch('https://europe-west6-spotlite-9da69.cloudfunctions.net/apiCareerCoachEmbed', {
  method: 'POST',
  headers: { 'X-API-Key': API_KEY, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    message: 'Quelles questions préparer pour un poste Lead Dev React ?',
    jobDescription: job.description
  })
});
// Lire le stream SSE token par token
const reader = coachRes.body.getReader();
// Coût total pipeline : ~0,12 € par profil qualifié
📋
JobPulse — Job board sectoriel, 8 000 offres actives
Offres scrappées depuis 40 sites · matching automatique candidats inscrits
Cas fictif illustratif
« On ingère 300 nouvelles offres par jour depuis des URLs. Spotlite les structure automatiquement et les matche sur notre base de candidats. Le taux de candidatures pertinentes a doublé. »
Problème
  • 300 offres/jour à structurer depuis des URLs
  • Données incomplètes ou mal formatées
  • Matching candidats trop générique
Résultat
  • Extraction structurée en temps réel
  • Coût : ~0,03 € / offre traitée
  • Candidatures pertinentes ×2
Pipeline (2 appels)
🌐URL offreextractJobFromUrl
🎯Matcher candidatmatchCandidateToJob
📩Alerte cibléenotif candidat
Voir le code d'intégration (JavaScript)
// 1. Extraire l'offre depuis l'URL scrapée
const job = await fetch('https://europe-west6-spotlite-9da69.cloudfunctions.net/apiExtractJobFromUrl', {
  method: 'POST',
  headers: { 'X-API-Key': API_KEY, 'Content-Type': 'application/json' },
  body: JSON.stringify({ jobUrl: scrapedUrl, language: 'fr' })
}).then(r => r.json());

// 2. Matcher sur les candidats inscrits (en batch)
for (const candidate of candidates) {
  const match = await fetch('https://europe-west6-spotlite-9da69.cloudfunctions.net/apiMatchCandidateToJob', {
    method: 'POST',
    headers: { 'X-API-Key': API_KEY, 'Content-Type': 'application/json' },
    body: JSON.stringify({ cvData: candidate.profile, jobDescription: job.missionSummary })
  }).then(r => r.json());

  if (match.matchScore >= 70) sendAlert(candidate, job); // notif email/push
}
// Coût : ~0,03 € / offre · 3 crédits extractJob + 7 crédits / match
🧠
ProfilIA — HR-Tech SaaS, 40 employés
Bilan personnalité passif intégré dans le parcours candidat · sans questionnaire
Cas fictif illustratif
« On envoie les lettres de motivation et notes de candidature à l'API. En quelques secondes, on obtient un profil Big Five complet — sans que le candidat ait rempli le moindre questionnaire. C'est ce qui différencie notre scoring des autres ATS. »
Problème
  • Questionnaires de personnalité : taux d'abandon 60 %
  • Résultats biaisés (candidates « optimisent » leurs réponses)
  • Données comportementales inutilisées dans les dossiers
Résultat
  • Profil personnalité sur 100 % des candidats
  • Coût : 30 crédits / bilan (≈ 0,30 €)
  • Taux de présélection pertinente +28 %
Pipeline (2 appels)
📝Textes libresassessPassiveProfile
🎯Score matchingmatchCandidateToJob
Shortlist enrichiescore + fit culturel
Voir le code d'intégration (JavaScript)
const API = 'https://europe-west6-spotlite-9da69.cloudfunctions.net';
const H = { 'X-API-Key': API_KEY, 'Content-Type': 'application/json' };
const post = (path, body) => fetch(API + path, { method: 'POST', headers: H, body: JSON.stringify(body) }).then(r => r.json());

async function enrichWithPersonality(candidate, jobDescription) {
  // 1. Bilan passif depuis les écrits du candidat (lettre de motivation, notes...)
  const bilan = await post('/apiAssessPassiveProfile', {
    texts: candidate.writtenTexts,  // [{ date, title, content }, ...]
    framework: 'big5',
    language: 'fr'
  });
  // bilan.scores.openness / conscientiousness / extraversion / agreeableness / neuroticism
  // bilan.scores.dominant_trait, bilan.insights, bilan.career_implications

  // 2. Matching CV ↔ poste
  const match = await post('/apiMatchCandidateToJob', {
    candidateProfile: candidate.profile,
    jobDescription
  });

  // 3. Décision combinée : score technique + fit culturel (conscientiousness + openness)
  const s = bilan.scores;
  const culturalFit = s.conscientiousness > 65 && s.openness > 60;
  return {
    matchScore: match.matchScore,
    dominantTrait: s.dominant_trait,
    confidence: bilan.confidence,   // 0–1 : fiabilité selon volume de texte
    insights: bilan.insights,
    culturalFit,
    shortlisted: match.matchScore >= 70 && culturalFit,
  };
}
// Coût : 30 crédits (bilan) + 7 crédits (match) ≈ 0,37 € par candidat
🛡️
DocuVerify — SaaS ATS industriel, secteurs BTP & logistique
Habilitations & certifications terrain · 400 candidats/mois traités
Cas fictif illustratif
« Nos recruteurs ne connaissent pas le CACES ou les habilitations électriques. L'API lit le document, extrait le titulaire et nous explique en une phrase ce que la certification autorise le candidat à faire. »
Problème
  • CACES, habilitations HN/HE, SST, AIPR… ~15 référentiels différents
  • Recruteurs obligés de chercher chaque norme manuellement
  • Saisie du titulaire et de la validité à la main dans le dossier
Résultat
  • Titulaire + intitulé professionnel extraits automatiquement
  • Résumé RH : « Ce document autorise le candidat à conduire des chariots en sécurité »
  • Score de cohérence : tri immédiat sans expertise métier
Pipeline (1 appel)
📎PDF / image habilitationcandidat téléverse
🛡️Vérifier la preuveverifyProof
Dossier enrichi ATStitulaire + résumé RH
Voir le code d'intégration (JavaScript)
const BASE = 'https://europe-west6-spotlite-9da69.cloudfunctions.net';
const HEADERS = { 'X-API-Key': process.env.SPOTLITE_API_KEY, 'Content-Type': 'application/json' };
const post = (path, body) =>
  fetch(BASE + path, { method: 'POST', headers: HEADERS, body: JSON.stringify(body) }).then(r => r.json());

async function verifyDocument(fileBase64, mimeType) {
  const result = await post('/apiVerifyProof', { fileBase64, mimeType, language: 'fr' });
  if (!result.accepted) {
    console.warn('Document non accepté :', result.rejectionReason);
    return null;
  }
  console.log('Titulaire :', result.holderName);
  console.log('Intitulé :', result.title);
  console.log('Résumé RH :', result.professionalSummary);
  console.log('Score cohérence :', result.confidenceScore, '/ 100');
  // Injecter dans le dossier ATS
  return {
    atsTitle: result.title,
    holder: result.holderName,
    summary: result.professionalSummary,
    score: result.confidenceScore,
    type: result.type,
    sources: result.sources
  };
}
// Coût : min. 5 crédits · facturation à la consommation réelle

🔗 Pipeline complet — Cabinet ESN (JS & Python)

De l'URL LinkedIn au dossier candidat prêt à présenter, en 4 appels API.

🔗Extraire profilextractProfileFromUrl
🌐Extraire offreextractJobFromUrl
🎯Score matchingmatchCandidateToJob
🤖Coaching IAcareerCoachEmbed
JavaScript (Node.js / fetch)
const BASE = 'https://europe-west6-spotlite-9da69.cloudfunctions.net';
const HEADERS = { 'X-API-Key': process.env.SPOTLITE_API_KEY, 'Content-Type': 'application/json' };
const post = (path, body) =>
  fetch(BASE + path, { method: 'POST', headers: HEADERS, body: JSON.stringify(body) }).then(r => r.json());

async function fullEsnPipeline(linkedinUrl, jobUrl) {
  // 1. Extraire le profil candidat
  const profile = await post('/apiExtractProfileFromUrl', { profileUrl: linkedinUrl, language: 'fr' });
  console.log('Profil extrait :', profile.name, '—', profile.headline);

  // 2. Extraire l'offre d'emploi
  const job = await post('/apiExtractJobFromUrl', { jobUrl, language: 'fr' });
  console.log('Offre extraite :', job.jobTitle, 'chez', job.company);

  // 3. Score de matching
  const match = await post('/apiMatchCandidateToJob', {
    cvData: profile,
    jobDescription: job.missionSummary
  });
  console.log('Score :', match.matchScore, '/ 100 —', match.recommendation);

  // 4. Préparer l'entretien avec le coach IA (SSE streaming)
  const coachRes = await fetch(BASE + '/apiCareerCoachEmbed', {
    method: 'POST', headers: HEADERS,
    body: JSON.stringify({
      message: `Quelles questions préparer pour le poste ${job.jobTitle} chez ${job.company} ?`,
      jobDescription: job.missionSummary
    })
  });
  const reader = coachRes.body.getReader();
  const decoder = new TextDecoder();
  process.stdout.write('\nCoach : ');
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    decoder.decode(value, { stream: true }).split('\n').forEach(line => {
      if (!line.startsWith('data:')) return;
      try { const e = JSON.parse(line.slice(5)); if (e.token) process.stdout.write(e.token); } catch {}
    });
  }
  // Coût total : ~13 crédits · profil(3) + offre(3) + match(7) · coach à la consommation
  return { profile, job, match };
}

fullEsnPipeline('https://linkedin.com/in/marie-dupont', 'https://linkedin.com/jobs/view/123456789');
Python (httpx / asyncio)
import httpx, asyncio, os, json

BASE = "https://europe-west6-spotlite-9da69.cloudfunctions.net"
HEADERS = {"X-API-Key": os.environ["SPOTLITE_API_KEY"], "Content-Type": "application/json"}

async def post(client, path, body):
    r = await client.post(BASE + path, json=body, headers=HEADERS)
    r.raise_for_status()
    return r.json()

async def full_esn_pipeline(linkedin_url, job_url):
    async with httpx.AsyncClient(timeout=60) as client:
        # 1. Extraire le profil
        profile = await post(client, "/apiExtractProfileFromUrl",
                                {"profileUrl": linkedin_url, "language": "fr"})
        print(f"Profil : {profile['name']} — {profile['headline']}")

        # 2. Extraire l'offre
        job = await post(client, "/apiExtractJobFromUrl",
                          {"jobUrl": job_url, "language": "fr"})
        print(f"Offre : {job['jobTitle']} chez {job['company']}")

        # 3. Matching
        match = await post(client, "/apiMatchCandidateToJob",
                            {"cvData": profile, "jobDescription": job["missionSummary"]})
        print(f"Score : {match['matchScore']}/100 — {match['recommendation']}")

        # 4. Coach IA (SSE streaming)
        async with client.stream("POST", BASE + "/apiCareerCoachEmbed", headers=HEADERS,
            json={"message": f"Questions pour {job['jobTitle']} chez {job['company']} ?",
                  "jobDescription": job["missionSummary"]}) as resp:
            async for line in resp.aiter_lines():
                if line.startswith("data:"):
                    ev = json.loads(line[5:])
                    if "token" in ev: print(ev["token"], end="", flush=True)

        return {"profile": profile, "job": job, "match": match}

asyncio.run(full_esn_pipeline(
    "https://linkedin.com/in/marie-dupont",
    "https://linkedin.com/jobs/view/123456789"
))
🧪 Tester en live Demander un accès Accès approuvé sous 24-48h • Sans engagement

Spotlite API

L'API Spotlite permet à votre entreprise d'intégrer les capacités d'analyse de candidats directement dans votre ATS, SIRH ou pipeline de recrutement. Toutes les analyses sont propulsées par des modèles IA multimodaux (vidéo, texte, CV).

Base URL :

https://europe-west6-spotlite-9da69.cloudfunctions.net
Format des réponses : Tous les endpoints retournent du JSON. Les erreurs ont toujours la structure { "error": { "code": "...", "message": "..." } }.

Obtenir l'accès API

1

Créer un compte entreprise Spotlite

Rendez-vous sur spotlite-app.com et créez un compte. Le wallet Spotlite associé à ce compte sera débité à chaque appel API.

2

Soumettre une demande d'accès

Dans l'application, allez dans Cockpit → Premium → API et remplissez le formulaire de demande. Précisez les actions dont vous avez besoin et votre volume estimé.

3

Validation par l'équipe Spotlite

L'équipe Spotlite examine votre demande (généralement sous 24–48h). Vous êtes notifié par email et dans l'app à l'approbation.

4

Récupérer votre clé API

Votre clé sk_live_... apparaît dans Cockpit → Premium → API. Conservez-la côté serveur uniquement — ne l'exposez jamais dans un front-end ou un dépôt Git.

5

Recharger votre wallet

Les appels API consomment des crédits depuis votre wallet Spotlite. Rechargez-le dans l'application avant de commencer.

Authentification

Incluez votre clé API dans le header X-API-Key de chaque requête.

# Avec curl
curl -X POST https://europe-west6-spotlite-9da69.cloudfunctions.net/apiMatchCandidateToJob \
  -H "X-API-Key: sk_live_votre_cle" \
  -H "Content-Type: application/json" \
  -d '{ ... }'
// Avec Node.js / fetch
const response = await fetch('https://europe-west6-spotlite-9da69.cloudfunctions.net/apiMatchCandidateToJob', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.SPOTLITE_API_KEY, // jamais en dur dans le code
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ /* ... */ }),
});
# Avec Python / requests
import requests, os

headers = {
    "X-API-Key": os.environ["SPOTLITE_API_KEY"],
    "Content-Type": "application/json",
}
resp = requests.post(
    "https://europe-west6-spotlite-9da69.cloudfunctions.net/apiMatchCandidateToJob",
    headers=headers,
    json={# ...}
)

🧪 Playground — Tester l'API

Entrez votre clé API ci-dessous et testez chaque endpoint directement depuis votre navigateur. Les crédits sont débités de votre compte réel.

GET Compte & Crédits ✓ Gratuit

Vérifie votre solde de crédits, les actions autorisées et l'historique récent. Parfait pour commencer.

POST Matcher candidat ↔ poste ⚡ ~7 crédits

Score de correspondance instantané entre un profil candidat et une offre d'emploi. Retourne le score, les compétences manquantes et un insight IA.

POST Analyser un dossier de candidature ⚡ ~10 crédits

Analyse combinée CV + lettre de motivation + contexte du poste. Retourne un score global, un score CV, un score motivation et une recommandation IA.

POST Career Coach IA (Dalia) ⚡ 1 crédit/message

Intégrez Dalia, le coach carrière IA Spotlite, dans votre plateforme. Réponses personnalisées en fonction du profil du candidat.

POST ⚡ Analyse multi-médias facturation à la consommation

Analysez 1 à 5 vidéos et/ou images en un seul appel. Retourne une analyse unifiée + insights multi-sources.

POST Générer un CV depuis une vidéo ⚡ Variable — à la consommation

Génère automatiquement un CV structuré (CareerModel JSON) à partir d'une vidéo de présentation candidat.

PUT Configurer les alertes de solde Gratuit

Configure le seuil d'alerte crédits et l'email de notification pour votre compte.

POST Analyser une vidéo candidat ⚡ Variable — à la consommation

Analyse complète d'une vidéo de présentation candidat : compétences, soft skills, score global, recommandation. Réponse asynchrone — retourne un jobId immédiatement.

POST Extraire une offre d'emploi ⚡ 3 crédits

Extrait les détails structurés d'une offre depuis une URL (LinkedIn, Indeed…) ou une description textuelle. Compatible avec matchCandidateToJob.

POST Extraire un profil candidat ⚡ 3 crédits

Extrait un CareerModel structuré depuis une URL LinkedIn ou un résumé textuel. Directement injectable dans matchCandidateToJob.

POST 🛡️ Vérifier une preuve documentaire ⚡ min. 5 crédits

Analyse la cohérence d'un document professionnel (diplôme, certification, habilitation…) et retourne le titulaire, un intitulé précis et un résumé vulgarisé pour les RH.

POST Bilan de personnalité passif ✨ ⚡ 30 crédits

Analyse passive de la personnalité d'un candidat depuis ses écrits. Envoyez les entrées textuelles (journal, notes, réflexions) et recevez un bilan structuré sans questionnaire.

✨ Prêt à intégrer l'IA Spotlite dans votre ATS ?

Demandez votre accès depuis l'app Spotlite et commencez à analyser des milliers de candidats en quelques minutes.

Obtenir l'accès API →

Système de crédits

Chaque appel API consomme des crédits depuis votre wallet Spotlite. Le solde est vérifié avant chaque appel — si insuffisant, l'appel est rejeté avec un 402 sans vous facturer.

Action Coût Type de réponse
analyzeMediaBundle Variable — à la consommation réelle
min. 5 crédits • formule: (tokens × tarif IA + compute CF) × 1.30 / 100
Synchrone • tokensUsed dans la réponse
analyzeVideoApplication Variable — à la consommation réelle
min. 5 crédits • tokens IA + 35 m€ compute
Asynchrone — retourne un jobId immédiatement
résultat dans Firestore apiJobs/{jobId} (30–60 s)
buildCvFromVideo Variable — à la consommation réelle
min. 5 crédits • tokens IA + 40 m€ compute
Asynchrone — retourne un jobId immédiatement
résultat dans Firestore apiJobs/{jobId} (30–60 s)
matchCandidateToJob 7 crédits Synchrone
analyzeCandidateBundle Variable si vidéo fournie
tokens IA + 25 m€ compute • forfait 3-5 cr. si texte seul
Synchrone • tokensUsed si vidéo
careerCoachEmbed 1 crédit Synchrone
extractJobFromUrl 3 crédits Synchrone
extractProfileFromUrl 3 crédits Synchrone
assessPassiveProfile 30 crédits Synchrone
verifyProof Variable — à la consommation réelle
min. 5 crédits • tokens IA + 45 m€ compute
Synchrone • tokensUsed dans la réponse
getAccountStats Gratuit Synchrone
Appels asynchrones : analyzeVideoApplication et buildCvFromVideo traitent des vidéos et répondent immédiatement avec un jobId (HTTP 202). Le résultat est disponible dans Firestore sous apiJobs/{jobId} après 30–60 secondes. Toutes les autres APIs sont synchrones — elles retournent le résultat directement dans la réponse HTTP.

⚡ Analyser un bundle multi-médias

POST /apiAnalyzeMediaBundle

Envoie 1 à 5 sources média (vidéos et/ou images) en un seul appel. Retourne une analyse unifiée du profil candidat ainsi qu'un champ exclusif crossSourceInsight qui synthétise les patterns visibles uniquement en croisant toutes les sources.

⚡ Facturation à la consommation réelle
Les crédits débités sont calculés après l'appel IA, basés sur les tokens réellement consommés :
coût(m€) = (inputTokens × 0.00000028 + outputTokens × 0.00000233 + compute(n_sources)) × 1000 × 1.30
crédits = max(5, ceil(coût_m€ / 100))
Le champ tokensUsed dans la réponse vous indique la consommation exacte.
Si l'analyse échoue : 0 crédit débité.

Limites

  • Maximum 5 sources par appel
  • Maximum 3 vidéos par appel (utilisez des images pour les captures statiques)
  • Maximum 1 lien de streaming (YouTube, Vimeo…) par appel — restriction du moteur IA. Pour analyser plusieurs vidéos, hébergez-les en fichier direct (mp4/webm) sur Firebase Storage ou équivalent.
  • Formats vidéo : mp4, webm, mov — ou URL YouTube/Vimeo (durée recommandée ≤ 30 min)
  • Formats image : jpg, jpeg, png, webp, gif
  • URLs publiques http/https uniquement (protection SSRF)

Corps de la requête

{
  "mediaItems": [                  // Requis — 1 à 5 items
    {
      "url": "https://...",        // URL publique de la source
      "type": "video" | "image"   // Optionnel : auto-détecté depuis l'extension
    }
  ],
  "candidateId": "string",        // Optionnel
  "jobDescription": "string",     // Optionnel : active le scoring de correspondance
  "language": "fr"                // Optionnel, défaut "fr"
}

Réponse (HTTP 200)

{
  "candidateId": "abc123",
  "sources": [
    { "url": "https://...", "type": "video", "mimeType": "video/mp4" },
    { "url": "https://...", "type": "image", "mimeType": "image/jpeg" }
  ],
  "hardSkills": ["React", "TypeScript", "Node.js"],
  "softSkills": ["Leadership", "Communication"],
  "transferableSkills": ["Gestion de projet"],
  "potentialJobMatches": ["Développeur Full Stack", "Tech Lead"],
  "expertiseLevel": "confirme",
  "employabilityScore": 84,
  "summary": {
    "global": "Candidat avec une solide maîtrise...",
    "perSource": [
      "Vidéo 1 : présentation fluide, expert React...",
      "Image 1 : portfolio structuré, projets variés..."
    ]
  },
  "transcript": "Bonjour, je m'appelle...",
  "detectedTools": ["VS Code", "Figma"],
  "semanticTags": ["frontend", "cloud"],
  "crossSourceInsight": "Les 2 sources confirment une expertise frontend solide. La vidéo révèle une aisance orale que le portfolio ne suggère pas.",
  "moderationScore": 1,
  "primarySector": "Informatique",
  "yearsOfExperienceEstimate": "5-10",

  // Si jobDescription fourni :
  "matchScore": 87,
  "matchedSkills": ["React", "TypeScript"],
  "missingSkills": ["AWS"],
  "matchInsight": "Excellent profil technique, seule la dimension cloud est à renforcer.",

  // Facturation
  "creditsUsed": 7,
  "remainingCredits": 93,
  "tokensUsed": { "input": 47230, "output": 1842, "total": 49072 }
}

Codes d'erreur spécifiques

Code HTTP Cause
too_many_items 400 Plus de 5 sources dans mediaItems
too_many_videos 400 Plus de 3 vidéos dans mediaItems
unsupported_format 400 Extension de fichier non supportée
type_mismatch 400 Type déclaré incohérent avec l'extension
invalid_url 400 URL non publique ou invalide
analysis_failed 500 Moteur d'analyse inaccessible ou URL inaccessible — 0 crédit débité

🛡️ Vérifier une preuve documentaire

POST /apiVerifyProof

Analyse la cohérence d'un document professionnel ou officiel (diplôme, certification, permis, habilitation, attestation…) à partir d'un PDF, d'une image ou d'un JSON décrivant le document. C'est le même moteur que le Drive de preuves Spotlite : même grounding Google, même barème, même normalisation.

La réponse inclut un résumé professionnel vulgarisé (professionalSummary) pensé pour un RH non spécialiste, le nom du titulaire (holderName) et un intitulé professionnel précis (title).

⚖️ Cadre légal
L'analyse évalue la cohérence du document (cohérence interne, plausibilité, correspondance avec l'organisme émetteur), jamais son authenticité ni sa véracité. Le confidenceScore reflète uniquement cette cohérence.
⚡ Facturation à la consommation réelle
coût(m€) = (inputTokens × 0.00000028 + outputTokens × 0.00000233 + compute) × 1000 × 1.30
crédits = max(5, ceil(coût_m€ / 100)) — le champ tokensUsed détaille la consommation.
Si l'analyse échoue : 0 crédit débité.

Limites

  • Formats fichier : application/pdf, image/jpeg, image/png, image/webp, image/gif
  • Taille maximale du fichier : 15 Mo
  • URLs publiques http/https uniquement (protection SSRF)
  • Un seul mode d'entrée par appel : fileBase64, fileUrl ou documentJson

Corps de la requête

{
  // Mode 1 — fichier encodé en base64
  "fileBase64": "JVBERi0xLjQ...",   // PDF ou image en base64 (sans préfixe data:)
  "mimeType": "application/pdf",     // Requis avec fileBase64

  // Mode 2 — fichier distant (alternatif)
  "fileUrl": "https://.../diplome.pdf",

  // Mode 3 — document décrit en JSON (alternatif)
  "documentJson": {
    "title": "CACES R489 catégorie 3",
    "issuer": "AFTRAL",
    "holder": "Marie Dupont",
    "date": "2024-03-12",
    "referenceNumber": "R489-3-2024-0098"
  },

  "language": "fr"                   // Optionnel : force la langue de sortie
}

Réponse (HTTP 200)

{
  "eligible": true,
  "documentQuality": "good",
  "accepted": true,                  // true si éligible, lisible et score > 60
  "type": "license",                 // diploma|certification|license|agreement|attestation|other
  "title": "CACES R489 catégorie 3 — Chariot élévateur frontal",
  "issuer": "AFTRAL",
  "holderName": "Marie Dupont",
  "professionalSummary": "Ce certificat atteste que la personne est habilitée à conduire en sécurité des chariots élévateurs frontaux (catégorie 3). C'est un prérequis réglementaire pour les postes de cariste en entrepôt ou en logistique.",
  "confidenceScore": 88,
  "verdict": "Cohérent",
  "explanation": "Le document présente une structure et une terminologie conformes au référentiel CACES R489...",
  "coherentPoints": ["Organisme certificateur reconnu", "Catégorie valide pour le référentiel R489"],
  "coherenceFlags": [],
  "rejectionReason": "",
  "diploma": null,
  "certification": null,
  "license": {
    "licenseType": "CACES R489",
    "category": "Catégorie 3",
    "authority": "AFTRAL",
    "referenceNumber": "R489-3-2024-0098",
    "issueDate": "2024-03-12",
    "validUntil": "2029-03-12"
  },
  "sources": [{ "title": "Référentiel CACES R489", "url": "https://..." }],
  "language": "fr",
  "creditsUsed": 5,
  "remainingCredits": 95,
  "tokensUsed": { "input": 12430, "output": 920, "total": 13350 }
}

Codes d'erreur spécifiques

CodeHTTPCause
invalid_argument400Aucun mode d'entrée fourni, ou mimeType manquant/non supporté
invalid_url400fileUrl non publique ou invalide
unsupported_type400Type de fichier distant non supporté (PDF/image attendu)
file_too_large413Fichier > 15 Mo
analysis_failed502Moteur d'analyse indisponible ou réponse inexploitable — 0 crédit débité

🧪 Tester l'endpoint

Test rapide avec un fichier local converti en base64 (cURL) :

curl -X POST \
  https://europe-west6-spotlite-9da69.cloudfunctions.net/apiVerifyProof \
  -H "X-API-Key: $SPOTLITE_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"fileBase64\": \"$(base64 -i diplome.pdf)\",
    \"mimeType\": \"application/pdf\"
  }"

Test depuis une URL publique (Node.js) :

const res = await fetch('https://europe-west6-spotlite-9da69.cloudfunctions.net/apiVerifyProof', {
  method: 'POST',
  headers: { 'X-API-Key': API_KEY, 'Content-Type': 'application/json' },
  body: JSON.stringify({ fileUrl: 'https://exemple.com/certificat.png' })
});
const proof = await res.json();
console.log(proof.title, proof.holderName, proof.confidenceScore, proof.professionalSummary);

Test sans fichier, à partir d'un JSON structuré (Python) :

import requests

res = requests.post(
    "https://europe-west6-spotlite-9da69.cloudfunctions.net/apiVerifyProof",
    headers={"X-API-Key": API_KEY},
    json={"documentJson": {
        "title": "Master Informatique",
        "issuer": "Université de Lille",
        "holder": "Karim Benali",
        "year": "2022",
    }},
)
print(res.json()["professionalSummary"])

💡 Cas d'usage — Pré-qualifier des certifications obligatoires

« Pour nos postes de cariste et d'électricien, chaque candidat téléverse ses habilitations. L'API les structure, en extrait le titulaire et nous dit en une phrase à quoi sert chaque document — nos chargés de recrutement n'ont plus besoin de connaître chaque référentiel métier. »
Problème
  • Documents métier hétérogènes (CACES, habilitations électriques, SST…)
  • Recruteurs non experts des référentiels
  • Saisie manuelle du titulaire et de la validité
Résultat
  • Titulaire + intitulé pro extraits automatiquement
  • professionalSummary lisible par tous les recruteurs
  • Tri immédiat via accepted et confidenceScore
Flux d'intégration
📎Document candidatPDF / image / JSON
🛡️Vérifier la preuveapiVerifyProof
Fiche enrichie ATStitulaire + résumé

Analyser une vidéo de candidature

POST /apiAnalyzeVideoApplication Analyse complète : compétences, soft skills, score global
⚡ Variable — à la consommation réelle (min. 5 crédits)

Paramètres

Champ Type Requis Description
videoUrl string Requis URL HTTPS accessible publiquement de la vidéo (mp4, webm, mov) — max 200 MB (~10 min)
candidateId string Requis Votre identifiant interne du candidat
jobDescription string Optionnel Description du poste pour contextualiser l'analyse
language string Optionnel fr (défaut), en, es, de

Exemple de requête

POST /apiAnalyzeVideoApplication
X-API-Key: sk_live_...
Content-Type: application/json

{
  "videoUrl": "https://storage.mon-ats.com/videos/candidat_42.mp4",
  "candidateId": "candidat_42",
  "jobDescription": "Développeur React senior, 5 ans d'expérience minimum, leadership d'équipe",
  "language": "fr"
}

Réponse immédiate (202)

{
  "jobId": "xK9pL2mNqRt8...",
  "status": "queued",
  "estimatedSeconds": 45,
  "creditsReserved": "variable"
}

Résultat final (polling)

// Interrogez GET /apiGetAccountStats ou stockez le jobId
// Résultat disponible dans Firestore : apiJobs/{jobId}
{
  "jobId": "xK9pL2mNqRt8...",
  "status": "completed",
  "result": {
    "overallScore": 78,
    "strengths": ["Communication claire", "Expérience React confirmée"],
    "weaknesses": ["Peu d'expérience management"],
    "recommendation": "hire",
    "transcript": "Bonjour, je m'appelle Marie...",
    "softSkills": {
      "confidence": 82,
      "clarity": 75,
      "enthusiasm": 90
    }
  },
  "creditsUsed": "variable",
  "tokensUsed": { "input": 4200, "output": 820, "total": 5020 }
}

Générer un CV depuis une vidéo

POST /apiBuildCvFromVideo Extrait et structure le parcours depuis une vidéo de présentation
⚡ Variable — à la consommation réelle (min. 5 crédits)

Exemple de requête

POST /apiBuildCvFromVideo
X-API-Key: sk_live_...
Content-Type: application/json

{
  "videoUrl": "https://storage.mon-ats.com/intros/marie_dupont.mp4",
  "candidateId": "marie_dupont_001",
  "language": "fr"
}

Résultat final

{
  "status": "completed",
  "result": {
    "fullName": "Marie Dupont",
    "summary": "Data scientist avec 5 ans d'expérience en NLP et ML appliqué au RH",
    "skills": ["Python", "TensorFlow", "SQL", "NLP"],
    "experience": [
      { "title": "Data Scientist", "company": "BNP Paribas", "duration": "3 ans" }
    ],
    "education": [
      { "degree": "Master IA", "school": "Paris-Saclay", "year": "2020" }
    ]
  }
}

Matcher un candidat à un poste

POST /apiMatchCandidateToJob Score de correspondance instantané entre un profil et une offre
⚡ ~7 crédits — selon volume (min. 5)

Exemple de requête

POST /apiMatchCandidateToJob
X-API-Key: sk_live_...
Content-Type: application/json

{
  "candidateProfile": {
    "skills": ["Python", "Machine Learning", "SQL"],
    "experience": "4 ans data scientist chez des startups fintech",
    "education": "Master Intelligence Artificielle, Paris-Saclay"
  },
  "jobDescription": "Data Scientist senior, spécialité NLP, 5 ans minimum, Python, gestion d'équipe souhaitée"
}

Réponse (200)

{
  "matchScore": 82,
  "matchedSkills": ["Python", "Machine Learning"],
  "missingSkills": ["NLP", "leadership"],
  "insight": "Bon profil technique, solide expérience Python/ML. Manque d'expérience NLP spécifique et de gestion d'équipe.",
  "creditsUsed": 7,
  "remainingBalance": 478
}

Analyser un dossier de candidature

POST /apiAnalyzeCandidateBundle Analyse combinée CV + lettre de motivation + profil
⚡ ~5–15 crédits selon volume

Exemple de requête

POST /apiAnalyzeCandidateBundle
X-API-Key: sk_live_...
Content-Type: application/json

{
  "cvText": "Marie Dupont — Data Scientist\n5 ans d'expérience...",
  "coverLetter": "Madame, Monsieur, Passionnée par le NLP...",
  "jobDescription": "Data Scientist NLP senior",
  "candidateId": "marie_001"
}

Réponse (200)

{
  "overallScore": 85,
  "cvScore": 88,
  "motivationScore": 79,
  "strengths": ["Expérience très pertinente", "Lettre personnalisée et convaincante"],
  "recommendation": "shortlist",
  "creditsUsed": "variable",
  "tokensUsed": { "input": 3200, "output": 600, "total": 3800 }
}

Career Coach (embed)

POST /apiCareerCoachEmbed Assistant de coaching carrière intégrable dans votre plateforme
⚡ 1 crédit — réponse synchrone

Exemple de requête

POST /apiCareerCoachEmbed
X-API-Key: sk_live_...
Content-Type: application/json

{
  "message": "Comment améliorer mon profil LinkedIn pour attirer des recruteurs tech ?",
  "candidateContext": "Développeur backend 3 ans, Python et Node.js, recherche poste senior",
  "language": "fr"
}

Réponse (200)

{
  "reply": "Pour attirer des recruteurs tech, misez sur 3 points clés : 1) Mettez vos technologies en titre (ex: 'Backend Engineer — Python & Node.js'), 2) Détaillez l'impact de vos projets en chiffres, 3) Contribuez à des projets open source et liez-les...",
  "creditsUsed": 1
}

Extraire une offre d'emploi

POST /apiExtractJobFromUrl Extrait les détails structurés d'une offre d'emploi depuis une URL ou description textuelle
⚡ 3 crédits par appel

Analyse intelligente avec 4 modes de fallback : description seule → URL+titre → URL seule → titre seul. Retourne un objet structuré compatible avec matchCandidateToJob.

Paramètres

Champ Type Requis Description
description string Optionnel Description textuelle de l'offre (priorité 1 si fournie)
jobUrl string Optionnel URL de la page offre (LinkedIn, Indeed, etc.)
jobTitle string Optionnel Intitulé du poste — renforce l'extraction
language string Optionnel fr (défaut), en, es, de, pt, it, nl, ar

Au moins un paramètre parmi description, jobUrl ou jobTitle est requis.

Exemple de requête

POST /apiExtractJobFromUrl
X-API-Key: sk_live_...
Content-Type: application/json

{
  "jobUrl": "https://www.linkedin.com/jobs/view/123456789",
  "jobTitle": "Développeur React Senior",
  "language": "fr"
}

Réponse (200)

{
  "jobTitle": "Développeur React Senior",
  "company": "TechCorp Paris",
  "location": "Paris, France",
  "contractType": "CDI",
  "salary": "55 000 € – 70 000 € / an",
  "requiredSkills": ["React", "TypeScript", "Node.js"],
  "niceToHave": ["GraphQL", "AWS"],
  "experienceYears": 5,
  "missionSummary": "Développer et maintenir des applications React haute performance...",
  "creditsUsed": 3,
  "remainingCredits": 475
}

Extraire un profil candidat

POST /apiExtractProfileFromUrl Extrait un profil structuré (CareerModel) depuis une URL LinkedIn, un CV en ligne ou un résumé textuel
⚡ 3 crédits par appel

Retourne un objet CareerModel directement injectable dans matchCandidateToJob ou analyzeCandidateBundle. 4 modes de fallback : résumé seul → URL+nom → URL seule → nom seul.

Paramètres

Champ Type Requis Description
summary string Optionnel Résumé / CV textuel du candidat (priorité 1 si fourni)
profileUrl string Optionnel URL du profil LinkedIn ou page CV en ligne
candidateName string Optionnel Nom complet du candidat — renforce l'extraction
language string Optionnel fr (défaut), en, es, de, pt, it, nl, ar

Au moins un paramètre parmi summary, profileUrl ou candidateName est requis.

Exemple de requête

POST /apiExtractProfileFromUrl
X-API-Key: sk_live_...
Content-Type: application/json

{
  "profileUrl": "https://linkedin.com/in/marie-dupont",
  "candidateName": "Marie Dupont",
  "language": "fr"
}

Réponse (200)

{
  "name": "Marie Dupont",
  "headline": "Développeuse Full-Stack • 5 ans XP",
  "skills": ["React", "Node.js", "Python"],
  "experiences": [
    { "title": "Lead Dev", "company": "StartupXYZ", "years": 3 }
  ],
  "education": [{ "degree": "Master Informatique", "school": "EPITA" }],
  "languages": ["Français", "Anglais"],
  "creditsUsed": 3,
  "remainingCredits": 472
}

Bilan de personnalité passif

Analyse passive de la personnalité d'un candidat à partir de son journal de carrière. Aucun questionnaire — tout est inféré depuis l'écriture, les thèmes récurrents et le style rédactionnel.

POST /apiAssessPassiveProfile Bilan de personnalité inféré depuis le journal
⚡ 30 crédits par appel

Paramètres

Champ Type Requis Description
texts array Tableau d'entrées textuelles. Chaque objet : { date?, title?, content }. Minimum 3 entrées avec content non vide.
framework string Framework de personnalité : big5 | disc | schein | mbti_style | hexaco | enneagram
language string Langue de la réponse. Défaut : fr. Valeurs : fr en es de pt it nl ar

Frameworks disponibles

Valeur Modèle Scores retournés
big5 OCEAN (Big Five) openness, conscientiousness, extraversion, agreeableness, neuroticism (0-100)
disc DISC comportemental D, I, S, C (0-100) + dominant_style + communication_advice
schein 8 Ancres de carrière top_anchor + anchors[] avec score et description
mbti_style Type MBTI-inspired* IE, SN, TF, JP (0-100) + type_code (ex: INTJ)
hexaco HEXACO 6 facteurs H, E, X, A, C, O (0-100)
enneagram Ennéagramme type_number (1-9) + motivation + fear + advice

* Non certifié par The Myers-Briggs Company — analyse stylistique uniquement.

Réponse commune (tous frameworks)

Champ Type Description
framework string Framework utilisé
scores object Scores spécifiques au framework (voir tableau ci-dessus)
confidence number Score de fiabilité 0.0-1.0 basé sur le volume de données
data_points integer Nombre d'entrées analysées
insights string[] 3-5 observations clés et personnalisées
career_implications string[] 3-5 implications actionnables pour recruteur/manager/coach
creditsUsed number Crédits consommés
remainingCredits number Solde restant

Codes d'erreur spécifiques

Code HTTP Description
missing_texts 400 Champ texts absent, vide ou non tableau
invalid_framework 400 Framework non supporté
insufficient_data 422 Moins de 3 entrées content valides (analyse non fiable)
analysis_failed 500 Erreur parsing réponse IA

Exemples

Requête — Big Five OCEAN
POST /apiAssessPassiveProfile
X-API-Key: sk_live_your_key
Content-Type: application/json

{
  "texts": [
    { "date": "2025-11-03", "title": "Nouvelle mission", "content": "J'ai accepté cette mission data science malgré les process rigides — la liberté technique et le sujet m'ont convaincu. J'ai besoin de construire quelque chose de réel." },
    { "date": "2025-12-02", "title": "Fin de sprint", "content": "Sprint intense. J'ai refactorisé tout le preprocessing sans qu'on me le demande. L'équipe a apprécié mais certains auraient préféré avancer plus vite. La perfectibilité a un coût." },
    { "date": "2026-01-08", "title": "Offre refusée", "content": "J'ai refusé un poste de lead tech. 70% management, ce n'est pas ce que je veux. Je préfère des missions techniques pointues et garder ma liberté." }
  ],
  "framework": "big5",
  "language": "fr"
}
Réponse — Big Five OCEAN
{
  "framework": "big5",
  "scores": {
    "openness": 82,
    "conscientiousness": 74,
    "extraversion": 41,
    "agreeableness": 68,
    "neuroticism": 29,
    "dominant_trait": "openness",
    "description": "Profil très ouvert aux idées nouvelles et créatif, avec une stabilité émotionnelle marquée. L'introversion modérée suggère un travail en profondeur plutôt qu'en réseau.",
    "confidence": 0.78,
    "data_points": 17,
    "insights": [
      "Exploration intellectuelle intense : 73% des entrées mentionnent des apprentissages ou lectures",
      "Faible névrosisme malgré 3 périodes de pression intense détectées",
      "Tendance à structurer ses pensées par écrit avant d'agir"
    ],
    "career_implications": [
      "Idéal pour des rôles nécessitant créativité et autonomie intellectuelle",
      "Éviter les environnements très procéduraux ou répétitifs",
      "Bon candidat pour le télétravail ou les missions longues sur un même projet"
    ]
  },
  "confidence": 0.78,
  "data_points": 17,
  "period_days": 90,
  "insights": ["..."],
  "career_implications": ["..."],
  "creditsUsed": 30,
  "remainingCredits": 470
}
Requête — Ancres de Schein
{
  "texts": [ /* même format... */ ],
  "framework": "schein",
  "language": "en"
}
Erreur — données insuffisantes (HTTP 422)
{
  "error": {
    "code": "insufficient_data",
    "message": "Données insuffisantes : 2 entrée(s) valide(s). Minimum 3 requis pour une analyse fiable.",
    "data_points": 2
  }
}

Compte & statistiques

GET /apiGetAccountStats Solde de crédits et historique d'utilisation
✓ Gratuit
GET /apiGetAccountStats
X-API-Key: sk_live_...

Réponse (200)

{
  "companyName": "TalentCorp SAS",
  "creditBalance": 478,
  "totalCreditsUsed": 1243,
  "allowedActions": ["analyzeVideoApplication", "matchCandidateToJob"],
  "usageByAction": {
    "analyzeVideoApplication": { "callCount": 52, "totalCredits": 936 },
    "matchCandidateToJob":     { "callCount": 44, "totalCredits": 308 }
  },
  "recentUsage": [
    { "action": "matchCandidateToJob",    "creditsUsed": 7,  "at": 1747700000000 },
    { "action": "analyzeVideoApplication", "creditsUsed": 18, "at": 1747690000000 }
  ]
}

Tests de personnalité (questionnaire)

Contrairement au bilan passif (qui déduit un profil d'un CV), ces trois endpoints vous laissent administrer un vrai test dans votre interface : vous récupérez les items, vous posez les questions, vous envoyez les réponses. Les clés de scoring ne quittent jamais nos serveurs.

Frameworks : mbti (choix forcé), big5, disc, enneagram (échelle de Likert 1-5).

POST /apiGetAssessmentItems Récupérer le questionnaire d'un test
✓ Gratuit

Retourne les items à afficher. Aucun crédit : vous pouvez charger le questionnaire avant même de savoir si le candidat ira au bout.

Paramètres

Champ Type Requis Description
framework string Requis mbti | big5 | disc | enneagram

Réponse (200)

{
  "framework": "big5",
  "format": "likert5",
  "scale": [ { "value": 1, "label": "Pas du tout d'accord" } ],
  "items": [ { "id": "b5_01", "label": "Je me lie facilement…" } ]
}
POST /apiScoreAssessment Scorer les réponses d'un candidat
⚡ 1 crédit par scoring

Scoring déterministe : aucune IA, aucun stockage. Les mêmes réponses donnent toujours le même résultat — ce qui rend le test défendable devant un candidat.

Paramètres

Champ Type Requis Description
framework string Requis Le même que celui utilisé pour récupérer les items
answers object Requis Carte itemId → réponse (clé d'option pour MBTI, entier 1-5 pour Likert)

Réponse (200)

{
  "framework": "big5",
  "scores": { "openness": 72, "conscientiousness": 64 },
  "interpretation": {
    "headline": "Curieuse et méthodique",
    "summary": "…",
    "typeCode": "INTJ",
    "dimensions": [ { "key": "openness", "label": "Ouverture" } ]
  }
}
POST /apiAssessmentNarrative Analyse IA d'un profil, en option relative à un poste
⚡ Facturé au token (minimum 3 crédits)

Forces, points de vigilance et conseils d'intégration. Fournissez soit answers (avec framework), soit directement une interpretation déjà scorée — inutile de repayer un scoring.

Paramètres

Champ Type Requis Description
framework string Optionnel Requis si vous envoyez answers
answers object Optionnel Réponses brutes (scorées au passage)
interpretation object Optionnel Résultat déjà scoré par apiScoreAssessment
jobTitle / jobDescription string Optionnel Rend l'analyse relative au poste visé

Réponse (200)

{
  "narrative": "Marie combine une forte ouverture…",
  "creditsCharged": 4,
  "remainingCredits": 474
}
⚖️ AI Act. Comme tout endpoint d'évaluation, la réponse porte un champ aiNotice à afficher tel quel. Aucune décision d'embauche ne peut reposer sur ce narratif seul.

🏢 API ATS — piloter un espace de recrutement

Tout ce que fait l'espace recruteur Spotlite (recruiter.spotlite-app.com) passe par ces endpoints : le site n'a aucun privilège que votre clé n'ait pas. Candidats, offres, candidatures, évaluations, rendez-vous, visio, signatures — vous pouvez reconstruire l'outil entier, ou n'en brancher qu'un morceau dans le vôtre.

Même authentification que l'API IA : en-tête X-API-Key, méthode POST, corps et réponse en JSON. Les erreurs gardent la structure { "error": { "code": "...", "message": "..." } }.
Gratuit, sauf mention contraire. La gestion courante (candidats, clients, offres, candidatures, notes) ne consomme aucun crédit. Sont facturés : la publication d'une offre (20 crédits, une seule fois), les traitements IA — débités du solde IA du pass, pas des crédits — et les envois de signature au-delà du quota. Un quota gratuit dépassé répond 402 pass_required.
Ce qui n'est pas documenté ici, volontairement. Les pages publiques (candidature par lien, salle visio, parcours de signature, prise de rendez-vous) s'appuient sur des endpoints joignables sans clé, protégés par des jetons à portée réduite et des plafonds par IP. Ce sont des rouages internes, pas des points d'intégration : ils peuvent changer sans préavis. Passez par les endpoints ci-dessous.

Candidats

Fiches candidats du compte : création, mise à jour, import en lot, enrichissement IA et partage anonymisé.

Endpoint Description
talentListCandidates Liste paginée, triée par date de mise à jour.
corps limit (≤ 200), cursor
talentSaveCandidate Crée ou met à jour une fiche. Sans candidateId, crée.
corps candidate {}, candidateId
talentDeleteCandidate Supprime définitivement une fiche.
corps candidateId
talentImportCandidatesBulk Import en lot (équivalent de l'import CSV du site).
corps candidates []
talentPolishCandidate Nettoie et complète une fiche par IA. Consomme le solde IA.
corps candidateId
talentParseCv Extrait un candidat depuis un CV (PDF/image). Consomme le solde IA.
corps fileBase64 | fileUrl, mimeType
talentParse Extraction structurée depuis du texte libre. Consomme le solde IA.
corps text
talentCreateCandidateShare Crée un lien public anonymisé vers une fiche. Le dossier complet (résultats de tests joints) exige un pass payant.
corps candidateId, hidden [], reveal [], anonymizeName, anonymizeLocation, includeAssessments
talentRevokeCandidateShare Désactive le lien public d'une fiche.
corps candidateId
talentMedia URL signée pour un média du compte (CV, pièce jointe).
corps path

Offres d'emploi

Brouillons, publication sur Spotlite, pages publiques de candidature et matching.

Endpoint Description
talentListJobs Toutes les offres du compte, brouillons compris.
talentSaveJob Crée ou met à jour une offre.
corps job {}, jobId
talentDeleteJob Supprime une offre.
corps jobId
talentPublishJob Publie l'offre sur Spotlite. Forfait unique de 20 crédits à la première mise en ligne.
corps jobId
talentUnpublishJob Retire l'offre de Spotlite.
corps jobId
talentImportJobsBulk Import en lot d'offres.
corps jobs []
talentCreateJobShare Crée la page publique de candidature d'une offre.
corps jobId
talentRevokeJobShare Désactive la page publique.
corps jobId
talentListJobMatches Candidats suggérés pour une offre.
corps jobId
talentMatchJob Score d'adéquation candidat ↔ offre. Consomme le solde IA.
corps jobId, candidateId

Candidatures

Ce que reçoivent vos offres, publiées ou partagées par lien.

Endpoint Description
talentListApplications Candidatures reçues, filtrables par offre.
corps jobId
talentUpdateApplication Change le statut ou les notes d'une candidature.
corps applicationId, status, note
talentApplicationToCandidate Convertit une candidature en fiche candidat.
corps applicationId

Clients & prospection

Entreprises pour lesquelles vous recrutez, et signaux faibles détectés à leur sujet.

Endpoint Description
talentListClients Liste des clients du compte.
talentSaveClient Crée ou met à jour un client.
corps client {}, clientRecordId
talentDeleteClient Supprime un client.
corps clientRecordId
talentImportClientsBulk Import en lot de clients.
corps clients []
talentCompanySignals Signaux faibles et besoins de recrutement scorés. Consomme le solde IA.
corps companyName, url

Notes & tâches

Le tableau de pilotage : notes, tâches, rattachements et positions.

Endpoint Description
talentListNotes Toutes les notes et tâches du tableau.
talentSaveNote Crée ou met à jour une note / tâche.
corps note {}, noteId
talentDeleteNote Supprime une note.
corps noteId
talentUpdateNotesLayout Enregistre les positions sur le tableau.
corps layout {}

Évaluations

Tests de personnalité envoyés aux candidats. 10 envois par mois offerts, illimité avec un pass payant (au-delà, la réponse est un 402 pass_required).

Endpoint Description
talentListAssessments Tests envoyés et leurs résultats.
talentCreateAssessment Envoie un test à un candidat.
corps candidateId, instrument (mbti | big5 | disc | enneagram)
talentGetAssessment Détail d'un test et de son rapport.
corps assessmentId
talentRevokeAssessment Annule un test non encore rempli.
corps assessmentId
talentAssessmentNarrative Analyse IA du résultat, relative à un poste. Consomme le solde IA.
corps assessmentId, jobId

Rendez-vous

Calendriers publics façon prise de rendez-vous. 30 réservations par mois offertes, illimité avec un pass payant. Les pages de réservation elles-mêmes s'appuient sur des endpoints anonymes, non documentés ici.

Endpoint Description
talentListEventTypes Vos types de rendez-vous.
talentSaveEventType Crée ou met à jour un type de rendez-vous.
corps eventType {}, eventTypeId
talentDeleteEventType Supprime un type de rendez-vous.
corps eventTypeId
talentListBookings Réservations reçues.
corps upcoming
talentCancelBooking Annule une réservation.
corps bookingId

Visio & entretiens

Salles P2P natives et entretiens IA. Jusqu'à 4 participants : gratuit. 5 à 6 participants : pass payant.

Endpoint Description
talentListRooms Salles du compte.
talentSaveRoom Crée ou modifie une salle.
corps id, title, maxParticipants, questions [], chatEnabled, screenShare
talentDeleteRoom Supprime une salle.
corps id
talentListRoomInterviews Entretiens visio transcrits.
talentDeleteRoomInterview Supprime un entretien enregistré.
corps id
roomTranscribeAnalyze Synthèse IA d'un entretien transcrit. Consomme le solde IA.
corps interviewId
talentListInterviews Modèles d'entretien IA.
talentSaveInterview Crée ou met à jour un modèle d'entretien IA.
corps interview {}, id
talentDeleteInterview Supprime un modèle.
corps id
talentListInterviewResults Entretiens passés, avec synthèse, score indicatif et transcription.

Signatures électroniques

Envoi de documents à signer. 3 envois par mois offerts, puis 10 crédits par document. Le parcours du signataire passe par des endpoints anonymes, non documentés ici.

Endpoint Description
talentListEnvelopes Documents envoyés et leur état.
talentCreateEnvelope Prépare un document à signer.
corps envelope {}
talentSignUploadUrl URL signée pour téléverser le PDF (PUT direct).
corps envelopeId, contentType
talentSendEnvelope Envoie aux signataires.
corps envelopeId
talentGetEnvelope Détail d'un document et de ses signatures.
corps envelopeId
talentVoidEnvelope Annule un document en cours.
corps envelopeId

Sourcing & vivier

Le vivier « Open to work » ne contient que des candidats qui ont explicitement accepté d'y figurer. Aucune capture de profil depuis un site tiers n'est possible, par conception.

Endpoint Description
talentSourcingSearch Cherche dans le vivier.
corps query, contractType, city, remote
talentSourcingImport Importe un profil du vivier dans vos candidats.
corps profileId

Collaboration & Copilot

Équipe, droits et discussion interne. Aucune clé API n'est jamais partagée avec un collaborateur : les invitations créent des accès distincts.

Endpoint Description
talentCollabState Membres, invitations et droits.
talentCreateInvite Invite un collaborateur (lien ou QR).
corps email, rights []
talentRevokeInvite Révoque une invitation.
corps inviteId
talentUpdateMember Modifie les droits d'un membre.
corps memberId, rights []
talentRemoveMember Retire un membre.
corps memberId
talentChatList Messages de la discussion d'équipe.
corps threadId
talentChatSend Envoie un message.
corps threadId, text

Compte & solde IA

État du compte et conversion de crédits Spotlite en solde IA.

Endpoint Description
talentLogin Valide la clé et renvoie l'état du compte : solde, permissions, pass actif. Gratuit.
corps — (la clé suffit)
talentAiTopup Convertit des crédits Spotlite en solde IA.
corps amount (m€ : 1000 | 5000 | 20000)

Politique d'utilisation acceptable

L'API Spotlite est mise à disposition dans le cadre d'un usage professionnel légitime lié au recrutement et à l'évaluation de candidats. En utilisant l'API, vous acceptez les conditions suivantes.

Règle Détail
Données personnelles Les vidéos et documents soumis doivent avoir été collectés avec le consentement explicite du candidat (RGPD Art. 6). Spotlite n'est pas responsable du traitement amont.
Usage non-discriminatoire Les scores générés sont des indicateurs d'aide à la décision. Toute décision de recrutement reste sous la responsabilité humaine de l'employeur, conformément au Code du travail.
Limites techniques Vidéos : max 200 MB (~10 min). URLs : publiques, HTTPS uniquement. Pas d'accès à des ressources internes (IP privées bloquées).
Abus & manipulation Toute tentative d'exploitation (fichiers malformés, vidéos excessivement longues, flooding) entraîne la suspension immédiate de la clé et peut donner lieu à une facturation du préjudice.
Revente & sous-traitance La revente ou redistribution des résultats à des tiers non autorisés est interdite sans accord écrit préalable.

Pour toute question : formulaire de contact

Codes d'erreur

HTTP code Cause Solution
401 missing_api_key Header X-API-Key absent Ajouter le header sur chaque requête
401 invalid_api_key Clé invalide ou révoquée Vérifier la clé dans le cockpit ou en demander une nouvelle
402 insufficient_credits Solde insuffisant Recharger le wallet dans l'app Spotlite
403 client_inactive Compte API suspendu Contacter via le formulaire de contact
403 action_not_allowed Action non incluse dans votre accès Soumettre une nouvelle demande pour l'action concernée
413 video_too_large Vidéo > 200 MB (endpoints vidéo uniquement) Réduire la durée ou la qualité d'encodage — 0 crédit débité
500 internal_error Erreur interne Spotlite Réessayer après quelques secondes. Si persistant : formulaire de contact

Exemple complet — Pipeline ATS

Intégration typique : présélection automatique de 100 candidats avec scoring vidéo + matching.

// Node.js — Pipeline complet : upload vidéo → analyse → match → shortlist
const SPOTLITE_API = 'https://europe-west6-spotlite-9da69.cloudfunctions.net';
const API_KEY = process.env.SPOTLITE_API_KEY;

async function processCandidate(candidate) {
  // 1. Vérifier le solde avant de commencer
  const stats = await fetch(`${SPOTLITE_API}/apiGetAccountStats`, {
    headers: { 'X-API-Key': API_KEY }
  }).then(r => r.json());

  if (stats.walletCredits < 22) { // 15 (vidéo) + 7 (match)
    throw new Error('Solde insuffisant pour traiter ce candidat');
  }

  // 2. Lancer l'analyse vidéo (async)
  const videoJob = await fetch(`${SPOTLITE_API}/apiAnalyzeVideoApplication`, {
    method: 'POST',
    headers: { 'X-API-Key': API_KEY, 'Content-Type': 'application/json' },
    body: JSON.stringify({
      videoUrl: candidate.videoUrl,
      candidateId: candidate.id,
      jobDescription: candidate.jobDescription,
      language: 'fr'
    })
  }).then(r => r.json());

  // 3. Matcher le profil CV (sync, pendant que la vidéo est traitée)
  const matchResult = await fetch(`${SPOTLITE_API}/apiMatchCandidateToJob`, {
    method: 'POST',
    headers: { 'X-API-Key': API_KEY, 'Content-Type': 'application/json' },
    body: JSON.stringify({
      candidateProfile: candidate.profile,
      jobDescription: candidate.jobDescription
    })
  }).then(r => r.json());

  // 4. Décision finale
  const shouldShortlist = matchResult.matchScore >= 70;
  console.log(`Candidat ${candidate.id} — match: ${matchResult.matchScore}% — jobId vidéo: ${videoJob.jobId}`);

  return {
    candidateId: candidate.id,
    matchScore: matchResult.matchScore,
    videoJobId: videoJob.jobId,
    shortlisted: shouldShortlist,
    missingSkills: matchResult.missingSkills,
  };
}

// Traitement de 100 candidats en parallèle (10 par batch)
async function processBatch(candidates) {
  const results = [];
  for (let i = 0; i < candidates.length; i += 10) {
    const batch = candidates.slice(i, i + 10);
    const batchResults = await Promise.allSettled(batch.map(processCandidate));
    results.push(...batchResults);
  }
  return results;
}
# Python — Même pipeline
import requests, os, time

API_BASE = "https://europe-west6-spotlite-9da69.cloudfunctions.net"
HEADERS = {
    "X-API-Key": os.environ["SPOTLITE_API_KEY"],
    "Content-Type": "application/json",
}

def match_candidate(candidate_profile: dict, job_description: str) -> dict:
    resp = requests.post(
        f"{API_BASE}/apiMatchCandidateToJob",
        headers=HEADERS,
        json={"candidateProfile": candidate_profile, "jobDescription": job_description},
        timeout=30
    )
    resp.raise_for_status()
    return resp.json()

# Gestion des erreurs
try:
    result = match_candidate(profile, job_desc)
    print(f"Score: {result['matchScore']}% — Crédits restants: {result['remainingBalance']}")
except requests.HTTPError as e:
    error = e.response.json().get("error", {})
    if error.get("code") == "insufficient_credits":
        print("Solde épuisé — recharger le wallet Spotlite")
    else:
        print(f"Erreur API: {error.get('message')}")

Pipeline enrichi — Bilan personnalité + Matching

Cas d'usage RH : enrichir chaque candidat avec un profil de personnalité passif avant le matching, sans questionnaire.

// Node.js — Pipeline : profil texte → bilan personnalité → matching
const SPOTLITE_API = 'https://europe-west6-spotlite-9da69.cloudfunctions.net';
const HEADERS = { 'X-API-Key': process.env.SPOTLITE_API_KEY, 'Content-Type': 'application/json' };

async function enrichAndMatch(candidate, jobDescription) {
  // 1. Bilan de personnalité passif depuis les textes du candidat
  const personality = await fetch(`${SPOTLITE_API}/apiAssessPassiveProfile`, {
    method: 'POST', headers: HEADERS,
    body: JSON.stringify({
      texts: candidate.writtenTexts,  // lettres de motivation, notes, journal...
      framework: 'big5',
      language: 'fr'
    })
  }).then(r => r.json());

  // 2. Matching CV ↔ poste (en parallèle si plusieurs candidats)
  const match = await fetch(`${SPOTLITE_API}/apiMatchCandidateToJob`, {
    method: 'POST', headers: HEADERS,
    body: JSON.stringify({
      candidateProfile: candidate.profile,
      jobDescription: jobDescription
    })
  }).then(r => r.json());

  // 3. Décision combinée : score technique + fit culturel
  const scores = personality.scores;
  const culturalFit = scores.conscientiousness > 65 && scores.openness > 60;

  return {
    candidateId: candidate.id,
    matchScore: match.matchScore,
    personality: {
      dominant: scores.dominant_trait,
      confidence: personality.confidence,
      insights: personality.insights,
    },
    culturalFit,
    shortlisted: match.matchScore >= 70 && culturalFit,
  };
}
# Python — Bilan passif depuis une liste de textes
import requests, os

API_BASE = "https://europe-west6-spotlite-9da69.cloudfunctions.net"
HEADERS = { "X-API-Key": os.environ["SPOTLITE_API_KEY"], "Content-Type": "application/json" }

def assess_personality(texts: list[dict], framework: str = "big5") -> dict:
    resp = requests.post(
        f"{API_BASE}/apiAssessPassiveProfile",
        headers=HEADERS,
        json={"texts": texts, "framework": framework, "language": "fr"},
        timeout=60
    )
    resp.raise_for_status()
    return resp.json()

# Exemple d'appel
texts = [
    {"date": "2025-11-03", "title": "Mission freelance", "content": "J'ai accepté cette mission data science..."},
    {"date": "2025-12-02", "title": "Sprint terminé",  "content": "J'ai refactorisé tout le preprocessing..."},
    {"date": "2026-01-08", "title": "Offre refusée",   "content": "J'ai refusé un poste 70% management..."},
]

result = assess_personality(texts, framework="big5")

# Résultat complet :
# result["scores"]["openness"]             → 85  (0-100)
# result["scores"]["conscientiousness"]    → 74
# result["scores"]["dominant_trait"]       → "openness"
# result["scores"]["description"]          → "Profil très ouvert..."
# result["insights"]                       → ["Exploration intellectuelle intense...", ...]
# result["career_implications"]            → ["Idéal pour rôles autonomes...", ...]
# result["confidence"]                     → 0.52  (6 entrées = confiance modérée)
print(f"Dominant trait: {result['scores']['dominant_trait']} — confidence: {result['confidence']}")
print(f"Insights: {result['insights']}")
Support : Pour toute question technique, utilisez le formulaire de contact en précisant votre companyName et le jobId ou timestamp de l'appel concerné.