Pour les développeurs

Référence de l’API

Tout le flux d’entretien IA, disponible sur du REST et du JSON simples. Créez des entretiens, invitez des candidats à grande échelle, lisez les résultats évalués et recevez des webhooks dès qu’un rapport est prêt.

URL de basehttps://api.airbasehq.com/v1

Prise en main

L’API est une surface REST versionnée sous /v1. Les requêtes et les réponses sont en JSON. Les ressources sont identifiées par de courts codes numériques, jamais par des ids internes, de sorte que tout ce que vous voyez dans une réponse peut être stocké en toute sécurité dans vos propres systèmes.

Une seule chose est nécessaire pour commencer : une clé API. Créez-la dans l’application sous Workspace, puis API Keys. La clé complète n’est affichée qu’une fois, à la création, alors copiez-la immédiatement et conservez-la comme un mot de passe.

Votre première requête
curl https://api.airbasehq.com/v1/me \
  -H "Authorization: Bearer $API_KEY"
Réponse 200
{
  "company": { "name": "Acme Inc." },
  "apiKey": {
    "name": "Production automation",
    "permissions": {
      "canManageInterviews": true,
      "canManageApplications": true
    }
  }
}

Authentification

Chaque requête transporte votre clé API sous forme de jeton bearer. Les clés sont limitées à votre espace de travail, de sorte qu’aucun id d’entreprise n’apparaît jamais dans un chemin.

Les clés ont la forme cuee_<keyId>_<secret>. Seul un hachage du secret est stocké de notre côté, c’est pourquoi la clé n’est affichée qu’une seule fois lorsque vous la créez. Les clés peuvent recevoir une date d’expiration et peuvent être révoquées à tout moment depuis la même page.

En-tête d’authentification
Authorization: Bearer cuee_0123456789abcdef0123456789abcdef_<64 hex chars>
Content-Type: application/json
  • Une clé absente ou mal formée renvoie 401 authentication_required. Une clé révoquée ou expirée renvoie 401 avec invalid_api_key ou api_key_expired.
  • Une clé valide qui n’a pas une permission requise renvoie 403 insufficient_permissions.

Permissions

Lorsque vous créez une clé, vous choisissez ce qu’elle peut faire. N’accordez que ce dont votre intégration a besoin. Ces trois portées couvrent aujourd’hui toute la surface publique :

PortéeLibellé dans l’applicationCe que cela débloque
canManageInterviewsInterviewsCréez et gérez des entretiens IA, invitez des candidats, consultez les résultats des entretiens.
canManageApplicationsTalent PoolConsultez et gérez les candidats, les postulants et les enregistrements de candidature associés.
canViewApplicationsView ApplicationsLisez les enregistrements de candidature et de candidat. Également requis pour gérer les webhooks.
  • Les clés créées dans les espaces de travail AI Interviewer disposent automatiquement de cet ensemble, sans rien à sélectionner.
  • Les endpoints indiquent les portées qu’ils requièrent ci-dessous. Lorsque deux portées sont listées, la clé a besoin des deux.

Limites de débit

Chaque clé API peut effectuer 200 requêtes par tranche de 60 secondes sur l’ensemble de la surface /v1. Chaque réponse inclut les en-têtes X-RateLimit-Limit, X-RateLimit-Remaining et X-RateLimit-Reset. Dépasser la limite renvoie HTTP 429 avec un en-tête Retry-After, de sorte qu’un client bien conçu n’a qu’à attendre et réessayer.

Des endpoints par lots existent pour que vous ayez rarement besoin de nombreuses requêtes : un seul appel d’invitation accepte jusqu’à 500 candidats.

Erreurs

Les erreurs utilisent partout une même enveloppe stable. Le champ code est une constante lisible par une machine qui ne sera jamais renommée, alors basez votre logique dessus plutôt que sur le message.

