L'API PVID Cloud vous permet de,
Cette documentation a pour but de vous guider dans l'intégration et la consommation de l'API PVID Cloud.
Pour utiliser l'API PVID Cloud vous devez posséder des identifiants. Vous pouvez demander vos identifiants directement au support PVID Cloud.
Pour l'ensemble des routes de l'API PVID Cloud il faut être en capacité de,
L'API PVID Cloud utilise OAuth 2.0 avec des tokens JWT. Toutes les requêtes API authentifiées nécessitent un token JWT valide 5 minutes.
Route permettant d'obtenir un token JWT pour pouvoir requêter les autres endpoints.
| Paramètre | Type | Description | Requis |
| — | — | — | — |
username | string | Identifiant de votre application | ✅ |
password | string | Mot de passe associé | ✅ |
| Code | Status |
| ——– | —– |
| 200 | OK |
Exemple de réponse
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_in": 300,
"refresh_expires_in": 1800,
"refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"not-before-policy": 0,
"session_state": "123e4567-e89b-12d3-a456-426614174000",
"scope": "email profile"
}
| Code | Status | Valeur |
| — | — | — |
| 400 | Bad Request | Missing credentials |
| 401 | Unauthorized | Invalid credentials or Keycloak error |
Pour vous authentifier, il faut ajouter ce header à toutes vos requêtes.
| Header | Valeur |
| — | — |
Authorization | Bearer ACCESS_TOKEN |
Route pour obtenir un nouveau token d'accès sans vous reconnecter.
| Paramètre | Type | Description | Requis |
| — | — | — | — |
refresh_token | string | Token de rafraîchissement obtenu lors du login | ✅ |
| Code | Status |
| — | — |
| 200 | OK |
Exemple de réponse
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_in": 300,
"refresh_expires_in": 1800,
"refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"not-before-policy": 0,
"session_state": "123e4567-e89b-12d3-a456-426614174000",
"scope": "email profile"
}
| Code | Status | Message |
| — | — | — |
| 400 | Bad Request | Missing refresh token |
| 400 | Bad Request | Invalid refresh token |
| 401 | Unauthorized | Invalid credentials or Keycloak error |
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...401 Unauthorized
Pour des raisons de sécurité, vos requêtes vers PVID Cloud doivent être signées et les réponses de notre API devront être déchiffrées.
Pour toutes les routes sécurisées (sauf /api/login et /api/refresh_token), un header signature est obligatoire. Ce header permet de valider l'authenticité de la requête.
| Header | Type | Description | Requis |
| — | — | — | — |
signature | string | Signature générée à partir du paramètre date | ✅ |
| Élément | Valeur |
| — | — |
| Algorithme | AES-256-CBC avec padding PKCS7 |
| Clé | 32 premiers caractères de SHA256(tokenSecret) en hexadécimal |
| IV | 16 premiers caractères de SHA256(SHA256(tokenSecret)) en hexadécimal |
| Valeur à chiffrer | Le paramètre date (format YYYY-MM-DD HH:mm:ss) |
| Sortie | Base64 |
| Code | Status | Slug | Message |
| — | — | — | — |
| 400 | Bad Request | invalid_date | Wrong date format (must be YYYY-MM-DD HH:mm:ss, eg: 2021-10-19 20:10:06) |
| 400 | Bad Request | empty_date | No date parameter found in your request |
| 400 | Bad Request | expired_date | Given datetime is older than current datetime |
| 400 | Bad Request | date_in_future | Given datetime must be set between call submission and +10 minutes |
| 400 | Bad Request | validation_error | Signature verification failed |
| 400 | Bad Request | failed_signature | Signature verification failed |
| 401 | Unauthorized | N/A | JWT Token not found |
| 401 | Unauthorized | N/A | Expired JWT Token |
| 401 | Unauthorized | user_not_exist | Unknown user |
| 500 | Bad Request | internal_server_error | Internal server error |
<?php function generateSignature(string $date, string $tokenSecret): string { $key = substr(hash('sha256', $tokenSecret), 0, 32); $iv = substr(hash('sha256', hash('sha256', $tokenSecret)), 0, 16); return openssl_encrypt($date, 'aes-256-cbc', $key, 0, $iv); } $date = "2026-05-20 14:30:00"; $tokenSecret = "votre-token-secret"; $signature = generateSignature($date, $tokenSecret); echo $signature;
import hashlib from Crypto.Cipher import AES import base64 def generate_signature(date: str, token_secret: str) -> str: key = hashlib.sha256(token_secret.encode()).hexdigest()[:32] iv = hashlib.sha256(hashlib.sha256(token_secret.encode()).hexdigest().encode()).hexdigest()[:16] cipher = AES.new(key.encode(), AES.MODE_CBC, iv.encode()) pad_len = 16 - (len(date) % 16) padded_date = date + chr(pad_len) * pad_len encrypted = cipher.encrypt(padded_date.encode()) return base64.b64encode(encrypted).decode() date = "2026-05-20 14:30:00" token_secret = "votre-token-secret" signature = generate_signature(date, token_secret) print(signature)
const crypto = require('crypto'); function generateSignature(date, tokenSecret) { const key = crypto.createHash('sha256').update(tokenSecret).digest('hex').slice(0, 32); const fullHash = crypto.createHash('sha256').update(tokenSecret).digest('hex'); const iv = crypto.createHash('sha256').update(fullHash).digest('hex').slice(0, 16); const cipher = crypto.createCipheriv('aes-256-cbc', key, iv); let encrypted = cipher.update(date, 'utf8', 'base64'); encrypted += cipher.final('base64'); return encrypted; } const date = "2026-05-20 14:30:00"; const tokenSecret = "votre-token-secret"; const signature = generateSignature(date, tokenSecret); console.log(signature);
import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.util.Base64; public class SignatureGenerator { private static String bytesToHex(byte[] bytes) { StringBuilder sb = new StringBuilder(); for (byte b : bytes) sb.append(String.format("%02x", b)); return sb.toString(); } public static String generateSignature(String date, String tokenSecret) throws Exception { MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); String fullHash = bytesToHex(sha256.digest(tokenSecret.getBytes(StandardCharsets.UTF_8))); String key = fullHash.substring(0, 32); sha256.reset(); String iv = bytesToHex(sha256.digest(fullHash.getBytes(StandardCharsets.UTF_8))).substring(0, 16); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES"), new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8))); byte[] encrypted = cipher.doFinal(date.getBytes(StandardCharsets.UTF_8)); return Base64.getEncoder().encodeToString(encrypted); } }
En dehors des routes d'authentification (/api/login et /api/refresh_token), toutes les réponses de l'API sont chiffrées. Il faut donc les déchiffrer pour pouvoir lire la réponse.
Le paramètre date est obligatoire.
| Élément | Valeur |
| — | — |
| Algorithme | AES-256-CBC |
| Clé | SHA256(date + tokenSecret) |
| IV | 16 premiers octets du SHA256 de la clé |
| Encodage Base64 |
<?php function decryptResponse(string $encryptedData, string $date, string $tokenSecret): array { // Décodage Base64 $encrypted = base64_decode($encryptedData); // Génération de la clé (SHA256 de date + tokenSecret) $key = hash('sha256', $date . $tokenSecret, true); // Génération de l'IV (16 premiers octets du SHA256 de la clé) $iv = substr(hash('sha256', $key, true), 0, 16); // Déchiffrement AES-256-CBC $decrypted = openssl_decrypt($encrypted, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv); // Conversion JSON en tableau return json_decode($decrypted, true); } $encryptedResponse = "U2FsdGVkX1+abc123..."; $date = "2026-05-20 14:30:00"; $tokenSecret = "votre-token-secret"; $data = decryptResponse($encryptedResponse, $date, $tokenSecret); print_r($data);
import base64 import hashlib import json from Crypto.Cipher import AES def decrypt_response(encrypted_data: str, date: str, token_secret: str) -> dict: # Décodage Base64 encrypted = base64.b64decode(encrypted_data) # Génération de la clé key = hashlib.sha256((date + token_secret).encode()).digest() # Génération de l'IV iv = hashlib.sha256(key).digest()[:16] # Déchiffrement AES-256-CBC cipher = AES.new(key, AES.MODE_CBC, iv) decrypted = cipher.decrypt(encrypted) # Suppression du padding PKCS7 padding_len = decrypted[-1] decrypted = decrypted[:-padding_len] return json.loads(decrypted.decode('utf-8')) # Exemple d'utilisation # pip install pycryptodome encrypted_response = "U2FsdGVkX1+abc123..." # Réponse chiffrée de l'API date = "2026-05-20 14:30:00" # Date envoyée dans la requête token_secret = "votre-token-secret" # Votre clé secrète API data = decrypt_response(encrypted_response, date, token_secret) print(data)
const crypto = require('crypto'); function decryptResponse(encryptedData, date, tokenSecret) { // Décodage Base64 const encrypted = Buffer.from(encryptedData, 'base64'); // Génération de la clé const key = crypto.createHash('sha256').update(date + tokenSecret).digest(); // Génération de l'IV const iv = crypto.createHash('sha256').update(key).digest().slice(0, 16); // Déchiffrement AES-256-CBC const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv); let decrypted = decipher.update(encrypted); decrypted = Buffer.concat([decrypted, decipher.final()]); return JSON.parse(decrypted.toString('utf-8')); } // Exemple d'utilisation const encryptedResponse = "U2FsdGVkX1+abc123..."; // Réponse chiffrée de l'API const date = "2026-05-20 14:30:00"; // Date envoyée dans la requête const tokenSecret = "votre-token-secret"; // Votre clé secrète API const data = decryptResponse(encryptedResponse, date, tokenSecret); console.log(data);
import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.util.Arrays; import java.util.Base64; public class ApiDecrypter { public static String decryptResponse(String encryptedData, String date, String tokenSecret) throws Exception { // Décodage Base64 byte[] encrypted = Base64.getDecoder().decode(encryptedData); // Génération de la clé (SHA256 de date + tokenSecret) MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); byte[] key = sha256.digest((date + tokenSecret).getBytes(StandardCharsets.UTF_8)); // Génération de l'IV (16 premiers octets du SHA256 de la clé) byte[] iv = Arrays.copyOf(sha256.digest(key), 16); // Déchiffrement AES-256-CBC Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv)); byte[] decrypted = cipher.doFinal(encrypted); return new String(decrypted, StandardCharsets.UTF_8); } } // Exemple d'utilisation public static void main(String[] args) throws Exception { String encryptedResponse = "U2FsdGVkX1+abc123..."; // Réponse chiffrée de l'API String date = "2026-05-20 14:30:00"; // Date envoyée dans la requête String tokenSecret = "votre-token-secret"; // Votre clé secrète API String json = decryptResponse(encryptedResponse, date, tokenSecret); System.out.println(json); }
Cette section concerne les 2 méthodes permettant de générer un lien PVID et de récupérer les résultats d'une vérification d'identité après verdict.
Générer un lien PVID à transmettre à un utilisateur pour qu'il puisse effectuer le parcours de vérification d'identité à distance.
| Paramètre | Type | Description | Requis |
| — | — | — | — |
date | string | Date pour chiffrement | ✅ |
customerEmail | string | Email de l'utilisateur à vérifier | ✅ |
urlAfterSuccess | string | URL de redirection après succès | ✅ |
urlAfterFailOrAbort | string | URL de redirection en cas d'échec/abandon | ✅ |
isPriority | integer | Traitement Express (0 ou 1). Surcoût applicable | ❌ |
linkValiditySharing | integer | Durée de validité en minutes (max 60) | ❌ |
disableLinkSharing | integer | Désactive le partage du lien via SMS/Qr-code (0 ou 1) | ❌ |
addUserAlertScreen | integer | Afficher un écran d'alerte à l'utilisateur (0 ou 1) | ❌ |
addUserAlertText | string | Ajouter un texte personnalisé sur l'écran d'alerte (5-120 caractères) | ❌ |
acceptedDocuments | array | Documents acceptés (ex: ["FR-passport"]) | ❌ |
webhook | string | URL de webhook pour cette vérification (surcharge du webhook global, HTTPS obligatoire) | ❌ |
webhook est optionnel : si non fourni, le webhook global de configuration sera utiliséid de la vérification pour les requêtes ultérieures (récupération des résultats)
| Code | Status |
| — | — |
| 201 | Created |
Exemple de réponse
{
"id": 987654,
"isPriority": 0,
"customerEmail": "jean.dupont@example.com",
"date": "2026-05-20 14:25:00",
"dateExpireLink": "2026-05-20 14:55:00",
"url": "https://pvid-front.../verify/abc123xyz789",
"urlAfterSuccess": "https://votre-site.com/verification/succes",
"urlAfterFailOrAbort": "https://votre-site.com/verification/echec",
"fullHashSha256": "a1b2c3d4e5f6..."
}
| Code | Status | Slug | Message |
| — | — | — | — |
| 400 | Bad Request | N/A | Request payload contains invalid "form" data. |
| 400 | Bad Request | no_email | No email parameter found in your request |
| 400 | Bad Request | email_not_valid | This value is not a valid email address. |
| 400 | Bad Request | empty_url_after_success | No urlAfterSuccess found in your request |
| 400 | Bad Request | empty_url_after_fail_or_abort | No urlAfterFailOrAbort found in your request |
| 400 | Bad Request | alert_text_too_short | This value is too short. It should have 5 characters or more. |
| 400 | Bad Request | alert_text_too_long | This value is too long. It should have 120 characters or less. |
| 422 | Unprocessable Entity | webhook | The webhook URL must use HTTPS |
| 422 | Unprocessable Entity | linkValiditySharing | This value should be of type int. |
| 422 | Unprocessable Entity | linkValiditySharing | This value should be positive. |
| 422 | Unprocessable Entity | disableLinkSharing | This value should be of type int. |
| 422 | Unprocessable Entity | disableLinkSharing | Values accepted : 0, 1. |
| 422 | Unprocessable Entity | addUserAlertScreen | This value should be of type int. |
| 422 | Unprocessable Entity | addUserAlertScreen | Values accepted : 0, 1. |
| 422 | Unprocessable Entity | acceptedDocuments | This value is not valid. |
| 422 | Unprocessable Entity | isPriority | This value should be of type int. |
| 422 | Unprocessable Entity | isPriority | Values accepted : 0, 1. |
| 400 | Bad Request | invalid_date | Wrong date format (must be YYYY-MM-DD HH:mm:ss, eg: 2021-10-19 20:10:06) |
| 400 | Bad Request | empty_date | No date parameter found in your request |
| 400 | Bad Request | expired_date | Given datetime is older than current datetime |
| 400 | Bad Request | date_in_future | Given datetime must be set between call submission and +10 minutes |
| 400 | Bad Request | validation_error | Signature verification failed |
| 400 | Bad Request | failed_signature | Signature verification failed |
| 401 | Unauthorized | N/A | JWT Token not found |
| 401 | Unauthorized | N/A | Expired JWT Token |
| 401 | Unauthorized | user_not_exist | Unknown user |
| 500 | Bad Request | internal_server_error | Internal server error |
Récupérer les informations d'un ID PVID donné.
| Code | Status |
| — | — |
| 200 | OK |
Exemple de réponse
{
"id": 987654,
"dateAdded": "2026-05-20 14:25:00",
"verdictDate": "2026-05-20 15:30:00",
"status": "done",
"verdict": "validated",
"document": {
"type": "ID_CARD",
"emitCountry": "FR",
"documentNumber": "AB1234567",
"documentNumberLast4": "4567",
"emitDate": "2020-01-15",
"expirationDate": "2030-01-15",
"holderFirstname": "Jean",
"holderLastname": "DUPONT",
"birthdate": "1985-06-20",
"holderNationality": "FR",
"holderGender": "M",
"holderBirthplace": "Paris",
"mrz": "IDFRADUPONT<<JEAN..."
},
"files": {
"availableUntil": "2026-06-20 15:30:00",
"front": "https://storage.../front.jpg",
"back": "https://storage.../back.jpg",
"biometry": "https://storage.../bio.jpg"
}
}
| Code | Status |
| — | — |
| 200 | OK |
Exemple de réponse
{
"id": 987654,
"dateAdded": "2026-05-20 14:25:00",
"verdictDate": "2026-05-20 15:30:00",
"status": "done",
"verdict": "refused",
"abortedReason": null,
"refusalReasons": [
{
"slug": "doc_is_expired",
"text": "Les documents que vous nous avez présentés sont expirés. Il faut impérativement présenter une pièce d'identité en cours de validité"
}
]
}
Voir la liste complète des motifs d'abandon
| Code | Status |
| — | — |
| 200 | OK |
Exemple de réponse
{
"id": 987654,
"dateAdded": "2026-05-20 14:25:00",
"verdictDate": "2026-05-20 15:25:00",
"status": "expired",
"verdict": null,
"abortedReason": "expired_link",
"refusalReasons": null
}
| Code | Status |
| — | — |
| 200 | OK |
Exemple de réponse
{
"id": 987654,
"dateAdded": "2026-05-20 14:25:00",
"verdictDate": "2026-05-20 14:35:00",
"status": "aborted",
"verdict": null,
"abortedReason": "canceled",
"refusalReasons": null
}
Voir la liste complète des motifs d'abandon
| Code | Status |
| — | — |
| 200 | OK |
Exemple de réponse
{
"id": 987654,
"dateAdded": "2026-05-20 14:25:00",
"verdictDate": null,
"status": "currently_created",
"verdict": null,
"abortedReason": null,
"refusalReasons": null
}
| Code | Status | Slug | Message |
| — | — | — | — |
| 400 | Bad Request | invalid_date | Wrong date format (must be YYYY-MM-DD HH:mm:ss, eg: 2021-10-19 20:10:06) |
| 400 | Bad Request | empty_date | No date parameter found in your request |
| 400 | Bad Request | expired_date | Given datetime is older than current datetime |
| 400 | Bad Request | date_in_future | Given datetime must be set between call submission and +10 minutes |
| 400 | Bad Request | validation_error | Signature verification failed |
| 400 | Bad Request | failed_signature | Signature verification failed |
| 401 | Unauthorized | N/A | JWT Token not found |
| 401 | Unauthorized | N/A | Expired JWT Token |
| 401 | Unauthorized | user_not_exist | Unknown user |
| 500 | Bad Request | internal_server_error | Internal server error |
Cette section concerne les méthodes et les endpoints permettant de personnaliser votre parcours PVID.
La personnalisation du parcours concerne les éléments suivants,
Récupérer les paramètres de la personnalisation actuelle.
| Paramètre | Type | Description | Requis |
| — | — | — | — |
date | string | Date pour le chiffrement (YYYY-MM-DD HH:mm:ss) | ✅ |
| Code | Status |
| — | — |
| 200 | OK |
Exemple de réponse
{
"webhook": "https://api.votre-domaine.com/pvid/webhook",
"linkValiditySharing": 60,
"disableLinkSharing": 0,
"addUserAlertScreen": 1,
"addUserAlertText": "<b>Attention</b>",
"titleColor": "#003366",
"textColor": "#333333",
"linkColorText": "#0066CC",
"buttonColorText": "#FFFFFF",
"buttonColorBackground": "#003366",
"ghostButtonColor": "#003366",
"acceptedDocuments": [...],
"logo": "data:image/png;base64,...",
"favicon": "data:image/x-icon;base64,..."
}
| Code | Status | Slug | Message |
| — | — | — | — |
| 400 | Bad Request | invalid_date | Wrong date format (must be YYYY-MM-DD HH:mm:ss, eg: 2021-10-19 20:10:06) |
| 400 | Bad Request | empty_date | No date parameter found in your request |
| 400 | Bad Request | expired_date | Given datetime is older than current datetime |
| 400 | Bad Request | date_in_future | Given datetime must be set between call submission and +10 minutes |
| 400 | Bad Request | validation_error | Signature verification failed |
| 400 | Bad Request | failed_signature | Signature verification failed |
| 401 | Unauthorized | N/A | JWT Token not found |
| 401 | Unauthorized | N/A | Expired JWT Token |
| 401 | Unauthorized | user_not_exist | Unknown user |
| 500 | Bad Request | internal_server_error | Internal server error |
Méthode permettant de remplacer entièrement la configuration actuelle de votre PVID.
Attention : cette méthode remplacera l'ensemble des données de configuration en place. Pour effectuer une modification partielle de la configuration utiliser la méthode “PATCH” de mise à jour de la configuration.
| Paramètre | Type | Description | Requis |
| — | — | — | — |
webhook | string | URL webhook (HTTPS obligatoire) | ❌ |
titleColor | string | Couleur titres (#XXXXXX) | ❌ |
textColor | string | Couleur textes (#XXXXXX) | ❌ |
linkColorText | string | Couleur liens (#XXXXXX) | ❌ |
buttonColorText | string | Couleur texte boutons (#XXXXXX) | ❌ |
buttonColorBackground | string | Couleur fond boutons (#XXXXXX) | ❌ |
ghostButtonColor | string | Couleur boutons fantômes (#XXXXXX) | ❌ |
linkValiditySharing | integer | Validité du lien en minutes (max: 60) | ❌ |
disableLinkSharing | integer | Désactiver partage (0 ou 1) | ❌ |
addUserAlertScreen | integer | Écran d'alerte fraude (0 ou 1) | ❌ |
addUserAlertText | string | Texte alerte (5-120 car.) | ❌ |
acceptedDocuments | array | Documents acceptés | ❌ |
date | string | Date chiffrement | ✅ |
| Code | Status | Slug | Message |
| — | — | — | — |
| 400 | Bad Request | alert_text_too_short | The alert's text you submitted is too short (less than 5 characters) |
| 400 | Bad Request | alert_text_too_long | The alert's text you submitted is too long (more than 120 characters) |
| 422 | Unprocessable Entity | champ en erreur parmi titleColor, textColor, linkColorText, buttonColorText, buttonColorBackground, ghostButtonColor | This value is not a valid CSS color |
| 422 | Unprocessable Entity | webhook | The webhook URL must use HTTPS |
| 422 | Unprocessable Entity | linkValiditySharing | This value should be of type int |
| 422 | Unprocessable Entity | linkValiditySharing | This value should be positive |
| 422 | Unprocessable Entity | disableLinkSharing | This value should be of type int |
| 422 | Unprocessable Entity | disableLinkSharing | Values accepted : 0, 1 |
| 422 | Unprocessable Entity | addUserAlertScreen | This value should be of type int |
| 422 | Unprocessable Entity | addUserAlertScreen | Values accepted : 0, 1 |
| 400 | Bad Request | invalid_date | Wrong date format (must be YYYY-MM-DD HH:mm:ss, eg: 2021-10-19 20:10:06) |
| 400 | Bad Request | empty_date | No date parameter found in your request |
| 400 | Bad Request | expired_date | Given datetime is older than current datetime |
| 400 | Bad Request | date_in_future | Given datetime must be set between call submission and +10 minutes |
| 400 | Bad Request | validation_error | Signature verification failed |
| 400 | Bad Request | failed_signature | Signature verification failed |
| 401 | Unauthorized | N/A | JWT Token not found |
| 401 | Unauthorized | N/A | Expired JWT Token |
| 401 | Unauthorized | user_not_exist | Unknown user |
| 500 | Bad Request | internal_server_error | Internal server error |
Méthode permettant de mettre à jour partiellement votre configuration PVID (seuls les champs fournis sont modifiés).
| Champ | Type | Description | Requis |
| — | — | — | — |
webhook | string | URL webhook (HTTPS obligatoire) | ❌ |
titleColor | string | Couleur titres (#XXXXXX) | ❌ |
textColor | string | Couleur textes (#XXXXXX) | ❌ |
linkColorText | string | Couleur liens (#XXXXXX) | ❌ |
buttonColorText | string | Couleur texte boutons (#XXXXXX) | ❌ |
buttonColorBackground | string | Couleur fond boutons (#XXXXXX) | ❌ |
ghostButtonColor | string | Couleur boutons fantômes (#XXXXXX) | ❌ |
linkValiditySharing | integer | Validité du lien en minutes (max: 60) | ❌ |
disableLinkSharing | integer | Désactiver partage (0 ou 1) | ❌ |
addUserAlertScreen | integer | Écran d'alerte fraude (0 ou 1) | ❌ |
addUserAlertText | string | Texte alerte (5-120 car.) | ❌ |
acceptedDocuments | array | Documents acceptés | ❌ |
date | string | Date chiffrement | ✅ |
| Code | Status | Slug | Message |
| — | — | — | — |
| 400 | Bad Request | alert_text_too_short | The alert's text you submitted is too short (less than 5 characters) |
| 400 | Bad Request | alert_text_too_long | The alert's text you submitted is too long (more than 120 characters) |
| 422 | Unprocessable Entity | champ en erreur parmi titleColor, textColor, linkColorText, buttonColorText, buttonColorBackground, ghostButtonColor | This value is not a valid CSS color |
| 422 | Unprocessable Entity | webhook | The webhook URL must use HTTPS |
| 422 | Unprocessable Entity | linkValiditySharing | This value should be of type int |
| 422 | Unprocessable Entity | linkValiditySharing | This value should be positive |
| 422 | Unprocessable Entity | disableLinkSharing | This value should be of type int |
| 422 | Unprocessable Entity | disableLinkSharing | Values accepted : 0, 1 |
| 422 | Unprocessable Entity | addUserAlertScreen | This value should be of type int |
| 422 | Unprocessable Entity | addUserAlertScreen | Values accepted : 0, 1 |
| 400 | Bad Request | invalid_date | Wrong date format (must be YYYY-MM-DD HH:mm:ss, eg: 2021-10-19 20:10:06) |
| 400 | Bad Request | empty_date | No date parameter found in your request |
| 400 | Bad Request | expired_date | Given datetime is older than current datetime |
| 400 | Bad Request | date_in_future | Given datetime must be set between call submission and +10 minutes |
| 400 | Bad Request | validation_error | Signature verification failed |
| 400 | Bad Request | failed_signature | Signature verification failed |
| 401 | Unauthorized | N/A | JWT Token not found |
| 401 | Unauthorized | N/A | Expired JWT Token |
| 401 | Unauthorized | user_not_exist | Unknown user |
| 500 | Bad Request | internal_server_error | Internal server error |
Méthode permettant de réinitialiser la configuration de votre PVID pour revenir au paramètres par défaut.
Attention : cette requête supprime tous vos paramètres personnalisés
| Paramètre | Type | Description | Requis |
| — | — | — | — |
date | string | Date pour le chiffrement | ✅ |
| Code | Status |
| — | — |
| 204 | No Content |
| Code | Status | Slug | Message |
| — | — | — | — |
| 400 | Bad Request | invalid_date | Wrong date format (must be YYYY-MM-DD HH:mm:ss, eg: 2021-10-19 20:10:06) |
| 400 | Bad Request | empty_date | No date parameter found in your request |
| 400 | Bad Request | expired_date | Given datetime is older than current datetime |
| 400 | Bad Request | date_in_future | Given datetime must be set between call submission and +10 minutes |
| 400 | Bad Request | validation_error | Signature verification failed |
| 400 | Bad Request | failed_signature | Signature verification failed |
| 401 | Unauthorized | N/A | JWT Token not found |
| 401 | Unauthorized | N/A | Expired JWT Token |
| 401 | Unauthorized | user_not_exist | Unknown user |
| 500 | Bad Request | internal_server_error | Internal server error |
Endpoint permettant de récupérer la liste des documents d'identité acceptés par PVID.
| Paramètre | Type | Description | Requis |
| — | — | — | — |
date | string | Date pour le chiffrement | ✅ |
| Code | Status |
| — | — |
| 200 | OK |
Exemple de réponse
{
"allowedDocuments": [
{ "code": "FR", "name": "France", "documents": ["passport", "id_card", "residence_permit"] },
{ "code": "BE", "name": "Belgium", "documents": ["passport", "id_card"] }
]
}
| Code | Status | Slug | Message |
| — | — | — | — |
| 400 | Bad Request | invalid_date | Wrong date format (must be YYYY-MM-DD HH:mm:ss, eg: 2021-10-19 20:10:06) |
| 400 | Bad Request | empty_date | No date parameter found in your request |
| 400 | Bad Request | expired_date | Given datetime is older than current datetime |
| 400 | Bad Request | date_in_future | Given datetime must be set between call submission and +10 minutes |
| 400 | Bad Request | validation_error | Signature verification failed |
| 400 | Bad Request | failed_signature | Signature verification failed |
| 401 | Unauthorized | N/A | JWT Token not found |
| 401 | Unauthorized | N/A | Expired JWT Token |
| 401 | Unauthorized | user_not_exist | Unknown user |
| 500 | Bad Request | internal_server_error | Internal server error |
Méthode permettant de mettre à jour le logo et le favicon du parcours PVID.
Cette requête utilise le multipart/form-data pour l'upload des fichiers.
| Champ | Type | Description | Requis |
| — | — | — | — |
logo | file | Image PNG/JPEG (hauteur max: 64px) | ❌ |
favicon | file | Image PNG/JPEG/ICO (hauteur max: 64px) | ❌ |
date | string | Date pour chiffrement (YYYY-MM-DD HH:mm:ss) | ✅ |
| Code | Status | Slug | Message |
| — | — | — | — |
| 422 | Unprocessable Entity | logo.path | This value should be of type string |
| 422 | Unprocessable Entity | favicon.path | This value should be of type string |
| 400 | Bad Request | invalid_date | Wrong date format (must be YYYY-MM-DD HH:mm:ss, eg: 2021-10-19 20:10:06) |
| 400 | Bad Request | empty_date | No date parameter found in your request |
| 400 | Bad Request | expired_date | Given datetime is older than current datetime |
| 400 | Bad Request | date_in_future | Given datetime must be set between call submission and +10 minutes |
| 400 | Bad Request | validation_error | Signature verification failed |
| 400 | Bad Request | failed_signature | Signature verification failed |
| 401 | Unauthorized | N/A | JWT Token not found |
| 401 | Unauthorized | N/A | Expired JWT Token |
| 401 | Unauthorized | user_not_exist | Unknown user |
| 500 | Bad Request | internal_server_error | Internal server error |
Méthode permettant de supprimer vos logo/favicon du parcours PVID pour remettre les logo/favicon par défaut.
Attention : cette requête supprime votre logo et votre favicon.
| Paramètre | Type | Description | Requis |
| — | — | — | — |
date | string | Date pour le chiffrement (YYYY-MM-DD HH:mm:ss) | ✅ |
| Code | Status |
| — | — |
| 204 | No Content |
| Code | Status | Slug | Message |
| ——– | —– | —– | ——– |
| 400 | Bad Request | invalid_date | Wrong date format (must be YYYY-MM-DD HH:mm:ss, eg: 2021-10-19 20:10:06) |
| 400 | Bad Request | empty_date | No date parameter found in your request |
| 400 | Bad Request | expired_date | Given datetime is older than current datetime |
| 400 | Bad Request | date_in_future | Given datetime must be set between call submission and +10 minutes |
| 400 | Bad Request | validation_error | Signature verification failed |
| 400 | Bad Request | failed_signature | Signature verification failed |
| 401 | Unauthorized | N/A | JWT Token not found |
| 401 | Unauthorized | N/A | Expired JWT Token |
| 401 | Unauthorized | user_not_exist | Unknown user |
| 500 | Bad Request | internal_server_error | Internal server error |
Cette section concerne les endpoints permettant de tester, en amont du parcours PVID, certains paramètres de compatibilité de l'appareil de l'utilisateur.
Méthode permettant de récupérer la liste des navigateurs, et leurs versions, pris en charge par le PVID.
| Paramètre | Type | Description | Requis |
| — | — | — | — |
date | string | Date pour le chiffrement (YYYY-MM-DD HH:mm:ss) | ✅ |
| Code | Status |
| — | — |
| 201 | OK |
Exemple de réponse
{
"safari": "14",
"chrome": "110",
"firefox": "66",
"samsung": "6",
"opera": "73",
"edge": "79",
"miui": "13.21"
}
Méthode permettant de tester la compatibilité du navigateur de l'utilisateur.
| Paramètre | Type | Description | Requis |
| — | — | — | — |
name | string | Nom du navigateur | ✅ |
version | string | Version du navigateur | ✅ |
| Code | Status |
| — | — |
| 201 | OK |
Exemple de réponse
{ "result": "The user's browser can be used for a PVID" }
| Code | Slug | Message |
| — | — | — |
| 400 | missing_browser_name | The browser's name is missing |
| 400 | missing_browser_version | The browser's version is missing |
| 400 | unknown_browser_name | The browser's name is unknown |
| 400 | invalid_browser_version | The browser's version is invalid |
| 400 | empty_date | No date parameter found in your request |
| 400 | invalid_date | Wrong date format (must be YYYY-MM-DD HH:mm:ss, eg: 2021-10-19 20:10:06) |
| 400 | expired_date | Given datetime is older than current datetime |
| 400 | date_in_future | Given datetime must be set between call submission and +10 minutes |
| 401 | error | JWT Token not found |
Méthode permettant de récupérer la résolution minimale requise pour la caméra de l'appareil de l'utilisateur.
| Paramètre | Type | Description | Requis |
| — | — | — | — |
date | string | Date pour le chiffrement (YYYY-MM-DD HH:mm:ss) | ✅ |
| Code | Status | Slug | Message |
| — | — | — | — |
| 400 | Bad Request | invalid_date | Wrong date format (must be YYYY-MM-DD HH:mm:ss, eg: 2021-10-19 20:10:06) |
| 400 | Bad Request | empty_date | No date parameter found in your request |
| 400 | Bad Request | expired_date | Given datetime is older than current datetime |
| 400 | Bad Request | date_in_future | Given datetime must be set between call submission and +10 minutes |
| 400 | Bad Request | validation_error | Signature verification failed |
| 400 | Bad Request | failed_signature | Signature verification failed |
| 401 | Unauthorized | N/A | JWT Token not found |
| 401 | Unauthorized | N/A | Expired JWT Token |
| 401 | Unauthorized | user_not_exist | Unknown user |
| 500 | Bad Request | internal_server_error | Internal server error |
Les événements webhook permettent au métier d'être informé en temps réel de chaque changement d'état d'un PVID.
Pour recevoir des événements webhook il faut renseigner une URL en HTTPS dans le paramètre webhook des endpoints POST /api/pvid, PUT /api/configs ou PATCH /api/configs.
Lors de la réception de l'événement vous devez nous retourner un code HTTP 200.
Si le code HTTP de réponse de votre part est différent, nous allons effectuer jusqu'à 48 tentatives. Nous augmenterons les timeout au fur et à mesure de ces tentatives selon le fonctionnement suivant,
Les événements transmis par PVID sont les suivants,
| Événement | Description | Déclencheur |
| — | — | — |
validated | PVID validé | L'identité a été vérifiée avec succès |
refused | PVID refusé | L'identité n'a pas pu être vérifiée |
aborted | PVID abandonné | L'utilisateur a abandonné le parcours |
expired | PVID expiré | Le lien de vérification a expiré |
Les informations des événements webhook sont transmises au format Json.
Exemple de payload
{
"pvid_id": "987654",
"event": "validated",
"event_date": "2026-05-20T15:30:00+00:00"
}
| Champ | Type | Description |
| — | — | — |
pvid_id | string | Identifiant unique de la vérification PVID |
event | string | Type d'événement (validated, refused, aborted, expired) |
event_date | string | Date et heure de l'événement (format ISO 8601) |
Voici un diagramme de séquence simplifié de l'utilisation de PVID Cloud.
La nomenclature des différents documents d'identité accepté par le PVID Docaposte est la suivante : {CODE_PAYS}-{TYPE_DOCUMENT}.
Ce format est à utiliser dans les endpoints permettant de personnaliser les titres acceptés par le PVID.
Il sera également utilisé dans l'ensemble des retours de l'API PVID Cloud.
FR-passport — Passeport françaisFR-id_card — Carte d'identité françaiseBE-residence_permit — Titre de séjour belgeLa liste complète des documents acceptés par pays est disponible via l'endpoint GET /api/configs/documents.
Lorsque l'utilisateur est redirigé vers l'URL url_after_fail_or_abort transmis dans la requête de génération d'un lien PVID PVID Cloud rajoute un paramètre dans l'URL contenant le motif d'abandon du parcours.
Les motifs retournés sont les suivants,
| Slug | Description |
| — | — |
canceled | L'utilisateur a quitté le parcours inopinément (fermeture du navigateur, rafraichissement de la page, …) |
expired_link | Le lien du PVID est expiré et ne permet plus d'effectuer le parcours |
no_camera | L'utilisateur n'a pas donné accès à sa caméra |
camera_resolution | La résolution de la caméra n'est pas suffisante |
browser_not_supported | Le navigateur utilisé n'est pas accepté pour le PVID |
browser_version | La version du navigateur utilisé n'est pas acceptée pour le PVID |
network | La connexion internet de l'utilisateur est insuffisante |
document_not_accepted | L'utilisateur ne possède aucun titre d'identité accepté pour le PVID |
recording_error | Nous avons rencontré une difficulté lors de la capture vidéo |
L'accès à la caméra n'est pas autorisé : url_after_fail_or_abort?error=no_camera
En plus d'être transmis dans l'URL url_after_fail_or_abort les motifs d'abandon sont également retournés via le paramètre abortedReason de la route GET /api/pvid/{id}.
Voici la liste des motifs de refus pour lesquels une vérification PVID peut être refusée.
| Slug | Description |
| — | — |
document_capture_bad_quality | Votre document d'identité n'a pas pu être analysé. Assurez-vous que votre pièce d'identité soit entièrement visible, bien cadrée et que la qualité vidéo permette de lire clairement les informations |
biometry_capture_bad_quality | La vérification n'a pas pu aboutir en raison de problèmes liés à la qualité de l'enregistrement ou à la reconnaissance du visage |
document_not_accepted | Le document présenté n'est pas accepté par notre service de vérification d'identité à distance |
document_expired | Le document présenté est expiré |
document_not_allowed | Votre document d'identité ne peut être accepté par nos services. Assurez-vous de présenter l'original (passeport, titre de séjour ou carte d'identité), en bon état sans étui ni obstruction visuelle. |
document_not_complete | Votre document n'a pas pu être entièrement analysé car une face est manquante ou mal cadrée |
action_not_complete | Les actions effectuées lors de la capture vidéo du document ou du visage n'ont pas été correctement réalisées et n'ont pas pu permettre la vérification de l'identité. Assurez-vous de bien respecter les consignes affichées lors du parcours (inclinaison et positionnement dans le cadre). |
biometry_not_conform | Les conditions dans lesquelles vous avez effectué votre vérification d'identité ne permettent pas de la traiter. Assurez-vous que votre visage soit entièrement visible, sans lunettes de soleil, verres teintés ou masque. |
fraud_is_suspected | Les conditions dans lesquelles vous avez effectué votre vérification d'identité ne permettent pas à notre service de la traiter |
period_expired | Un problème technique a empêché le traitement de votre vérification d'identité dans les delais impartis |
technical_error | Un problème technique a empêché le traitement de votre vérification d'identité |