Spotlite API
Ajoutez l'analyse IA de candidats à votre ATS en quelques heures — sans infrastructure ML, sans forfait fixe.
« 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. »
- 200 candidatures/semaine avec vidéo
- 2 recruteurs à temps plein pour trier
- Délais de réponse : 12 jours
- Présélection automatisée en < 5 min
- Coût : ~100 €/semaine (vs 2×SMIC RH)
- Délais ramenés à 2 jours
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
« 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. »
- Qualification manuelle de 500 profils/mois
- 45 min par profil pour rédiger un dossier
- Perte de candidats faute de réactivité
- Dossier complet généré en 30 secondes
- Coût : ~0,12 € par profil qualifié
- Taux de placement +34 %
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é
« 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é. »
- 300 offres/jour à structurer depuis des URLs
- Données incomplètes ou mal formatées
- Matching candidats trop générique
- Extraction structurée en temps réel
- Coût : ~0,03 € / offre traitée
- Candidatures pertinentes ×2
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
« 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. »
- Questionnaires de personnalité : taux d'abandon 60 %
- Résultats biaisés (candidates « optimisent » leurs réponses)
- Données comportementales inutilisées dans les dossiers
- Profil personnalité sur 100 % des candidats
- Coût : 30 crédits / bilan (≈ 0,30 €)
- Taux de présélection pertinente +28 %
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
« 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. »
- 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
- 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
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.
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"
))
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
{ "error": { "code": "...", "message": "..." } }.
Obtenir l'accès API
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.
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é.
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.
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.
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.
Vérifie votre solde de crédits, les actions autorisées et l'historique récent. Parfait pour commencer.
Score de correspondance instantané entre un profil candidat et une offre d'emploi. Retourne le score, les compétences manquantes et un insight IA.
Analyse combinée CV + lettre de motivation + contexte du poste. Retourne un score global, un score CV, un score motivation et une recommandation IA.
Intégrez Dalia, le coach carrière IA Spotlite, dans votre plateforme. Réponses personnalisées en fonction du profil du candidat.
Analysez 1 à 5 vidéos et/ou images en un seul appel. Retourne une analyse unifiée + insights multi-sources.
Génère automatiquement un CV structuré (CareerModel JSON) à partir d'une vidéo de présentation candidat.
Configure le seuil d'alerte crédits et l'email de notification pour votre compte.
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.
Extrait les détails structurés d'une offre depuis une URL (LinkedIn,
Indeed…) ou une description textuelle. Compatible avec matchCandidateToJob.
Extrait un CareerModel structuré depuis une URL LinkedIn ou
un résumé textuel. Directement injectable dans matchCandidateToJob.
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.
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édiatementré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édiatementré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 |
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
/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.
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.30cré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/httpsuniquement (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
/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).
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.
coût(m€) = (inputTokens × 0.00000028 + outputTokens × 0.00000233 + compute) × 1000 × 1.30cré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/httpsuniquement (protection SSRF) - Un seul mode d'entrée par appel :
fileBase64,fileUrloudocumentJson
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
| Code | HTTP | Cause |
|---|---|---|
invalid_argument | 400 | Aucun mode d'entrée fourni, ou mimeType manquant/non supporté |
invalid_url | 400 | fileUrl non publique ou invalide |
unsupported_type | 400 | Type de fichier distant non supporté (PDF/image attendu) |
file_too_large | 413 | Fichier > 15 Mo |
analysis_failed | 502 | Moteur 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. »
- 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é
- Titulaire + intitulé pro extraits automatiquement
professionalSummarylisible par tous les recruteurs- Tri immédiat via
acceptedetconfidenceScore
Analyser une vidéo de candidature
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
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
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
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)
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
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
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.
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
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"
}
{
"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
}
{
"texts": [ /* même format... */ ],
"framework": "schein",
"language": "en"
}
{
"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
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).
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…" } ]
}
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" } ]
}
}
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
}
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.
X-API-Key, méthode
POST, corps et réponse en JSON. Les erreurs gardent la structure
{ "error": { "code": "...", "message": "..." } }.
402 pass_required.
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']}")
companyName
et le jobId ou timestamp de l'appel concerné.