API de vérification en direct
Planifiez une session de vérification d'identité en direct pour votre client depuis votre propre système, suivez-la, annulez-la ou reprogrammez-la, et recevez la décision de l'animateur par webhook — le tout associé à votre propre code de référence.
Une vérification d'identité en direct est une session vidéo en direct animée par un humain : un membre de votre équipe (l'animateur) rencontre le demandeur par caméra, vérifie sa pièce d'identité en direct et enregistre une décision. Jusqu'à présent, ces sessions ne pouvaient être planifiées que depuis le tableau de bord. Les points de terminaison ci-dessous permettent à votre système de planifier une session pour l'un de vos clients, de la suivre et de recevoir le résultat, par API et par webhook, le tout associé à votre propre code de référence (externalRef).
Tous les points de terminaison se trouvent sous /api/v1/kyc/conferences et sont authentifiés par une clé d'API (Authorization: Bearer ck_live_…). Voir Authentification.
La vérification d'identité en direct (cette page : une session vidéo en direct avec un animateur humain) est une fonctionnalité différente de Live ID Validation (ouverture de session biométrique pour un utilisateur récurrent, voir Live ID Validation).
Fonctionnement
sequenceDiagram
participant You as Your system
participant C as Certivant
participant A as Applicant
participant H as Host (your team)
You->>C: POST /conferences (externalRef, host, scheduledFor)
C-->>You: 201 conference + applicantJoinUrl
C->>A: Invitation email / SMS (join link)
C-->>You: webhook conference.scheduled
H->>C: Joins from the dashboard
C-->>You: webhook conference.started
A->>C: Consents + joins the session
H->>C: Records the decision
C-->>You: webhook conference.completed (decision, externalRef)
- Vous planifiez la session pour une demande de vérification existante, ou vous créez le demandeur et la vérification en ligne. Vous désignez l'animateur : un membre de votre organisation qui conduit la session depuis le tableau de bord.
- Certivant invite le demandeur (par courriel et/ou SMS, dans sa langue) et vous renvoie également le lien de connexion.
- Avant d'entrer dans la session, le demandeur répond à la fenêtre de consentement. Le consentement à l'enregistrement vidéo est toujours recueilli ; le reste de la fenêtre peut être ignoré avec
consentScreen: falselorsque vous avez déjà recueilli ces consentements dans votre propre application. - Lorsque l'animateur enregistre la décision, vous recevez
conference.completedavec la décision, son effet sur la vérification et votreexternalRef.
Avant de commencer
| Exigence | Où |
|---|---|
Clé d'API avec kyc:write (planifier / modifier) et kyc:read (lire) | KYC → Développeur |
| Votre forfait inclut la visioconférence | Votre abonnement. Sinon, 402 |
| Vérification d'identité en direct activée dans vos paramètres | KYC → Paramètres → Vérification d’identité en direct. Sinon, 403 |
| Votre compte est vérifié par Certivant | Intégration. Sinon, 403 |
| L'animateur dispose de l'autorisation d'approbation manuelle | Le propriétaire, ou un rôle / un utilisateur figurant dans vos paramètres d'approbation manuelle. Sinon, 403 |
| Une URL de webhook (facultatif, pour les résultats) | KYC → Paramètres → Webhooks : voir Webhooks → Configuration |
Toutes les dates et heures sont au format ISO-8601 avec fuseau horaire : 2026-10-06T14:00:00Z ou 2026-10-06T10:00:00-04:00. Une date-heure sans fuseau horaire est refusée, afin qu'une session ne puisse jamais tomber à la mauvaise heure à cause du fuseau local d'un serveur. Les réponses sont toujours en UTC (…Z).
Planifier une vérification en direct
POST /api/v1/kyc/conferences Portée : kyc:write
Corps de la requête
{
"kycRequestId": "req_01J…",
"host": { "email": "compliance@yourcompany.com" },
"externalRef": "order-10042",
"title": "Account opening — video check",
"scheduledFor": "2026-10-06T10:00:00-04:00",
"language": "fr",
"consentScreen": true,
"applicantFileUploadEnabled": false,
"channels": { "email": true, "sms": true }
}
| Champ | Type | Obligatoire | Remarques |
|---|---|---|---|
kycRequestId | chaîne | l'un des deux | Une demande de vérification existante dans votre organisation. |
applicant | objet | l'un des deux | Crée le demandeur et la vérification en ligne à la place (voir ci-dessous). Un demandeur ayant le même courriel dans votre organisation est réutilisé. |
host.userId / host.email | chaîne | oui (l'un des deux) | Le membre qui conduit la session. Doit disposer de l'autorisation d'approbation manuelle. |
externalRef | chaîne | non | Votre code de référence (identifiant de commande ou de dossier). Repris dans chaque réponse et chaque webhook. 200 caractères maximum. |
title | chaîne | non | Affiché à l'animateur. 200 caractères maximum. |
scheduledFor | date-heure ISO | l'un des deux | Moment où la session a lieu. Doit être dans le futur. |
startNow | booléen | l'un des deux | true = planifier pour maintenant (le demandeur est invité immédiatement). |
expiresAt | date-heure ISO | non | Fin de la fenêtre de connexion. Par défaut : scheduledFor + 24 h. |
language | énumération | non | en, fr, es, de, it, pt, nl, pl, ro, tr, ar, zh, ja, ko, hi, vi, id, ru, uk, sv. Langue de l'invitation, enregistrée comme langue préférée du demandeur (tout texte sans traduction dans cette langue est envoyé en anglais). Par défaut : la langue enregistrée du demandeur, sinon la langue par défaut de votre organisation. |
consentScreen | booléen | non | Par défaut true : le demandeur confirme le partage de renseignements personnels, la prise de photo et l'enregistrement vidéo. false = la fenêtre demande uniquement le consentement à l'enregistrement vidéo, qui ne peut jamais être désactivé. |
faceCheckSkippable | booléen | non | Omis : le réglage du formulaire de la demande, sinon celui de votre organisation (la vérification du visage est obligatoire sauf modification). false force la vérification du visage obligatoire dans la fenêtre de consentement. true offre l'alternative non biométrique, seulement là où la loi en exige une (p. ex. au Québec) : le demandeur peut refuser la vérification du visage, aucun selfie ni détection du vivant n'est exécuté, et l'hôte compare visuellement le visage avec la pièce d'identité pendant l'appel. La fenêtre n'inclut pas la vérification du visage lorsque consentScreen vaut false. |
applicantFileUploadEnabled | booléen | non | Par défaut false : pendant la session, le demandeur ne peut prendre des photos qu'avec sa caméra. true autorise aussi l'envoi de fichiers depuis son appareil. L'animateur peut modifier ce paramètre pendant la session. |
channels.email / channels.sms | booléen | non | Mode de remise de l'invitation. Par défaut : courriel ; SMS au téléphone d'un demandeur nouvellement créé. Pour un demandeur existant, son téléphone enregistré ne reçoit un SMS que si sms: true. Les deux à false = rien n'est envoyé : vous transmettez vous-même applicantJoinUrl. |
Demandeur en ligne (à la place de kycRequestId) :
{
"applicant": {
"fullName": "Jane Doe",
"email": "jane@example.com",
"phone": "+14155551234",
"emailVerified": false,
"phoneVerified": false,
"externalRef": "customer-881"
},
"host": { "userId": "usr_01H…" },
"externalRef": "order-10042",
"startNow": true
}
Les invitations par SMS sont facturées comme les invitations du tableau de bord (d'abord les SMS inclus dans votre forfait, puis votre solde de crédits). Si aucun des deux ne couvre le coût, le SMS est ignoré et le courriel est tout de même envoyé.
Exemple
curl -X POST https://app.certivant.com/api/v1/kyc/conferences \
-H "Authorization: Bearer $CERTIVANT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"kycRequestId": "req_01J...",
"host": { "email": "compliance@yourcompany.com" },
"externalRef": "order-10042",
"scheduledFor": "2026-10-06T10:00:00-04:00",
"language": "en"
}'
Réponse 201
L'objet de conférence, plus :
{
"id": "cmf…",
"kycRequestId": "req_01J…",
"externalRef": "order-10042",
"status": "scheduled",
"scheduledFor": "2026-10-06T14:00:00.000Z",
"expiresAt": "2026-10-07T14:00:00.000Z",
"applicantJoinUrl": "https://app.certivant.com/forms/conference/3f9c…",
"invitation": { "email": true, "sms": false }
}
applicantJoinUrl n'est renvoyé qu'ici et par renvoi de l'invitation : Certivant ne conserve qu'un haché du lien, il ne peut donc pas être relu plus tard. applicantWasReused est ajouté pour un demandeur en ligne. invitation indique les canaux sur lesquels l'invitation a été envoyée.
Erreurs
| Statut | Quand |
|---|---|
400 | Validation échouée (error.issues), kycRequestId et applicant tous deux présents ou tous deux absents, scheduledFor et startNow tous deux présents ou tous deux absents, une heure passée, expiresAt non postérieur à scheduledFor, ou l'animateur n'est pas membre de votre organisation. |
402 | Votre forfait n'inclut pas la vérification en direct. |
403 | La clé ne possède pas kyc:write ; compte en attente de vérification ; vérification en direct désactivée dans les paramètres ; ou l'animateur n'a pas l'autorisation d'approbation manuelle. |
404 | kycRequestId n'existe pas dans votre organisation. |
409 | La vérification a déjà une conférence accessible. Le corps contient son conferenceId : reprogrammez ou annulez-la plutôt. Une requête POST relancée n'invite donc jamais le client deux fois. |
Obtenir une vérification en direct
GET /api/v1/kyc/conferences/:id Portée : kyc:read
Renvoie l'objet de conférence. Un identifiant appartenant à une autre organisation renvoie un 404.
curl https://app.certivant.com/api/v1/kyc/conferences/cmf… \
-H "Authorization: Bearer $CERTIVANT_API_KEY"
Rechercher par votre code de référence
GET /api/v1/kyc/conferences/by-external-ref/:externalRef Portée : kyc:read
La conférence la plus récente portant ce externalRef (encodé pour une URL). Renvoie 404 si aucune n'existe.
curl https://app.certivant.com/api/v1/kyc/conferences/by-external-ref/order-10042 \
-H "Authorization: Bearer $CERTIVANT_API_KEY"
Lister les vérifications en direct
GET /api/v1/kyc/conferences Portée : kyc:read
| Requête | Remarques |
|---|---|
status | scheduled, in_progress, completed, cancelled ou expired (une session encore planifiée dont la fenêtre de connexion s'est refermée sans que la session ait eu lieu : un défaut de présentation). scheduled exclut les sessions expirées. |
externalRef | Correspondance exacte sur votre code de référence. |
kycRequestId | Conférences d'une même vérification. |
scheduledFrom / scheduledTo | Date-heures ISO, bornes incluses, sur scheduledFor. |
createdFrom / createdTo | Date-heures ISO, bornes incluses, sur la date de création. |
page / limit | Par défaut 1 / 20, limit maximum 100. |
Du plus récent au plus ancien, avec un critère de départage stable afin que la pagination n'ignore ni ne répète jamais une ligne.
curl "https://app.certivant.com/api/v1/kyc/conferences?status=completed&createdFrom=2026-10-01T00:00:00Z" \
-H "Authorization: Bearer $CERTIVANT_API_KEY"
{
"data": [ { "id": "cmf…", "status": "completed", "externalRef": "order-10042" } ],
"pagination": { "page": 1, "limit": 20, "total": 1, "totalPages": 1 }
}
Reprogrammer
POST /api/v1/kyc/conferences/:id/reschedule Portée : kyc:write
{
"scheduledFor": "2026-10-08T15:30:00+02:00",
"expiresAt": "2026-10-09T15:30:00+02:00",
"title": "Rescheduled video check",
"externalRef": "order-10042"
}
Seule une conférence scheduled peut être déplacée (409 une fois qu'elle a commencé, qu'elle est terminée ou qu'elle a été annulée). La nouvelle heure doit être dans le futur et avant la fin de la fenêtre de connexion : passez un expiresAt plus tardif pour prolonger la fenêtre. title et externalRef sont facultatifs : omettez-les pour les conserver, passez null pour les effacer. Renvoie l'objet de conférence accompagné de previousScheduledFor, et déclenche conference.rescheduled.
La reprogrammation ne prévient pas le demandeur : appelez renvoi de l'invitation pour lui envoyer la nouvelle heure.
Annuler
POST /api/v1/kyc/conferences/:id/cancel Portée : kyc:write
{ "reason": "Customer cancelled the order" }
Le corps est facultatif. Annule une conférence scheduled : le lien du demandeur cesse de fonctionner et conference.cancelled est déclenché avec votre reason. Une session déjà in_progress renvoie 409 : l'animateur y met fin depuis la session. La demande de vérification elle-même reste inchangée.
Renvoyer l'invitation
POST /api/v1/kyc/conferences/:id/resend-invitation Portée : kyc:write
{ "channels": { "email": true, "sms": false }, "language": "es" }
Émet un nouveau lien de connexion (l'ancien cesse de fonctionner) et renvoie l'invitation sur les channels : par défaut le courriel, plus le SMS lorsqu'un téléphone est enregistré. Si les deux canaux sont à false, rien n'est envoyé : utilisez vous-même le applicantJoinUrl renvoyé (copyOnly: true). Autorisé tant que la conférence est scheduled ou in_progress et que sa fenêtre de connexion est ouverte, sinon 409.
L'objet de conférence
{
"id": "cmf…",
"kycRequestId": "req_01J…",
"applicantId": "cma…",
"organizationId": "org_01H…",
"externalRef": "order-10042",
"title": "Account opening — video check",
"status": "completed",
"expired": false,
"scheduledFor": "2026-10-06T14:00:00.000Z",
"expiresAt": "2026-10-07T14:00:00.000Z",
"startedAt": "2026-10-06T14:01:12.000Z",
"endedAt": "2026-10-06T14:14:40.000Z",
"durationSeconds": 808,
"createdAt": "2026-10-01T09:00:00.000Z",
"updatedAt": "2026-10-06T14:14:40.000Z",
"host": { "userId": "usr_01H…", "name": "Sam Reviewer", "email": "compliance@yourcompany.com" },
"applicant": {
"id": "cma…",
"fullName": "Jane Doe",
"email": "jane@example.com",
"externalRef": "customer-881",
"joinedAt": "2026-10-06T14:02:03.000Z"
},
"additionalApplicants": [],
"additionalKycRequestIds": [],
"consent": {
"screenEnabled": true,
"requiredPurposes": ["share_information", "photo_capture", "video_recording"],
"given": true,
"at": "2026-10-06T14:01:58.000Z",
"version": "2026-10-04"
},
"decision": {
"decision": "verified",
"outcome": "approved",
"requestStatus": "APPROVED",
"notes": "ID checked on camera, matches the applicant.",
"decidedByUserId": "usr_01H…",
"documentDecisions": { "verified": 2, "rejected": 0, "needsResubmission": 0 }
},
"recording": { "available": true, "deliveryStatus": "delivered", "durationSeconds": 790, "sha256": "9b1f…" },
"certificate": null
}
| Champ | Remarques |
|---|---|
status | scheduled → in_progress (l'animateur a rejoint) → completed (décision enregistrée ou session terminée), ou cancelled. |
expired | true lorsqu'une session scheduled a vu sa fenêtre de connexion se refermer sans avoir eu lieu (défaut de présentation). Filtrez avec status=expired. |
consent.given | La réponse du demandeur principal : null tant qu'il n'a pas répondu, false s'il a refusé (la session est alors annulée). requiredPurposes contient toujours video_recording. |
decision.decision | Le verdict de l'animateur : verified, rejected, needs_more_info ou inconclusive. null tant qu'aucune décision n'a été prise. |
decision.outcome | Effet sur la vérification : approved, rejected ou escalated (needs_more_info / inconclusive la mettent en attente d'examen). requestStatus est le statut de la vérification. |
recording.available | true une fois l'enregistrement assemblé et stocké ou livré à votre stockage. Le média lui-même n'est jamais exposé par cette API. |
certificate | Le certificat de vérification de la vérification lorsqu'il a été émis : id, certificateNumber, verificationUrl. |
Webhooks
Les vérifications en direct émettent cinq événements vers votre URL de webhook (l'URL propre au formulaire lorsque la vérification provient d'un formulaire qui en définit une, sinon l'URL par défaut de votre organisation). Ils sont déclenchés pour chaque conférence, qu'elle ait été planifiée depuis le tableau de bord ou par l'API, signés et relancés exactement comme tous les autres webhooks Certivant.
| Événement | Déclenché lorsque |
|---|---|
conference.scheduled | Une conférence est créée (planifiée pour plus tard, ou démarrée immédiatement). |
conference.rescheduled | Une conférence planifiée est déplacée à une nouvelle heure. Contient previousScheduledFor. |
conference.cancelled | Une conférence est annulée, par votre appel API ou parce que le demandeur a refusé le consentement. Contient cancellation. |
conference.started | L'animateur démarre la session (première connexion). Une fois par démarrage : les reconnexions de l'animateur ne le répètent pas. |
conference.completed | L'animateur enregistre la décision finale. Contient decision. |
Un renvoi d'invitation n'est pas un nouvel événement conference.scheduled.
Il n'existe pas encore d'événement no_show. Une session qui n'a jamais lieu reste
scheduled et est signalée avec expired: true une fois sa fenêtre de connexion
refermée : interrogez GET /conferences?status=expired pour repérer les défauts de présentation.
Charge utile
Contrairement aux événements kyc.request.*, les événements de conférence utilisent un corps au format de conférence. Chaque événement contient les identifiants, votre externalRef, le statut et les horodatages :
{
"event": "conference.completed",
"conferenceId": "cmf…",
"kycRequestId": "req_01J…",
"applicantId": "cma…",
"organizationId": "org_01H…",
"externalRef": "order-10042",
"title": "Account opening — video check",
"status": "completed",
"scheduledFor": "2026-10-06T14:00:00.000Z",
"expiresAt": "2026-10-07T14:00:00.000Z",
"startedAt": "2026-10-06T14:01:12.000Z",
"endedAt": null,
"durationSeconds": null,
"createdAt": "2026-10-01T09:00:00.000Z",
"updatedAt": "2026-10-06T14:13:55.000Z",
"host": { "userId": "usr_01H…", "name": "Sam Reviewer", "email": "compliance@yourcompany.com" },
"consent": {
"screenEnabled": true,
"requiredPurposes": ["share_information", "photo_capture", "video_recording"],
"given": true,
"at": "2026-10-06T14:01:58.000Z"
},
"decision": {
"decision": "rejected",
"outcome": "rejected",
"requestStatus": "REJECTED",
"notes": "Face does not match the ID photo.",
"decidedByUserId": "usr_01H…",
"documentDecisions": { "verified": 0, "rejected": 1, "needsResubmission": 0 }
},
"recording": { "available": false, "deliveryStatus": "pending", "durationSeconds": null, "sha256": null },
"certificate": null,
"timestamp": "2026-10-06T14:13:55.000Z"
}
Champs propres à chaque événement :
conference.rescheduled:previousScheduledFor.conference.cancelled:cancellation.reasonetcancellation.cancelledBy(api,host,applicant,system). Un consentement refusé arrive aveccancelledBy: "applicant",reason: "applicant_refused_consent".conference.completed:decision(comme dans l'objet de conférence). La session peut encore être en cours lorsque la décision est enregistrée ;endedAtpeut donc valoirnull, et l'enregistrement est généralement encore en cours d'assemblage (recording.available: false) : lisezGET /conferences/:idplus tard pour connaître l'état final de l'enregistrement.
Les charges utiles ne contiennent jamais de média, de liens d'enregistrement ni de jetons de connexion.
Vérifier la signature
Chaque livraison est signée avec le secret de signature de votre webhook. L'en-tête recommandé est X-Certivant-Signature-V2: t=<unix>,v1=<hex>, où v1 est le HMAC-SHA256 de `${t}.${rawBody}`. Rejetez les horodatages décalés de plus de 300 secondes et dédoublonnez à partir de X-Certivant-Event-Id. (Les anciens en-têtes X-KYC-Signature / X-KYC-Timestamp / X-KYC-Event-Id sont toujours envoyés.)
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyCertivantWebhook(rawBody: string, headers: Headers, secret: string) {
const parts = (headers.get("X-Certivant-Signature-V2") ?? "").split(",").map((p) => p.split("="));
const t = parts.find(([k]) => k === "t")?.[1] ?? "";
if (!t || Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
return parts.some(
([k, v]) => k === "v1" && v?.length === expected.length && timingSafeEqual(Buffer.from(v), Buffer.from(expected)),
);
}
// In your handler:
const event = JSON.parse(rawBody);
if (event.event === "conference.completed") {
await orders.markIdentityChecked(event.externalRef, event.decision.outcome);
}
Détails complets et autres langages : Vérification de la signature.
Limites de débit
Ces points de terminaison partagent la limite de débit de l'API de votre organisation avec le reste de l'API v1 : voir Limites de débit.
Sessions hébergées
Générez une URL à courte durée de vie où le demandeur effectue la capture des documents, le contrôle de vivacité et les étapes d'identification.
API de la liste de blocage
Ajoutez, listez et retirez des entrées de la liste de blocage depuis votre propre système — adresses courriel, numéros de téléphone, numéros de document et adresses ou plages IP, ou le visage, les appareils et l'adresse IP d'une vérification existante.