Outils pour utilisateurs

Français | English


Documentation API PVID Cloud

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

  • ✅ Vérifications d'identité à distance conformes ANSSI.
  • 📱 Capture biométrique et document via application web mobile sécurisée.
  • 🔗 Intégration simple via API REST authentifiée.
  • 📊 Suivi temps réel via webhooks événementiels.
  • 🎨 Interface entièrement personnalisable à votre marque.
  • 🔐 Chiffrement AES-256-CBC des données sensibles.

🔗 Ressources

  • URL de base production :
    https://pvid-back.docaposte.fr/api
  • URL de base intégration :
    https://pvid-back.pvid-integration.integration.idv-docaposte.com/api

🔧 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 :

  • ✅ Authentification réussie en intégration
  • ✅ Token stocké côté serveur backend (jamais côté client)
  • ✅ Logique de refresh_token en place
  • ✅ Gestion des tokens expirés testée

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 :

  • ✅ Fonction de déchiffrement implémentée et testée
  • ✅ Gestion des erreurs de déchiffrement
  • ✅ Clé générée correctement avec votre tokenSecret
  • ✅ Validation sur un exemple réel en intégration

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 :

  • idUser : Identifiant unique utilisateur (> 0)
  • customerEmail : Email de l'utilisateur
  • urlAfterSuccess : URL de redirection après succès
  • urlAfterFailOrAbort : URL de redirection après échec/abandon
  • date : Date actuelle (YYYY-MM-DD HH:mm:ss)

Points de contrôle :

  • ✅ Vérification créée avec succès (statut 201)
  • URL retournée ouverte correctement dans le navigateur
  • ✅ ID de la vérification stocké de manière persistante
  • ✅ Lien expirant après 60 minutes
  • ✅ 5 vérifications de test réussies

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 :

  • Status = done + Verdict = validated → Identité vérifiée ✅
  • Status = done + Verdict = refused → Identité rejetée ❌
  • Status = expired → Lien expiré (rejouer depuis l'étape 3)
  • Status = aborted → Utilisateur a quitté le parcours
  • Status = currently_created → En attente (pas encore commencé)

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

  • Informations du document (type, numéro, dates, MRZ)
  • Données de l'utilisateur (nom, prénom, date de naissance, nationalité)
  • URLs des fichiers (front du titre, verso, biométrie/visage)
  • Date d'expiration des fichiers (72 heures pour biométrie, 30 jours pour documents)

Points de contrôle :

  • ✅ Récupération du verdict réussie pour les 3 cas (validated, refused, other)
  • ✅ Déchiffrement des données sensibles fonctionnel
  • ✅ Gestion des statuts intermédiaires testée
  • ✅ Erreurs gérées (404, 403, 401)
  • ✅ 5 tests de flux complet réussis (création → vérif → résultats)

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

  • Réduit drastiquement la charge sur votre infrastructure (pas de polling)
  • Vous recevez le verdict en temps réel (< 1 seconde après la décision)
  • Évite les appels API inutiles et réduit vos coûts
  • Permet une meilleure expérience utilisateur (pas d'attente)

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 :

  • ✅ Endpoint webhook implémenté et accessible
  • ✅ Réponse HTTP 2xx retournée dans les 5 secondes
  • ✅ Payload parsé correctement
  • ✅ Idempotence gérée (éviter les doublons)
  • ✅ 10 webhooks de test reçus et traités

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

  • Cet appel doit être exécuté par votre serveur backend, jamais depuis le navigateur de l'utilisateur.
  • Le token d'accès est valide 300 secondes (5 minutes).
  • Le refresh token est valide 1800 secondes (30 minutes).
  • Utilisez le token via le header : Authorization: Bearer eyJhbGciOiJSUzI1NiIs…
  • Si vous utilisez un token expiré, vous recevrez une erreur 401 Unauthorized.

⚠️ 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

  • Le paramètre webhook est optionnel — si non fourni, le webhook global de configuration sera utilisé
  • Cet appel doit être exécuté par votre serveur backend, jamais depuis le navigateur de l'utilisateur
  • L'URL url retournée doit être ouverte dans le navigateur de l'utilisateur final
  • ⚠️ Conservez l'id de la vérification pour les requêtes ultérieures (récupération des résultats)
  • L'URL de vérification expire après le délai spécifié (par défaut 30 minutes, max 60 minutes)

📤 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

  • Répondez rapidement : Retournez un code HTTP 2xx dans les 5 secondes maximum.
  • Traitez en asynchrone : Stockez l'événement et traitez-le en arrière-plan.
  • Gérez les doublons : Les événements peuvent être envoyés plusieurs fois. Utilisez pvid_id pour l'idempotence.
  • Vérifiez l'origine : Validez que la requête provient bien de nos serveurs (filtrage IP, hash de signature, etc.).
  • Loggez les erreurs : Enregistrez les requêtes échouées pour débogage.
  • Testez en intégration : Utilisez l'environnement d'intégration avant la production.

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

This website uses cookies. By using the website, you agree with storing cookies on your computer. Also, you acknowledge that you have read and understand our Privacy Policy. If you do not agree, please leave the website.

Plus d’informations