> ## Documentation Index
> Fetch the complete documentation index at: https://docs.didit.me/llms.txt
> Use this file to discover all available pages before exploring further.

# SDK KYC Flutter : vérification d'identité mobile en Dart

> SDK KYC Flutter Dart de Didit : iOS et Android natifs, détection du vivant, passeport NFC. Dès $0.33 par KYC complet, $10 gratuits/mois, sans contrat.



Prompt d'intégration du SDK Flutter
        Copier le prompt dans le presse-papiers



      
      

\<pre>

\`Integrate Didit identity verification into my Flutter app using the native SDK.

\## Requirements
\- Flutter 3.3+, Dart 3.11+
\- iOS 13.0+ (NFC requires iOS 15+)
\- Android API 23+ (Android 6.0)

\## Installation
flutter pub add didit_sdk

iOS — the plugin picks the native variant from the $DiditSdkIosVariant Ruby global in ios/Podfile (or the DIDIT_SDK_IOS_VARIANT env var); valid values are 'all' (default), 'core', 'autodetection', 'nfc'. At the top of ios/Podfile:
$DiditSdkIosVariant = 'all'
platform :ios, ['all', 'nfc'].include?($DiditSdkIosVariant) ? '15.0' : '13.0'
Inside the target block, add the matching subspec:
pod 'DiditSDK/All', :podspec => 'https://raw.githubusercontent.com/didit-protocol/sdk-ios/4.13.2/DiditSDK.podspec'
The tag in that URL must equal the native iOS SDK version the didit_sdk release pins (4.13.2 pins 4.13.2); do not use 'main', because the plugin requires that exact version and pod install fails when the URL resolves to a different one.
'all' and 'nfc' require platform :ios, '15.0'; 'core' and 'autodetection' run on iOS 13.0+.
Then: cd ios && pod install
Info.plist must include NSCameraUsageDescription, NSMicrophoneUsageDescription, NSPhotoLibraryUsageDescription (required) and NFCReaderUsageDescription if using NFC.

Android — for no-NFC builds add to android/gradle.properties:
diditSdkAndroidNfcEnabled=false
For NFC-enabled (default), add to android/app/build.gradle.kts inside android block:
packaging {
  resources {
      pickFirsts += "META-INF/versions/9/OSGI-INF/MANIFEST.MF"
  }
}

\## Backend: Create Session (your server)
POST https://verification.didit.me/v3/session/
Headers: { "x-api-key": DIDIT_API_KEY, "Content-Type": "application/json" }
Body: { "workflow_id": DIDIT_WORKFLOW_ID, "vendor_data": "user-id" }
Response: { "session_id": "uuid", "session_token": "short-token", "url": "https://verify.didit.me/session/short-token", "status": "Not Started" }
Send session_token to the Flutter app (never expose DIDIT_API_KEY to the device).

\## Integration
import 'package:didit_sdk/sdk_flutter.dart';

// Option A: With session token from your backend (recommended)
final result = await DiditSdk.startVerification('session-token-from-backend');

// Option B: With workflow ID (SDK creates session internally)
final result = await DiditSdk.startVerificationWithWorkflow('your-workflow-id',
vendorData: 'user-123',
);

// Handle result
switch (result) {
case VerificationCompleted(:final session):
if (session.status == VerificationStatus.approved) { /* grant access */ }
case VerificationCancelled():
// User dismissed the verification flow
case VerificationFailed(:final error):
// SDK error — show error.message
}

\## Result Statuses (VerificationStatus enum)
\- VerificationStatus.approved — user identity verified, grant access
\- VerificationStatus.declined — verification failed, show retry or contact support
\- VerificationStatus.pending  — needs manual review by your compliance team

\## Error Types (VerificationErrorType enum)
sessionExpired, networkError, cameraAccessDenied, notInitialized, apiError, retryBlocked, unknown

\## Advanced Session Parameters
contact_details, expected_details, and metadata are only available via the Session Token method (your backend creates the session). The Dart startVerificationWithWorkflow API only accepts workflowId, vendorData, and config.

\## Full Verification Results
The SDK only returns the status. Full decision data (document fields, face match scores, AML hits, etc.) arrives via webhook to your backend.
Set up webhooks: https://docs.didit.me/integration/webhooks

\## Rate Limits (session creation)
\- 600 session-create requests / minute per x-api-key (see https://docs.didit.me/integration/rate-limiting)
\- On 429, retry honouring Retry-After / X-RateLimit-* headers

\## Environment Variables (backend only — never expose in the app)
\- DIDIT_API_KEY — from Didit Console > API & Webhooks
\- DIDIT_WORKFLOW_ID — from Didit Console > Workflows

\## Docs
\- Flutter SDK: https://docs.didit.me/integration/native-sdks/flutter-sdk
\- GitHub: https://github.com/didit-protocol/sdk-flutter
\- pub.dev: https://pub.dev/packages/didit_sdk
\- Webhooks: https://docs.didit.me/integration/webhooks
\- API Reference: https://docs.didit.me/api-reference/overview`

