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

# Intégration API KYC : guide du flux de bout en bout

> Intégrez l'API REST Didit de bout en bout : créez une session, gérez les webhooks et récupérez les décisions KYC en quelques minutes.

Ce guide vous présente le flux complet pour intégrer la vérification d'identité Didit via l'API REST : obtenir des identifiants, créer une session, la présenter à l'utilisateur et recevoir les résultats.

***

## Étape 1 : Obtenir vos identifiants

Vous disposez de deux méthodes pour obtenir une `api_key` :

### Option A : Inscription programmatique (sans navigateur)

Idéale pour le CI/CD et les agents IA. Deux appels API — `POST /programmatic/register/` puis `POST /programmatic/verify-email/` — renvoient directement l'`api_key`.

→ Consultez [Inscription programmatique](https://docs.didit.me/fr/integration/programmatic-registration.md) pour le flux complet, les règles de mot de passe, la durée de validité du JWT et la politique de verrouillage.

### Option B : Depuis la console Didit

1. Accédez à la [console Didit](https://business.didit.me).
2. Créez ou sélectionnez votre organisation.
3. Rendez-vous dans **Paramètres** → **API et Webhooks**.
4. Copiez :
   * **Clé API** — utilisée comme en-tête `x-api-key` pour chaque appel de vérification.
   * **Clé secrète du webhook** — utilisée pour vérifier les signatures HMAC des webhooks.

### Variables d'environnement

```bash theme={null}
# Required
DIDIT_API_KEY=your_api_key_here
DIDIT_WEBHOOK_SECRET=your_webhook_secret_here

# Your workflow ID (from the Console)
DIDIT_WORKFLOW_ID=your_workflow_id_here
```

***

## Étape 2 : Créer un workflow

Avant de créer des sessions, vous avez besoin d'un workflow de vérification. Les workflows définissent les étapes de vérification que les utilisateurs doivent suivre.

### Créer dans la console

1. Accédez à la [console Didit](https://business.didit.me) → **Workflows**
2. Cliquez sur **Créer un workflow**
3. Choisissez un modèle de base :

| Modèle | Idéal pour |
| - | - |
| **KYC** | Vérification d'identité complète avec pièce d'identité |
| **Vérification d'âge adaptative** | Vérification de l'âge avec pièce d'identité facultative en secours |
| **Authentification biométrique** | Revérification des utilisateurs récurrents |
| **Vérification d'adresse** | Vérification du justificatif de domicile |

4. Ajoutez des fonctionnalités optionnelles :

| Fonctionnalité | Description |
| - | - |
| Vérification NFC | Lire les données de la puce du passeport ou de la pièce d'identité |
| Détection du vivant | Empêcher l'usurpation grâce à une vidéo selfie |
| Correspondance faciale | Comparer le selfie à la photo du document |
| Vérification du téléphone | Vérifier le numéro de téléphone par SMS |
| Vérification de l'e-mail | Vérifier l'adresse e-mail |
| Filtrage AML | Effectuer des vérifications par rapport aux listes de sanctions et de PEP |
| Validation en base de données | Vérifier par rapport aux bases de données gouvernementales |
| Analyse de l'appareil et de l'IP | Détecter les VPN, les proxys et la géolocalisation |

5. Copiez votre **ID du workflow**

> [!NOTE]
>
> Consultez la [documentation des workflows](https://docs.didit.me/fr/console/workflows.md) pour découvrir les
> options de configuration détaillées.

***

## Étape 3 : Créer une session de vérification

Appelez l'API pour créer une session pour votre utilisateur.

### Requête

```bash theme={null}
curl -X POST https://verification.didit.me/v3/session/ \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "workflow_id": "your-workflow-id",
    "callback": "https://yourapp.com/verification-complete",
    "vendor_data": "user-123"
  }'
```

### Paramètres de requête

| Paramètre | Requis | Description |
| - | - | - |
| `workflow_id` | Oui | L'UUID de votre workflow depuis la Console. |
| `callback` | Non | URL vers laquelle l'utilisateur est redirigé après la vérification. Didit ajoute `verificationSessionId` et `status` comme paramètres de requête. Prend en charge les schémas personnalisés (par ex. `myapp://`). Utilise par défaut l'URL de rappel configurée dans le workflow en cas d'omission. |
| `callback_method` | Non | Quel appareil gère la redirection : `initiator`, `completer` ou `both`. Valeur par défaut : `initiator`. |
| `vendor_data` | Non | Votre identifiant interne d'utilisateur — renvoyé dans les webhooks pour corrélation. |
| `metadata` | Non | Objet JSON arbitraire stocké avec la session et renvoyé dans les webhooks. |
| `language` | Non | Indication de la langue de l'UI (ISO 639-1, par ex. `en`, `es`). La langue du navigateur est détectée automatiquement si omis. |
| `contact_details` | Non | E-mail/téléphone de l'utilisateur pour le pré-remplissage et les notifications par e-mail. |
| `expected_details` | Non | Données attendues de l'utilisateur pour la validation croisée avec les données vérifiées. |
| `portrait_image` | Conditionnel | Image de visage encodée en Base64 (2 Mo max ; JPEG, PNG, WebP ou TIFF). Visage de référence pour les workflows d'Authentification biométrique / Correspondance faciale prioritaire. Facultatif lorsque l'utilisateur identifié par `vendor_data` dispose déjà d'un visage stocké (visage issu d'une détection du vivant approuvée, photo d'ePassport, portrait de document d'identité ou visage de profil enregistré) : Didit le réutilise automatiquement ; `400` si aucun des deux n'est disponible. |
| `sandbox_scenario` | Non | Slug de scénario réservé au sandbox (par ex. `approve`, `decline_aml_hit`) qui pré-remplit automatiquement la session avec des données fictives. |

### Exemple complet avec toutes les options

```bash theme={null}
curl -X POST https://verification.didit.me/v3/session/ \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "workflow_id": "11111111-2222-3333-4444-555555555555",
    "callback": "https://yourapp.com/verification-complete",
    "vendor_data": "user-123",
    "metadata": {
      "plan": "premium",
      "signup_source": "mobile-app"
    },
    "contact_details": {
      "email": "alex.sample@example.com",
      "email_lang": "en",
      "send_notification_emails": true,
      "phone": "+15550101000"
    },
    "expected_details": {
      "first_name": "John",
      "last_name": "Doe",
      "date_of_birth": "1990-01-01",
      "expected_document_types": ["P", "ID"]
    }
  }'
```

> [!NOTE]
>
> `expected_details.expected_document_types` restreint l'étape de vérification d'identité
> à des types de documents spécifiques — l'écran de sélection de documents n'affiche que
> les types demandés. Valeurs autorisées : `P` (passeport), `ID` (carte nationale d'identité), `DL`
> (permis de conduire), `RP` (titre de séjour), `HIC` (carte d'assurance maladie),
> `TC` (carte fiscale), `SSC` (carte de sécurité sociale). Les valeurs sont insensibles à la casse
> et dédupliquées ; les valeurs inconnues renvoient `400`. Consultez la
> [Référence de création de session](https://docs.didit.me/fr/sessions-api/create-session.md) pour le schéma complet.

### Réponse

`201 Created` :

```json theme={null}
{
  "session_id": "11111111-2222-3333-4444-555555555555",
  "session_number": 43762,
  "session_token": "3FaJ9wLqX2Mz",
  "url": "https://verify.didit.me/session/3FaJ9wLqX2Mz",
  "vendor_data": "user-123",
  "metadata": {
    "plan": "premium",
    "signup_source": "mobile-app"
  },
  "status": "Not Started",
  "workflow_id": "11111111-2222-3333-4444-555555555555",
  "workflow_version": 3,
  "callback": "https://yourapp.com/verification-complete"
}
```

### Champs clés de la réponse

| Champ | Description |
| - | - |
| `session_id` | UUID unique pour cette session. Utilisez-le pour `GET /v3/session/{sessionId}/decision/` et les webhooks. |
| `session_number` | Numéro de séquence lisible par un humain affiché dans la console Didit. |
| `session_token` | Token de 12 caractères compatible avec les URL, qui autorise l'utilisateur final à ouvrir le parcours hébergé et initialise les SDK natifs (le SDK web prend `url` à la place). Traitez-le comme un secret. |
| `url` | URL de vérification hébergée vers laquelle rediriger l'utilisateur, ou à intégrer dans une iframe. (Le nom du champ est `url`, pas `verification_url`.) Lorsque la requête inclut `language`, l'URL contient un segment de chemin de langue — par ex. `https://verify.didit.me/en/session/3FaJ9wLqX2Mz`. |
| `status` | Statut actuel — `"Not Started"` pour une session nouvellement créée. |
| `workflow_id` | Identifiant de workflow stable sur lequel s'exécute la session. |
| `workflow_version` | Version publiée du workflow à laquelle la session a été rattachée lors de sa création. |

> [!NOTE]
>
> **Idempotence.** Lorsque `vendor_data` est fourni et qu'une session inachevée
> (`Not Started`, `In Progress`, `Resubmitted` ou `Awaiting User`) avec le
> même `vendor_data` existe déjà sur la dernière version publiée du workflow,
> cette session existante est renvoyée (toujours en `201`) au lieu d'en créer une
> en double — avec son `callback` et ses `metadata` mis à jour avec les nouvelles valeurs, et
> son `status` reflétant la session existante.

> [!NOTE]
>
> **Un `x-api-key` manquant renvoie `403`** avec
> `{"detail": "You do not have permission to perform this action."}`, le
> même corps qu'une clé valide sans autorisation. Une clé incorrecte, révoquée ou expirée
> renvoie `401`. Des crédits insuffisants renvoient
> `400`. Consultez la [Référence de l'API de création de session](https://docs.didit.me/fr/sessions-api/create-session.md) pour
> la documentation complète.

***

## Étape 4 : Présenter la vérification à l'utilisateur

Choisissez la façon de présenter le parcours de vérification à votre utilisateur :

### Option A : SDK natif (recommandé pour mobile)

Utilisez le `session_token` avec les SDK natifs pour l'intégration de la caméra, du NFC et de la biométrie.

→ [SDK iOS](https://docs.didit.me/fr/integration/native-sdks/ios-sdk.md) · [SDK Android](https://docs.didit.me/fr/integration/native-sdks/android-sdk.md) · [React Native](https://docs.didit.me/fr/integration/native-sdks/react-native-sdk.md) · [Flutter](https://docs.didit.me/fr/integration/native-sdks/flutter-sdk.md)

### Option B : iframe InContext (recommandé pour le web)

Intégrez la vérification dans votre page à l'aide de l'`url` renvoyée à l'Étape 3.

```html theme={null}
<iframe
  src="{url}"
  style="width: 100%; height: 700px; border: none;"
  allow="camera; microphone; geolocation; clipboard-write; fullscreen; autoplay; encrypted-media; digital-credentials-get"
></iframe>
```

→ [iframe InContext](https://docs.didit.me/fr/integration/web-sdks/incontext-iframe.md)

### Option C : Redirection

Redirigez l'utilisateur vers l'URL de vérification.

```javascript theme={null}
window.location.href = session.url;
```

→ [Redirection web](https://docs.didit.me/fr/integration/web-sdks/web-redirect.md)

### Option D : WebView mobile

Pour les applications mobiles sans SDK natif.

```jsx theme={null}
// React Native
<WebView source={{ uri: session.url }} />
```

→ [WebView sur iOS/Android](https://docs.didit.me/fr/integration/web-sdks/webview-in-ios-android.md)

***

## Étape 5 : Recevoir les résultats

### Par webhook (recommandé)

Les webhooks avertissent votre serveur en temps réel lorsque le statut de la vérification change.

**Configuration :**

1. Créez une destination de webhook dans la [Console](https://business.didit.me) (**API et Webhooks**) ou via [`POST /v3/webhook/destinations/`](https://docs.didit.me/fr/management-api/webhook/create-destination) avec `webhook_version: "v3"` et un tableau `subscribed_events` (par exemple `["status.updated"]`)
2. Conservez la `secret_shared_key` renvoyée lors de la création — il s'agit du secret HMAC pour toutes les variantes de signature
3. Implémentez un endpoint de webhook sur votre serveur

**Exemple de payload de webhook :**

À l'intérieur de `decision`, chaque résultat par fonctionnalité est fourni sous la forme d'un **tableau au pluriel** (`id_verifications[]`, `nfc_verifications[]`, `liveness_checks[]`, `face_matches[]`, …) — une entrée par nœud dans le workflow. Il s'agit du contrat V3 et il est fondamental : ne développez jamais votre logique autour d'une clé au singulier comme `nfc` / `id_verification` / `liveness`.

```json theme={null}
{
  "event_id": "9c0c8b8a-1111-4222-9333-444444444444",
  "webhook_type": "status.updated",
  "timestamp": 1774970000,
  "created_at": 1774969994,
  "application_id": "11111111-2222-3333-4444-555555555555",
  "environment": "live",
  "session_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
  "status": "Approved",
  "workflow_id": "22222222-3333-4444-5555-666666666666",
  "workflow_version": 4,
  "vendor_data": "user-123",
  "metadata": { "plan": "premium" },
  "decision": {
    "session_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
    "status": "Approved",
    "id_verifications": [
      { "node_id": "id_verification_1", "status": "Approved", "document_type": "Passport", "warnings": [] }
    ],
    "liveness_checks": [
      { "node_id": "liveness_1", "status": "Approved", "method": "PASSIVE", "score": 95.4, "warnings": [] }
    ],
    "face_matches": [
      { "node_id": "face_match_1", "status": "Approved", "score": 96.1, "warnings": [] }
    ],
    "reviews": []
  }
}
```

L'objet `decision` n'est présent que lorsque `status` est `Approved`, `Declined`, `In Review` ou `Abandoned` (un événement `Resubmitted` contient `resubmit_info` à la place). Didit réutilise le même `event_id` pour les tentatives de relance et les destinations multiples — basez votre idempotence dessus. Consultez [Modèles de données](https://docs.didit.me/fr/reference/data-models.md) pour connaître l'ensemble des tableaux au pluriel et des champs.

**Vérifier la signature (X-Signature-V2, recommandé) :**

`X-Signature-V2` est un HMAC-SHA256 calculé sur un réencodage canonique du corps JSON : flottants à valeur entière réduits en entiers, clés triées récursivement, séparateurs compacts, Unicode préservé.

```javascript theme={null}
const crypto = require("crypto");

// Match Didit's float normalisation: whole-valued floats serialise as ints.
function shortenFloats(data) {
  if (Array.isArray(data)) return data.map(shortenFloats);
  if (data !== null && typeof data === "object") {
    return Object.fromEntries(
      Object.entries(data).map(([key, value]) => [key, shortenFloats(value)])
    );
  }
  return data;
}

// Sort object keys recursively before re-stringifying.
function sortKeys(obj) {
  if (Array.isArray(obj)) return obj.map(sortKeys);
  if (obj !== null && typeof obj === "object") {
    return Object.keys(obj).sort().reduce((acc, key) => {
      acc[key] = sortKeys(obj[key]);
      return acc;
    }, {});
  }
  return obj;
}

function verifyWebhook(rawBody, signatureV2, timestamp, secret) {
  // 1. Reject anything older than 5 minutes
  const now = Math.floor(Date.now() / 1000);
  if (Math.abs(now - parseInt(timestamp, 10)) > 300) return false;

  // 2. Recompute HMAC-SHA256 against the canonical JSON Didit signs
  const canonical = JSON.stringify(sortKeys(shortenFloats(JSON.parse(rawBody))));
  const expected = crypto.createHmac("sha256", secret).update(canonical, "utf8").digest("hex");

  // 3. Constant-time compare
  const a = Buffer.from(expected, "utf8");
  const b = Buffer.from(signatureV2, "utf8");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}
```

→ [Documentation complète des webhooks](https://docs.didit.me/fr/integration/webhooks.md), incluant `X-Signature` sur les octets bruts, `X-Signature-Simple` obsolète sur l'enveloppe seule, et comment choisir. Didit réessaie les livraisons ayant échoué (`5xx`, `404`, délai d'expiration) jusqu'à 2 fois (\~1 min, puis \~4 min) et le délai d'expiration est de **5 secondes** — renvoyez un code `2xx` rapidement.

### Par API (optionnel)

Récupérez le même payload de décision à tout moment :

```bash theme={null}
curl https://verification.didit.me/v3/session/{session_id}/decision/ \
  -H "x-api-key: YOUR_API_KEY"
```

La structure de la réponse (tableaux au pluriel) est identique à celle de l'objet `decision` contenu dans le payload du webhook, et l'endpoint peut être appelé à n'importe quel moment du cycle de vie de la session. Utilisez les webhooks pour les mises à jour en temps réel et réservez les appels d'API pour la récupération rétroactive des données, les audits et les reconstructions — l'endpoint `/decision/` est soumis à une limite de débit de 600 requêtes GET/min par clé API (voir [Limites de débit](https://docs.didit.me/fr/integration/rate-limiting.md)). Sémantique des erreurs : un `session_id` inconnu renvoie un `404` avec `{"detail": "Not found."}` ; les échecs d'authentification se traduisent par un `403` — cet endpoint ne renvoie jamais `401`. Les URL des médias dans la réponse sont des liens pré-signés à courte durée de vie — récupérez-les rapidement plutôt que de les stocker durablement.

→ [Référence de l'API de récupération de session](https://docs.didit.me/fr/sessions-api/retrieve-session.md)

***

## Exemples de code complets

### Node.js / Express

```javascript theme={null}
const express = require("express");
const crypto = require("crypto");
const app = express();

// Create verification session
app.post("/api/verify", async (req, res) => {
  const { userId } = req.body;

  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,
      callback: `${process.env.APP_URL}/verification-complete`,
      vendor_data: userId,
    }),
  });

  const session = await response.json();

  // Store session_id associated with user
  await db.users.update({
    where: { id: userId },
    data: { verificationSessionId: session.session_id },
  });

  res.json({
    verificationUrl: session.url,             // hosted verification URL
    sessionToken: session.session_token,      // for native/web SDKs
  });
});

// Handle webhook — uses verifyWebhook / shortenFloats / sortKeys from Step 5
app.post(
  "/api/webhook",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const signature = req.get("X-Signature-V2");
    const timestamp = req.get("X-Timestamp");

    if (
      !verifyWebhook(
        req.body.toString("utf8"),
        signature,
        timestamp,
        process.env.DIDIT_WEBHOOK_SECRET,
      )
    ) {
      return res.status(401).json({ error: "Invalid signature" });
    }

    const payload = JSON.parse(req.body);
    const { event_id, session_id, status, vendor_data } = payload;

    // Idempotency: the same event_id is reused on retries and fan-out.
    // Skip events you have already processed (e.g. a processed_events table).

    // Update user's verification status (status values are case-sensitive strings)
    if (status === "Approved") {
      db.users.update({
        where: { id: vendor_data },
        data: { isVerified: true, verifiedAt: new Date() },
      });
    }

    // Return 2xx quickly (Didit's delivery timeout is 5s); do heavy work asynchronously
    res.json({ received: true });
  },
);

app.listen(3000);
```

### Python / FastAPI

```python theme={null}
from fastapi import FastAPI, Request, HTTPException
import httpx
import hashlib
import hmac
import json
import os
import time

app = FastAPI()

@app.post("/api/verify")
async def create_verification(user_id: str):
    async with httpx.AsyncClient() as client:
        response = await client.post(
            "https://verification.didit.me/v3/session/",
            headers={
                "x-api-key": os.environ["DIDIT_API_KEY"],
                "Content-Type": "application/json"
            },
            json={
                "workflow_id": os.environ["DIDIT_WORKFLOW_ID"],
                "callback": f"{os.environ['APP_URL']}/verification-complete",
                "vendor_data": user_id
            }
        )

    session = response.json()
    return {
        "verification_url": session["url"],            # hosted verification URL
        "session_token": session["session_token"],     # for native/web SDKs
    }

def shorten_floats(data):
    """Match Didit: whole-valued floats serialise as ints."""
    if isinstance(data, dict):
        return {k: shorten_floats(v) for k, v in data.items()}
    if isinstance(data, list):
        return [shorten_floats(x) for x in data]
    if isinstance(data, float) and data.is_integer():
        return int(data)
    return data

@app.post("/api/webhook")
async def handle_webhook(request: Request):
    payload = await request.json()
    signature = request.headers.get("X-Signature-V2")
    timestamp = request.headers.get("X-Timestamp")

    # Verify timestamp freshness
    if not signature or not timestamp or abs(int(time.time()) - int(timestamp)) > 300:
        raise HTTPException(status_code=401, detail="Request too old")

    # Recompute Didit's V2 canonical JSON: shortened floats, sorted keys,
    # compact separators, Unicode preserved (ensure_ascii=False)
    canonical = json.dumps(
        shorten_floats(payload),
        sort_keys=True,
        separators=(",", ":"),
        ensure_ascii=False,
    )
    expected = hmac.new(
        os.environ["DIDIT_WEBHOOK_SECRET"].encode("utf-8"),
        canonical.encode("utf-8"),
        hashlib.sha256
    ).hexdigest()

    if not hmac.compare_digest(signature, expected):
        raise HTTPException(status_code=401, detail="Invalid signature")

    # Idempotency: dedupe on payload["event_id"] (reused on retries)

    if payload["status"] == "Approved":
        # Update user in database
        pass

    return {"received": True}
```

***

## Statuts de session

Voici les 10 valeurs possibles pour `status` — des chaînes de caractères exactes et sensibles à la casse (notez que `Kyc Expired` n'utilise qu'un seul `K` majuscule) :

| Statut | Description |
| - | - |
| `Not Started` | Session créée, mais l'utilisateur n'a pas encore ouvert le lien. |
| `In Progress` | L'utilisateur effectue actuellement la vérification. |
| `In Review` | Vérification signalée pour un examen humain. |
| `Approved` | Tous les contrôles ont été validés. |
| `Declined` | Un ou plusieurs contrôles ont échoué. |
| `Resubmitted` | Le réviseur a demandé de refaire certaines étapes spécifiques. |
| `Awaiting User` | Session parente KYB en attente que les parties KYC enfants terminent leur vérification. |
| `Abandoned` | L'utilisateur a commencé mais n'a pas terminé dans le temps imparti. |
| `Expired` | La session a expiré avant que l'utilisateur n'ouvre le lien. |
| `Kyc Expired` | Session précédemment approuvée dont le document a dépassé sa date d'expiration. |

Consultez [Statuts de vérification](https://docs.didit.me/fr/integration/verification-statuses.md) pour découvrir le cycle de vie complet, les transitions et le diagramme Mermaid.

***

## Prochaines étapes

1. **Choisissez votre méthode d'intégration** :
   * Applications mobiles → [SDK natifs](https://docs.didit.me/fr/integration/native-sdks/overview.md)
   * Applications web → [SDK web](https://docs.didit.me/fr/integration/web-sdks/overview.md)
2. **Configurez les webhooks** : [Webhooks](https://docs.didit.me/fr/integration/webhooks.md)
3. **Planifiez la capacité** : [Limites de débit](https://docs.didit.me/fr/integration/rate-limiting.md)
4. **Gérez chaque statut** : [Statuts de vérification](https://docs.didit.me/fr/integration/verification-statuses.md)
5. **Personnalisez votre workflow** : [Workflows](https://docs.didit.me/fr/console/workflows.md)
6. **Vérifiez que l'API est accessible** : [Vérification de l'état](https://docs.didit.me/fr/api-reference/healthcheck.md)

## Besoin d'aide

* **Assistance** : [support@didit.me](mailto:support@didit.me)
* **Onboarding programmatique** : [Inscription programmatique](https://docs.didit.me/fr/integration/programmatic-registration.md)


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