CandidalyzeAPI Docs

API REST Candidalyze

L'API Candidalyze permet d'intégrer l'analyse de CV et la comparaison de candidats directement dans vos outils RH. Tous les échanges se font en JSON via HTTPS.

Base URL

https://api.candidalyze.com/api/v1

Authentification

Deux méthodes d'authentification sont disponibles :

API Key (endpoints data)

Pour les appels d'analyse et de comparaison. Transmettez votre clé dans le header X-API-Key.

curl -X POST https://api.candidalyze.com/api/v1/analyses \
  -H "X-API-Key: sk_live_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"cv_text": "...", "job_title": "...", "job_description": "..."}'

Bearer JWT (endpoints management)

Pour la gestion des clés API et webhooks. Utilisez le token JWT de votre session Supabase.

curl https://api.candidalyze.com/api/v1/keys \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

Rate Limits

Les limites par défaut sont appliquées par clé API :

30
requêtes / minute
1 000
requêtes / jour

En cas de dépassement, l'API retourne un statut 429 Too Many Requests avec un header Retry-After.

Endpoints

POST/api/v1/analyses

Lance une analyse de CV par rapport à une fiche de poste. Le traitement est asynchrone — l'API retourne immédiatement un ID à interroger via GET.

Paramètres

cv_textstring (requis)— Texte du CV du candidat
job_titlestring (requis)— Intitulé du poste
job_descriptionstring— Description complète du poste
candidate_namestring— Nom du candidat (défaut: "Candidat API")
languagestring— Langue du rapport: fr, en, es, it, de (défaut: fr)

Corps de la requête

{
  "cv_text": "Expérience: 5 ans en développement React...",
  "job_title": "Développeur senior React",
  "job_description": "Nous recherchons un développeur senior React...",
  "candidate_name": "Alice Martin",
  "language": "fr"
}

Réponse

// 202 Accepted
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "pending",
  "credits_remaining": 14,
  "message": "Analysis started. Poll GET /api/v1/analyses/:id or listen for webhook analysis.completed."
}
GET/api/v1/analyses

Liste les analyses effectuées, triées par date de création décroissante.

Paramètres

limitnumber— Nombre max de résultats (défaut: 20, max: 100)
offsetnumber— Décalage pour la pagination

Réponse

{
  "analyses": [
    {
      "id": "ana_abc123",
      "status": "completed",
      "overall_score": 82,
      "created_at": "2026-02-24T10:30:00Z"
    }
  ],
  "total": 42,
  "limit": 20,
  "offset": 0
}
GET/api/v1/analyses/:id

Retourne le détail complet d'une analyse.

Paramètres

idstring— Identifiant de l'analyse

Réponse

{
  "id": "ana_abc123",
  "status": "completed",
  "overall_score": 82,
  "strengths": ["5 ans d'expérience React", "Stack technique alignée"],
  "concerns": ["Pas d'expérience en management"],
  "recommendations": ["Vérifier les compétences en architecture"],
  "criteria_scores": {
    "technical_skills": 90,
    "experience": 85,
    "soft_skills": 70
  },
  "summary": "Candidat solide avec un profil technique fort...",
  "created_at": "2026-02-24T10:30:00Z"
}
POST/api/v1/compare

Lance une comparaison entre 2 à 10 analyses existantes. Le traitement est asynchrone — l'API retourne immédiatement un ID à interroger via GET.

Paramètres

analysis_idsstring[] (requis)— Tableau de 2-10 IDs d'analyses complétées
job_titlestring— Intitulé du poste (par défaut celui de la première analyse)
languagestring— Langue du rapport: fr, en, es, it, de (défaut: fr)

Corps de la requête

{
  "analysis_ids": [
    "550e8400-e29b-41d4-a716-446655440000",
    "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
  ],
  "job_title": "Développeur senior React",
  "language": "fr"
}

Réponse