Enveloppe d’erreur
{
  "error": {
    "code": "ai_interview_not_published",
    "message": "Publish the interview before inviting candidates."
  }
}
HTTPCodesSignification
400invalid_request, validation_failedLe corps ou les paramètres de la requête ont échoué à la validation.
401authentication_required, invalid_api_key, api_key_expiredLa clé est absente, mal formée, révoquée ou expirée.
403insufficient_permissions, ai_interview_not_publishedLa clé n’a pas une portée, ou l’entretien ne peut pas encore accepter de candidats.
404not_found, ai_interview_not_found, candidate_not_foundLa ressource n’existe pas dans votre espace de travail.
429limite de débit dépasséeTrop de requêtes. Respectez l’en-tête Retry-After.
500operation_failedQuelque chose a échoué de notre côté. Vous pouvez réessayer avec un délai exponentiel.

Espace de travail

Identifiez l’espace de travail derrière une clé et inspectez ce que la clé peut faire. Utile comme test de connexion.

Qui suis-je

GET/v1/me

Renvoie le nom de l’entreprise et les permissions de la clé appelante. Fonctionne avec n’importe quelle clé valide, sans portée requise. Les plateformes d’intégration l’utilisent pour étiqueter le compte connecté.

Requête
curl https://api.airbasehq.com/v1/me \
  -H "Authorization: Bearer $API_KEY"
Réponse 200
{
  "company": { "name": "Acme Inc." },
  "apiKey": {
    "name": "Zapier",
    "permissions": {
      "canManageInterviews": true,
      "canManageApplications": true
    }
  }
}

Entretiens IA

Un entretien IA est un filtre réutilisable : un titre, un contexte de poste facultatif et des questions. Publiez-le, puis invitez autant de candidats que vous le souhaitez. Chaque candidat reçoit un lien personnel et l’IA mène, enregistre et évalue la conversation.

Créer un entretien IA

POST/v1/ai-interviewsInterviews

Crée un entretien IA autonome, par exemple un par poste dans votre ATS. Laissez les questions vides pour les configurer plus tard dans l’application, ou passez-en jusqu’à 50.

Corps

titlestringRequis1 à 255 caractères

Nom de l’entretien, généralement le poste qu’il filtre.

contentstringOptionneljusqu’à 10 000 caractères

Contexte du poste que l’intervieweur IA utilise pour orienter la conversation.

questionsstring[]Optionneljusqu’à 50 éléments

Les questions à poser. Chacune de 1 à 1 000 caractères.

rubricTemplateCodenumberOptionnel

Grille d’évaluation servant de référence. Par défaut, la grille de votre espace de travail.

status"draft" | "published"Optionneldraft par défaut

Seuls les entretiens publiés peuvent recevoir des candidats.

Requête
curl -X POST https://api.airbasehq.com/v1/ai-interviews \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Backend Engineer AI Screen",
    "content": "Screen for Node.js and distributed systems experience.",
    "questions": ["Tell me about a service you built and scaled."],
    "status": "published"
  }'
Réponse 201
{
  "interview": {
    "code": 27,
    "title": "Backend Engineer AI Screen",
    "content": "Screen for Node.js and distributed systems experience.",
    "status": "published",
    "createdAt": "2026-07-01T09:12:00.000Z",
    "updatedAt": "2026-07-01T09:12:00.000Z"
  }
}

Lister les entretiens IA

GET/v1/ai-interviewsInterviews

Liste paginée de vos entretiens avec le nombre de candidats. Filtrez par statut ou recherchez par titre.

Requête

pageintegerOptionnel1 par défaut

Numéro de page.

perPageintegerOptionnel1 à 100, 20 par défaut

Résultats par page.

statusstringOptionnel

L’un de draft, published, paused, archived.

searchstringOptionnel2 à 500 caractères

Recherche par titre.

Requête
curl "https://api.airbasehq.com/v1/ai-interviews?status=published&perPage=20" \
  -H "Authorization: Bearer $API_KEY"