\</pre>



      
      

Coller dans

Un plugin Flutter multiplateforme qui encapsule les SDK natifs iOS et Android et fournit une API Dart unifiée pour la vérification d'identité.> [card] [Dépôt GitHub](https://github.com/didit-protocol/sdk-flutter)
>
> Consultez le code source et les exemples sur GitHub> [card] [Package pub.dev](https://pub.dev/packages/didit_sdk)
>
> didit\_sdk> [!NOTE]
>
> ### **SDK natif** : C'est l'approche recommandée pour les applications Flutter. Le SDK encapsule les SDK natifs iOS et Android via des canaux de plateforme pour offrir la meilleure expérience utilisateur, une gestion optimisée de la caméra et une prise en charge complète du NFC.***## Prérequis| Exigence | Version minimale |
| - | - |
| Flutter | 3.3+ |
| Dart | 3.11+ |### Prérequis par plateforme| Plateforme | Version minimale | Remarques |
| - | - | - |
| iOS | 13.0+ | La lecture du passeport NFC nécessite iOS 15.0+ |
| Android | API 23+ (6.0) | Java 17+ |> [!NOTE]
>
> **Le consentement biométrique nécessite la version 4.10.0 ou ultérieure des SDK natifs.** Pour les personnes soumises aux lois de l'Illinois, du Texas ou du Colorado, Didit peut demander un [consentement biométrique](https://docs.didit.me/fr/getting-started/biometric-consent.md) explicite avant la première capture du visage. Une version du SDK Flutter qui épingle les SDK natifs iOS et Android en 4.10.0 ou ultérieure affiche cette étape. Les versions antérieures ne peuvent pas l'afficher : leurs sessions se déroulent comme auparavant, et la décision indique que le consentement n'a pas été recueilli (`biometric_consent.reason: "BIOMETRIC_CONSENT_UNSUPPORTED_SDK"`). Mettez à jour si vous vérifiez des personnes dans ces États.***## Installation```bash theme={null}
flutter pub add didit_sdk
```Ou ajoutez-le manuellement à votre `pubspec.yaml` :```yaml theme={null}
dependencies:
  didit_sdk: ^4.13.2
```Puis exécutez :```bash theme={null}
flutter pub get
```### Variantes du SDK natif`didit_sdk` embarque le SDK natif complet.
Pour livrer un binaire plus léger, dépendez plutôt d'un package de variante : il expose exactement la même API Dart et épingle le SDK natif correspondant sur les deux plateformes :| Package | Capture automatique | Lecture du passeport NFC | iOS minimum |
| - | - | - | - |
| `didit_sdk` (par défaut) | Oui | Oui | 15.0 |
| `didit_sdk_core` | Non | Non | 13.0 |
| `didit_sdk_autodetection` | Oui | Non | 13.0 |
| `didit_sdk_nfc` | Non | Oui | 15.0 |```yaml theme={null}
dependencies:
  didit_sdk_core: ^4.13.2
```Dépendez d'un seul de ces packages et importez depuis celui que vous avez choisi, par exemple `package:didit_sdk_core/sdk_flutter.dart`.### Configuration iOSLe plugin prend en charge les deux gestionnaires de dépendances iOS de Flutter : Swift Package Manager et CocoaPods.#### Swift Package ManagerSi la prise en charge de Swift Package Manager est activée dans votre version de Flutter (à activer avec `flutter config --enable-swift-package-manager` sur Flutter 3.24+), le plugin est utilisé automatiquement comme package Swift.
Aucune configuration du Podfile n'est nécessaire : la dépendance native DiditSDK se résout seule depuis GitHub.
Dans Xcode, réglez la cible de déploiement iOS de votre application (cible Runner et projet) sur le minimum requis par le package choisi : 15.0 pour `didit_sdk` et `didit_sdk_nfc`, 13.0 pour `didit_sdk_core` et `didit_sdk_autodetection`.#### CocoaPodsAvec `didit_sdk`, le plugin sélectionne la variante du SDK natif iOS à partir de `$DiditSdkIosVariant`, une variable globale Ruby que vous définissez dans le fichier `ios/Podfile` de votre application (le pod DiditSDK n'est pas disponible sur CocoaPods trunk) :```ruby theme={null}
# Pick one: 'all', 'core', 'autodetection', 'nfc'
$DiditSdkIosVariant = 'all'

didit_sdk_ios_deployment_target = ['all', 'nfc'].include?($DiditSdkIosVariant) ? '15.0' : '13.0'
platform :ios, didit_sdk_ios_deployment_target

didit_sdk_ios_pod = case $DiditSdkIosVariant
                    when 'all' then 'DiditSDK/All'
                    when 'core' then 'DiditSDK/Core'
                    when 'autodetection' then 'DiditSDK/AutoDetection'
                    when 'nfc' then 'DiditSDK/NFC'
                    end
didit_sdk_ios_podspec = 'https://raw.githubusercontent.com/didit-protocol/sdk-ios/4.13.2/DiditSDK.podspec'

target 'Runner' do
  use_frameworks!

  pod didit_sdk_ios_pod, :podspec => didit_sdk_ios_podspec

  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end

post_install do |installer|
  installer.pods_project.targets.each do |target|
    flutter_additional_ios_build_settings(target)
    target.build_configurations.each do |config|
      config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = didit_sdk_ios_deployment_target
    end
  end
end
```Si vous utilisez un package de variante (`didit_sdk_core`, `didit_sdk_autodetection`, `didit_sdk_nfc`), il épingle directement sa sous-spécification native et ignore `$DiditSdkIosVariant` : gardez la ligne `pod` de votre Podfile et la cible de déploiement alignées sur cette variante (par exemple `pod 'DiditSDK/Core', :podspec => ...` avec `platform :ios, '13.0'`).> [!NOTE]
>
> Les variantes `all` et `nfc` nécessitent une cible de déploiement iOS 15.0+ ; `core` et `autodetection` peuvent cibler iOS 13.0. Lors d'un changement de variante, nettoyez d'abord CocoaPods (`rm -rf Pods Podfile.lock`) pour que la variante précédente du SDK ne soit pas réutilisée.> [!WARNING]
>
> Le tag de l'URL du podspec doit correspondre à la version du SDK natif iOS épinglée par votre version de `didit_sdk` (`4.13.2` épingle le natif `4.13.2`). Si l'URL pointe vers un tag plus ancien, `pod install` échoue à résoudre la dépendance : CocoaPods trouve un podspec qui déclare cette version antérieure, alors que le plugin exige une correspondance exacte.
> Lorsque vous mettez à jour `didit_sdk`, passez le tag de cette URL à la même version.### Configuration AndroidPar défaut, le plugin dépend du SDK Android complet, NFC inclus. Pour compiler sans NFC, ajoutez ceci à `android/gradle.properties` :```properties theme={null}
diditSdkAndroidNfcEnabled=false
```Cela remplace la dépendance `me.didit:didit-sdk` par `me.didit:didit-sdk-core`, ce qui supprime le module de lecture NFC et ses dépendances JMRTD, SCUBA et BouncyCastle.Lorsque le NFC est activé (par défaut), ajoutez cette règle de packaging à `android/app/build.gradle.kts` pour résoudre un fichier de métadonnées en double provenant de BouncyCastle :```kotlin theme={null}
android {
    packaging {
        resources {
            pickFirsts += "META-INF/versions/9/OSGI-INF/MANIFEST.MF"
        }
    }
}
```Sans cette règle, la compilation échoue avec une erreur `mergeDebugJavaResource`. Cette règle n'est pas nécessaire lorsque `diditSdkAndroidNfcEnabled=false`.> [!NOTE]
>
> Le SDK natif Android se résout depuis un dépôt Maven GitHub distant (depuis le plugin 3.4.0) : aucune configuration Maven manuelle n'est nécessaire dans votre application.***## Autorisations### iOSAjoutez les clés suivantes au fichier `Info.plist` de votre application :| Autorisation | Clé Info.plist | Description | Obligatoire |
| - | - | - | - |
| Caméra | `NSCameraUsageDescription` | Numérisation du document et vérification faciale | Oui |
| Microphone | `NSMicrophoneUsageDescription` | Enregistrement vidéo pour les contrôles de détection du vivant | Oui |
| Photothèque | `NSPhotoLibraryUsageDescription` | Importation de documents depuis la galerie de l'appareil | Oui |
| NFC | `NFCReaderUsageDescription` | Lecture des puces NFC des passeports et des cartes d'identité | Si vous utilisez le NFC |
| Localisation | `NSLocationWhenInUseUsageDescription` | Géolocalisation pour la prévention de la fraude | Facultative |> [!WARNING]
>
> Si une clé de confidentialité iOS obligatoire est absente, iOS arrête l'application dès que le SDK tente d'accéder à cette ressource protégée. Par exemple, si `NSCameraUsageDescription` est absente, l'application plante lorsque l'utilisateur appuie sur le bouton de prise de photo de la caméra de document.#### Configuration NFCPour activer la lecture NFC des passeports et des cartes d'identité équipés d'une puce :1) **Ajoutez la capacité NFC** dans Xcode :
   * Sélectionnez votre cible > **Signing & Capabilities** > **+ Capability** > **Near Field Communication Tag Reading**

2) **Ajoutez les identifiants ISO7816** au fichier `Info.plist` :```xml theme={null}
<key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>
<array>
    <string>D23300000045737445494420763335</string>
    <string>A0000002471001</string>
    <string>A0000002472001</string>
    <string>00000000000000</string>
    <string>D392F000260100000001</string>
</array>
````D392F000260100000001` permet au SDK de lire la carte My Number du Japon via [JPKI](https://docs.didit.me/fr/core-technology/nfc-verification/overview.md#carte-my-number-japonaise-jpki) : sans cet identifiant, iOS ne signale pas la carte au SDK. La lecture de la carte My Number n'est pas encore activée en production, et cet identifiant n'a aucun effet sur les autres documents : vous pouvez donc l'ajouter dès maintenant.3) **Ajoutez un fichier d'entitlements** avec la lecture des tags NFC activée :```xml theme={null}
<key>com.apple.developer.nfc.readersession.formats</key>
<array>
    <string>TAG</string>
</array>
```Assurez-vous que le profil de provisioning de l'application inclut la capacité NFC Tag Reading. Si l'identifiant de bundle n'est pas configuré pour le NFC dans votre compte Apple Developer, Xcode échouera à la signature avec une erreur indiquant l'absence de l'entitlement `com.apple.developer.nfc.readersession.formats`.Cette configuration NFC n'est pas nécessaire pour les variantes `core` ou `autodetection` (`$DiditSdkIosVariant`), ni pour les packages `didit_sdk_core` et `didit_sdk_autodetection`, car aucun d'entre eux ne lit les puces.### AndroidLes autorisations suivantes sont déclarées dans le fichier `AndroidManifest.xml` du SDK et fusionnées automatiquement :| Autorisation | Description | Obligatoire |
| - | - | - |
| `INTERNET` | Accès réseau pour la communication avec l'API | Oui |
| `ACCESS_NETWORK_STATE` | Détection de la disponibilité du réseau | Oui |
| `CAMERA` | Numérisation du document et vérification faciale | Oui |
| `NFC` | Lecture des puces NFC des passeports et des cartes d'identité | Si vous utilisez le NFC |Les fonctionnalités matérielles de caméra et de NFC sont déclarées comme facultatives (`android:required="false"`), afin que votre application puisse être installée sur des appareils dépourvus de ces fonctionnalités. Lorsque `diditSdkAndroidNfcEnabled=false`, le SDK n'ajoute ni l'autorisation NFC ni la fonctionnalité NFC Android.#### Autorisations à l'exécutionLe SDK gère automatiquement les demandes d'autorisations Android à l'exécution. Lorsque l'utilisateur atteint une étape qui nécessite l'accès à la caméra :1) Le SDK demande l'autorisation d'accès à la caméra si elle n'a pas encore été accordée
2) Si l'utilisateur **refuse** l'autorisation, un message d'erreur s'affiche avec un bouton **Try Again**
3) Si l'utilisateur **accorde** l'autorisation, le parcours de vérification se poursuitVous n'avez pas besoin de demander l'autorisation d'accès à la caméra dans le code de votre application avant d'appeler `startVerification()` : le SDK gère cela en interne.***## Démarrage rapide```dart theme={null}
import 'package:didit_sdk/sdk_flutter.dart';

// Start verification with a session token from your backend
final result = await DiditSdk.startVerification('your-session-token');

switch (result) {
  case VerificationCompleted(:final session):
    if (session.status == VerificationStatus.approved) {
      print('Identity verified!');
    }
  case VerificationCancelled():
    print('User cancelled');
  case VerificationFailed(:final error):
    print('Error: ${error.message}');
}
```***## Méthodes d'intégrationLe SDK prend en charge deux méthodes d'intégration :### Méthode 1 (token de session, recommandée en production)Créez une session sur votre backend à l'aide de l'[API de création d'une session de vérification](https://docs.didit.me/fr/sessions-api/create-session.md), puis transmettez le token au SDK :```dart theme={null}
import 'package:didit_sdk/sdk_flutter.dart';

// Your backend creates a session and returns the token
final sessionToken = await yourBackend.createVerificationSession(userId);

// Pass the token to the SDK
final result = await DiditSdk.startVerification(sessionToken);
```Cette approche vous donne un contrôle total sur :- L'association des sessions à vos utilisateurs (`vendor_data`)
- La définition de métadonnées personnalisées
- La configuration des callbacks par session### Méthode 2 (ID de workflow, intégration simplifiée)Pour des intégrations plus simples, le SDK peut créer directement des sessions à partir de votre ID de workflow, sans backend :```dart theme={null}
import 'package:didit_sdk/sdk_flutter.dart';

final result = await DiditSdk.startVerificationWithWorkflow(
  'your-workflow-id',
  vendorData: 'user-123',
  config: const DiditConfig(loggingEnabled: true),
);
```> [!NOTE]
>
> Les paramètres avancés de session (`contact_details`, `expected_details`, `metadata`) ne sont pris en charge **que** par la méthode du token de session, où votre backend appelle l'[API de création de session](https://docs.didit.me/fr/sessions-api/create-session.md) avec la prise en charge complète des paramètres. Transmettez le `session_token` renvoyé à `DiditSdk.startVerification()`.***## ConfigurationPersonnalisez le comportement du SDK en passant un objet `DiditConfig` :```dart theme={null}
final result = await DiditSdk.startVerification(
  'your-session-token',
  config: const DiditConfig(
    languageCode: 'es',       // Force Spanish language
    showLanguageSelector: true, // Let the user change language
    fontFamily: 'Avenir',     // Custom font
    loggingEnabled: true,     // Enable debug logging
  ),
);
```Pour `startVerificationWithWorkflow`, passez `config` comme paramètre nommé :```dart theme={null}
final result = await DiditSdk.startVerificationWithWorkflow(
  'your-workflow-id',
  vendorData: 'user-123',
  config: const DiditConfig(
    languageCode: 'es',
    showLanguageSelector: true,
    loggingEnabled: true,
  ),
);
```### Options de configuration| Propriété | Type | Valeur par défaut | Description |
| - | - | - | - |
| `languageCode` | `String?` | Paramètres régionaux de l'appareil | Code de langue ISO 639-1 (par exemple `"en"`, `"fr"`, `"ar"`) |
| `showLanguageSelector` | `bool` | `false` | Affiche le sélecteur de langue de style web sur l'écran d'accueil |
| `fontFamily` | `String?` | Police système | Nom de la famille de polices personnalisée (doit être enregistrée au niveau natif) |
| `loggingEnabled` | `bool` | `false` | Active la journalisation de débogage du SDK |
| `showCloseButton` | `bool` | `true` | Affiche le bouton de fermeture (X) sur les écrans des étapes de vérification |
| `showExitConfirmation` | `bool` | `true` | Affiche une boîte de dialogue de confirmation lorsque l'utilisateur tente de quitter |
| `closeOnComplete` | `bool` | `false` | Ferme automatiquement l'interface de vérification une fois celle-ci terminée |
| `defaultDocumentCamera` | `CameraLens?` | Valeur native par défaut (`CameraLens.back`) | Objectif utilisé à l'ouverture de l'écran de capture du document |
| `defaultLivenessCamera` | `CameraLens?` | Valeur native par défaut (`CameraLens.front`) | Objectif utilisé à l'ouverture de l'écran de capture du visage (détection du vivant) |
| `showDocumentCameraSwitchButton` | `bool` | `true` | Affiche le bouton de changement de caméra pendant la capture sur l'écran de capture du document |
| `showLivenessCameraSwitchButton` | `bool` | `true` | Affiche le bouton de changement de caméra pendant la capture sur l'écran de capture du visage |### Options de caméraLes options de caméra correspondent aux [options de caméra du SDK Web](https://docs.didit.me/fr/integration/web-sdks/javascript-sdk.md#options-de-caméra).
Chaque objectif est `CameraLens.front` ou `CameraLens.back`.
Si l'appareil ne dispose pas de l'objectif demandé, le SDK utilise la première caméra disponible.
Le bouton de changement de caméra est aussi masqué sur les appareils qui n'ont qu'une seule caméra.
Définissez une option de changement de caméra sur `false` pour verrouiller l'utilisateur sur l'objectif par défaut de cette étape.```dart theme={null}
config: const DiditConfig(
  defaultLivenessCamera: CameraLens.back,
  showLivenessCameraSwitchButton: false,
),
```> [!NOTE]
>
> **Thème et couleurs** : les couleurs, les arrière-plans et les réglages de l'écran d'introduction se configurent dans vos [paramètres de marque blanche](https://business.didit.me) dans la Didit Console, et non dans la configuration du SDK. Cela garantit une identité visuelle cohérente sur toutes les plateformes.***## Prise en charge des languesLe SDK prend en charge **54 langues**. Si aucune langue n'est spécifiée, le SDK utilise les paramètres régionaux de l'appareil, avec l'anglais comme langue de repli.```dart theme={null}
// Use device locale (default)
await DiditSdk.startVerification(token);

// Force specific language
await DiditSdk.startVerification(token,
  config: const DiditConfig(languageCode: 'fr'),
);

// Let the user change language on the welcome screen
await DiditSdk.startVerification(token,
  config: const DiditConfig(showLanguageSelector: true),
);
```**[Voir toutes les langues prises en charge →](https://docs.didit.me/fr/integration/supported-languages.md)*****## Paramètres de session avancésLes paramètres comme `contact_details`, `expected_details` et `metadata` sont **uniquement** pris en charge avec la méthode du token de session. Votre backend appelle [`POST /v3/session/`](https://docs.didit.me/fr/sessions-api/create-session) avec la prise en charge complète des paramètres, puis transmet le `session_token` renvoyé au SDK.```dart theme={null}
// Your backend handles the full session creation:
// POST /v3/session/ with contact_details, expected_details, metadata, etc.
final sessionToken = await yourBackend.createSession(userId);

// The SDK only needs the token
final result = await DiditSdk.startVerification(sessionToken);
```> [!NOTE]
>
> La méthode Dart `startVerificationWithWorkflow` n'accepte que `workflowId`, `vendorData` et `config`. Les versions antérieures à la 3.4.0 exposaient les paramètres `contactDetails`, `expectedDetails` et `metadata`, mais ils ont été supprimés, car l'endpoint Unilink ne les prend pas en compte.***## Gestion des résultats`startVerification` et `startVerificationWithWorkflow` renvoient toutes deux un `Future<VerificationResult>`. Le résultat est une classe scellée : utilisez la correspondance de motifs pour déterminer le résultat.### Cas de résultat| Cas | Description |
| - | - |
| `VerificationCompleted` | Le parcours de vérification est terminé (consultez `session.status` pour le résultat) |
| `VerificationCancelled` | L'utilisateur a annulé le parcours de vérification |
| `VerificationFailed` | Une erreur s'est produite pendant la vérification |### Propriétés de SessionData| Propriété | Type | Description |
| - | - | - |
| `sessionId` | `String` | Identifiant unique de la session |
| `status` | `VerificationStatus` | `approved`, `pending` ou `declined` |### Types d'erreurL'énumération `VerificationErrorType` expose les cas suivants :| Erreur | Description |
| - | - |
| `sessionExpired` | La session a expiré |
| `networkError` | Problème de connectivité réseau |
| `cameraAccessDenied` | Autorisation de la caméra non accordée |
| `notInitialized` | SDK non initialisé (Android uniquement) |
| `apiError` | Échec de la requête API |
| `retryBlocked` | Nombre maximal de tentatives dépassé pour l'étape en cours |
| `unknown` | Autre erreur : consultez `error.message` pour les détails |### Perte de connexionSi l'appareil perd sa connexion pendant la vérification, le SDK recouvre le parcours d'un écran « Pas de connexion Internet » au lieu de laisser une étape se charger indéfiniment.
Il continue de vérifier la connexion en arrière-plan et ferme l'écran automatiquement dès que Didit redevient joignable. L'utilisateur reprend alors à l'étape où il se trouvait.
L'écran propose **Réessayer** et, sauf si le bouton de fermeture est désactivé, **Quitter la vérification**.
Quitter met fin à la vérification avec un résultat `VerificationFailed` dont le type d'erreur est `networkError`, ce qui permet à votre application de savoir que l'utilisateur est parti à cause de la connexion.### Exemple complet de gestion des résultats```dart theme={null}
import 'package:didit_sdk/sdk_flutter.dart';

Future<void> verify(String token) async {
  final result = await DiditSdk.startVerification(token);

  switch (result) {
    case VerificationCompleted(:final session):
      switch (session.status) {
        case VerificationStatus.approved:
          print('Approved! Session: ${session.sessionId}');
          // User is verified — grant access
        case VerificationStatus.pending:
          print('Under review. Session: ${session.sessionId}');
          // Show "verification in progress" UI
        case VerificationStatus.declined:
          print('Declined. Session: ${session.sessionId}');
          // Handle declined verification
      }

    case VerificationCancelled(:final session):
      print('User cancelled.');
      if (session != null) {
        print('Session: ${session.sessionId}');
      }
      // Maybe show retry option

    case VerificationFailed(:final error):
      print('Error [${error.type}]: ${error.message}');
      // Handle error — show retry or contact support
  }
}
```***## Exemple de bout en bout (session backend → SDK Flutter → webhook)Il s'agit de l'intégration prête pour la production : votre backend crée la session, votre application Flutter reçoit le `session_token`, et le SDK exécute le parcours. La décision finale est transmise à votre backend par webhook. Ne faites jamais confiance au seul résultat côté client.### 1. Backend – créer la session```ts theme={null}
// Node.js / Express example. Run on your server. NEVER ship DIDIT_API_KEY to the device.
import express from "express";

const app = express();
app.use(express.json());

app.post("/api/verification/start", async (req, res) => {
  const response = await fetch("https://verification.didit.me/v3/session/", {
    method: "POST",
    headers: {
      "x-api-key": process.env.DIDIT_API_KEY!,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      workflow_id: process.env.DIDIT_WORKFLOW_ID,
      vendor_data: req.body.userId, // your stable user id
    }),
  });

  if (!response.ok) {
    return res.status(500).json({ error: `Didit ${response.status}` });
  }

  const { session_id, session_token } = await response.json();
  // Persist session_id <-> userId so you can match the webhook later.
  res.json({ session_id, session_token });
});
```### 2. Flutter – échanger le token et démarrer le SDK```dart theme={null}
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:didit_sdk/sdk_flutter.dart';

