Table des matières

Français | English


Documentation API PVID Cloud

🎯 Intégrez une solution de vérification d'identité certifiée ANSSI


🔗 Ressources


🔧 Opérations principales

  1. Authentification : Authentification OAuth 2.0 avec tokens JWT (validité 5 min).
  2. Configuration : Personnalisation couleurs, logo, favicon, documents acceptés.
  3. Création vérification : Initiation d'un parcours PVID pour un utilisateur.
  4. Résultats : Récupération statut et verdict (validé, refusé, expiré).
  5. Chiffrement : Déchiffrement AES-256-CBC des réponses sensibles.
  6. Tests compatibilité : Vérification navigateur, caméra et résolution.
  7. Webhooks : Notifications asynchrones d'événements.

🚀 Checklist minimale — Étapes obligatoires pour la production

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.


Étape 1 : Authentification — Obtenir un token JWT

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


Étape 2 : Déchiffrement — Implémenter AES-256-CBC

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


Étape 3 : Créer une vérification — Générer un lien pour l'utilisateur

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 :

Points de contrôle :

Documentation complète : Section 5 — Initier une vérification


Étape 4 : Récupérer les résultats — Consulter le verdict et les données

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 :

Données retournées en cas de succès :

Points de contrôle :

Documentation complète : Section 6 — Récupérer les résultats


Étape 5 (Fortement recommandée) : Webhooks — Notifications temps réel

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é.


Résumé de validation avant production

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é


Étapes optionnelles (peuvent être intégrées ultérieurement)

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

1. Authentification

Toutes les requêtes API authentifiées nécessitent un token JWT valide 5 minutes.


1.1 Se connecter à l'API (POST /api/login)

Voir dans le Swagger

Obtenez un token JWT pour accéder aux autres endpoints.

🔧 Requête CURL – Production

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"
  }'

🔧 Requête CURL – Intégration

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ètres requis

Paramètre Obligatoire Type Description
username string Identifiant de votre application
password string Mot de passe associé

📤 Réponse — Succès (200 OK)

{
  "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"
}

💬 Commentaires importants


⚠️ Erreurs possibles

Erreur (400) — Identifiants manquants :

{ "error": "Missing credentials" }

Erreur (401) — Identifiants invalides :

{ "error": "Invalid credentials or Keycloak error" }

1.2 Headers d'authentification

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"

1.3 Rafraîchir le token (POST /api/refresh_token)

Obtenez un nouveau token d'accès sans vous reconnecter.

🔧 Requête CURL

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ètres

Paramètre Obligatoire Type Description
refresh_token string Token de rafraîchissement obtenu lors du login

📤 Réponse — Succès (200 OK)

{
  "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"
}

⚠️ Erreurs possibles

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" }

2. Configuration initiale

Avant de proposer PVID à vos utilisateurs, configurez votre branding et vos préférences.


2.1 Lister les documents acceptés (GET /api/config/documents)

Commencez par consulter quels documents vous pouvez accepter par pays.

📥 Paramètres query

Paramètre Obligatoire Type Description
date string Date pour chiffrement (format YYYY-MM-DD HH:mm:ss)

🔧 Requête CURL

curl -X GET "https://pvid-back.docaposte.fr/api/config/documents?date=2026-05-20%2014:30:00" \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."

📤 Réponse — Succès (200 OK)

{
    "allowedDocuments": [
        {
            "code": "FR",
            "name": "France",
            "documents": ["passport", "id_card", "residence_permit"]
        },
        {
            "code": "BE",
            "name": "Belgium",
            "documents": ["passport", "id_card"]
        }
    ]
}

2.2 Récupérer la configuration actuelle (GET /api/config)

Consultez votre configuration existante.

📥 Paramètres query

Paramètre Obligatoire Type Description
date string Date pour chiffrement (format YYYY-MM-DD HH:mm:ss)

🔧 Requête CURL

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"

📤 Réponse — Succès (200 OK)

{
    "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,..."
}

⚠️ Erreurs possibles

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" }

2.3 Remplacer la configuration complète (PUT /api/config)

Configurez votre branding, vos documents acceptés et votre webhook global.

