Marvin Recruiter API

Offres et candidatures

Lire les offres publiées d'un client et y déposer des candidatures avec CV

Ce guide s'adresse aux outils de sourcing et de préqualification qui envoient des candidats sur les offres d'un client Marvin. Deux appels suffisent : lire les offres que le client publie sur son site carrière, puis déposer une candidature sur l'une d'elles.

La candidature arrive dans l'onglet Candidatures de l'offre, où le recruteur la traite comme une candidature reçue sur son site carrière. Le CV est analysé et le profil du candidat rattaché ou créé.

Clé d'API

L'administrateur du client génère la clé depuis Paramètres → Intégrations, sur la page du connecteur de votre outil, puis vous la transmet avec l'identifiant de son compte ({slug}).

  • C'est une clé partenaire, générée par un connecteur prévu pour ces routes : partner:projects:read ouvre la lecture des offres, partner:candidates:write le dépôt des candidatures. Toutes les clés partenaires ne les ouvrent pas — en cas de 403, vérifiez avec Marvin que votre connecteur est bien celui-ci.
  • Elle s'envoie uniquement dans le header X-Marvin-Public-Key. Passée dans l'URL (?key=), elle est refusée (401).
  • C'est un secret côté serveur : ne l'exposez jamais dans du code exécuté par un navigateur.
X-Marvin-Public-Key: pk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Lire les offres

GET https://api.marvins.ai/v1/public/careers/{slug}/feed.xml

Le flux liste les offres publiées sur le site carrière du client : ce sont les seules sur lesquelles une candidature peut être déposée. Chaque offre est un élément <job>, dont <id> est l'identifiant à utiliser pour le dépôt.

curl "https://api.marvins.ai/v1/public/careers/{slug}/feed.xml" \
  -H "X-Marvin-Public-Key: pk_live_…"

La même liste existe en JSON : GET https://api.marvins.ai/v1/public/careers/{slug}.

Les erreurs du flux sont au format XML (<error><code>…</code><message>…</message></error>) : 401 et 403 comme ci-dessous. Un slug qui n'est pas celui du client de la clé renvoie 403 slug_mismatch.

Déposer une candidature

POST https://api.marvins.ai/v1/public/careers/{slug}/jobs/{jobId}/apply

où {jobId} est l'<id> de l'offre lu dans le flux. Le corps s'envoie en multipart/form-data (pour joindre le CV) ou en application/json.

ChampTypeRequisDescription
firstNamestringouiPrénom (100 caractères max.)
lastNamestringouiNom (100 caractères max.)
emailstringouiE-mail du candidat
phoneNumberstringnonTéléphone
linkedinstringnonURL du profil LinkedIn
motivationTextstringnonMessage, notes de préqualification (5 000 caractères max.)
resumefilenonCV (resume ou cv), en multipart
languagesstringnonLangues du candidat, séparées par une virgule (fr, en)
citystringnonVille du candidat (voir « Localisation » ci-dessous)
postalCodestringnonCode postal du candidat
countryCodestringnonPays du candidat, code ISO 3166-1 alpha-2
champs perso—nonChamps personnalisés du formulaire du client

firstName, lastName ou email manquant ou invalide renvoie 400. Le client peut rendre d'autres champs obligatoires dans son formulaire de candidature : un tel champ manquant renvoie 422, avec son nom dans error.field.

CV : PDF, DOC, DOCX, JPEG, PNG, WEBP ou GIF, 10 Mo maximum (au-delà : 413).

