https://pvid-back.docaposte.fr/api
https://pvid-back.pvid-integration.integration.idv-docaposte.com/api
Cette section résume les 4 étapes obligatoires pour mettre en production votre intégration PVID Cloud. Une 5e étape (webhooks) est fortement recommandée pour optimiser votre architecture.
Objectif : Valider vos identifiants et générer un token JWT pour accéder à l'API.
Actions :
1. Obtenez vos identifiants (username/password) auprès du support technique 2. Testez l'endpoint de login en environnement d'intégration :
POST /api/login
3. Stockez le token d'accès et le refresh token de manière sécurisée (backend uniquement) 4. Implémentez la logique de rafraîchissement de token (validité 5 minutes) 5. Testez la gestion d'erreur d'authentification (401 Unauthorized)
Points de contrôle :
Documentation complète : Section 1 — Authentification
Objectif : Être capable de déchiffrer les réponses sensibles de l'API.
Actions :
1. Choisissez votre langage de programmation (PHP, Python, JavaScript, Java, etc.) 2. Implémentez la fonction de déchiffrement AES-256-CBC selon votre stack 3. Testez le déchiffrement avec des réponses chiffrées en intégration 4. Validez que la clé de chiffrement est générée correctement : SHA256(date + tokenSecret) 5. Gérez les erreurs de déchiffrement (mauvaise clé, padding invalide, etc.)
Points de contrôle :
Documentation complète : Section 4 — Chiffrement des réponses
Code d'exemple : Disponible en PHP, Python, JavaScript et Java
Objectif : Créer une vérification et obtenir l'URL à rediriger vers votre utilisateur.
Actions :
1. Collectez les informations utilisateur requises (idUser, email) 2. Définissez les URLs de redirection post-succès et post-échec 3. Créez une vérification :
POST /api/pvid
4. Stockez l'ID de la vérification (crucial pour récupérer les résultats plus tard) 5. Redirigez l'utilisateur vers l'URL fournie (valide 30-60 minutes) 6. Testez le flux complet sur 5 vérifications fictives en intégration
Paramètres obligatoires :
idUser : Identifiant unique utilisateur (> 0)customerEmail : Email de l'utilisateururlAfterSuccess : URL de redirection après succèsurlAfterFailOrAbort : URL de redirection après échec/abandondate : Date actuelle (YYYY-MM-DD HH:mm:ss)Points de contrôle :
Documentation complète : Section 5 — Initier une vérification
Objectif : Récupérer le verdict final et les données personnelles après vérification utilisateur.
Actions :
1. Attendez que l'utilisateur complète le parcours PVID 2. Récupérez l'ID de la vérification stocké à l'étape précédente 3. Appelez l'endpoint de résultats :
GET /api/pvid/{id}
4. Déchiffrez la réponse avec votre fonction AES-256-CBC (étape 2) 5. Traitez les 4 statuts possibles : done, expired, aborted, currently_created 6. Testez les 3 verdicts finaux : validated, refused, null
Statuts et verdicts possibles :
done + Verdict = validated → Identité vérifiée ✅done + Verdict = refused → Identité rejetée ❌expired → Lien expiré (rejouer depuis l'étape 3)aborted → Utilisateur a quitté le parcourscurrently_created → En attente (pas encore commencé)Données retournées en cas de succès :
Points de contrôle :
Documentation complète : Section 6 — Récupérer les résultats
Objectif : Recevoir les notifications immédiatement au lieu de faire du polling sur l'API.
Pourquoi c'est recommandé :
Actions :
1. Implémentez un endpoint HTTP POST capable de recevoir les webhooks 2. Parsez le payload webhook (pvid_id, status, verdict, event_date) 3. Vérifiez l'authenticité du webhook (origine, signature si disponible) 4. Traitez l'événement de manière asynchrone (ne bloquez pas la réponse) 5. Retournez un code HTTP 2xx dans les 5 secondes 6. Testez l'endpoint webhook avec des appels manuels en intégration
Payload webhook reçu :
{ "pvid_id": "987654", "status": "done", "verdict": "validated", "event_date": "2026-05-20T15:30:00+00:00" }
Points de contrôle :
Documentation complète : Section 7 — Webhooks
Note importante : Sans webhooks, vous devrez implémenter du polling en appelant régulièrement
GET /api/pvid/{id}
pour vérifier si un verdict a été prononcé. Les webhooks éliminent cette nécessité.
Complétez cette checklist avant de demander l'accès production :
| Étape | Validation | Obligatoire | Status |
|---|---|---|---|
| 1. Authentification | Token JWT obtenu et rafraîchi avec succès | ✅ | |
| 2. Déchiffrement | Fonction AES-256-CBC implémentée et testée | ✅ | |
| 3. Création | 5 vérifications créées et URLs de lien valides | ✅ | |
| 4. Résultats | 5 flux complets création→vérif→résultats réussis | ✅ | |
| 5. Webhooks | Endpoint webhook reçoit et traite les événements | ⭐ | |
| 6. Sécurité | Tokens/clés stockés côté backend uniquement | ✅ | |
| 7. Gestion erreurs | Tous les codes HTTP (400, 401, 403, 500) gérés | ✅ | |
| 8. Logs | Actions critiques journalisées pour audit | ✅ |
Légende : ✅ = Obligatoire | ⭐ = Fortement recommandé
Vous pouvez intégrer ces étapes après la mise en production :
| Étape | Bénéfice | Documentation |
|---|---|---|
| Configuration | Personnaliser branding + paramètres globaux | Section 2 |
| Tests compatibilité | Valider navigateur/caméra avant redirection | Section 3 |
Toutes les requêtes API authentifiées nécessitent un token JWT valide 5 minutes.
Obtenez un token JWT pour accéder aux autres endpoints.
curl -X POST "https://pvid-back.docaposte.fr/api/login" \ -H "accept: application/json" \ -H "Content-Type: application/json" \ -d '{ "username": "votre-identifiant", "password": "votre-mot-de-passe" }'
curl -X POST "https://pvid-back.pvid-integration.integration.idv-docaposte.com/api/login" \ -H "accept: application/json" \ -H "Content-Type: application/json" \ -d '{ "username": "votre-identifiant", "password": "votre-mot-de-passe" }'
| Paramètre | Obligatoire | Type | Description |
|---|---|---|---|
username | ✅ | string | Identifiant de votre application |
password | ✅ | string | Mot de passe associé |
{ "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" }
Authorization: Bearer eyJhbGciOiJSUzI1NiIs…401 Unauthorized.Erreur (400) — Identifiants manquants :
{ "error": "Missing credentials" }
Erreur (401) — Identifiants invalides :
{ "error": "Invalid credentials or Keycloak error" }
Ajoutez cet header à toutes vos requêtes authentifiées :
| Header | Valeur |
|---|---|
Authorization | Bearer ACCESS_TOKEN |
Exemple complet :
curl -X GET "https://pvid-back.docaposte.fr/api/config?date=2026-05-20%2014:30:00" \ -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \ -H "accept: application/json"
Obtenez un nouveau token d'accès sans vous reconnecter.
curl -X POST "https://pvid-back.docaposte.fr/api/refresh_token" \ -H "accept: application/json" \ -H "Content-Type: application/json" \ -d '{ "refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." }'
| Paramètre | Obligatoire | Type | Description |
|---|---|---|---|
refresh_token | ✅ | string | Token de rafraîchissement obtenu lors du login |
{ "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" }
Erreur (400) — Refresh token manquant :
{ "error": "Missing refresh token" }
Erreur (400) — Refresh token invalide (configuration) :
{ "error": "Invalid refresh token" }
Erreur (401) — Refresh token invalide (Keycloak) :
{ "error": "Invalid credentials or Keycloak error" }
Avant de proposer PVID à vos utilisateurs, configurez votre branding et vos préférences.
Commencez par consulter quels documents vous pouvez accepter par pays.
| Paramètre | Obligatoire | Type | Description |
|---|---|---|---|
date | ✅ | string | Date pour chiffrement (format YYYY-MM-DD HH:mm:ss) |
curl -X GET "https://pvid-back.docaposte.fr/api/config/documents?date=2026-05-20%2014:30:00" \ -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."
{ "allowedDocuments": [ { "code": "FR", "name": "France", "documents": ["passport", "id_card", "residence_permit"] }, { "code": "BE", "name": "Belgium", "documents": ["passport", "id_card"] } ] }
Consultez votre configuration existante.
| Paramètre | Obligatoire | Type | Description |
|---|---|---|---|
date | ✅ | string | Date pour chiffrement (format YYYY-MM-DD HH:mm:ss) |
curl -X GET "https://pvid-back.docaposte.fr/api/config?date=2026-05-20%2014:30:00" \ -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." \ -H "accept: application/json"
{ "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": ["FR-passport", "FR-id_card"], "logo": "data:image/png;base64,...", "favicon": "data:image/x-icon;base64,..." }
Erreur (400) — Date manquante :
{ "slug": "empty_date", "message": "No date parameter found in your request" }
Erreur (400) — Format de date invalide :
{ "slug": "invalid_date", "message": "Wrong date format (must be YYYY-MM-DD HH:mm:ss, eg: 2021-10-19 20:10:06)" }
Erreur (400) — Date expirée (date passée) :
{ "slug": "expired_date", "message": "Given datetime is older than current datetime" }
Erreur (400) — Date trop dans le futur (max +10 min) :
{ "slug": "date_too_far_in_future", "message": "The date parameter must not be more than 10 minutes in the future" }
Erreur (401) — Token invalide :
{ "error": "Invalid credentials or Keycloak error" }
Erreur (500) — Erreur serveur :
{ "slug": "internal_server_error", "message": "Internal server error" }
Configurez votre branding, vos documents acceptés et votre webhook global.
| Champ | Obligatoire | Type | Description |
|---|---|---|---|
webhook | ❌ | string | URL webhook global (HTTPS obligatoire) |
titleColor | ❌ | string | Couleur titres au format #XXXXXX |
textColor | ❌ | string | Couleur textes au format #XXXXXX |
linkColorText | ❌ | string | Couleur liens au format #XXXXXX |
buttonColorText | ❌ | string | Couleur texte boutons au format #XXXXXX |
buttonColorBackground | ❌ | string | Couleur fond boutons au format #XXXXXX |
ghostButtonColor | ❌ | string | Couleur boutons fantômes au format #XXXXXX |
linkValiditySharing | ❌ | integer | Validité lien en minutes (max 60) |
disableLinkSharing | ❌ | integer | Désactiver partage SMS/QR (0 ou 1) |
addUserAlertScreen | ❌ | integer | Écran d'alerte fraude (0 ou 1) |
addUserAlertText | ❌ | string | Texte d'alerte (5-120 caractères) |
acceptedDocuments | ❌ | array | Documents acceptés (ex: [“FR-passport”, “FR-id_card”]) |
date | ✅ | string | Date pour chiffrement |
curl -X PUT "https://pvid-back.docaposte.fr/api/config" \ -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." \ -H "Content-Type: application/json" \ -d '{ "webhook": "https://api.votre-domaine.com/pvid/webhook", "titleColor": "#003366", "textColor": "#333333", "linkColorText": "#0066CC", "buttonColorText": "#FFFFFF", "buttonColorBackground": "#003366", "ghostButtonColor": "#003366", "linkValiditySharing": 60, "disableLinkSharing": 0, "addUserAlertScreen": 1, "addUserAlertText": "<b>Attention</b> vérification obligatoire", "acceptedDocuments": ["FR-passport", "FR-id_card"], "date": "2026-05-20 14:30:00" }'
Même format que GET
/api/config
Erreur (400) — Validation échouée :
{ "slug": "validation_error", "message": "Validation failed", "details": ["The webhook URL must use HTTPS"] }
Erreur (401) — Utilisateur inconnu :
{ "slug": "user_not_exist", "message": "Unknown user" }
Erreur (500) — Erreur serveur :
{ "slug": "internal_server_error", "message": "Internal server error" }
Modifie uniquement les champs fournis sans écraser la configuration existante.
curl -X PATCH "https://pvid-back.docaposte.fr/api/config" \ -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." \ -H "Content-Type: application/json" \ -d '{ "titleColor": "#FF6600", "buttonColorBackground": "#FF6600", "date": "2026-05-20 14:30:00" }'
Même format que GET
/api/config
Erreur (400) — Validation échouée :
{ "slug": "validation_error", "message": "Validation failed" }
Erreur (401) — Utilisateur inconnu :
{ "slug": "user_not_exist", "message": "Unknown user" }
Erreur (500) — Erreur serveur :
{ "slug": "internal_server_error", "message": "Internal server error" }
Téléchargez votre logo et favicon. Les fichiers doivent être convertis en base64.
| Champ | Obligatoire | Type | Description |
|---|---|---|---|
logo | ❌ | string | Logo en base64 (hauteur max 64px) |
favicon | ❌ | string | Favicon en base64 |
date | ✅ | string | Date pour chiffrement |
curl -X POST "https://pvid-back.docaposte.fr/api/config/files" \ -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." \ -H "Content-Type: application/json" \ -d '{ "logo": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+P+/HgAFhAJ/wlseKgAAAABJRU5ErkJggg==", "favicon": "data:image/x-icon;base64,AAABAAEAEBAQAAEABAAoAQAAFgAAACgAAAAQAAAAIAAAAAEABAAAAAAAgAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAA/4QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "date": "2026-05-20 14:30:00" }'
Même format que GET
/api/config
curl -X DELETE "https://pvid-back.docaposte.fr/api/config/files" \ -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."
Aucun contenu retourné.
Supprime tous les paramètres personnalisés et restaure les valeurs par défaut.
curl -X DELETE "https://pvid-back.docaposte.fr/api/config?date=2026-05-20%2014:30:00" \ -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."
Aucun contenu retourné.
Erreur (400) — Date invalide :
{ "slug": "invalid_date", "message": "Wrong date format (must be YYYY-MM-DD HH:mm:ss, eg: 2021-10-19 20:10:06)" }
Erreur (401) — Token invalide :
{ "error": "Invalid credentials or Keycloak error" }
Erreur (500) — Erreur serveur :
{ "slug": "internal_server_error", "message": "Internal server error" }
Avant de lancer vos utilisateurs en production, vérifiez la compatibilité de leurs navigateurs et caméras.
Consultez les versions minimales de navigateurs supportées.
curl -X GET "https://pvid-back.docaposte.fr/api/browser/list" \ -H "accept: application/json"
{ "safari": "14", "chrome": "110", "firefox": "66", "samsung": "6", "opera": "73", "edge": "79", "miui": "13.21" }
Testez si la version du navigateur d'un utilisateur est compatible.
curl -X GET "https://pvid-back.docaposte.fr/api/browser/check?name=chrome&version=115.0.5790" \ -H "accept: application/json"
| Paramètre | Obligatoire | Type | Description |
|---|---|---|---|
name | ✅ | string | Nom du navigateur (chrome, firefox, safari, edge, samsung, opera, miui) |
version | ✅ | string | Version du navigateur (ex: 115.0.5790) |
{ "result": "The user's browser can be used for a PVID" }
{ "slug": "browser_not_supported", "message": "Le navigateur n'est pas supporté ou la version est trop ancienne" }
Erreur (400) — Paramètre manquant (name) :
{ "slug": "validation_error", "message": "Missing required parameter: name" }
Erreur (400) — Paramètre manquant (version) :
{ "slug": "validation_error", "message": "Missing required parameter: version" }
Erreur (400) — Navigateur invalide :
{ "slug": "validation_error", "message": "Invalid browser name" }
Récupère la résolution minimale requise pour la caméra.
curl -X GET "https://pvid-back.docaposte.fr/api/camera/resolution?date=2026-05-20%2014:30:00" \ -H "accept: application/json"
| Paramètre | Obligatoire | Type | Description |
|---|---|---|---|
date | ✅ | string | Date pour chiffrement (YYYY-MM-DD HH:mm:ss) |
{ "height": 720, "width": 1280 }
Erreur (400) — Date invalide :
{ "slug": "invalid_date", "message": "Wrong date format (must be YYYY-MM-DD HH:mm:ss, eg: 2021-10-19 20:10:06)" }
Erreur (401) — Token invalide :
{ "error": "Invalid credentials or Keycloak error" }
Erreur (500) — Erreur serveur :
{ "slug": "internal_server_error", "message": "Internal server error" }
Certaines réponses de l'API sont chiffrées en AES-256-CBC pour protéger vos données personnelles.
| Élément | Valeur |
|---|---|
| Algorithme | AES-256-CBC |
| Clé | SHA256(date + tokenSecret ) |
| IV | 16 premiers octets du SHA256 de la clé |
| Encodage | Base64 |
Paramètre obligatoire :
date
au format YYYY-MM-DD HH:mm:ss Validité : entre l'instant présent et 10 minutes plus tard (la date ne doit pas être antérieure à l'instant de l'appel).
<?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); } // Exemple d'utilisation $encryptedResponse = "U2FsdGVkX1+abc123..."; // Réponse chiffrée de l'API $date = "2026-05-20 14:30:00"; // Date envoyée dans la requête $tokenSecret = "votre-token-secret"; // Votre clé secrète API $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); } 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); } }
Créez une vérification pour chaque utilisateur qui souhaite se faire vérifier.
Créez une nouvelle vérification et obtenez l'URL à rediriger vers l'utilisateur.
curl -X POST "https://pvid-back.docaposte.fr/api/pvid" \ -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." \ -H "Content-Type: application/json" \ -d '{ "idUser": 12345, "customerEmail": "jean.dupont@example.com", "isPriority": 0, "urlAfterSuccess": "https://votre-site.com/verification/succes", "urlAfterFailOrAbort": "https://votre-site.com/verification/echec", "linkValiditySharing": 30, "disableLinkSharing": 0, "addUserAlertScreen": 1, "addUserAlertText": "<b>Important</b>", "acceptedDocuments": ["FR-passport", "FR-id_card"], "webhook": "https://api.votre-domaine.com/pvid/webhook/session-123", "titleColor": "#003366", "textColor": "#333333", "linkColorText": "#0066CC", "buttonColorText": "#FFFFFF", "buttonColorBackground": "#003366", "ghostButtonColor": "#003366", "date": "2026-05-20 14:30:00" }'
| Champ | Obligatoire | Type | Description |
|---|---|---|---|
idUser | ✅ | integer | Identifiant unique utilisateur (> 0) |
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 prioritaire (0 ou 1). Surcoût applicable |
linkValiditySharing | ❌ | integer | Durée de validité en minutes (max 60) |
disableLinkSharing | ❌ | integer | Désactiver partage SMS/QR (0 ou 1) |
addUserAlertScreen | ❌ | integer | Afficher écran d'alerte fraude (0 ou 1) |
addUserAlertText | ❌ | string | Texte d'alerte (5-120 caractères) |
acceptedDocuments | ❌ | array | Documents acceptés (ex: [“FR-passport”]) |
webhook | ❌ | string | Webhook pour cette vérification (surcharge du webhook global, 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) |
date | ✅ | string | Date pour chiffrement |
webhook est optionnel — si non fourni, le webhook global de configuration sera utiliséurl retournée doit être ouverte dans le navigateur de l'utilisateur final'id de la vérification pour les requêtes ultérieures (récupération des résultats){ "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..." }
Erreur (400) — Email invalide :
{ "slug": "email_not_valid", "message": "L'adresse email est invalide" }
Erreur (400) — Email manquant :
{ "slug": "no_email", "message": "No email parameter found in your request" }
Erreur (400) — URL succès manquante :
{ "slug": "empty_url_after_success", "message": "No url_after_success found in your request" }
Erreur (400) — URL échec manquante :
{ "slug": "empty_url_after_fail_or_abort", "message": "No url_after_fail_or_abort found in your request" }
Erreur (400) — Texte alerte trop court :
{ "slug": "alert_text_too_short", "message": "Le texte d'alerte doit contenir au moins 5 caractères" }
Erreur (400) — Texte alerte trop long :
{ "slug": "alert_text_too_long", "message": "Le texte d'alerte ne doit pas dépasser 120 caractères" }
Erreur (400) — Date invalide :
{ "slug": "invalid_date", "message": "Wrong date format (must be YYYY-MM-DD HH:mm:ss, eg: 2021-10-19 20:10:06)" }
Erreur (400) — Date expirée :
{ "slug": "expired_date", "message": "Given datetime is older than current datetime" }
Erreur (401) — Token invalide :
{ "error": "Invalid credentials or Keycloak error" }
Erreur (500) — Erreur serveur :
{ "slug": "internal_server_error", "message": "Internal server error" }
Une fois que l'utilisateur a complété son parcours, consultez le verdict et les données.
Récupérez l'état et le résultat d'une vérification.
curl -X GET "https://pvid-back.docaposte.fr/api/pvid/987654?date=2026-05-20%2014:30:00" \ -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." \ -H "accept: application/json"
| Paramètre | Obligatoire | Type | Description |
|---|---|---|---|
id | ✅ | integer | Identifiant de la vérification (obtenu à la création) |
date | ✅ | string | Date pour chiffrement (YYYY-MM-DD HH:mm:ss) |
{ "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" } }
{ "id": 987654, "dateAdded": "2026-05-20 14:25:00", "verdictDate": "2026-05-20 15:30:00", "status": "done", "verdict": "refused", "abortedReason": null, "refusalReasons": [ { "slug": "document_expired", "text": "Document expiré" }, { "slug": "manual_id_quality", "text": "Qualité insuffisante" } ] }
{ "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 }
{ "id": 987654, "dateAdded": "2026-05-20 14:25:00", "verdictDate": "2026-05-20 14:35:00", "status": "aborted", "verdict": null, "abortedReason": "canceled", "refusalReasons": null }
{ "id": 987654, "dateAdded": "2026-05-20 14:25:00", "verdictDate": null, "status": "currently_created", "verdict": null, "abortedReason": null, "refusalReasons": null }
| Valeur | Verdict | Description |
|---|---|---|
currently_created | null | Vérification créée, en attente de l'utilisateur |
done | validated ou refused | Vérification terminée |
expired | null | Le lien a expiré |
aborted | null | L'utilisateur a abandonné |
| Slug | Description |
|---|---|
user_alert_screen | Quitté sur l'écran d'alerte fraude |
camera_resolution | Résolution caméra insuffisante |
no_camera | Pas de caméra détectée |
network | Problème réseau |
browser_not_supported | Navigateur non supporté |
browser_version | Version navigateur insuffisante |
recording_error | Erreur d'enregistrement vidéo |
document_not_accepted | Document non accepté |
expired_link | Lien expiré |
canceled | Annulé par l'utilisateur |
timed_out | Délai dépassé |
internal_server_error | Erreur interne serveur |
unknown_error | Erreur inconnue |
Erreur (400) — Date invalide :
{ "slug": "invalid_date", "message": "Wrong date format (must be YYYY-MM-DD HH:mm:ss, eg: 2021-10-19 20:10:06)" }
Erreur (400) — Date manquante :
{ "slug": "empty_date", "message": "No date parameter found in your request" }
Erreur (400) — Date expirée :
{ "slug": "expired_date", "message": "Given datetime is older than current datetime" }
Erreur (401) — Token invalide :
{ "error": "Invalid credentials or Keycloak error" }
Erreur (403) — Accès refusé :
{ "slug": "access_denied", "message": "You do not have access to this PVID" }
Erreur (404) — Vérification introuvable :
{ "slug": "pvid_not_found", "message": "This PVID does not exist" }
Erreur (500) — Erreur serveur :
{ "slug": "internal_server_error", "message": "Internal server error" }
Configurez un webhook pour recevoir des notifications automatiques lors des changements de statut.
Les webhooks peuvent être configurés de deux manières :
Via l'endpoint PUT ou PATCH
/api/config
, vous définissez un webhook par défaut pour toutes vos vérifications.
Paramètre : webhook (string, HTTPS obligatoire)
Exemple :
{ "webhook": "https://api.votre-domaine.com/pvid/webhook" }
Lors de la création d'une vérification (POST
/api/pvid
), vous pouvez spécifier un webhook différent pour une vérification spécifique.
Paramètre : webhook (string, optionnel, HTTPS obligatoire)
Exemple :
{ "webhook": "https://api.votre-domaine.com/pvid/webhook/session-123" }
Priorité : Le webhook de vérification surcharge le webhook global si fourni.
| Événement | Status | Verdict | Description |
|---|---|---|---|
| Validation | done | validated | L'identité a été vérifiée avec succès |
| Refus | done | refused | L'identité n'a pas pu être vérifiée |
| Abandon | aborted | null | L'utilisateur a abandonné le parcours |
| Expiration | expired | null | Le lien de vérification a expiré |
PVID envoie une requête POST à votre URL avec le payload suivant :
{ "pvid_id": "987654", "status": "done", "verdict": "validated", "event_date": "2026-05-20T15:30:00+00:00" }
| Champ | Type | Description | |
|---|---|---|---|
pvid_id | string | Identifiant unique de la vérification | |
status | string | Nouveau statut (done, expired, aborted) | |
verdict | string\ | null | Verdict si status=done (validated, refused) |
event_date | string | Date/heure ISO 8601 de l'événement |
pvid_id pour l'idempotence.| Élément | Durée | Notes |
|---|---|---|
| Données biométriques (vidéo) | 72 heures | Suppression automatique |
| Documents scannés | 30 jours | Accessibles via API |
| Rapports finalisés | 2 mois | Accessibles via web et API |
| Rapports archivés | 7 ans | Accès utilisateur uniquement |
| Code | Signification | Action recommandée |
|---|---|---|
200 | OK — Succès | Traiter la réponse normalement |
201 | Created — Ressource créée | Traiter la réponse normalement |
204 | No Content — Succès sans contenu | Pas de contenu à traiter |
400 | Bad Request — Requête invalide | Vérifier les paramètres envoyés |
401 | Unauthorized — Token invalide/expiré | Vous reconnecter et obtenir un nouveau token |
403 | Forbidden — Accès refusé | Vérifier les permissions de l'utilisateur |
404 | Not Found — Ressource introuvable | Vérifier l'ID de la vérification |
500 | Internal Server Error — Erreur serveur | Contacter le support technique |
Les documents utilisent le format : {CODE_PAYS}-{TYPE_DOCUMENT}
| Format | Pays | Document |
|---|---|---|
FR-passport | France | Passeport |
FR-id_card | France | Carte d'identité |
FR-residence_permit | France | Titre de séjour |
BE-passport | Belgique | Passeport |
BE-id_card | Belgique | Carte d'identité |
DE-passport | Allemagne | Passeport |
ES-id_card | Espagne | Carte d'identité |
IT-id_card | Italie | Carte d'identité |
NL-passport | Pays-Bas | Passeport |
PT-id_card | Portugal | Carte d'identité |
Pour la liste complète, consultez
GET /api/config/documents
.
| Phase | Étape | Acteur | Action | Description |
|---|---|---|---|---|
| Initialisation | 1 | Client → API | POST /api/login | Authentification avec identifiants |
| ↓ | 2 | API → Client | Réponse tokens | access_token (5 min) + refresh_token (30 min) |
| Configuration | 3 | Client → API | GET /api/config | Récupération config existante (optionnel) |
| ↓ | 4 | API → Client | Configuration | Réponse chiffrée AES-256-CBC |
| Création | 5 | Client → API | POST /api/pvid | Création vérification utilisateur |
| ↓ | 6 | API → Client | ID + URL | Identifiant vérification + lien PVID |
| ↓ | 7 | Client → Navigateur | Redirection | L'utilisateur ouvre le lien PVID |
| Parcours | 8 | Utilisateur | Capture | Document + selfie/vidéo (hors API) |
| ↓ | 9 | Navigateur → Client | Callback | Redirection post-succès ou post-échec |
| Notification | 10 | API → Client | Webhook | Notification d'événement en temps réel |
| ↓ | 11 | Client → API | HTTP 2xx | Accusé de réception webhook |
| Résultats | 12 | Client → API | GET /api/pvid/{id} | Requête explicite de résultats |
| ↓ | 13 | API → Client | Résultats | Verdict + données + fichiers (chiffrés) |
| Ressource | Limite | Notes |
| Validité du lien PVID | 60 minutes maximum | Configurable par vérification |
| Conservation des fichiers | 30 jours | Accessibles via API |
| Requêtes API | 1000/minute | Par application |
| Taille logo | 2 Mo max | Format PNG recommandé |
| Hauteur logo | 64 pixels max | Ratio d'aspect préservé |
| Texte d'alerte | 5-120 caractères | Texte brut ou HTML simple |
| Tokens JWT | Valide 5 minutes | Utilisez refresh token pour prolonger |
© Docaposte 2026 — Tous droits réservés