Réponse 200
{
  "interviews": [
    {
      "code": 27,
      "title": "Backend Engineer AI Screen",
      "status": "published",
      "candidateCount": 42,
      "createdAt": "2026-07-01T09:12:00.000Z",
      "updatedAt": "2026-07-02T14:03:00.000Z"
    }
  ],
  "pagination": { "page": 1, "perPage": 20, "total": 1, "totalPages": 1 }
}

Obtenir un entretien IA

GET/v1/ai-interviews/:codeInterviews

Détail complet d’un entretien, y compris ses questions, le canal, la durée et la grille d’évaluation qu’il utilise comme référence. Les champs pas encore configurés reviennent à null.

Chemin

codeintegerRequis

Le code de l’entretien issu des réponses de création ou de liste.

Réponse 200
{
  "interview": {
    "code": 27,
    "title": "Backend Engineer AI Screen",
    "content": "Screen for Node.js and distributed systems experience.",
    "status": "published",
    "questions": ["Tell me about a service you built and scaled."],
    "channel": "audio",
    "duration": 15,
    "scorecard": { "code": 12, "name": "Backend Engineer Scorecard" },
    "createdAt": "2026-07-01T09:12:00.000Z",
    "updatedAt": "2026-07-02T14:03:00.000Z"
  }
}

Inviter des candidats

POST/v1/ai-interviews/:code/invitationsInterviewsTalent Pool

Invite jusqu’à 500 candidats en un seul appel et renvoie un lien d’entretien personnel pour chacun. Entièrement idempotent : réinviter quelqu’un renvoie son lien existant avec le statut skipped, de sorte que les nouvelles tentatives sont toujours sûres. L’entretien doit être publié.

Corps

candidatesobject[]Requis1 à 500 éléments

Objets candidat, voir les champs ci-dessous.

Objet candidat (champs principaux)

emailstringOptionnele-mail valide

Requis sauf si phone est fourni.

phonestringOptionnelE.164 recommandé

Requis sauf si email est fourni. Requis pour les entretiens téléphoniques.

firstName / lastNamestringOptionnel

Un nom est requis, soit séparé, soit sous forme de fullName.

fullNamestringOptionnel

Alternative aux noms séparés. Séparé automatiquement.

externalIdstringOptionnel

Votre propre id pour le candidat, renvoyé dans les résultats.

preferredCommunicationChannel"email" | "sms"Optionnel

Comment l’invitation est distribuée.

country, city, locationstringOptionnel

Indices de localisation.

linkedIn, portfolio, websitestringOptionnel

Liens de profil.

currentTitle, currentCompanystringOptionnel

Poste actuel.

resumeTextstringOptionneljusqu’à 200 000 caractères

Texte brut du CV. Analysé pour alimenter le profil du candidat.

source, referralSourcestringOptionnel

Attribution. referralSource l’emporte si les deux sont renseignés.

experiences, educations, skills, certifications, languagesobject[]Optionnel

Données de profil structurées. Transmettez ce que vous avez, tout est facultatif.

Requête
curl -X POST https://api.airbasehq.com/v1/ai-interviews/27/invitations \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "candidates": [
      {
        "fullName": "Ada Lovelace",
        "email": "[email protected]",
        "phone": "+14155552671",
        "externalId": "ats-4711"
      }
    ]
  }'
Réponse 200
{
  "summary": {
    "invitedCount": 1,
    "failedCount": 0,
    "skippedCount": 0,
    "phoneDroppedCount": 0
  },
  "candidates": [
    {
      "row": 1,
      "status": "invited",
      "applicationCode": 3,
      "candidateCode": 18,
      "email": "[email protected]",
      "phone": "+14155552671",
      "interviewUrl": "https://interviewer.cuee.ai/<token>",
      "warnings": [],
      "error": null
    }
  ]
}
  • Le statut de chaque ligne est invited, skipped ou failed. Skipped signifie que le candidat avait déjà été invité et que le même lien est renvoyé.
  • Les liens d’entretien expirent après environ 30 jours.
  • Si le paramètre de distribution de l’entretien est "Skip, deliver the link yourself", nous n’envoyons rien et vous distribuez interviewUrl par votre propre canal.