Langues : languages ajoute les langues du candidat à sa fiche. Chaque langue est désignée par son code ISO 639-1 à deux lettres (fr, en… ; fra n'est pas reconnu) ou par son nom en français ou en anglais, parmi la centaine de langues courantes que Marvin connaît. La candidature ne modifie ni ne retire les langues que la fiche porte déjà ; une valeur non reconnue est ignorée sans faire échouer la candidature. Au plus 10 langues distinctes sont retenues par candidature.

Localisation : city renseigne la localisation du candidat sur sa fiche, rattachée au référentiel de lieux de Marvin (une ville, jamais une rue) ; le recruteur peut ensuite rechercher ses candidats par zone. Une précision après une virgule est ignorée (Lyon, France vaut Lyon). postalCode départage les communes homonymes (Saint-Martin + 38410). countryCode limite la recherche à un pays, en code ISO 3166-1 alpha-2 (FR, BE, CH…) ; sans lui, la France est privilégiée mais une ville étrangère reste trouvée. Une localisation déjà présente sur la fiche n'est jamais remplacée. Une ville introuvable est ignorée ; une valeur que Marvin ne sait pas lire (dans city, postalCode ou countryCode) fait ignorer toute la localisation. Dans les deux cas, la candidature est acceptée. postalCode seul, sans city, est ignoré. En JSON, postalCode peut aussi s'envoyer en nombre.

Provenance : elle est fixée par la clé. La candidature et le candidat sont attribués à votre outil, sans paramètre à passer.

Doublons : une candidature pour une offre où le même e-mail a déjà une candidature active est acceptée (201) et mise en quarantaine pour arbitrage par le recruteur. Aucun profil en double n'est créé.

curl -X POST \
  "https://api.marvins.ai/v1/public/careers/{slug}/jobs/{jobId}/apply" \
  -H "X-Marvin-Public-Key: pk_live_…" \
  -F "firstName=Marie" \
  -F "lastName=Dupont" \
  -F "email=marie.dupont@example.com" \
  -F "motivationText=Disponible sous un mois, prétentions 45 k€." \
  -F "resume=@/chemin/vers/cv.pdf"

Candidature spontanée

Pour un candidat qui ne vise aucune offre : POST https://api.marvins.ai/v1/public/careers/{slug}/apply, avec les mêmes champs.

Champ personnalisé à choix multiple

Sur ces deux routes, un champ personnalisé n'est accepté que s'il figure dans le formulaire de candidature du client ; les autres sont ignorés. Pour un champ à choix multiple, la valeur s'envoie toujours sous forme de chaîne :

  • une option : son libellé, par exemple Alpha ou Conseil, audit ;
  • plusieurs options : un tableau JSON sérialisé en chaîne, par exemple ["Alpha","Beta"]. Chaque élément du tableau est une chaîne ; un élément vide ou d'un autre type est écarté.

La casse est indifférente, de même que les espaces en début et en fin de libellé. Si l'une des valeurs n'est pas une option du champ, la candidature est refusée : 409, avec error.code à conflict, et ni le candidat ni la candidature ne sont créés. Exception : une nouvelle candidature du même e-mail sur une offre où il a déjà une candidature active est mise en quarantaine (201, voir Doublons) ; ses champs personnalisés ne sont alors ni vérifiés ni enregistrés.

Les options d'un champ se lisent dans GET https://api.marvins.ai/v1/public/careers/{slug} : dans data.formConfig.fields, fieldKey est le nom du champ à envoyer et options la liste de ses options.

Deux limites :

  • dans un corps application/json, un tableau natif ("domaines": ["Alpha", "Beta"]) n'est pas lu, seules les chaînes le sont : envoyez "domaines": "[\"Alpha\",\"Beta\"]" ;
  • en multipart/form-data, un champ répété ne garde que sa dernière valeur.

Réponses

Succès : 201 avec l'identifiant de la candidature, { "data": { "id": "…" } }.

Les erreurs du dépôt ont la forme { "error": { "code": "…", "message": "…" } } ({ "error": { "code": "…", "field": "…" } } pour un 422). Testez le code, qui est stable.

StatutcodeCas
400validation_errorChamp obligatoire manquant ou invalide, type de fichier refusé
401unauthorizedClé absente, malformée, révoquée, ou clé partenaire passée dans l'URL
403insufficient_scopeDroit manquant sur la clé — le message nomme le scope manquant
403slug_mismatchLecture du flux : slug d'un autre client que la clé
404job_not_foundOffre inconnue, ou dépôt avec le slug d'un autre client que la clé
410job_removedOffre fermée, dépubliée ou supprimée
413—CV trop lourd (plus de 10 Mo) — testez le statut, pas le code
422missing_required_fieldChamp rendu obligatoire par le client — error.field nomme le champ
429rate_limitedTrop de dépôts (30 par minute et par clé)

Bonnes pratiques

  • Relisez le flux avant d'envoyer : une offre fermée ou dépubliée renvoie 410.
  • Envoyez le CV quand vous l'avez : c'est lui qui remplit le profil du candidat.
  • Mettez vos notes de préqualification dans motivationText : le recruteur les lit sur la candidature.

Pour les schémas complets, voir la Référence API — Careers (Public).

On this page