Référence API
Authentifiez-vous et interrogez l'API de recrutement pour les offres d'emploi, les candidats et les candidatures.
Pourquoi c’est important
L’API de recrutement offre aux systèmes externes un accès en lecture à vos données de recrutement — les sites d’emploi récupèrent les offres publiées, les systèmes SIRH synchronisent les fiches candidats, les tableaux de bord suivent les indicateurs du pipeline. Les portées des jetons garantissent que chaque intégration ne voit que ce dont elle a besoin.
URL de base
Tous les points d’accès de l’API sont accessibles via :
https://startupkit.app/api/v1
Remplacez startupkit.app par votre domaine Kit réel.
Authentification
Chaque requête nécessite un jeton API dans l’en-tête Authorization :
Authorization: Bearer YOUR_TOKEN
Format alternatif (les deux fonctionnent) :
Authorization: token YOUR_TOKEN
Créez des jetons dans Settings > API. Chaque jeton doit disposer de portées explicites pour accéder aux points d’accès de recrutement.
| Portée | Accès |
|---|---|
job_postings:read |
Offres d’emploi et étapes |
candidates:read |
Données personnelles des candidats (nom, e-mail, téléphone), téléchargement de CV |
applications:read |
Candidatures, historique des étapes, téléchargement de CV |
Les jetons sans portée ne peuvent pas accéder aux points d’accès de recrutement.
Format et cycle de vie des jetons
- Format : chaîne hexadécimale de 32 caractères (par ex.
a1b2c3d4e5f6...) - Suivi de la dernière utilisation : chaque requête réussie met à jour
last_used_at - Expiration : un champ optionnel
expires_atpeut être défini lors de la création du jeton - Alertes d’inactivité : les jetons non utilisés depuis plus de 30 jours déclenchent des notifications administrateur
- Révocation automatique : les jetons sont automatiquement révoqués lorsqu’un utilisateur est retiré du compte
Bonne pratique : créez des jetons distincts par intégration avec les portées minimales nécessaires.
Portée du compte
Chaque jeton API est lié à un compte spécifique. Le compte est déterminé automatiquement à partir du jeton — aucun paramètre account_id n’est nécessaire.
Lorsqu’un membre de l’équipe est retiré d’un compte, ses jetons pour ce compte sont automatiquement révoqués.
Réponses d’erreur
| Statut | Signification | Causes courantes |
|---|---|---|
400 |
Requête incorrecte | Contexte de compte manquant (cas limite avec l’authentification par session) |
401 |
Non autorisé | Jeton manquant, invalide ou expiré |
403 |
Interdit | Le jeton ne dispose pas de la portée requise pour ce point d’accès |
404 |
Non trouvé | La ressource n’existe pas ou n’appartient pas à votre compte |
422 |
Entité non traitable | Erreurs de validation (principalement pour les points d’accès hors recrutement) |
Pagination
Les points d’accès de liste renvoient des résultats paginés :
{
"data": [...],
"pagination": {
"current_page": 1,
"total_pages": 3,
"total_count": 42,
"per_page": 20
}
}
Ajoutez ?page=2 pour récupérer les pages suivantes.
Offres d’emploi
Portée requise : job_postings:read
Lister les offres d’emploi
GET /api/v1/job_postings
Exemple :
curl -H "Authorization: Bearer YOUR_TOKEN" \
https://startupkit.app/api/v1/job_postings?status=published
Paramètres :
| Paramètre | Type | Description |
|---|---|---|
status |
string | Filtrer par draft, published ou closed |
page |
integer | Numéro de page |
Réponse :
{
"data": [
{
"id": "job_abc123",
"title": "Senior Rails Developer",
"status": "published",
"department": "Engineering",
"location": "New York, NY",
"employment_type": "full_time",
"remote": true,
"salary_min": "120000.0",
"salary_max": "180000.0",
"salary_currency": "USD",
"salary_period": "year",
"published_at": "2026-01-15T10:00:00Z",
"closed_at": null,
"created_at": "2026-01-10T08:30:00Z",
"updated_at": "2026-02-01T14:20:00Z",
"stages": [
{
"id": "stg_def456",
"name": "Screening",
"stage_type": "default",
"position": 0
}
],
"counts": {
"total_applications": 24,
"active_applications": 18,
"rejected": 6
}
}
],
"pagination": { ... }
}
Obtenir une offre d’emploi
GET /api/v1/job_postings/:id
Renvoie la même structure qu’un élément de la réponse de liste.
Lister les étapes
GET /api/v1/job_postings/:job_posting_id/stages
Réponse :
{
"data": [
{
"id": "stg_def456",
"name": "Screening",
"stage_type": "default",
"position": 0
}
]
}
Les étapes sont classées par position. Pas de pagination.
Candidats
Portée requise : candidates:read
Lister les candidats
GET /api/v1/candidates
Exemple :
curl -H "Authorization: Bearer YOUR_TOKEN" \
https://startupkit.app/api/v1/[email protected]
Paramètres :
| Paramètre | Type | Description |
|---|---|---|
search |
string | Rechercher par nom ou e-mail |
page |
integer | Numéro de page |
Réponse :
{
"data": [
{
"id": "cand_ghi789",
"first_name": "John",
"last_name": "Doe",
"email": "[email protected]",
"phone": "+1-555-0100",
"status": "active",
"github_username": "johndoe",
"created_at": "2026-01-20T09:15:00Z",
"applications": [
{
"id": "app_jkl012",
"job_posting_id": "job_abc123",
"job_posting_title": "Senior Rails Developer",
"current_stage_name": "Interview",
"status": "active",
"submitted_at": "2026-01-20T09:15:00Z"
}
]
}
],
"pagination": { ... }
}
Obtenir un candidat
GET /api/v1/candidates/:id
Renvoie la même structure qu’un élément de la réponse de liste.
Candidatures
Portée requise : applications:read
Lister les candidatures
GET /api/v1/applications
Exemple :
curl -H "Authorization: Bearer YOUR_TOKEN" \
https://startupkit.app/api/v1/applications?job_posting_id=job_abc123&status=active
Paramètres :
| Paramètre | Type | Description |
|---|---|---|
job_posting_id |
string | Filtrer par identifiant préfixé de l’offre d’emploi |
status |
string | Filtrer par active ou rejected |
page |
integer | Numéro de page |
Réponse :
{
"data": [
{
"id": "app_jkl012",
"job_posting_id": "job_abc123",
"candidate_id": "cand_ghi789",
"current_stage_name": "Interview",
"status": "active",
"submitted_at": "2026-01-20T09:15:00Z",
"created_at": "2026-01-20T09:15:00Z",
"updated_at": "2026-02-10T11:30:00Z",
"candidate": {
"id": "cand_ghi789",
"first_name": "John",
"last_name": "Doe",
"email": "[email protected]"
},
"stage_history": [
{
"id": "sp_mno345",
"stage_name": "Screening",
"stage_type": "default",
"status": "completed",
"started_at": "2026-01-20T09:15:00Z",
"completed_at": "2026-01-25T16:00:00Z"
},
{
"id": "sp_pqr678",
"stage_name": "Interview",
"stage_type": "interview",
"status": "in_progress",
"started_at": "2026-01-25T16:00:00Z",
"completed_at": null
}
]
}
],
"pagination": { ... }
}
Obtenir une candidature
GET /api/v1/applications/:id
Renvoie la même structure qu’un élément de la réponse de liste. Lorsque le jeton dispose de la portée candidates:read et qu’un CV est joint à la candidature, la réponse inclut les métadonnées du CV :
{
"id": "app_jkl012",
"resume_filename": "john_doe_resume.pdf",
"resume_content_type": "application/pdf",
"resume_byte_size": 245760,
...
}
Ces champs sont omis lorsqu’aucun CV n’est joint ou lorsque la portée candidates:read est absente.
Télécharger le CV
Portées requises : applications:read + candidates:read
GET /api/v1/applications/:application_id/resume
Télécharge le CV du candidat pour la candidature donnée. Renvoie une redirection 302 vers une URL signée à durée limitée (valide 30 minutes).
Exemple :
curl -L -H "Authorization: Bearer YOUR_TOKEN" \
-o resume.pdf \
https://startupkit.app/api/v1/applications/app_jkl012/resume
Réponse :
302 Found– Redirection vers l’URL de téléchargement signée. Utilisez-L(suivre les redirections) avec curl.403 Forbidden– Le jeton ne dispose pas de la portéeapplications:readoucandidates:read.404 Not Found– Candidature non trouvée, ou aucun CV joint.
Notes :
- Les URL signées expirent après 30 minutes. En cas d’expiration, effectuez une nouvelle demande.
- L’en-tête
Content-Dispositionest défini surattachment(déclenche le téléchargement par le navigateur). - Types de fichiers de CV : PDF, DOC, DOCX (tels que téléchargés par le candidat).
Masquage des données personnelles des candidats
Lorsqu’un jeton dispose de la portée applications:read mais pas de candidates:read, les données du candidat dans les réponses de candidature sont réduites au seul identifiant :
{
"candidate": {
"id": "cand_ghi789"
}
}
Les champs first_name, last_name et email ne sont pas inclus. Cela permet aux intégrations de sites d’emploi de suivre le statut des candidatures sans exposer les coordonnées des candidats.
Dépannage
Problèmes d’authentification
401 Unauthorized
Si vous recevez une réponse 401, vérifiez :
- Format du jeton : assurez-vous que le jeton correspond exactement à celui affiché dans Settings > API (chaîne hexadécimale de 32 caractères)
- En-tête Authorization : doit être
Authorization: Bearer TOKENouAuthorization: token TOKEN - Expiration du jeton : vérifiez si
expires_atest dépassé dans Settings > API - Appartenance au compte : vérifiez que le propriétaire du jeton est toujours membre du compte
403 Forbidden
Lorsque vous recevez une erreur 403, le jeton est valide mais ne dispose pas de la portée requise :
- Vérifiez les portées requises : chaque point d’accès indique la portée nécessaire (par ex.
job_postings:read) - Ajoutez les portées manquantes : modifiez le jeton dans Settings > API et ajoutez la portée requise
- Format des portées : doit correspondre exactement —
job_postings:read, et nonjob_postingsouread
Problèmes d’accès aux données
Tableau data vide dans les réponses
Si l’API renvoie {"data": [], "pagination": {...}} alors que vous attendez des résultats :
- Isolation des comptes : les jetons API ne voient que les données de leur compte associé
- Vérifiez le compte : consultez à quel compte le jeton appartient dans Settings > API
- Accès inter-comptes : les jetons ne peuvent pas accéder aux données d’autres comptes, même si l’utilisateur en est membre
Les données personnelles du candidat sont masquées
Si vous ne voyez que {"candidate": {"id": "cand_..."}} sans nom ni e-mail :
- Comportement attendu : cela se produit lorsque le jeton dispose de la portée
applications:readmais pas decandidates:read - Ajoutez la portée : pour voir les informations complètes du candidat, ajoutez la portée
candidates:readà votre jeton - Confidentialité par conception : cela permet aux intégrations de suivre les candidatures sans exposer les coordonnées
Problèmes de requêtes et de filtrage
Erreur 404 lors de la recherche d’enregistrements spécifiques
- Utilisez les identifiants préfixés : les offres d’emploi utilisent le format
job_abc123, et non des identifiants de base de données comme42 - Sensibilité à la casse : les identifiants préfixés sont sensibles à la casse
- Portée du compte : la ressource doit appartenir au compte du jeton
Les paramètres de filtre ne fonctionnent pas
- Valeurs de statut valides :
- Offres d’emploi :
draft,published,closed(sensible à la casse) - Candidatures :
active,rejected(sensible à la casse)
- Offres d’emploi :
- Format des paramètres : utilisez
?status=published, et non?status=Publishedou?filter[status]=published
Questions fréquentes
Comment fonctionne la pagination ?
Tous les points d’accès de liste renvoient 20 éléments par page (fixe). Utilisez ?page=2 pour la page suivante. L’objet pagination indique total_pages et total_count.
Les jetons expirent-ils automatiquement ?
Uniquement si vous définissez expires_at lors de la création du jeton. Sinon, les jetons restent valides jusqu’à ce que :
- Ils soient révoqués manuellement dans Settings > API
- L’utilisateur soit retiré du compte
- Le compte soit supprimé
Dois-je utiliser un seul jeton pour plusieurs intégrations ?
Non. La bonne pratique est d’utiliser un jeton par intégration avec des portées minimales. Cela facilite :
- L’audit des requêtes par intégration (via
last_used_at) - La révocation de l’accès à une intégration spécifique sans affecter les autres
- L’attribution de permissions différentes à différents systèmes
Quel est le lien entre les webhooks et l’API ?
Les webhooks vous notifient des événements (nouvelle candidature, changement d’étape). L’API vous permet d’interroger l’état actuel. Utilisez les webhooks pour déclencher votre système, puis l’API pour récupérer les détails complets.
Puis-je obtenir plus de 20 éléments par page ?
Non. La taille de page est fixée à 20 éléments pour garantir des performances constantes. Utilisez le paramètre page pour parcourir l’ensemble des résultats.
Aide-mémoire
- Créez un jeton API dans Settings > API — chaque jeton est lié à ce compte
- Attribuez uniquement les portées nécessaires à votre intégration (bonne pratique : un jeton par intégration avec des portées minimales)
- Vérifiez
expires_atdans Settings > API pour connaître la date d’expiration du jeton (le cas échéant) - Incluez
Authorization: Bearer TOKENdans chaque requête - Utilisez les identifiants préfixés (par ex.
job_abc123) dans les URL, et non les identifiants de base de données - Gérez les réponses
400et422pour les erreurs de validation - Gérez les réponses
401— votre jeton peut être expiré ou révoqué - Gérez les réponses
403— votre jeton peut ne pas disposer de la portée requise - Utilisez le paramètre
pagepour paginer les ensembles de résultats volumineux (20 éléments par page)