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

# SDK web

> Une capture guidée dans le navigateur : document, selfie, envoi, verdict.

Le SDK web `@verifner/web` guide la personne pas à pas : choix du document, photo du recto (et du verso pour une carte), selfie, envoi. Il mesure en direct la netteté, les reflets et la luminosité, recadre la photo sur le cadre affiché et la compresse pour les connexions lentes. Il n'a aucune dépendance et fonctionne avec ou sans framework.

## Comment ça marche

```mermaid theme={null}
sequenceDiagram
  participant N as Navigateur
  participant S as Votre serveur
  participant V as VerifNer
  N->>S: La personne commence la vérification
  S->>V: POST /v1/sessions (clé secrète)
  V-->>S: id + client_token
  S-->>N: id + client_token
  N->>V: Photos, via le SDK (client_token)
  V-->>N: Verdict affiché
  V->>S: Webhook verification.completed
```

La clé secrète reste sur votre serveur. Le navigateur ne reçoit qu'un **jeton client**, valable 15 minutes, pour une seule session et un seul envoi réussi.

<Warning>
  Ne vous fiez pas au résultat renvoyé dans le navigateur pour prendre une décision : il peut être modifié. Utilisez le [webhook](/guides/webhooks) ou `GET /v1/sessions/{id}` depuis votre serveur.
</Warning>

## 1. Ouvrez une session depuis votre serveur

<CodeGroup>
  ```javascript Node.js theme={null}
  app.post('/kyc/session', async (req, res) => {
    const r = await fetch('https://api.verifner.com/v1/sessions', {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${process.env.VERIFNER_API_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        reference: req.user.id,   // votre identifiant, renvoyé tel quel
        document_type: 'cni',     // facultatif : sinon la personne choisit
      }),
    });
    const session = await r.json();
    res.json({ sessionId: session.id, clientToken: session.client_token });
  });
  ```

  ```python Python theme={null}
  @app.post("/kyc/session")
  def open_session():
      r = requests.post(
          "https://api.verifner.com/v1/sessions",
          headers={"Authorization": f"Bearer {os.environ['VERIFNER_API_KEY']}"},
          json={"reference": current_user.id, "document_type": "cni"},
          timeout=10,
      )
      s = r.json()
      return {"sessionId": s["id"], "clientToken": s["client_token"]}
  ```

  ```bash curl theme={null}
  curl https://api.verifner.com/v1/sessions \
    -H "Authorization: Bearer $VERIFNER_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"reference": "client-4821", "document_type": "cni"}'
  ```
</CodeGroup>

Réponse :

```json theme={null}
{
  "id": "ses_ztkotjy7xytlxxfmhm5x",
  "mode": "test",
  "status": "pending",
  "reference": "client-4821",
  "document_type": "cni",
  "verification_id": null,
  "expires_at": "2026-09-26T10:15:00.000Z",
  "created_at": "2026-09-26T10:00:00.000Z",
  "client_token": "vn_ct_fjtsBilZpNknFBUFeKTnJpob3o3zU7JI",
  "url": "https://verify.verifner.com/#ses_ztkotjy7xytlxxfmhm5x.vn_ct_fjtsBilZpNknFBUFeKTnJpob3o3zU7JI"
}
```

| Paramètre | Type | Description |
| - | - | - |
| `reference` | string, 200 max | Votre identifiant pour ce client ou ce parcours. Renvoyé tel quel, jamais interprété |
| `document_type` | `cni` ou `passport` | Impose un document. Omis, la personne choisit |
| `ttl_seconds` | entier, 60 à 3600 | Durée de validité de la session. 900 (15 minutes) par défaut |

Le `client_token` n'est renvoyé qu'une fois.

<Tip>
  **Pas envie d'intégrer le SDK ?** La réponse contient aussi `url`, un lien vers notre page de capture hébergée (`https://verify.verifner.com/#…`). Envoyez-le à la personne par SMS, WhatsApp ou e-mail : elle fait tout sur son téléphone, et vous recevez le résultat par webhook comme d'habitude. Le lien ne sert qu'une fois et expire avec la session.
</Tip>

## 2. Montez le SDK dans votre page

<Tabs>
  <Tab title="Balise script">
    ```html theme={null}
    <div id="kyc"></div>
    <script src="https://cdn.jsdelivr.net/npm/@verifner/web@0.1/dist/verifner.iife.js"></script>
    <script type="module">
      const { sessionId, clientToken } = await fetch('/kyc/session', { method: 'POST' }).then((r) => r.json());

      VerifNer.create({
        sessionId,
        clientToken,
        apiUrl: 'https://api.verifner.com',
        lang: 'fr',
        onComplete: (result) => {
          // Affichez la suite ; la décision se prend côté serveur.
          window.location.href = '/kyc/merci';
        },
        onError: (error) => console.warn(error.code),
      }).mount(document.getElementById('kyc'));
    </script>
    ```
  </Tab>

  <Tab title="npm">
    ```bash theme={null}
    npm install @verifner/web
    ```

    ```javascript theme={null}
    import { create } from '@verifner/web';

    const flow = create({
      sessionId,
      clientToken,
      apiUrl: 'https://api.verifner.com',
      lang: 'fr',
      onComplete: (result) => router.push('/kyc/merci'),
    });
    flow.mount(document.getElementById('kyc'));

    // En quittant la page (React : dans le cleanup d'un useEffect)
    flow.destroy();
    ```
  </Tab>
