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

# Flutter-KYC-SDK: Dart-Verifizierung für Mobile Apps

> Didit Flutter-KYC-SDK: native iOS/Android, Liveness, NFC-Pass. Ab $0.33 pro KYC-Prüfung, $10 gratis/Monat, keine Verträge.



Prompt zur Flutter-SDK-Integration
        Prompt in die Zwischenablage kopieren



      
      

\<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>



      
      

Einfügen in

Ein plattformübergreifendes Flutter-Plugin, das die nativen iOS- und Android-SDKs kapselt und eine einheitliche Dart-API für die Identitätsverifizierung bereitstellt.> [card] [GitHub-Repository](https://github.com/didit-protocol/sdk-flutter)
>
> Quellcode und Beispiele auf GitHub ansehen> [card] [pub.dev-Paket](https://pub.dev/packages/didit_sdk)
>
> didit\_sdk> [!NOTE]
>
> ### **Natives SDK**: Dies ist der empfohlene Ansatz für Flutter-Apps. Das SDK kapselt die nativen iOS- und Android-SDKs über Plattform-Channels und bietet damit das beste Nutzererlebnis, eine optimierte Kamerasteuerung und volle NFC-Unterstützung.***## Anforderungen| Anforderung | Mindestversion |
| - | - |
| Flutter | 3.3+ |
| Dart | 3.11+ |### Plattformanforderungen| Plattform | Mindestversion | Hinweise |
| - | - | - |
| iOS | 13.0+ | Das NFC-Auslesen des Reisepasses erfordert iOS 15.0+ |
| Android | API 23+ (6.0) | Java 17+ |> [!NOTE]
>
> **Die biometrische Einwilligung erfordert natives SDK 4.10.0 oder neuer.** Für Personen, die unter die Gesetze von Illinois, Texas oder Colorado fallen, kann Didit vor der ersten Gesichtsaufnahme eine ausdrückliche [biometrische Einwilligung](https://docs.didit.me/de/getting-started/biometric-consent.md) einholen. Eine Flutter-SDK-Version, die die nativen iOS- und Android-SDKs auf Version 4.10.0 oder höher festlegt, zeigt diesen Schritt an. Ältere Versionen können den Schritt nicht anzeigen: Ihre Sitzungen laufen wie bisher weiter, und die Entscheidung meldet die Einwilligung als nicht erhoben (`biometric_consent.reason: "BIOMETRIC_CONSENT_UNSUPPORTED_SDK"`). Aktualisieren Sie das SDK, wenn Sie Personen in diesen Bundesstaaten verifizieren.***## Installation```bash theme={null}
flutter pub add didit_sdk
```Alternativ fügen Sie es manuell zu Ihrer `pubspec.yaml` hinzu:```yaml theme={null}
dependencies:
  didit_sdk: ^4.13.2
```Führen Sie anschließend aus:```bash theme={null}
flutter pub get
```### Varianten des nativen SDK`didit_sdk` enthält das vollständige native SDK.
Um ein kleineres Binary auszuliefern, binden Sie stattdessen ein Varianten-Paket ein. Es stellt exakt dieselbe Dart-API bereit und fixiert auf beiden Plattformen das passende native SDK:| Paket | Automatische Aufnahme | NFC-Auslesen des Reisepasses | Minimale iOS-Version |
| - | - | - | - |
| `didit_sdk` (Standard) | Ja | Ja | 15.0 |
| `didit_sdk_core` | Nein | Nein | 13.0 |
| `didit_sdk_autodetection` | Ja | Nein | 13.0 |
| `didit_sdk_nfc` | Nein | Ja | 15.0 |```yaml theme={null}
dependencies:
  didit_sdk_core: ^4.13.2
```Binden Sie genau eines dieser Pakete ein und importieren Sie aus dem von Ihnen gewählten Paket, zum Beispiel `package:didit_sdk_core/sdk_flutter.dart`.### iOS-EinrichtungDas Plugin unterstützt beide iOS-Abhängigkeitsmanager von Flutter: Swift Package Manager und CocoaPods.#### Swift Package ManagerWenn in Ihrer Flutter-Version die Unterstützung für Swift Package Manager aktiviert ist (ab Flutter 3.24+ mit `flutter config --enable-swift-package-manager` aktivieren), wird das Plugin automatisch als Swift-Paket eingebunden.
Eine Podfile-Konfiguration ist nicht nötig. Die native DiditSDK-Abhängigkeit wird automatisch von GitHub aufgelöst.
Legen Sie das iOS-Deployment-Target Ihrer App in Xcode (Runner-Target und Projekt) auf das Minimum des gewählten Pakets fest: 15.0 für `didit_sdk` und `didit_sdk_nfc`, 13.0 für `didit_sdk_core` und `didit_sdk_autodetection`.#### CocoaPodsBei `didit_sdk` wählt das Plugin die Variante des nativen iOS-SDK über `$DiditSdkIosVariant` aus, eine globale Ruby-Variable, die Sie in der `ios/Podfile` Ihrer App setzen (das DiditSDK-Pod ist nicht im CocoaPods-Trunk verfügbar):```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
```Wenn Sie ein Varianten-Paket verwenden (`didit_sdk_core`, `didit_sdk_autodetection`, `didit_sdk_nfc`), fixiert es sein natives Subspec direkt und ignoriert `$DiditSdkIosVariant`. Halten Sie in diesem Fall die `pod`-Zeile und das Deployment-Target Ihrer Podfile passend zur Variante (zum Beispiel `pod 'DiditSDK/Core', :podspec => ...` mit `platform :ios, '13.0'`).> [!NOTE]
>
> Die Varianten `all` und `nfc` erfordern ein Deployment-Target von iOS 15.0+; `core` und `autodetection` können auf iOS 13.0 abzielen. Beim Wechseln der Variante bereinigen Sie zuerst CocoaPods (`rm -rf Pods Podfile.lock`), damit die bisherige SDK-Variante nicht wiederverwendet wird.> [!WARNING]
>
> Der Tag in der podspec-URL muss mit der nativen iOS-SDK-Version übereinstimmen, die Ihre `didit_sdk`-Version fixiert (`4.13.2` fixiert die native Version `4.13.2`). Verweist die URL auf einen älteren Tag, schlägt `pod install` fehl: CocoaPods findet dann eine podspec, die die ältere Version deklariert, während das Plugin eine exakte Übereinstimmung verlangt. Wenn Sie `didit_sdk` aktualisieren, setzen Sie den Tag in dieser URL auf dieselbe Version.### Android-EinrichtungStandardmäßig hängt das Plugin vom vollständigen Android-SDK inklusive NFC ab. Um ohne NFC zu bauen, fügen Sie Folgendes zu `android/gradle.properties` hinzu:```properties theme={null}
diditSdkAndroidNfcEnabled=false
```Dadurch wechselt die Abhängigkeit von `me.didit:didit-sdk` zu `me.didit:didit-sdk-core`, und das NFC-Lesemodul sowie seine Abhängigkeiten JMRTD/SCUBA/BouncyCastle entfallen.Wenn NFC aktiviert ist (Standard), fügen Sie diese Packaging-Regel zu `android/app/build.gradle.kts` hinzu, um eine doppelte Metadatendatei von BouncyCastle aufzulösen:```kotlin theme={null}
android {
    packaging {
        resources {
            pickFirsts += "META-INF/versions/9/OSGI-INF/MANIFEST.MF"
        }
    }
}
```Ohne diese Regel schlägt der Build mit einem `mergeDebugJavaResource`-Fehler fehl. Die Regel ist nicht nötig, wenn `diditSdkAndroidNfcEnabled=false` ist.> [!NOTE]
>
> Das native Android-SDK wird ab Plugin-Version 3.4.0 aus einem entfernten GitHub-Maven-Repository aufgelöst. Eine manuelle Maven-Konfiguration in Ihrer App ist nicht nötig.***## Berechtigungen### iOSFügen Sie der `Info.plist` Ihrer App folgende Schlüssel hinzu:| Berechtigung | Info.plist-Schlüssel | Beschreibung | Erforderlich |
| - | - | - | - |
| Kamera | `NSCameraUsageDescription` | Dokumentenscan und Gesichtsverifizierung | Ja |
| Mikrofon | `NSMicrophoneUsageDescription` | Videoaufnahme für Liveness-Prüfungen | Ja |
| Fotomediathek | `NSPhotoLibraryUsageDescription` | Hochladen von Dokumenten aus der Galerie des Geräts | Ja |
| NFC | `NFCReaderUsageDescription` | Auslesen von NFC-Chips in Pässen und Ausweisen | Bei Verwendung von NFC |
| Standort | `NSLocationWhenInUseUsageDescription` | Geolokalisierung zur Betrugsprävention | Optional |> [!WARNING]
>
> Fehlt ein erforderlicher iOS-Datenschutzschlüssel, beendet iOS die App, sobald das SDK auf die geschützte Ressource zugreifen möchte. Fehlt beispielsweise `NSCameraUsageDescription`, stürzt die App ab, wenn der Nutzer die Aufnahmeschaltfläche der Dokumentenkamera antippt.#### NFC-KonfigurationSo aktivieren Sie das NFC-Auslesen für Pässe und Ausweise mit Chips:1) **NFC Capability** in Xcode hinzufügen:
   * Wählen Sie Ihr Target > **Signing & Capabilities** > **+ Capability** > **Near Field Communication Tag Reading**

2) **ISO7816-Identifier** zur `Info.plist` hinzufügen:```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` ermöglicht dem SDK das Auslesen der japanischen My Number Card über [JPKI](https://docs.didit.me/de/core-technology/nfc-verification/overview.md#japan-my-number-card-jpki). Ohne diesen Identifier meldet iOS die Karte nicht an das SDK. Das Auslesen der My Number Card ist in der Produktion noch nicht aktiviert, und der Identifier wirkt sich nicht auf andere Dokumente aus. Sie können ihn daher bereits jetzt hinzufügen.3) **Eine Entitlements-Datei hinzufügen**, in der das Lesen von NFC-Tags aktiviert ist:```xml theme={null}
<key>com.apple.developer.nfc.readersession.formats</key>
<array>
    <string>TAG</string>
</array>
```Stellen Sie sicher, dass das Provisioning-Profil der App die Capability NFC Tag Reading enthält. Ist die Bundle-ID in Ihrem Apple-Developer-Konto nicht für NFC konfiguriert, schlägt die Signierung in Xcode mit einem fehlenden `com.apple.developer.nfc.readersession.formats`-Entitlement fehl.Diese NFC-Konfiguration ist nicht erforderlich für die Varianten `core` oder `autodetection` (`$DiditSdkIosVariant`) oder für die Pakete `didit_sdk_core` / `didit_sdk_autodetection`. Keine dieser Varianten bzw. Pakete liest Chips aus.### AndroidDie folgenden Berechtigungen sind in der `AndroidManifest.xml` des SDK deklariert und werden automatisch zusammengeführt:| Berechtigung | Beschreibung | Erforderlich |
| - | - | - |
| `INTERNET` | Netzwerkzugriff für die API-Kommunikation | Ja |
| `ACCESS_NETWORK_STATE` | Erkennung der Netzwerkverfügbarkeit | Ja |
| `CAMERA` | Dokumentenscan und Gesichtsverifizierung | Ja |
| `NFC` | Auslesen von NFC-Chips in Pässen und Ausweisen | Bei Verwendung von NFC |Die Hardware-Funktionen für Kamera und NFC sind als optional deklariert (`android:required="false"`). Ihre App kann daher auch auf Geräten ohne diese Funktionen installiert werden. Ist `diditSdkAndroidNfcEnabled=false` gesetzt, fügt das SDK die Android-NFC-Berechtigung und die NFC-Funktion nicht hinzu.#### LaufzeitberechtigungenDas SDK verarbeitet Laufzeitberechtigungsanfragen unter Android automatisch. Erreicht der Nutzer einen Schritt, der Kamerazugriff benötigt:1) Fordert das SDK die Kameraberechtigung an, sofern sie noch nicht erteilt wurde.
2) Verweigert der Nutzer die Berechtigung, wird eine Fehlermeldung mit der Schaltfläche **Try Again** angezeigt.
3) Erteilt der Nutzer die Berechtigung, wird der Verifizierungsablauf fortgesetzt.Sie müssen die Kameraberechtigung im Code Ihrer App nicht anfordern, bevor Sie `startVerification()` aufrufen. Das SDK übernimmt dies intern.***## Schnellstart```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}');
}
```***## IntegrationsmethodenDas SDK unterstützt zwei Integrationsmethoden:### Methode 1: Sitzungs-Token (für die Produktion empfohlen)Erstellen Sie eine Sitzung in Ihrem Backend über die [Create Verification Session API](https://docs.didit.me/de/sessions-api/create-session.md) und übergeben Sie das Token dann an das 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);
```Mit diesem Ansatz behalten Sie die volle Kontrolle über:- Die Zuordnung von Sitzungen zu Ihren Benutzern (`vendor_data`)
- Die Festlegung benutzerdefinierter Metadaten
- Die Konfiguration von Callbacks pro Sitzung### Methode 2: Workflow-ID (einfachere Integration)Für einfachere Integrationen kann das SDK Sitzungen direkt mit Ihrer Workflow-ID erstellen. Ein Backend ist dafür nicht erforderlich:```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]
>
> Erweiterte Sitzungsparameter (`contact_details`, `expected_details`, `metadata`) werden **nur** über die Methode mit Sitzungs-Token unterstützt. Dabei ruft Ihr Backend die [Create Session API](https://docs.didit.me/de/sessions-api/create-session.md) auf, die alle Parameter vollständig unterstützt. Übergeben Sie das zurückgegebene `session_token` an `DiditSdk.startVerification()`.***## KonfigurationPassen Sie das Verhalten des SDK an, indem Sie ein `DiditConfig`-Objekt übergeben:```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
  ),
);
```Für `startVerificationWithWorkflow` übergeben Sie `config` als benannten Parameter:```dart theme={null}
final result = await DiditSdk.startVerificationWithWorkflow(
  'your-workflow-id',
  vendorData: 'user-123',
  config: const DiditConfig(
    languageCode: 'es',
    showLanguageSelector: true,
    loggingEnabled: true,
  ),
);
```### Konfigurationsoptionen| Eigenschaft | Typ | Standard | Beschreibung |
| - | - | - | - |
| `languageCode` | `String?` | Gerätesprache | ISO-639-1-Sprachcode (z. B. `"en"`, `"fr"`, `"ar"`) |
| `showLanguageSelector` | `bool` | `false` | Die Sprachauswahl im Webstil auf dem Begrüßungsbildschirm anzeigen |
| `fontFamily` | `String?` | Systemschrift | Name einer benutzerdefinierten Schriftfamilie (muss nativ registriert sein) |
| `loggingEnabled` | `bool` | `false` | SDK-Debug-Logging aktivieren |
| `showCloseButton` | `bool` | `true` | Schließen-Schaltfläche (X) auf den Bildschirmen der Verifizierungsschritte anzeigen |
| `showExitConfirmation` | `bool` | `true` | Bestätigungsdialog anzeigen, wenn der Benutzer die Verifizierung verlassen möchte |
| `closeOnComplete` | `bool` | `false` | Verifizierungsoberfläche nach Abschluss automatisch schließen |
| `defaultDocumentCamera` | `CameraLens?` | Nativer Standard (`CameraLens.back`) | Objektiv, das verwendet wird, wenn der Bildschirm zur Dokumentaufnahme geöffnet wird |
| `defaultLivenessCamera` | `CameraLens?` | Nativer Standard (`CameraLens.front`) | Objektiv, das verwendet wird, wenn der Bildschirm zur Gesichtsaufnahme (Liveness) geöffnet wird |
| `showDocumentCameraSwitchButton` | `bool` | `true` | Den Kameraumschalter während der Aufnahme auf dem Bildschirm zur Dokumentaufnahme anzeigen |
| `showLivenessCameraSwitchButton` | `bool` | `true` | Den Kameraumschalter während der Aufnahme auf dem Bildschirm zur Gesichtsaufnahme anzeigen |### KameraoptionenDie Kameraoptionen entsprechen den [Kameraoptionen des Web SDK](https://docs.didit.me/de/integration/web-sdks/javascript-sdk.md#kameraoptionen).
Jedes Objektiv ist entweder `CameraLens.front` oder `CameraLens.back`.
Wenn das Gerät nicht über das angeforderte Objektiv verfügt, verwendet das SDK die erste verfügbare Kamera.
Der Kameraumschalter wird außerdem auf Geräten mit nur einer Kamera ausgeblendet.
Setzen Sie eine Umschalter-Option auf `false`, damit der Benutzer auf das Standardobjektiv dieses Schritts festgelegt wird.```dart theme={null}
config: const DiditConfig(
  defaultLivenessCamera: CameraLens.back,
  showLivenessCameraSwitchButton: false,
),
```> [!NOTE]
>
> **Gestaltung und Farben**: Farben, Hintergründe und Einstellungen des Einführungsbildschirms konfigurieren Sie über Ihre [White-Label-Einstellungen](https://business.didit.me) in der Didit Console und nicht in der SDK-Konfiguration. So bleibt das Branding auf allen Plattformen einheitlich.***## SprachunterstützungDas SDK unterstützt **54 Sprachen**. Wenn keine Sprache angegeben ist, verwendet das SDK die Gerätesprache, und Englisch dient als Ausweichsprache.```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),
);
```**[Alle unterstützten Sprachen anzeigen →](https://docs.didit.me/de/integration/supported-languages.md)*****## Erweiterte SitzungsparameterParameter wie `contact_details`, `expected_details` und `metadata` werden **nur** über die Sitzungs-Token-Methode unterstützt. Ihr Backend ruft [`POST /v3/session/`](https://docs.didit.me/de/sessions-api/create-session) mit voller Parameterunterstützung auf und übergibt anschließend den zurückgegebenen `session_token` an das 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]
>
> Die Dart-Methode `startVerificationWithWorkflow` akzeptiert nur `workflowId`, `vendorData` und `config`. Versionen vor 3.4.0 stellten die Parameter `contactDetails`, `expectedDetails` und `metadata` bereit. Diese wurden entfernt, weil der Unilink-Endpoint sie nicht berücksichtigt.***## Ergebnisse verarbeitenSowohl `startVerification` als auch `startVerificationWithWorkflow` geben ein `Future<VerificationResult>` zurück. Das Ergebnis ist eine versiegelte Klasse (sealed class). Verwenden Sie Pattern Matching, um das Ergebnis zu bestimmen.### Ergebnisfälle| Fall | Beschreibung |
| - | - |
| `VerificationCompleted` | Verifizierungsablauf abgeschlossen (das Ergebnis finden Sie in `session.status`) |
| `VerificationCancelled` | Der Benutzer hat den Verifizierungsablauf abgebrochen |
| `VerificationFailed` | Bei der Verifizierung ist ein Fehler aufgetreten |### SessionData-Eigenschaften| Eigenschaft | Typ | Beschreibung |
| - | - | - |
| `sessionId` | `String` | Eindeutige Sitzungs-ID |
| `status` | `VerificationStatus` | `approved`, `pending` oder `declined` |### FehlertypenDie Enum `VerificationErrorType` stellt die folgenden Fälle bereit:| Fehler | Beschreibung |
| - | - |
| `sessionExpired` | Die Sitzung ist abgelaufen |
| `networkError` | Problem mit der Netzwerkverbindung |
| `cameraAccessDenied` | Die Kameraberechtigung wurde nicht erteilt |
| `notInitialized` | SDK nicht initialisiert (nur Android) |
| `apiError` | API-Anfrage fehlgeschlagen |
| `retryBlocked` | Maximale Anzahl an Wiederholungsversuchen für den aktuellen Schritt überschritten |
| `unknown` | Sonstiger Fehler: Details finden Sie in `error.message` |### VerbindungsverlustWenn das Gerät während der Verifizierung die Verbindung verliert, blendet das SDK einen Bildschirm mit der Meldung „No internet connection“ über dem Ablauf ein, anstatt einen Schritt im Ladezustand hängen zu lassen.
Das SDK prüft im Hintergrund weiter die Verbindung und schließt den Bildschirm automatisch, sobald Didit wieder erreichbar ist. Der Benutzer macht dann an dem Schritt weiter, an dem er zuvor war.
Der Bildschirm bietet **Try Again** und, sofern die Schließen-Schaltfläche nicht deaktiviert ist, **Exit Verification**.
Beim Verlassen endet die Verifizierung mit einem `VerificationFailed`-Ergebnis, dessen Fehlertyp `networkError` ist. So erkennt Ihre App, dass der Benutzer den Ablauf aufgrund des Verbindungsverlusts verlassen hat.### Vollständiges Beispiel zur Ergebnisverarbeitung```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
  }
}
```***## End-to-End-Beispiel (Backend-Sitzung → Flutter SDK → Webhook)Dies ist die produktionsreife Integration: Ihr Backend erstellt die Sitzung, Ihre Flutter-App empfängt den `session_token`, und das SDK führt den Ablauf aus. Die endgültige Entscheidung wird per Webhook an Ihr Backend übermittelt. Vertrauen Sie niemals allein dem clientseitigen Ergebnis.### 1. Backend: Session erstellen```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: Sitzungs-Token austauschen und SDK starten```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: endgültige Entscheidung per Webhook empfangen```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);
});
```Details zur HMAC-Signaturprüfung finden Sie im [Webhooks-Leitfaden](https://docs.didit.me/de/integration/webhooks.md).***## Vollständiges Beispiel```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.