// 202 Accepted
{
  "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "type": "comparison",
  "status": "pending",
  "candidate_count": 2,
  "message": "Comparison started. Poll GET /api/v1/comparisons/:id or listen for webhook comparison.completed."
}
GET/api/v1/comparisons/:id

Retourne le détail d'une comparaison. Le champ type indique comparison (2 candidats) ou multi_comparison (3+). Le résultat est inclus uniquement quand status = completed.

Paramètres

idstring— Identifiant de la comparaison

Réponse

{
  "comparison": {
    "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "type": "comparison",
    "status": "completed",
    "job_title": "Développeur senior React",
    "candidates": [
      { "name": "Alice Martin", "score": 85 },
      { "name": "Bob Dupont", "score": 72 }
    ],
    "result": {
      "verdict": { "winner": "A", "confidence": 0.82, "summary": "...", "decisionFactors": [...] },
      "scoreComparison": { "overall": { "a": 85, "b": 72 }, ... },
      "relativeStrengths": { "aOverB": [...], "bOverA": [...] },
      "differentiatingQuestions": { "a": [...], "b": [...] }
    },
    "created_at": "2026-02-24T11:00:00Z",
    "updated_at": "2026-02-24T11:00:15Z"
  }
}
GET/api/v1/credits

Retourne les crédits d'analyse restants, le quota de comparaisons et les informations du plan. Accessible avec n'importe quel scope.

Réponse

{
  "credits_remaining": 15,
  "bonus_remaining": 3,
  "total_available": 18,
  "comparisons_used": 7,
  "comparisons_limit": 20,
  "plan_type": "pro",
  "period_end": "2026-03-01T00:00:00Z"
}
POST/api/v1/keys

Crée une nouvelle clé API. La clé complète n'est retournée qu'une seule fois.

Corps de la requête

{
  "name": "Production Backend",
  "scopes": ["analyses:read", "analyses:create"]
}

Réponse

{
  "id": "key_abc123",
  "key": "sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "key_prefix": "sk_live_xxxx",
  "name": "Production Backend",
  "scopes": ["analyses:read", "analyses:create"],
  "rate_limit_per_minute": 30,
  "rate_limit_per_day": 1000
}
GET/api/v1/keys

Liste les clés API actives. Le secret complet n'est jamais retourné.

Réponse

{
  "keys": [
    {
      "id": "key_abc123",
      "key_prefix": "sk_live_xxxx",
      "name": "Production Backend",
      "scopes": ["analyses:read", "analyses:create"],
      "last_used_at": "2026-02-24T09:15:00Z",
      "total_requests": 1523,
      "created_at": "2026-01-15T10:00:00Z"
    }
  ]
}
DELETE/api/v1/keys/:id

Révoque une clé API. L'action est immédiate et irréversible.

Paramètres

idstring— Identifiant de la clé

Réponse

{
  "success": true,
  "message": "API key revoked successfully"
}
POST/api/v1/webhooks

Crée un nouveau webhook. Le secret de signature n'est retourné qu'une seule fois.

Corps de la requête

{
  "url": "https://votre-serveur.com/webhook",
  "events": ["analysis.completed", "analysis.failed"],
  "description": "Notification Slack"
}

Réponse

{
  "id": "wh_abc123",
  "secret": "whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "url": "https://votre-serveur.com/webhook",
  "events": ["analysis.completed", "analysis.failed"]
}
GET/api/v1/webhooks

Liste les webhooks configurés.

Réponse

{
  "webhooks": [
    {
      "id": "wh_abc123",
      "url": "https://votre-serveur.com/webhook",
      "events": ["analysis.completed", "analysis.failed"],
      "description": "Notification Slack",
      "last_triggered_at": "2026-02-24T09:15:00Z",
      "last_status_code": 200,
      "consecutive_failures": 0
    }
  ]
}
DELETE/api/v1/webhooks/:id

Supprime un webhook.

Paramètres

idstring— Identifiant du webhook

Réponse