class VerifyButton extends StatefulWidget {
  const VerifyButton({super.key, required this.userId});
  final String userId;

  @override
  State<VerifyButton> createState() => _VerifyButtonState();
}

class _VerifyButtonState extends State<VerifyButton> {
  bool _loading = false;

  Future<void> _verify() async {
    setState(() => _loading = true);
    try {
      final resp = await http.post(
        Uri.parse('https://your-backend.example.com/api/verification/start'),
        headers: const {'Content-Type': 'application/json'},
        body: jsonEncode({'userId': widget.userId}),
      );
      final sessionToken = (jsonDecode(resp.body) as Map)['session_token'] as String;

      final result = await DiditSdk.startVerification(sessionToken);

      switch (result) {
        case VerificationCompleted(:final session):
          if (session.status == VerificationStatus.approved) {
            // Optimistic UI; rely on the webhook delivered to your backend as the source of truth.
          }
        case VerificationCancelled():
          // User dismissed the flow
          break;
        case VerificationFailed(:final error):
          // Show retry — error.type / error.message
          break;
      }
    } finally {
      if (mounted) setState(() => _loading = false);
    }
  }

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: _loading ? null : _verify,
      child: Text(_loading ? 'Starting...' : 'Verify identity'),
    );
  }
}
```### 3. Backend – recevoir la décision finale via webhook```ts theme={null}
app.post("/api/webhooks/didit", express.raw({ type: "application/json" }), (req, res) => {
  // 1. Verify the X-Signature header (see Webhooks docs)
  // 2. Parse the body
  const event = JSON.parse(req.body.toString());
  if (event.webhook_type === "status.updated") {
    // event.session_id, event.status ("Approved" | "Declined" | "In Review" | ...)
    // Look up the user by session_id and update their verification state.
  }
  res.sendStatus(200);
});
```Consultez le [guide des webhooks](https://docs.didit.me/fr/integration/webhooks.md) pour les détails de la vérification de signature HMAC.***## Exemple complet```dart theme={null}
import 'package:flutter/material.dart';
import 'package:didit_sdk/sdk_flutter.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Didit SDK Example',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF1A1A1A)),
        useMaterial3: true,
      ),
      home: const VerificationScreen(),
    );
  }
}

