Aller au contenu

Dernière mise à jour : 15 octobre 2026

Candidate Analyzer API

Port local (Docker Compose) : 30003 | Repo : solyntek/djinn/services/djinn-candidate-analyzer

Aide à la décision de recrutement : scoring déterministe, explicable et rejouable de candidats par rapport à un poste. Aucun LLM ni embeddings dans le calcul (ADR-028). Le service classe et explique ; il ne décide jamais, ne rejette jamais.

Endpoints

Méthode Route Description
GET /health Health check (sans authentification) ; renvoie version et scoring_version
POST /v1/analyze Scorer 1 candidat vs 1 poste
POST /v1/analyze/batch Scorer 1 à 100 candidats, retour trié par score (meilleur d'abord)
POST /v1/analyze/competency-file Scorer un dossier de compétences (texte brut, 500 000 caractères max) : les termes du poste trouvés dans le texte deviennent les compétences
POST /v1/hr/analyses/{analysis_id}/decisions Enregistrer la décision humaine prise sur une analyse (201)
POST /v1/hr/subjects/ref Références pseudonymes d'un candidat, une par mois (limitée à 10/min)
PUT /v1/hr/settings Rétention du journal et arrêt d'urgence par client

Scoring (5 dimensions)

Dimension Poids par défaut Méthode
Skills 0.30 Correspondance par jetons entiers de required_skills / preferred_skills (80 % / 20 % si les deux sont fournis)
Experience 0.25 Mois d'expérience (postes qui se chevauchent comptés une fois) vs min_experience_years ; non évaluée sans min_experience_years
Education 0.15 required_education trouvé dans l'intitulé ou le domaine d'un diplôme
Languages 0.15 required_languages trouvées parmi les langues du candidat
Location 0.15 Lieu du candidat vs location et remote_policy du poste ; non évaluée sans location du poste, si remote_policy vaut remote, ou si anonymize est vrai

Les poids (weights, optionnels) doivent totaliser 1.0. Ils sont renormalisés sur les seules dimensions évaluées. La correspondance ignore casse et accents, normalise les formes genrées françaises et garde exacts les noms à symboles (C, C++, C#, .NET, R, Go, F#) : java ne correspond pas à javascript.

Le score global est compris entre 0 et 1 ; fit_level vaut excellent (>= 0.8), good (>= 0.6), moderate (>= 0.4) ou weak.

Options de requête (les trois routes d'analyse)

Champ Défaut Description
weights 0.30 / 0.25 / 0.15 / 0.15 / 0.15 Poids par dimension, somme = 1.0
anonymize false Retire le lieu pour toute la requête ; la dimension lieu n'est alors pas évaluée
as_of aujourd'hui (UTC) Date de référence du calcul de l'expérience, renvoyée dans la réponse
lang fr Langue de l'explication (fr ou en)

candidate.id est l'identifiant du candidat chez vous : utilisez une valeur opaque (ni e-mail, ni entier séquentiel). Il est pseudonymisé avant journalisation. firstname, lastname, email, phone, linkedin et raw_text sont dépréciés : acceptés pour que la sortie CV de doc-extract puisse être envoyée telle quelle, mais ignorés, jamais journalisés, et listés dans minimization.ignored_fields.

Contenu de la réponse

analysis_id, scoring_version, notice_version, as_of, overall_score, fit_level, weights_used, une entrée par dimension (score, weight appliqué, evaluated, evidence.matched / evidence.missing, rationale issue d'un gabarit versionné, template_id), proxy_criteria (lieu et/ou formation lorsqu'ils ont pesé sur le score), human_review_required (toujours true), explanation, minimization, strengths, gaps, summary. Même entrée, même scoring_version et même as_of donnent toujours le même score.

Décision humaine, contestation et arrêt d'urgence

  • POST /v1/hr/analyses/{analysis_id}/decisions : decision vaut shortlisted, not_retained, on_hold, review_requested (le candidat conteste, RGPD art. 22(3)) ou revised ; reviewer_ref est un identifiant opaque du relecteur ; supersedes est le decision_id remplacé. La réponse donne decision_id, agrees_with_score (calculé par le serveur) et seconds_since_scoring.
  • POST /v1/hr/subjects/ref avec {"subject_id": "<candidate.id>"} renvoie le subject_ref pseudonyme de chaque mois dont la clé existe encore. C'est l'entrée du POST /v1/gdpr/export d'audit-trail (subject_type: "resource_id") pour répondre à une demande d'accès.
  • PUT /v1/hr/settings : retention_months (6 à 60, 6 par défaut) et scoring_enabled. false est l'arrêt d'urgence : les routes d'analyse répondent 423. Réduire la rétention purge immédiatement ; l'augmenter ne ramène pas les clés détruites.

Scores et décisions sont écrits dans l'audit trail sans donnée d'identité. La rétention se fait par destruction des clés mensuelles de pseudonymisation : ensuite, les références ne sont plus reliables côté Djinn (ce n'est pas une anonymisation au sens du RGPD).

Erreurs

Les erreurs de ce service ont la forme {"detail": {"code": "...", "message": "..."}} :

HTTP code Signification
404 analysis_not_found Analyse inconnue, purgée, ou d'un autre client
422 no_evaluable_criteria Le poste et les poids ne laissent aucune dimension évaluable : fixez au moins un critère de poids positif
422 (validation) Requête invalide (poids ne totalisant pas 1.0, remote_policy inconnu, plus de 100 candidats...)
423 scoring_disabled Arrêt d'urgence activé pour ce client
500 audit emission failed : rien n'a été écrit, réessayer
503 Base non configurée

Notice et limites (ADR-028)

Le recrutement est un usage à haut risque au sens de l'AI Act européen (annexe III, 4(a)). Djinn est le fournisseur, vous êtes le déployeur ; détourner la finalité fait de vous un fournisseur (art. 25). notice_version identifie la notice d'utilisation (art. 13) qui décrit le scoring renvoyé avec chaque analyse. En particulier :

  • Aide à la décision uniquement : chaque analyse exige une relecture humaine (human_review_required: true). N'utilisez pas le score comme décision automatisée.
  • Correspondance lexicale : la liste de compétences d'un candidat peut être gonflée ; aucune évaluation de la personnalité ni des soft skills.
  • Critères indirects : le lieu et la formation restent configurables et sont signalés dans proxy_criteria ; un rapport de biais est publié pour chaque scoring_version.
  • L'exactitude n'est pas mesurée dans cette version (un plan de mesure existe).

Exemple

curl -X POST http://localhost:30003/v1/analyze \
  -H "X-API-Key: votre-cle-locale" \
  -H "Content-Type: application/json" \
  -d '{
    "job": {"title": "Python Developer", "required_skills": ["Python", "FastAPI"]},
    "candidate": {"id": "cand-7f3a", "skills": ["Python", "FastAPI", "Docker"]},
    "lang": "fr"
  }'
curl -X POST https://api.dev.djinn.solyntek.io/v1/analyze \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "job": {"title": "Python Developer", "required_skills": ["Python", "FastAPI"]},
    "candidate": {"id": "cand-7f3a", "skills": ["Python", "FastAPI", "Docker"]},
    "lang": "fr"
  }'

Référence interactive

Générée à chaque build depuis la spec versionnée openapi/candidate-analyzer-v1.json (ADR-011) — aucune route documentée à la main.

Djinn Candidate Analyzer 0.1.0

Aide à la décision de recrutement : scoring explicable, journalisé et minimisé (ADR-028)


Endpoints


GET /health

Health

Description

Health check, without authentication.

Responses

{
    "status": "string",
    "version": "string",
    "scoring_version": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "properties": {
        "status": {
            "type": "string",
            "title": "Status",
            "default": "ok"
        },
        "version": {
            "type": "string",
            "title": "Version",
            "default": "0.1.0"
        },
        "scoring_version": {
            "type": "string",
            "title": "Scoring Version",
            "default": "2026.10.1"
        }
    },
    "type": "object",
    "title": "HealthResponse",
    "description": "Response for health check."
}

POST /v1/analyze

Analyze

Description

Score one candidate against one job: decision support, never a rejection.

Input parameters

Parameter In Type Default Nullable Description
X-API-Key header No

Request body

{
    "weights": {
        "skills": 10.12,
        "experience": 10.12,
        "education": 10.12,
        "languages": 10.12,
        "location": 10.12
    },
    "anonymize": true,
    "as_of": null,
    "lang": "fr",
    "job": {
        "title": "string",
        "description": "string",
        "required_skills": [
            "string"
        ],
        "preferred_skills": [
            "string"
        ],
        "min_experience_years": null,
        "required_languages": [
            "string"
        ],
        "required_education": null,
        "location": null,
        "remote_policy": null
    },
    "candidate": {
        "id": null,
        "firstname": null,
        "lastname": null,
        "email": null,
        "phone": null,
        "linkedin": null,
        "raw_text": null,
        "skills": [
            "string"
        ],
        "languages": [
            "string"
        ],
        "experiences": [
            {
                "company": "string",
                "title": "string",
                "location": "string",
                "start_date": "string",
                "end_date": "string",
                "description": "string"
            }
        ],
        "diplomas": [
            {
                "school": "string",
                "degree": "string",
                "field_of_study": "string",
                "start_date": "string",
                "end_date": "string"
            }
        ],
        "certifications": [
            {}
        ],
        "location": null
    }
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the request body
{
    "properties": {
        "weights": {
            "$ref": "#/components/schemas/ScoringWeights"
        },
        "anonymize": {
            "type": "boolean",
            "title": "Anonymize",
            "description": "Retire le lieu (profil et expériences) pour tout le lot ; la localisation n'est alors pas évaluée.",
            "default": false
        },
        "as_of": {
            "anyOf": [
                {
                    "type": "string",
                    "format": "date"
                },
                {
                    "type": "null"
                }
            ],
            "title": "As Of",
            "description": "Date de référence du calcul de l'expérience ; défaut : date du jour (UTC), renvoyée dans l'analyse."
        },
        "lang": {
            "type": "string",
            "enum": [
                "fr",
                "en"
            ],
            "title": "Lang",
            "description": "Langue de l'explication.",
            "default": "fr"
        },
        "job": {
            "$ref": "#/components/schemas/JobDescription"
        },
        "candidate": {
            "$ref": "#/components/schemas/CandidateProfile"
        }
    },
    "type": "object",
    "required": [
        "job",
        "candidate"
    ],
    "title": "AnalyzeRequest",
    "description": "Requête d'analyse d'un candidat."
}

Responses

{
    "analysis_id": "ba081868-516e-40d6-a19a-19dcca795e3f",
    "candidate_id": null,
    "scoring_version": "string",
    "notice_version": "string",
    "as_of": "2022-04-13",
    "overall_score": 10.12,
    "fit_level": "excellent",
    "weights_used": {},
    "dimension_scores": [
        {
            "dimension": "skills",
            "score": 10.12,
            "weight": 10.12,
            "evaluated": true,
            "evidence": {
                "matched": [
                    "string"
                ],
                "missing": [
                    "string"
                ]
            },
            "rationale": "string",
            "template_id": "string",
            "details": "string"
        }
    ],
    "proxy_criteria": [
        "location"
    ],
    "human_review_required": true,
    "explanation": [
        "string"
    ],
    "minimization": {
        "ignored_fields": [
            "string"
        ],
        "dimensions_not_evaluated": [
            "string"
        ]
    },
    "strengths": [
        "string"
    ],
    "gaps": [
        "string"
    ],
    "summary": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "properties": {
        "analysis_id": {
            "type": "string",
            "format": "uuid",
            "title": "Analysis Id"
        },
        "candidate_id": {
            "anyOf": [
                {
                    "type": "string"
                },
                {
                    "type": "null"
                }
            ],
            "title": "Candidate Id"
        },
        "scoring_version": {
            "type": "string",
            "title": "Scoring Version"
        },
        "notice_version": {
            "type": "string",
            "title": "Notice Version"
        },
        "as_of": {
            "type": "string",
            "format": "date",
            "title": "As Of"
        },
        "overall_score": {
            "type": "number",
            "maximum": 1.0,
            "minimum": 0.0,
            "title": "Overall Score"
        },
        "fit_level": {
            "type": "string",
            "enum": [
                "excellent",
                "good",
                "moderate",
                "weak"
            ],
            "title": "Fit Level"
        },
        "weights_used": {
            "additionalProperties": {
                "type": "number"
            },
            "propertyNames": {
                "enum": [
                    "skills",
                    "experience",
                    "education",
                    "languages",
                    "location"
                ]
            },
            "type": "object",
            "title": "Weights Used"
        },
        "dimension_scores": {
            "items": {
                "$ref": "#/components/schemas/DimensionScore"
            },
            "type": "array",
            "title": "Dimension Scores"
        },
        "proxy_criteria": {
            "items": {
                "type": "string",
                "enum": [
                    "location",
                    "education"
                ]
            },
            "type": "array",
            "title": "Proxy Criteria",
            "description": "Critères indirects (lieu, formation) qui ont pesé : poids > 0 et dimension évaluée."
        },
        "human_review_required": {
            "type": "boolean",
            "const": true,
            "title": "Human Review Required",
            "default": true
        },
        "explanation": {
            "items": {
                "type": "string"
            },
            "type": "array",
            "title": "Explanation"
        },
        "minimization": {
            "$ref": "#/components/schemas/MinimizationReport"
        },
        "strengths": {
            "items": {
                "type": "string"
            },
            "type": "array",
            "title": "Strengths"
        },
        "gaps": {
            "items": {
                "type": "string"
            },
            "type": "array",
            "title": "Gaps"
        },
        "summary": {
            "type": "string",
            "title": "Summary",
            "default": ""
        }
    },
    "type": "object",
    "required": [
        "analysis_id",
        "scoring_version",
        "notice_version",
        "as_of",
        "overall_score",
        "fit_level",
        "weights_used",
        "dimension_scores",
        "explanation",
        "minimization"
    ],
    "title": "CandidateAnalysis",
    "description": "Analyse d'un candidat : aide à la décision, jamais un rejet."
}

POST /v1/analyze/batch

Analyze Batch

Description

Score up to 100 candidates against one job, best first.

Input parameters

Parameter In Type Default Nullable Description
X-API-Key header No

Request body

{
    "weights": {
        "skills": 10.12,
        "experience": 10.12,
        "education": 10.12,
        "languages": 10.12,
        "location": 10.12
    },
    "anonymize": true,
    "as_of": null,
    "lang": "fr",
    "job": {
        "title": "string",
        "description": "string",
        "required_skills": [
            "string"
        ],
        "preferred_skills": [
            "string"
        ],
        "min_experience_years": null,
        "required_languages": [
            "string"
        ],
        "required_education": null,
        "location": null,
        "remote_policy": null
    },
    "candidates": [
        {
            "id": null,
            "firstname": null,
            "lastname": null,
            "email": null,
            "phone": null,
            "linkedin": null,
            "raw_text": null,
            "skills": [
                "string"
            ],
            "languages": [
                "string"
            ],
            "experiences": [
                {
                    "company": "string",
                    "title": "string",
                    "location": "string",
                    "start_date": "string",
                    "end_date": "string",
                    "description": "string"
                }
            ],
            "diplomas": [
                {
                    "school": "string",
                    "degree": "string",
                    "field_of_study": "string",
                    "start_date": "string",
                    "end_date": "string"
                }
            ],
            "certifications": [
                {}
            ],
            "location": null
        }
    ]
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the request body
{
    "properties": {
        "weights": {
            "$ref": "#/components/schemas/ScoringWeights"
        },
        "anonymize": {
            "type": "boolean",
            "title": "Anonymize",
            "description": "Retire le lieu (profil et expériences) pour tout le lot ; la localisation n'est alors pas évaluée.",
            "default": false
        },
        "as_of": {
            "anyOf": [
                {
                    "type": "string",
                    "format": "date"
                },
                {
                    "type": "null"
                }
            ],
            "title": "As Of",
            "description": "Date de référence du calcul de l'expérience ; défaut : date du jour (UTC), renvoyée dans l'analyse."
        },
        "lang": {
            "type": "string",
            "enum": [
                "fr",
                "en"
            ],
            "title": "Lang",
            "description": "Langue de l'explication.",
            "default": "fr"
        },
        "job": {
            "$ref": "#/components/schemas/JobDescription"
        },
        "candidates": {
            "items": {
                "$ref": "#/components/schemas/CandidateProfile"
            },
            "type": "array",
            "maxItems": 100,
            "minItems": 1,
            "title": "Candidates"
        }
    },
    "type": "object",
    "required": [
        "job",
        "candidates"
    ],
    "title": "AnalyzeBatchRequest",
    "description": "Requête d'analyse de plusieurs candidats (100 au plus)."
}

Responses

{
    "job_title": "string",
    "candidates": [
        {
            "analysis_id": "edbb6810-c94b-4121-94fb-ff3896acbd9c",
            "candidate_id": null,
            "scoring_version": "string",
            "notice_version": "string",
            "as_of": "2022-04-13",
            "overall_score": 10.12,
            "fit_level": "excellent",
            "weights_used": {},
            "dimension_scores": [
                {
                    "dimension": "skills",
                    "score": 10.12,
                    "weight": 10.12,
                    "evaluated": true,
                    "evidence": {
                        "matched": [
                            "string"
                        ],
                        "missing": [
                            "string"
                        ]
                    },
                    "rationale": "string",
                    "template_id": "string",
                    "details": "string"
                }
            ],
            "proxy_criteria": [
                "location"
            ],
            "human_review_required": true,
            "explanation": [
                "string"
            ],
            "minimization": {
                "ignored_fields": [
                    "string"
                ],
                "dimensions_not_evaluated": [
                    "string"
                ]
            },
            "strengths": [
                "string"
            ],
            "gaps": [
                "string"
            ],
            "summary": "string"
        }
    ],
    "total_analyzed": 0
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "properties": {
        "job_title": {
            "type": "string",
            "title": "Job Title"
        },
        "candidates": {
            "items": {
                "$ref": "#/components/schemas/CandidateAnalysis"
            },
            "type": "array",
            "title": "Candidates"
        },
        "total_analyzed": {
            "type": "integer",
            "title": "Total Analyzed"
        }
    },
    "type": "object",
    "required": [
        "job_title",
        "candidates",
        "total_analyzed"
    ],
    "title": "AnalyzeBatchResponse",
    "description": "Réponse d'analyse batch (triée par score décroissant)."
}

POST /v1/analyze/competency-file

Analyze Competency File

Description

Score a competency file: the job's terms found in the text become the skills.

The text is read once and never stored or journaled; the other dimensions have no input, so those the job sets a criterion for are evaluated with nothing found.

Input parameters

Parameter In Type Default Nullable Description
X-API-Key header No

Request body

{
    "weights": {
        "skills": 10.12,
        "experience": 10.12,
        "education": 10.12,
        "languages": 10.12,
        "location": 10.12
    },
    "anonymize": true,
    "as_of": null,
    "lang": "fr",
    "job": {
        "title": "string",
        "description": "string",
        "required_skills": [
            "string"
        ],
        "preferred_skills": [
            "string"
        ],
        "min_experience_years": null,
        "required_languages": [
            "string"
        ],
        "required_education": null,
        "location": null,
        "remote_policy": null
    },
    "competency_text": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the request body
{
    "properties": {
        "weights": {
            "$ref": "#/components/schemas/ScoringWeights"
        },
        "anonymize": {
            "type": "boolean",
            "title": "Anonymize",
            "description": "Retire le lieu (profil et expériences) pour tout le lot ; la localisation n'est alors pas évaluée.",
            "default": false
        },
        "as_of": {
            "anyOf": [
                {
                    "type": "string",
                    "format": "date"
                },
                {
                    "type": "null"
                }
            ],
            "title": "As Of",
            "description": "Date de référence du calcul de l'expérience ; défaut : date du jour (UTC), renvoyée dans l'analyse."
        },
        "lang": {
            "type": "string",
            "enum": [
                "fr",
                "en"
            ],
            "title": "Lang",
            "description": "Langue de l'explication.",
            "default": "fr"
        },
        "job": {
            "$ref": "#/components/schemas/JobDescription"
        },
        "competency_text": {
            "type": "string",
            "maxLength": 500000,
            "minLength": 1,
            "title": "Competency Text"
        }
    },
    "type": "object",
    "required": [
        "job",
        "competency_text"
    ],
    "title": "CompetencyFileRequest",
    "description": "Requête d'analyse d'un dossier de compétences (texte brut, jamais journalisé)."
}

Responses

{
    "analysis_id": "9f96978d-27cc-4ad3-8383-beb359ead27b",
    "candidate_id": null,
    "scoring_version": "string",
    "notice_version": "string",
    "as_of": "2022-04-13",
    "overall_score": 10.12,
    "fit_level": "excellent",
    "weights_used": {},
    "dimension_scores": [
        {
            "dimension": "skills",
            "score": 10.12,
            "weight": 10.12,
            "evaluated": true,
            "evidence": {
                "matched": [
                    "string"
                ],
                "missing": [
                    "string"
                ]
            },
            "rationale": "string",
            "template_id": "string",
            "details": "string"
        }
    ],
    "proxy_criteria": [
        "location"
    ],
    "human_review_required": true,
    "explanation": [
        "string"
    ],
    "minimization": {
        "ignored_fields": [
            "string"
        ],
        "dimensions_not_evaluated": [
            "string"
        ]
    },
    "strengths": [
        "string"
    ],
    "gaps": [
        "string"
    ],
    "summary": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "properties": {
        "analysis_id": {
            "type": "string",
            "format": "uuid",
            "title": "Analysis Id"
        },
        "candidate_id": {
            "anyOf": [
                {
                    "type": "string"
                },
                {
                    "type": "null"
                }
            ],
            "title": "Candidate Id"
        },
        "scoring_version": {
            "type": "string",
            "title": "Scoring Version"
        },
        "notice_version": {
            "type": "string",
            "title": "Notice Version"
        },
        "as_of": {
            "type": "string",
            "format": "date",
            "title": "As Of"
        },
        "overall_score": {
            "type": "number",
            "maximum": 1.0,
            "minimum": 0.0,
            "title": "Overall Score"
        },
        "fit_level": {
            "type": "string",
            "enum": [
                "excellent",
                "good",
                "moderate",
                "weak"
            ],
            "title": "Fit Level"
        },
        "weights_used": {
            "additionalProperties": {
                "type": "number"
            },
            "propertyNames": {
                "enum": [
                    "skills",
                    "experience",
                    "education",
                    "languages",
                    "location"
                ]
            },
            "type": "object",
            "title": "Weights Used"
        },
        "dimension_scores": {
            "items": {
                "$ref": "#/components/schemas/DimensionScore"
            },
            "type": "array",
            "title": "Dimension Scores"
        },
        "proxy_criteria": {
            "items": {
                "type": "string",
                "enum": [
                    "location",
                    "education"
                ]
            },
            "type": "array",
            "title": "Proxy Criteria",
            "description": "Critères indirects (lieu, formation) qui ont pesé : poids > 0 et dimension évaluée."
        },
        "human_review_required": {
            "type": "boolean",
            "const": true,
            "title": "Human Review Required",
            "default": true
        },
        "explanation": {
            "items": {
                "type": "string"
            },
            "type": "array",
            "title": "Explanation"
        },
        "minimization": {
            "$ref": "#/components/schemas/MinimizationReport"
        },
        "strengths": {
            "items": {
                "type": "string"
            },
            "type": "array",
            "title": "Strengths"
        },
        "gaps": {
            "items": {
                "type": "string"
            },
            "type": "array",
            "title": "Gaps"
        },
        "summary": {
            "type": "string",
            "title": "Summary",
            "default": ""
        }
    },
    "type": "object",
    "required": [
        "analysis_id",
        "scoring_version",
        "notice_version",
        "as_of",
        "overall_score",
        "fit_level",
        "weights_used",
        "dimension_scores",
        "explanation",
        "minimization"
    ],
    "title": "CandidateAnalysis",
    "description": "Analyse d'un candidat : aide à la décision, jamais un rejet."
}

POST /v1/hr/analyses/{analysis_id}/decisions

Record Decision

Description

Journal a human decision; agreement with the score is computed here.

review_requested records a contest (GDPR art. 22(3)), revised the new review; supersedes names the decision_id it replaces. Unknown, purged and other tenants' analyses answer the same 404.

Input parameters

Parameter In Type Default Nullable Description
analysis_id path string No
X-API-Key header No

Request body

{
    "decision": "shortlisted",
    "reviewer_ref": "string",
    "supersedes": null
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the request body
{
    "properties": {
        "decision": {
            "type": "string",
            "enum": [
                "shortlisted",
                "not_retained",
                "on_hold",
                "review_requested",
                "revised"
            ],
            "title": "Decision"
        },
        "reviewer_ref": {
            "type": "string",
            "maxLength": 200,
            "minLength": 1,
            "title": "Reviewer Ref",
            "description": "Identifiant opaque du relecteur chez le client ; pseudonymisé avant journalisation."
        },
        "supersedes": {
            "anyOf": [
                {
                    "type": "string",
                    "format": "uuid"
                },
                {
                    "type": "null"
                }
            ],
            "title": "Supersedes",
            "description": "`decision_id` de la décision remplacée."
        }
    },
    "type": "object",
    "required": [
        "decision",
        "reviewer_ref"
    ],
    "title": "DecisionRequest",
    "description": "Décision humaine sur une analyse (spec §4.3)."
}

Responses

{
    "decision_id": "f4fd8c93-bc50-4176-a366-2f9c361102ea",
    "agrees_with_score": true,
    "seconds_since_scoring": 0
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "properties": {
        "decision_id": {
            "type": "string",
            "format": "uuid",
            "title": "Decision Id"
        },
        "agrees_with_score": {
            "type": "boolean",
            "title": "Agrees With Score"
        },
        "seconds_since_scoring": {
            "type": "integer",
            "title": "Seconds Since Scoring"
        }
    },
    "type": "object",
    "required": [
        "decision_id",
        "agrees_with_score",
        "seconds_since_scoring"
    ],
    "title": "DecisionResponse"
}

{
    "detail": [
        {
            "loc": [
                null
            ],
            "msg": "string",
            "type": "string",
            "input": null,
            "ctx": {}
        }
    ]
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "properties": {
        "detail": {
            "items": {
                "$ref": "#/components/schemas/ValidationError"
            },
            "type": "array",
            "title": "Detail"
        }
    },
    "type": "object",
    "title": "HTTPValidationError"
}

POST /v1/hr/subjects/ref

Subject Refs

Description

Pseudonymous reference of a candidate, one per month whose key still exists.

Input of audit-trail's POST /v1/gdpr/export (subject_type="resource_id"). Each reference returned is journaled; Kong rate-limits this route harder.

Input parameters

Parameter In Type Default Nullable Description
X-API-Key header No

Request body

{
    "subject_id": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the request body
{
    "properties": {
        "subject_id": {
            "type": "string",
            "maxLength": 200,
            "minLength": 1,
            "title": "Subject Id",
            "description": "Le `CandidateProfile.id` des analyses."
        }
    },
    "type": "object",
    "required": [
        "subject_id"
    ],
    "title": "SubjectRefRequest"
}

Responses

{
    "subject_refs": [
        {
            "period": "string",
            "subject_ref": "string"
        }
    ]
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "properties": {
        "subject_refs": {
            "items": {
                "$ref": "#/components/schemas/SubjectRef"
            },
            "type": "array",
            "title": "Subject Refs"
        }
    },
    "type": "object",
    "required": [
        "subject_refs"
    ],
    "title": "SubjectRefResponse"
}

{
    "detail": [
        {
            "loc": [
                null
            ],
            "msg": "string",
            "type": "string",
            "input": null,
            "ctx": {}
        }
    ]
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "properties": {
        "detail": {
            "items": {
                "$ref": "#/components/schemas/ValidationError"
            },
            "type": "array",
            "title": "Detail"
        }
    },
    "type": "object",
    "title": "HTTPValidationError"
}

PUT /v1/hr/settings

Put Settings

Description

Retention (6 to 60 months) and the per-tenant emergency stop, journaled.

Lowering the retention purges in the same transaction; raising it does not bring destroyed keys back.

Input parameters

Parameter In Type Default Nullable Description
X-API-Key header No

Request body

{
    "retention_months": 0,
    "scoring_enabled": true
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the request body
{
    "properties": {
        "retention_months": {
            "type": "integer",
            "maximum": 60.0,
            "minimum": 6.0,
            "title": "Retention Months",
            "description": "Conservation du journal RH, en mois (6 à 60)."
        },
        "scoring_enabled": {
            "type": "boolean",
            "title": "Scoring Enabled",
            "description": "false = arrêt d'urgence : les routes d'analyse répondent 423."
        }
    },
    "type": "object",
    "required": [
        "retention_months",
        "scoring_enabled"
    ],
    "title": "HrSettingsUpdate"
}

Responses

{
    "retention_months": 0,
    "scoring_enabled": true
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "properties": {
        "retention_months": {
            "type": "integer",
            "maximum": 60.0,
            "minimum": 6.0,
            "title": "Retention Months",
            "default": 6
        },
        "scoring_enabled": {
            "type": "boolean",
            "title": "Scoring Enabled",
            "default": true
        }
    },
    "type": "object",
    "title": "HrSettings"
}

{
    "detail": [
        {
            "loc": [
                null
            ],
            "msg": "string",
            "type": "string",
            "input": null,
            "ctx": {}
        }
    ]
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "properties": {
        "detail": {
            "items": {
                "$ref": "#/components/schemas/ValidationError"
            },
            "type": "array",
            "title": "Detail"
        }
    },
    "type": "object",
    "title": "HTTPValidationError"
}

Schemas

AnalyzeBatchRequest

Name Type Description
anonymize boolean Retire le lieu (profil et expériences) pour tout le lot ; la localisation n'est alors pas évaluée.
as_of Date de référence du calcul de l'expérience ; défaut : date du jour (UTC), renvoyée dans l'analyse.
candidates Array<CandidateProfile>
job JobDescription
lang string Langue de l'explication.
weights ScoringWeights

AnalyzeBatchResponse

Name Type Description
candidates Array<CandidateAnalysis>
job_title string
total_analyzed integer

AnalyzeRequest

Name Type Description
anonymize boolean Retire le lieu (profil et expériences) pour tout le lot ; la localisation n'est alors pas évaluée.
as_of Date de référence du calcul de l'expérience ; défaut : date du jour (UTC), renvoyée dans l'analyse.
candidate CandidateProfile
job JobDescription
lang string Langue de l'explication.
weights ScoringWeights

CandidateAnalysis

Name Type Description
analysis_id string(uuid)
as_of string(date)
candidate_id
dimension_scores Array<DimensionScore>
explanation Array<string>
fit_level string
gaps Array<string>
human_review_required boolean
minimization MinimizationReport
notice_version string
overall_score number
proxy_criteria Array<string> Critères indirects (lieu, formation) qui ont pesé : poids > 0 et dimension évaluée.
scoring_version string
strengths Array<string>
summary string
weights_used

CandidateProfile

Name Type Description
certifications Array<>
diplomas Array<Diploma>
email Ignoré (minimisation), jamais journalisé ; déprécié.
experiences Array<Experience>
firstname Ignoré (minimisation), jamais journalisé ; déprécié.
id Identifiant opaque du candidat chez le client (ni e-mail, ni entier séquentiel). Pseudonymisé avant journalisation.
languages Array<string>
lastname Ignoré (minimisation), jamais journalisé ; déprécié.
linkedin Ignoré (minimisation), jamais journalisé ; déprécié.
location
phone Ignoré (minimisation), jamais journalisé ; déprécié.
raw_text Ignoré (minimisation), jamais journalisé ; déprécié.
skills Array<string>

CompetencyFileRequest

Name Type Description
anonymize boolean Retire le lieu (profil et expériences) pour tout le lot ; la localisation n'est alors pas évaluée.
as_of Date de référence du calcul de l'expérience ; défaut : date du jour (UTC), renvoyée dans l'analyse.
competency_text string
job JobDescription
lang string Langue de l'explication.
weights ScoringWeights

DecisionRequest

Name Type Description
decision string
reviewer_ref string Identifiant opaque du relecteur chez le client ; pseudonymisé avant journalisation.
supersedes `decision_id` de la décision remplacée.

DecisionResponse

Name Type Description
agrees_with_score boolean
decision_id string(uuid)
seconds_since_scoring integer

DimensionScore

Name Type Description
details string Déprécié : reprend `rationale`.
dimension string
evaluated boolean
evidence Evidence
rationale string
score number
template_id string
weight number Poids appliqué, après renormalisation sur les dimensions évaluées.

Diploma

Name Type Description
degree string
end_date string
field_of_study string
school string
start_date string

Evidence

Name Type Description
matched Array<string>
missing Array<string>

Experience

Name Type Description
company string
description string
end_date string Même format ; vide, « present » ou « en cours » pour un poste en cours.
location string
start_date string AAAA ou AAAA-MM : premier jour du mois (janvier si l'année seule).
title string

HealthResponse

Name Type Description
scoring_version string
status string
version string

HrSettings

Name Type Description
retention_months integer
scoring_enabled boolean

HrSettingsUpdate

Name Type Description
retention_months integer Conservation du journal RH, en mois (6 à 60).
scoring_enabled boolean false = arrêt d'urgence : les routes d'analyse répondent 423.

HTTPValidationError

Name Type Description
detail Array<ValidationError>

JobDescription

Name Type Description
description string
location
min_experience_years
preferred_skills Array<string>
remote_policy onsite | hybrid | remote. Common aliases (e.g. full-remote, télétravail, hybride, présentiel) are normalised; any other value is refused (422). remote: the location dimension is not evaluated.
required_education
required_languages Array<string>
required_skills Array<string>
title string

MinimizationReport

Name Type Description
dimensions_not_evaluated Array<string> Processing dimensions skipped because of that minimisation.
ignored_fields Array<string> Input paths received then dropped before processing (e.g. `email`, `experiences[].location`).

ScoringWeights

Name Type Description
education number
experience number
languages number
location number
skills number

SubjectRef

Name Type Description
period string Mois de la clé, AAAA-MM.
subject_ref string

SubjectRefRequest

Name Type Description
subject_id string Le `CandidateProfile.id` des analyses.

SubjectRefResponse

Name Type Description
subject_refs Array<SubjectRef>

ValidationError

Name Type Description
ctx
input
loc Array<>
msg string
type string