{
  "success": true,
  "message": "Webhook deleted successfully"
}

ATS — Positions & Candidates

Cabinet & Enterprise plans only. Requires positions:read, positions:write, candidates:read, or candidates:write scopes.

GET/api/v1/positions

List all job positions. Filter by status with ?status=active. Supports pagination.

Paramètres

limitnumber— Max results (default 50, max 100)
offsetnumber— Pagination offset (default 0)
statusstring— Filter by status: draft, active, paused, closed

Réponse

{
  "positions": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "title": "Développeur senior React",
      "description": "Nous recherchons...",
      "location": "Paris",
      "contract_type": "cdi",
      "department": "Engineering",
      "status": "active",
      "custom_criteria": [{ "name": "React", "weight": 3 }],
      "candidates_count": 12,
      "created_at": "2026-03-01T10:00:00Z",
      "updated_at": "2026-03-05T14:30:00Z"
    }
  ],
  "limit": 50,
  "offset": 0
}
POST/api/v1/positions

Create a new job position.

Paramètres

titlestring (required)— Job title (max 200 chars)
descriptionstring— Job description (max 5000 chars)
locationstring— Location (max 200 chars)
contract_typestring— cdi, cdd, interim, freelance, stage, alternance, other
departmentstring— Department name
statusstring— draft, active, paused, closed (default: active)
custom_criteriaarray— Custom scoring criteria [{name, weight}]

Corps de la requête

{
  "title": "Développeur senior React",
  "description": "Nous recherchons un développeur senior React...",
  "location": "Paris",
  "contract_type": "cdi",
  "department": "Engineering",
  "status": "active",
  "custom_criteria": [
    { "name": "React/Next.js", "weight": 3 },
    { "name": "TypeScript", "weight": 2 }
  ]
}

Réponse

// 201 Created
{
  "position": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "title": "Développeur senior React",
    "status": "active",
    "created_at": "2026-03-10T10:00:00Z"
  }
}
GET/api/v1/positions/:id

Get details of a specific position.

Paramètres

idstring— Position UUID

Réponse

{
  "position": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "title": "Développeur senior React",
    "description": "Nous recherchons...",
    "location": "Paris",
    "contract_type": "cdi",
    "department": "Engineering",
    "status": "active",
    "custom_criteria": [{ "name": "React", "weight": 3 }],
    "tag_colors": { "urgent": "#ef4444" },
    "candidates_count": 12,
    "created_at": "2026-03-01T10:00:00Z",
    "updated_at": "2026-03-05T14:30:00Z"
  }
}
PUT/api/v1/positions/:id

Update a position. Only send the fields you want to change.

Paramètres

idstring— Position UUID

Corps de la requête

{
  "status": "paused",
  "description": "Updated job description..."
}

Réponse

{
  "position": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "title": "Développeur senior React",
    "status": "paused",
    "updated_at": "2026-03-10T15:00:00Z"
  }
}
DELETE/api/v1/positions/:id

Delete a position. Candidates linked to this position are not deleted.

Paramètres

idstring— Position UUID

Réponse

{
  "success": true,
  "message": "Position deleted successfully"
}
GET/api/v1/positions/:id/candidates

List all candidates linked to a position, with scores, pipeline status, and tags.

Paramètres

idstring— Position UUID
limitnumber— Max results (default 50, max 100)
offsetnumber— Pagination offset (default 0)
pipeline_statusstring— Filter: new, shortlist, interview, offer, hired, rejected

Réponse

{
  "candidates": [
    {
      "id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
      "candidate_name": "Alice Martin",
      "job_title": "Développeur senior React",
      "overall_score": 85,
      "recommendation": "strongly_recommend",
      "pipeline_status": "shortlist",
      "is_starred": true,
      "tags": ["senior", "react-expert"],
      "analysis_summary": "Profil technique solide...",
      "created_at": "2026-03-02T10:30:00Z"
    }
  ],
  "limit": 50,
  "offset": 0
}
PATCH/api/v1/candidates/:id/pipeline