Lister les candidats d’un entretien

GET/v1/ai-interviews/:code/candidatesTalent Pool

Liste paginée de candidats avec l’étape actuelle, le statut de traitement et le dernier score. Le tri recently_scored ne renvoie que les candidats évalués, le score le plus récent en premier, ce qui réduit le sondage des résultats récents à une seule ligne.

Requête

pageintegerOptionnel1 par défaut

Numéro de page.

perPageintegerOptionnel1 à 100, 50 par défaut

Résultats par page.

sortstringOptionnelnewest | recently_scored

newest par défaut. Utilisez recently_scored pour sonder les résultats.

Requête
curl "https://api.airbasehq.com/v1/ai-interviews/27/candidates?sort=recently_scored" \
  -H "Authorization: Bearer $API_KEY"
Réponse 200
{
  "candidates": [
    {
      "applicationCode": 3,
      "candidateCode": 18,
      "name": "Ada Lovelace",
      "email": "[email protected]",
      "phone": "+14155552671",
      "currentStage": { "name": "AI Interview", "category": "interview", "orderIndex": 2 },
      "isReady": true,
      "processingStatus": "completed",
      "appliedAt": "2026-07-02T10:00:00.000Z",
      "latestScore": { "overallScore": 82, "scoredAt": "2026-07-02T11:05:00.000Z" },
      "latestInterview": { "interviewStatus": "completed", "channel": "phone" }
    }
  ],
  "pagination": { "page": 1, "perPage": 50, "total": 1, "totalPages": 1 }
}

Obtenir le résultat d’un candidat

GET/v1/ai-interviews/:code/candidates/:applicationCodeTalent Pool

Le rapport complet d’un candidat : profil, scores par critère avec justificatifs, paires question-réponse, tentatives d’entretien et liens vers l’enregistrement et la transcription.

Chemin

codeintegerRequis

Le code de l’entretien.

applicationCodeintegerRequis

Issu des réponses d’invitation, des listes de candidats ou des webhooks.

Réponse 200 (abrégée)
{
  "candidate": {
    "applicationCode": 3,
    "candidateCode": 18,
    "name": "Ada Lovelace",
    "evaluation": {
      "overallScore": 82,
      "summary": "Strong communication and relevant systems experience.",
      "criteria": [
        {
          "criterionName": "Technical depth",
          "score": 85,
          "evidenceCommentary": "Described sharding a Postgres cluster in detail."
        }
      ]
    },
    "interviews": [
      {
        "attemptNumber": 1,
        "interviewStatus": "completed",
        "channel": "phone",
        "cheatStatus": "no_suspicion",
        "recordingUrl": "https://…signed…",
        "transcriptUrl": "https://…signed…"
      }
    ]
  }
}
  • recordingUrl et transcriptUrl sont des liens signés qui expirent après environ 4 heures. Récupérez-les et stockez-les rapidement si vous archivez les résultats.
  • Les deux valent null jusqu’à ce que le traitement post-entretien soit terminé. Attendez le webhook interview.scored pour savoir que le rapport est prêt.

Vivier de talents

Votre vivier de talents regroupe chaque candidat que votre espace de travail a déjà vu, consultable avec l’IA. Ajoutez des candidats directement, sans rien planifier, et retrouvez-les plus tard en langage naturel.

Rechercher dans le vivier de talents

GET/v1/candidatesView Applications

Un seul champ de recherche, deux comportements. Un e-mail, un nom ou un numéro de téléphone correspond directement. Tout le reste lance une recherche sémantique sur les CV, l’expérience, les compétences et les évaluations passées, de sorte que "senior React developer in Berlin" fonctionne tout simplement.

Requête

searchstringOptionnel2 à 500 caractères