</Tabs>

La page doit être servie en **HTTPS** : les navigateurs n'ouvrent la caméra que sur une origine sécurisée (ou `localhost`).

## Options

| Option | Type | Défaut | Description |
| - | - | - | - |
| `sessionId` | string | requis | L'`id` de la session |
| `clientToken` | string | requis | Le `client_token` de la session |
| `apiUrl` | string | requis | `https://api.verifner.com` |
| `lang` | `fr` ou `en` | `fr` | Langue de l'interface |
| `documentType` | `cni` ou `passport` | | Saute l'écran de choix du document |
| `selfie` | boolean | `true` | Demander un selfie |
| `theme` | `light`, `dark` ou `auto` | `auto` | Suit le thème du système en `auto` |
| `layout` | `card` ou `fullscreen` | `card` | `card` s'insère dans votre page ; `fullscreen` occupe tout l'écran, pour une page dédiée |
| `font` | URL ou `false` | Satoshi | Police du parcours. Par défaut Satoshi, servie par verify.verifner.com ; une URL de fichier `.woff2` pour la vôtre ; `false` pour reprendre celle de votre page |
| `showVerdict` | boolean | `true` | Montrer le résultat à la personne à la fin : « Identité vérifiée », « Vérification en cours » ou « Vérification non aboutie », jamais les raisons. `false` : elle voit seulement « Vérification envoyée » |
| `onComplete` | `(result) => void` | | Appelé avec `{ id, verdict, score, document_type, latency_ms, created_at }` |
| `onError` | `({ code, message }) => void` | | Appelé sur une erreur affichée à la personne |
| `onStep` | `(step) => void` | | Chaque changement d'étape : `intro`, `document`, `prepare`, `front`, `back`, `selfie`, `review`, `uploading`, `result`, `error`. Utile pour vos statistiques |

## Erreurs dans le navigateur

| `code` | Cause | Ce que voit la personne |
| - | - | - |
| `unreadable`, `not_an_image` | Photo trop floue ou illisible | Un bouton pour reprendre la photo |
| `session_expired` | Session de plus de 15 minutes | Un message : il faut recommencer. Ouvrez une nouvelle session |
| `session_completed` | Session déjà utilisée | Un message : il faut recommencer |
| `network` | Pas de connexion | Un bouton pour renvoyer |

Une erreur `422` laisse la session utilisable : la personne peut reprendre ses photos sans que vous rouvriez de session.

## 3. Récupérez le résultat côté serveur

Deux possibilités, que vous pouvez combiner :

* **Webhook** (recommandé) : VerifNer appelle votre serveur à chaque verdict. Voir [Webhooks](/guides/webhooks).
* **Lecture** : `GET /v1/sessions/{id}` avec la clé secrète. Quand `status` vaut `completed`, `result` contient le verdict et `verification_id` pointe vers la vérification complète.

```json theme={null}
{
  "id": "ses_ztkotjy7xytlxxfmhm5x",
  "status": "completed",
  "reference": "client-4821",
  "verification_id": "vrf_w6e35w8a9iehsf6shzuw",
  "result": { "verdict": "verified", "final_verdict": null, "score": 0.97, "document_type": "cni" }
}
```

## Les écrans

<Steps>
  <Step title="Accueil">Les deux étapes, la durée (environ une minute), le consentement et le choix de la langue.</Step>
  <Step title="Choix du document">Carte d'identité (AES ou ancienne) ou passeport. Sauté si vous passez `documentType`.</Step>
  <Step title="Préparation">Une illustration et quatre conseils : lumière, à plat, les quatre coins, pas de reflet. On peut aussi importer une photo de la galerie.</Step>
  <Step title="Caméra">Un cadre au format du document, des indications en direct (« Trop sombre », « Reflet », « Ne bougez plus… ») et une prise automatique dès que l'image est nette et stable.</Step>
  <Step title="Vérification de la photo">La photo prise et une liste de contrôle, pour reprendre ou valider. Pour une carte, un écran « Retournez votre carte » mène au verso.</Step>
  <Step title="Selfie">Des conseils, puis un ovale pour placer le visage. Le selfie se prend uniquement à la caméra, jamais depuis la galerie.</Step>
  <Step title="Envoi et résultat">L'avancement de l'envoi, puis le résultat : « Identité vérifiée », « Vérification en cours » (un agent examine le dossier) ou « Vérification non aboutie », sans les raisons. Chaque erreur dit quoi faire et propose de réessayer.</Step>
</Steps>

Tout le parcours est en français et en anglais, en police Satoshi, avec des textes grands et lisibles sur téléphone, en thème clair ou sombre.

## Ce que fait le SDK pour vous

* **Contrôle qualité en direct**, quatre fois par seconde : netteté, reflets, luminosité. Le bouton passe au vert quand l'image est stable et propre.
* **Recadrage** sur le cadre affiché, avec une petite marge, pour que le document remplisse l'image.
* **Compression** à environ 300 Ko par photo, pour les réseaux 3G.
* **Repli** sur le sélecteur de fichier quand il n'y a pas de caméra.
* **Styles isolés** sous `.vn-root` : le SDK ne touche pas au reste de votre page.