Move a candidate through the recruitment pipeline.

Paramètres

idstring— Candidate (analysis) UUID

Corps de la requête

{
  "status": "interview"
}

Réponse

{
  "success": true,
  "id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
  "pipeline_status": "interview"
}
PATCH/api/v1/candidates/:id/tags

Set tags on a candidate. Replaces all existing tags.

Paramètres

idstring— Candidate (analysis) UUID

Corps de la requête

{
  "tags": ["urgent", "senior", "react-expert"]
}

Réponse

{
  "success": true,
  "id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
  "tags": ["urgent", "senior", "react-expert"]
}
GET/api/v1/candidates/:id/notes

List all notes/comments on a candidate.

Paramètres

idstring— Candidate (analysis) UUID

Réponse

{
  "notes": [
    {
      "id": "note_abc123",
      "content": "Excellent entretien technique. À revoir pour l'offre.",
      "author_name": "Marie Dupont",
      "created_at": "2026-03-05T14:00:00Z",
      "updated_at": "2026-03-05T14:00:00Z"
    }
  ]
}
POST/api/v1/candidates/:id/notes

Add a note to a candidate.

Paramètres

idstring— Candidate (analysis) UUID
contentstring (required)— Note content (max 5000 chars)
author_namestring— Author name (default: "API")

Corps de la requête

{
  "content": "Disponible pour un entretien la semaine prochaine.",
  "author_name": "Jean Martin"
}

Réponse

// 201 Created
{
  "note": {
    "id": "note_def456",
    "content": "Disponible pour un entretien la semaine prochaine.",
    "author_name": "Jean Martin",
    "created_at": "2026-03-10T16:00:00Z"
  }
}

Webhooks

Signature HMAC-SHA256

Chaque requête webhook inclut un header X-Candidalyze-Signature contenant une signature HMAC-SHA256 du body, calculée avec votre secret. Le type d'événement est transmis via X-Candidalyze-Event.

const crypto = require('crypto');

// Headers: X-Candidalyze-Signature, X-Candidalyze-Event
function verifyWebhookSignature(body, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(body)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

Événements disponibles

ÉvénementDescription
analysis.completedUne analyse de CV est terminée
analysis.failedUne analyse de CV a échoué
comparison.completedUne comparaison multi-candidats est terminée
interview_kit.readyUn kit d'entretien est prêt

Payload d'exemple

{
  "event": "analysis.completed",
  "timestamp": "2026-02-24T10:30:00Z",
  "data": {
    "id": "ana_abc123",
    "status": "completed",
    "overall_score": 82,
    "candidate_name": "Alice Martin"
  }
}

Politique de retry

En cas d'échec (statut HTTP hors 2xx), le webhook est rejoué avec un backoff exponentiel. Après 10 échecs consécutifs, le webhook est automatiquement désactivé. Vous pouvez le supprimer et en recréer un pour le réactiver.

Codes d'erreur

CodeSignificationDescription
400Bad RequestParamètres manquants ou invalides
401UnauthorizedClé API manquante ou invalide
402Payment RequiredCrédits épuisés ou quota de comparaisons atteint
403ForbiddenScope insuffisant pour cette action
404Not FoundRessource introuvable
429Too Many RequestsRate limit dépassé, réessayez après le délai indiqué
500Internal Server ErrorErreur serveur, contactez le support

Format d'erreur

{
  "error": "Invalid API key",
  "code": "INVALID_KEY",
  "status": 401
}

Plans et limites

FonctionnalitéCabinetEnterprise
Clés API max55
Webhooks max55
Rate limit / min3030
Rate limit / jour1 0001 000
Scopes analysesOuiOui
Scopes comparaisonsOuiOui
Scopes webhooksOuiOui
ATS (positions, candidates, pipeline, notes)OuiOui

Créez un compte pour commencer à utiliser l'API.