E-mail, nom, téléphone ou une requête en langage naturel. Omettez-le pour lister du plus récent au plus ancien.

pageintegerOptionnel1 par défaut

Numéro de page.

perPageintegerOptionnel1 à 100, 20 par défaut

Résultats par page.

Requête
curl "https://api.airbasehq.com/v1/candidates?search=senior+react+developer+in+berlin" \
  -H "Authorization: Bearer $API_KEY"
Réponse 200
{
  "candidates": [
    {
      "candidateCode": 18,
      "name": "Ada Lovelace",
      "email": "[email protected]",
      "profileSummary": "Senior engineer, 8 years across fintech and infra.",
      "applicationCount": 2,
      "applications": [
        {
          "applicationCode": 3,
          "jobTitle": "Backend Engineer",
          "stageName": "AI Interview",
          "averageScore": 82
        }
      ],
      "match": {
        "distance": 0.18,
        "type": "semantic",
        "text": "Led a 6 person platform team building React tooling in Berlin."
      }
    }
  ],
  "pagination": { "page": 1, "perPage": 20, "total": 1, "totalPages": 1 }
}
  • match n’apparaît que sur les correspondances sémantiques. distance est la distance cosinus, plus elle est faible, plus la correspondance est proche.

Ajouter un candidat

POST/v1/candidatesTalent Pool

Ajoute un candidat au vivier de talents et le rend consultable immédiatement. Dédoublonné par e-mail et téléphone : réajouter quelqu’un complète les champs manquants et n’écrase jamais ce qui existe, de sorte que la synchronisation depuis un autre système est sûre.

Corps

…candidate fieldsobjectOptionnel

Le même objet candidat que pour les invitations : nom plus e-mail ou téléphone requis, tout le reste facultatif.

aboutCandidatestringOptionneljusqu’à 10 000 caractères

Notes libres stockées sur le profil.

Requête
curl -X POST https://api.airbasehq.com/v1/candidates \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "fullName": "Grace Hopper",
    "email": "[email protected]",
    "currentTitle": "Staff Engineer",
    "resumeText": "…"
  }'
Réponse 201
{
  "candidate": {
    "candidateCode": 19,
    "status": "created",
    "name": "Grace Hopper",
    "email": "[email protected]",
    "phone": null
  }
}
  • Renvoie 201 avec le statut created pour les nouveaux candidats, ou 200 avec le statut existing en cas de dédoublonnage.

Webhooks

Les webhooks envoient les événements à votre serveur au moment où ils se produisent, vous n’avez donc jamais à sonder. Abonnez une URL https aux événements qui vous intéressent et vérifiez chaque livraison avec sa signature.

L’événement phare est interview.scored : il se déclenche lorsque le rapport est réellement prêt, scores et résumé compris, et pas seulement à la fin de l’appel.

ÉvénementSe déclenche quand
interview.scoredLe rapport IA est prêt : le score global, le résumé et les critères sont disponibles.
interview.completedLa session d’entretien s’est terminée. L’évaluation peut encore être en cours.
interview.startedLe candidat a commencé l’entretien.
interview.cancelledL’entretien a été annulé.
candidate.appliedUn candidat s’est inscrit de lui-même via un lien d’entretien partagé.
candidate.invitedUn candidat a été invité, via l’API ou par un recruteur.

Créer un webhook

POST/v1/webhooksView Applications

Abonne un endpoint https à un ou plusieurs événements. Le secret de signature est renvoyé une seule fois, uniquement à la création. Vous pouvez conserver jusqu’à 10 webhooks actifs.

Corps

urlstringRequishttps uniquement

Votre endpoint. Les adresses privées et internes sont rejetées.

eventsstring[]Requis1 à 6 éléments

Événements du catalogue ci-dessus.

Requête
curl -X POST https://api.airbasehq.com/v1/webhooks \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/hooks/airbase",
    "events": ["interview.scored", "candidate.applied"]
  }'