📋 Paramètres

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

🔧 Requête CURL

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"
  }'

📤 Réponse — Succès (201 Created)

Même format que GET

/api/config

⚠️ Erreurs possibles

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" }

2.4 Mettre à jour partiellement la configuration (PATCH /api/config)

Modifie uniquement les champs fournis sans écraser la configuration existante.

🔧 Requête CURL — Modification des couleurs

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"
  }'

📤 Réponse — Succès (201 Created)

Même format que GET

/api/config

⚠️ Erreurs possibles

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" }

2.5 Charger logo et favicon (POST /api/config/files)

Téléchargez votre logo et favicon. Les fichiers doivent être convertis en base64.

📋 Paramètres

Champ Obligatoire Type Description
logo string Logo en base64 (hauteur max 64px)
favicon string Favicon en base64
date string Date pour chiffrement

🔧 Requête CURL

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"
  }'

📤 Réponse — Succès (201 Created)

Même format que GET

/api/config

2.6 Supprimer logo et favicon (DELETE /api/config/files)

🔧 Requête CURL

curl -X DELETE "https://pvid-back.docaposte.fr/api/config/files" \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."

📤 Réponse — Succès (204 No Content)

Aucun contenu retourné.


2.7 Réinitialiser la configuration (DELETE /api/config)

Supprime tous les paramètres personnalisés et restaure les valeurs par défaut.

🔧 Requête CURL

curl -X DELETE "https://pvid-back.docaposte.fr/api/config?date=2026-05-20%2014:30:00" \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."

📤 Réponse — Succès (204 No Content)

Aucun contenu retourné.


⚠️ Erreurs possibles

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" }

3. Tests de compatibilité (pré-production)

Avant de lancer vos utilisateurs en production, vérifiez la compatibilité de leurs navigateurs et caméras.


3.1 Lister les navigateurs supportés (GET /api/browser/list)

Consultez les versions minimales de navigateurs supportées.

🔧 Requête CURL

curl -X GET "https://pvid-back.docaposte.fr/api/browser/list" \
  -H "accept: application/json"

📤 Réponse — Succès (200 OK)

{
    "safari": "14",
    "chrome": "110",
    "firefox": "66",
    "samsung": "6",
    "opera": "73",
    "edge": "79",
    "miui": "13.21"
}

3.2 Vérifier un navigateur spécifique (GET /api/browser/check)

Testez si la version du navigateur d'un utilisateur est compatible.

🔧 Requête CURL

curl -X GET "https://pvid-back.docaposte.fr/api/browser/check?name=chrome&version=115.0.5790" \
  -H "accept: application/json"

📥 Paramètres

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)

📤 Réponse — Navigateur compatible (200 OK)

{ "result": "The user's browser can be used for a PVID" }

📤 Réponse — Navigateur incompatible (400 Bad Request)

{ "slug": "browser_not_supported", "message": "Le navigateur n'est pas supporté ou la version est trop ancienne" }

⚠️ Erreurs possibles

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" }

3.3 Exigences caméra (GET /api/camera/resolution)

Récupère la résolution minimale requise pour la caméra.

🔧 Requête CURL

curl -X GET "https://pvid-back.docaposte.fr/api/camera/resolution?date=2026-05-20%2014:30:00" \
  -H "accept: application/json"

📥 Paramètres

Paramètre Obligatoire Type Description
date string Date pour chiffrement (YYYY-MM-DD HH:mm:ss)

📤 Réponse — Succès (200 OK)

{
    "height": 720,
    "width": 1280
}

⚠️ Erreurs possibles

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" }

4. Chiffrement des réponses

Certaines réponses de l'API sont chiffrées en AES-256-CBC pour protéger vos données personnelles.


4.1 Mécanisme de chiffrement

É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).


4.2 Déchiffrement en PHP

<?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);
?>

4.3 Déchiffrement en Python

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)

4.4 Déchiffrement en JavaScript/Node.js

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);

4.5 Déchiffrement en Java

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);
    }
}

5. Initier une vérification

Créez une vérification pour chaque utilisateur qui souhaite se faire vérifier.