class VerificationScreen extends StatefulWidget {
  const VerificationScreen({super.key});

  @override
  State<VerificationScreen> createState() => _VerificationScreenState();
}

class _VerificationScreenState extends State<VerificationScreen> {
  final _tokenController = TextEditingController();
  bool _loading = false;

  @override
  void dispose() {
    _tokenController.dispose();
    super.dispose();
  }

  Future<void> _startVerification() async {
    final token = _tokenController.text.trim();
    if (token.isEmpty) {
      _showAlert('Error', 'Please enter a session token.');
      return;
    }

    setState(() => _loading = true);

    try {
      final result = await DiditSdk.startVerification(
        token,
        config: const DiditConfig(loggingEnabled: true),
      );

      switch (result) {
        case VerificationCompleted(:final session):
          _showAlert(
            'Verification Complete',
            'Status: ${session.status.name}\nSession: ${session.sessionId}',
          );
        case VerificationCancelled():
          _showAlert('Cancelled', 'The user cancelled the verification.');
        case VerificationFailed(:final error):
          _showAlert('Failed', '${error.type.name}: ${error.message}');
      }
    } catch (e) {
      _showAlert('Error', 'Unexpected error: $e');
    } finally {
      setState(() => _loading = false);
    }
  }

  void _showAlert(String title, String message) {
    showDialog(
      context: context,
      builder: (ctx) => AlertDialog(
        title: Text(title),
        content: Text(message),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(ctx),
            child: const Text('OK'),
          ),
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Padding(
          padding: const EdgeInsets.all(24),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              TextField(
                controller: _tokenController,
                decoration: InputDecoration(
                  hintText: 'Enter session token...',
                  border: OutlineInputBorder(
                    borderRadius: BorderRadius.circular(8),
                  ),
                ),
                autocorrect: false,
              ),
              const SizedBox(height: 16),
              ElevatedButton(
                onPressed: _loading ? null : _startVerification,
                style: ElevatedButton.styleFrom(
                  backgroundColor: const Color(0xFF1A1A1A),
                  foregroundColor: Colors.white,
                  padding: const EdgeInsets.symmetric(vertical: 16),
                  shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(8),
                  ),
                ),
                child: _loading
                    ? const SizedBox(
                        height: 20,
                        width: 20,
                        child: CircularProgressIndicator(
                          strokeWidth: 2,
                          color: Colors.white,
                        ),
                      )
                    : const Text(
                        'Start Verification',
                        style: TextStyle(fontWeight: FontWeight.w600),
                      ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
```***


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.