Réponse 201
{
  "webhook": {
    "id": "3e1f1a3e-1111-2222-3333-444455556666",
    "url": "https://example.com/hooks/airbase",
    "events": ["interview.scored", "candidate.applied"],
    "status": "active",
    "secret": "whsec_<64 hex chars>",
    "createdAt": "2026-07-01T09:12:00.000Z"
  }
}

Lister les webhooks

GET/v1/webhooksView Applications

Tous les abonnements avec l’état de livraison : les échecs consécutifs ainsi que le dernier succès et le dernier échec. Les secrets ne sont plus jamais renvoyés.

Réponse 200
{
  "webhooks": [
    {
      "id": "3e1f1a3e-1111-2222-3333-444455556666",
      "url": "https://example.com/hooks/airbase",
      "events": ["interview.scored"],
      "status": "active",
      "consecutiveFailures": 0,
      "lastSuccessAt": "2026-07-02T11:05:03.000Z",
      "lastFailureAt": null,
      "lastFailureReason": null,
      "createdAt": "2026-07-01T09:12:00.000Z"
    }
  ]
}

Supprimer un webhook

DELETE/v1/webhooks/:webhookIdView Applications

Désabonne l’endpoint. Les livraisons s’arrêtent immédiatement.

Chemin

webhookIduuidRequis

L’id du webhook issu de la création ou de la liste.

Réponse 200
{ "deleted": true }

Recevoir les livraisons

Chaque livraison est un POST vers votre URL avec le nom de l’événement dans l’en-tête X-Cuee-Event et un corps signé. Répondez avec un code 2xx quelconque en moins de 10 secondes. Toute autre réponse est réessayée avec un délai exponentiel, et après 20 échecs consécutifs l’abonnement est désactivé, ce que vous pouvez voir dans la liste des webhooks.

Livraison pour interview.scored
POST /hooks/airbase HTTP/1.1
Content-Type: application/json
User-Agent: Cuee-Webhooks/1.0
X-Cuee-Event: interview.scored
X-Cuee-Delivery: 8b7f4c2a-9d3e-4f5a-b6c7-d8e9f0a1b2c3
X-Cuee-Signature: t=1751449503,v1=<hex hmac>

{
  "id": "8b7f4c2a-9d3e-4f5a-b6c7-d8e9f0a1b2c3",
  "event": "interview.scored",
  "createdAt": "2026-07-02T11:05:03.000Z",
  "data": {
    "interviewCode": 27,
    "jobTitle": "Backend Engineer AI Screen",
    "applicationCode": 3,
    "candidateCode": 18,
    "name": "Ada Lovelace",
    "email": "[email protected]",
    "phone": "+14155552671",
    "interviewStatus": "completed",
    "channel": "phone",
    "completedAt": "2026-07-02T10:58:41.000Z",
    "cheatStatus": "no_suspicion",
    "overallScore": 82,
    "summary": "Strong communication and relevant systems experience.",
    "summaryBullets": ["8 years backend experience", "Clear system design answers"],
    "scoredAt": "2026-07-02T11:05:00.000Z"
  }
}
Vérifier la signature (Node.js)
import { createHmac, timingSafeEqual } from 'node:crypto'

function verify(rawBody, signatureHeader, secret) {
  const { t, v1 } = Object.fromEntries(
    signatureHeader.split(',').map((part) => part.split('='))
  )
  // Reject replays older than 5 minutes
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false

  const expected = createHmac('sha256', secret)
    .update(t + '.' + rawBody)
    .digest('hex')
  return timingSafeEqual(Buffer.from(expected), Buffer.from(v1))
}
  • La signature est un HMAC SHA-256 de "<timestamp>.<raw body>" avec votre secret whsec. Vérifiez toujours par rapport au corps brut de la requête, avant toute analyse JSON.
  • Les charges utiles ne contiennent que des codes numériques, jamais d’ids internes. Utilisez applicationCode avec l’endpoint de résultat du candidat pour récupérer le rapport complet.