5.1 Créer une vérification d'identité (POST /api/pvid)

Créez une nouvelle vérification et obtenez l'URL à rediriger vers l'utilisateur.

🔧 Requête CURL

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"
  }'

📋 Paramètres

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

💬 Notes importantes


📤 Réponse — Succès (201 Created)

{
    "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..."
}

⚠️ Erreurs possibles

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" }

6. Récupérer les résultats

Une fois que l'utilisateur a complété son parcours, consultez le verdict et les données.


6.1 Consulter le statut et verdict (GET /api/pvid/{id})

Récupérez l'état et le résultat d'une vérification.

🔧 Requête CURL

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ètres

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)

6.2 Réponse — Vérification validée

{
    "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"
    }
}

6.3 Réponse — Vérification refusée

{
    "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" }
    ]
}

6.4 Réponse — Vérification expirée

{
    "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
}

6.5 Réponse — Vérification abandonnée

{
    "id": 987654,
    "dateAdded": "2026-05-20 14:25:00",
    "verdictDate": "2026-05-20 14:35:00",
    "status": "aborted",
    "verdict": null,
    "abortedReason": "canceled",
    "refusalReasons": null
}

6.6 Réponse — Vérification en cours

{
    "id": 987654,
    "dateAdded": "2026-05-20 14:25:00",
    "verdictDate": null,
    "status": "currently_created",
    "verdict": null,
    "abortedReason": null,
    "refusalReasons": null
}

6.7 Statuts possibles

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é

6.8 Raisons d'abandon (abortedReason)

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

⚠️ Erreurs possibles

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" }

7. Webhooks (notifications temps réel)

Configurez un webhook pour recevoir des notifications automatiques lors des changements de statut.


7.1 Configuration du webhook

Les webhooks peuvent être configurés de deux manières :

Webhook global (configuration générale)

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"
}

Webhook par vérification (surcharge)

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.


7.2 Événements déclenchant un webhook

É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é

7.3 Payload du webhook

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"
}

📋 Champs du payload

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

7.4 Bonnes pratiques


8. Référence — Conservation et archivage

É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

9. Référence — Codes d'erreur HTTP

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

10. Référence — Formats des documents acceptés

Les documents utilisent le format : {CODE_PAYS}-{TYPE_DOCUMENT}


10.1 Exemples de pays supportés

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

.


11. Diagramme de séquence — Flux complet

Flux d'intégration PVID Cloud

sequenceDiagram participant Client as Serveur Client participant API as API PVID Cloud participant Browser as Navigateur Utilisateur rect rgb(200, 220, 255) Note over Client,Browser: Phase 1 : Initialisation & Authentification Client->>API: 1. POST /api/login Note right of Client: Identifiants API-->>Client: 2. access_token + refresh_token end rect rgb(200, 250, 200) Note over Client,Browser: Phase 2 : Configuration (optionnel) Client->>API: 3. GET /api/config Note right of Client: Bearer + date API-->>Client: 4. Configuration (chiffrée AES-256-CBC) end rect rgb(255, 240, 200) Note over Client,Browser: Phase 3 : Création Vérification Client->>API: 5. POST /api/pvid Note right of Client: idUser, email, URLs, docs, webhook, date API-->>Client: 6. ID vérification + URL PVID Client->>Browser: 7. Redirection vers URL PVID Note right of Client: Valide 30-60 minutes end rect rgb(255, 220, 220) Note over Browser: Phase 4 : Parcours Utilisateur Browser->>Browser: 8. Capture document + selfie Note over Browser: Hors API Browser-->>Client: 9. Callback (succès ou échec) end rect rgb(220, 240, 220) Note over Client,API: Phase 5 : Notifications & Résultats API->>Client: 10. Webhook : status, verdict, event_date Note right of API: Notification en temps réel Client-->>API: 11. HTTP 2xx (accusé de réception) Client->>API: 12. GET /api/pvid/{id} Note right of Client: Bearer + date API-->>Client: 13. Résultats (verdict, document, fichiers chiffrés) end


Tableau détaillé des étapes

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)

12. Référence — Limites et quotas

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