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

# API de création de workflow : construire un flux KYC

> Créez par API un workflow Didit avec des fonctionnalités ordonnées : KYC, KYB, AML et biométrie. Paiement à l'appel, sans contrat.



Prompt de l'API de création de workflow
        Copier le prompt dans le presse-papiers



      
      

\<pre>

\`Create a simple Didit workflow through the Management API.

Endpoint:
POST https://verification.didit.me/v3/workflows/

Authentication:
Use the x-api-key header with my Didit API key.

Requirements:
\- Use the simple v3 API shape with features.
\- Send features in the exact order users should complete them.
\- Do not send workflow_graph, branches, actions, webhooks, or conditional logic.
\- Didit will convert the features array into a linear node-based workflow and add the final status step automatically.
\- Use uppercase feature values such as OCR, LIVENESS, FACE_MATCH, AML, QUESTIONNAIRE, EMAIL_VERIFICATION, PHONE_VERIFICATION, or PROOF_OF_ADDRESS.
\- Configure each feature with the fields from /management-api/workflows/feature-configs.
\- For an OCR feature, omit config.documents_allowed or send an empty object to allow every supported document type. Send config.documents_allowed only to restrict the allowed documents.
\- Put dependency features first. For example, put OCR before FACE_MATCH, NFC, DATABASE_VALIDATION, or user AML checks that depend on document data.
\- For a QUESTIONNAIRE feature, first create the questionnaire with POST /v3/questionnaires/ and use the returned questionnaire_id as config.questionnaire_uuid.
\- Store the workflow uuid returned by the response. Use it as workflow_id when creating sessions and as settings_uuid when updating this workflow.

Example KYC request body:
\{
"workflow_label": "Standard KYC",
"features": [
\{
  "feature": "OCR",
  "config": {
    "documents_allowed": {
      "USA": {
        "DL": {
          "enabled": 1,
          "sides": 2
        }
      }
    },
    "duplicated_user_action": "REVIEW"
  }
},
\{
  "feature": "LIVENESS",
  "config": {
    "face_liveness_method": "PASSIVE"
  }
},
\{
  "feature": "FACE_MATCH"
}
]
}

Example questionnaire workflow body:
\{
"workflow_label": "Source of Funds",
"features": [
\{
  "feature": "QUESTIONNAIRE",
  "config": {
    "questionnaire_uuid": "11111111-2222-3333-4444-555555555555",
    "review_questionnaire_manually": true
  }
}
]
}

Build the API call, handle validation errors, and return the workflow uuid from the response.`

\</pre>



      
      

Coller dans

Utilisez cet endpoint pour créer des workflows via l'API sans construire vous-même les nœuds du graphe de workflow. Envoyez un tableau `features` dans l'ordre dans lequel les utilisateurs doivent effectuer les vérifications. Didit le convertit en interne en workflow basé sur des nœuds et ajoute automatiquement l'étape finale indiquant le statut.Cet endpoint `POST /v3/workflows/` ne crée que des workflows **simples et linéaires**, à partir d'une simple liste `features`. Pour le **routage conditionnel et les branchements, Document AI, ainsi que les nœuds de refus et de décision**, utilisez le **[format en graphe](#branchements-et-workflows-à-nœuds-graphe)** décrit ci-dessous (il alimente l'éditeur visuel de la console et les outils de workflow du [Didit MCP](https://docs.didit.me/fr/integration/mcp/overview.md)).Consultez [Configurations des fonctionnalités de workflow](https://docs.didit.me/fr/management-api/workflows/feature-configs.md) pour connaître chaque champ `config` accepté par chaque fonctionnalité.## Workflow KYC de base```json theme={null}
{
  "workflow_label": "Standard KYC",
  "features": [
    {
      "feature": "OCR",
      "config": {
        "documents_allowed": {
          "USA": {
            "DL": {
              "enabled": 1,
              "sides": 2
            }
          }
        },
        "duplicated_user_action": "REVIEW"
      }
    },
    {
      "feature": "LIVENESS",
      "config": {
        "face_liveness_method": "PASSIVE"
      }
    },
    {
      "feature": "FACE_MATCH"
    }
  ]
}
```## Workflow de questionnaireCréez d'abord le questionnaire avec `POST /v3/questionnaires/`. Utilisez ensuite le `questionnaire_id` renvoyé comme `questionnaire_uuid`.```json theme={null}
{
  "workflow_label": "Source of Funds",
  "features": [
    {
      "feature": "QUESTIONNAIRE",
      "config": {
        "questionnaire_uuid": "11111111-2222-3333-4444-555555555555",
        "review_questionnaire_manually": true
      }
    }
  ]
}
```L'ordre des fonctionnalités compte. Par exemple, placez `OCR` avant `FACE_MATCH`, `NFC`, `DATABASE_VALIDATION`, ou les contrôles AML sur les utilisateurs qui dépendent des données du document.## Référence de configuration des fonctionnalitésChaque élément de `features[]` a la structure suivante :```json theme={null}
{
  "feature": "OCR",
  "label": "ID Verification",
  "config": {}
}
```L'objet `config` exact dépend de la fonctionnalité. Consultez la liste complète des champs, les valeurs par défaut, les paramètres obligatoires, les plages de valeurs et les exemples dans [Configurations des fonctionnalités de workflow](https://docs.didit.me/fr/management-api/workflows/feature-configs.md).Pour `OCR`, omettez `config.documents_allowed`, envoyez `null` ou envoyez `{}` pour autoriser tous les types de documents pris en charge. Ne l'incluez que si vous souhaitez restreindre le workflow à certains pays ou types de documents. Pour `QUESTIONNAIRE`, `config.questionnaire_uuid` est obligatoire.## Paramètres de workflow facultatifsVous pouvez inclure n'importe lequel de ces champs de premier niveau lors de la création d'un workflow pour contrôler le comportement des sessions. Ils sont tous facultatifs et prennent les valeurs par défaut du workflow lorsqu'ils sont omis.| Champ | Type | Description |
| - | - | - |
| `is_white_label_enabled` | booléen | Active la personnalisation en marque blanche pour les sessions créées avec ce workflow. Par défaut `false`. |
| `is_desktop_allowed` | booléen | Autorise l'exécution du parcours de vérification sur les navigateurs de bureau. Par défaut `false`. |
| `max_retry_attempts` | entier (0–10) | Nombre maximal de tentatives de réessai autorisées après une vérification refusée. `0` bloque l'utilisateur dès le premier refus. Par défaut `3`. |
| `retry_window_days` | entier (1–365) ou `null` | Fenêtre glissante, en jours, utilisée pour compter les tentatives de réessai (`null` = limite portant sur toute la durée). Par défaut `7`. |
| `session_expiration_time` | entier (3600–2419200) | Nombre de secondes avant l'expiration d'une session non terminée. Minimum 1 heure, maximum 4 semaines. Par défaut `604800` (7 jours). |
| `response_attributes` | objet ou `null` | Limite les champs renvoyés pour chaque fonctionnalité, par exemple `{"id_verifications": ["full_name", "document_number"], "face_matches": null}`. `null` ou une clé absente renvoie tous les champs. Une liste vide ne renvoie que `status`, `warnings`, `node_id`, `is_nfc_skipped` et `skip_reason`, et les autres champs ont la valeur `null`. |```json theme={null}
{
  "workflow_label": "Standard KYC",
  "is_white_label_enabled": true,
  "is_desktop_allowed": true,
  "max_retry_attempts": 5,
  "retry_window_days": 14,
  "session_expiration_time": 86400,
  "features": [
    { "feature": "OCR" },
    { "feature": "LIVENESS" },
    { "feature": "FACE_MATCH" }
  ]
}
```## Branchements et workflows à nœuds (graphe)Une liste `features` linéaire ne permet pas d'exprimer « si X alors Y ». Pour les **branchements conditionnels** (refuser selon un statut, router selon un champ extrait, conditionner une étape à une correspondance), une étape de requête **Document AI**, ou des nœuds terminaux **decline/decision**, les workflows Didit utilisent un **graphe de nœuds**, c'est-à-dire la même structure que celle produite par le générateur visuel de la console.> [!NOTE]
>
> Le moyen le plus simple de construire un workflow en graphe est le **[Didit MCP](https://docs.didit.me/fr/integration/mcp/overview.md)** : demandez à un agent en langage courant (« après la vérification d'identité, si la profession est ≈ "Software Engineer", ajoutez une étape de justificatif de fonds ») et il construit, valide et enregistre le graphe pour vous. La structure ci-dessous est celle que produisent ces outils (et la console).### Structure du grapheUn graphe est un objet `{ start_node, nodes }`. `nodes` est une association entre un identifiant de nœud et un nœud. Les arêtes sont implicites : chaque nœud pointe vers l'avant via `next` (inconditionnel) et/ou `branches[].goto` (conditionnel).| Type de nœud | Structure |
| - | - |
| `feature` | `{ "node_type": "feature", "feature": "OCR", "config": {…}, "next": "<id>", "branches": [] }` |
| `branch` | `{ "node_type": "branch", "branches": [ { "id", "logic": "and"\|"or", "rules": [<rule>], "goto": "<id>" } ], "next": "<fallback id>" }` |
| `status` (terminal) | `{ "node_type": "status", "session_status": "Approved"\|"Declined"\|"In Review"\|"Determine" }` |
| `action` | `{ "node_type": "action", "actions": [ { "type": "add_tag"\|"add_review"\|… } ], "next": "<id>" }` |
| `webhook` | `{ "node_type": "webhook", "config": { "url", "method" }, "branches": [], "next": "<id>" }` |`start_node` doit être un nœud de fonctionnalité. Les branches sont évaluées dans l'ordre, **la première correspondance l'emporte** ; un `rules: []` vide correspond à la branche « else » de repli (placez-la en dernier). `session_status: "Determine"` décide automatiquement à partir des résultats combinés des fonctionnalités.> [!WARNING]
>
> **Chaque nœud de branche doit avoir une branche else explicite**, c'est-à-dire une branche avec `rules: []` qui route les sessions ne correspondant à aucune des conditions. Ne vous fiez pas au `next` du nœud de branche comme repli : un `next` seul s'affiche comme une arête ambiguë dans le générateur et est facile à oublier. Lorsque vous envoyez un nœud de branche avec un `next` et sans branche else, l'API **convertit ce `next` en branche else explicite** à votre place (ainsi `{ …, "branches": [<conditions>], "next": "liveness" }` est enregistré comme `branches: [<conditions>, { "id": "else", "rules": [], "goto": "liveness" }]`). Créez la branche else vous-même pour être explicite ; la conversion est un filet de sécurité, pas un substitut.### Règles de branchement et opérateursChaque règle compare un **champ** à une **valeur** à l'aide d'un **opérateur** :```json theme={null}
{ "field": "kyc.extra_fields.profession", "operator": "fuzzy_match", "value": "Software Engineer", "score": 80 }
```- **Champs** désignent un résultat de fonctionnalité (`kyc.status`, `aml.status`, `document_ai.status`, `face_match.score`, …) ou une valeur extraite (`kyc.full_name`, `kyc.age`, `kyc.nationality`, `kyc.extra_fields.profession`, un champ Document AI `document_ai.<key>`, `expected_details.*`). Une branche ne peut lire qu'un champ dont la fonctionnalité a été exécutée sur tous les chemins qui y mènent.
- **Opérateurs :** `equals`, `not_equals`, `greater_than(_or_equals)`, `less_than(_or_equals)`, `contains`, `not_contains`, `in`, `not_in`, `is_empty`, `is_not_empty`, `regex`, et **`fuzzy_match`** (champs de type chaîne uniquement ; nécessite un `score` de 0 à 100, soit un seuil de similarité, par exemple une profession présentant une similarité d'au moins 80% avec « Software Engineer »).### Brancher selon la raison pour laquelle la NFC n'a pas été exécutéeAprès un nœud `NFC`, utilisez `nfc.skip_reason` avec `equals` ou `in` pour router les sessions selon la raison pour laquelle la lecture de la puce n'a pas eu lieu. Le champ possède ces sept valeurs stables :- `USER_SKIPPED`
- `DOCUMENT_WITHOUT_CHIP`
- `CHIP_CERTIFICATE_UNAVAILABLE`
- `DEVICE_WITHOUT_NFC`
- `INTEGRATION_WITHOUT_NFC_ACCESS`
- `MRZ_KEY_UNAVAILABLE`
- `JPKI_UNAVAILABLE`Consultez [Raisons de non-exécution NFC](https://docs.didit.me/fr/core-technology/nfc-verification/warnings-nfc-verification.md#motifs-d’omission) pour le sens de chaque valeur. Lorsque la NFC réussit, `nfc.skip_reason` vaut `null` : aucune règle `equals` ou `in` portant sur une raison de non-exécution ne correspond alors.Le champ est également résolu pour les nœuds NFC ignorés en raison d'une condition de garde. Cela inclut les cas où aucun rapport ni avertissement NFC n'existe, et les cas où `allow_nfc_skip` vaut `false` mais où la condition de garde liée au document ou à l'appareil empêche l'exécution de la NFC. Vous pouvez donc router ces sessions sans vous appuyer sur une entrée `nfc_verifications[]`.> [!NOTE]
>
> Les sessions enregistrées avant que la capacité NFC ne devienne à trois états peuvent avoir `has_device_nfc_support: false`. Une ancienne session web peut donc être résolue en `DEVICE_WITHOUT_NFC` au lieu de `INTEGRATION_WITHOUT_NFC_ACCESS`.Ce graphe utilise `equals` lorsqu'une session est ignorée par l'utilisateur, `in` pour les omissions liées à une condition de garde ou à une capacité, et une branche else explicite pour une NFC réussie. La branche vient après le nœud `NFC`, et chaque destination pointe vers l'avant, vers un nœud terminal :```json theme={null}
{
  "start_node": "ocr",
  "nodes": {
    "ocr": {
      "node_type": "feature",
      "feature": "OCR",
      "next": "nfc"
    },
    "nfc": {
      "node_type": "feature",
      "feature": "NFC",
      "config": { "allow_nfc_skip": false },
      "next": "route_nfc"
    },
    "route_nfc": {
      "node_type": "branch",
      "branches": [
        {
          "id": "user_skipped",
          "logic": "and",
          "rules": [
            { "field": "nfc.skip_reason", "operator": "equals", "value": "USER_SKIPPED" }
          ],
          "goto": "manual_review"
        },
        {
          "id": "nfc_unavailable",
          "logic": "and",
          "rules": [
            {
              "field": "nfc.skip_reason",
              "operator": "in",
              "value": [
                "DOCUMENT_WITHOUT_CHIP",
                "CHIP_CERTIFICATE_UNAVAILABLE",
                "DEVICE_WITHOUT_NFC",
                "INTEGRATION_WITHOUT_NFC_ACCESS",
                "MRZ_KEY_UNAVAILABLE",
                "JPKI_UNAVAILABLE"
              ]
            }
          ],
          "goto": "manual_review"
        },
        {
          "id": "else",
          "logic": "and",
          "rules": [],
          "goto": "approved"
        }
      ]
    },
    "manual_review": { "node_type": "status", "session_status": "In Review" },
    "approved": { "node_type": "status", "session_status": "Approved" }
  }
}
```Pour exprimer « la NFC n'a pas été effectuée pour une raison connue », utilisez `in` avec **les sept** valeurs. Conservez une branche else explicite pour une NFC réussie (`nfc.skip_reason: null`) ainsi que pour toute valeur future que votre graphe ne reconnaît pas encore.### Document AI (par exemple justificatif de fonds)Un nœud de fonctionnalité `DOCUMENT_AI` demande un ou plusieurs documents et extrait des champs nommés :```json theme={null}
{
  "node_type": "feature",
  "feature": "DOCUMENT_AI",
  "label": "Proof of Funds",
  "config": {
    "document_ai_documents": [
      {
        "document_key": "proof_of_funds",
        "title": "Proof of Funds",
        "description": "Upload a recent bank statement.",
        "fields": [
          {
            "key": "account_holder",
            "name": "Account holder",
            "instruction": "The full name of the account holder as printed on the statement.",
            "type": "text",
            "required": true,
            "is_full_name": true
          },
          {
            "key": "closing_balance",
            "name": "Closing balance",
            "instruction": "The closing balance amount.",
            "type": "number",
            "required": true
          }
        ]
      }
    ],
    "document_ai_missing_required_fields_action": "REVIEW",
    "document_ai_name_mismatch_action": "REVIEW"
  },
  "next": "final_decision"
}
```Chaque champ configuré devient une valeur utilisable dans une branche, `document_ai.<key>`, et le résultat de l'étape est `document_ai.status`.Définissez `"is_full_name": true` sur le champ qui contient le nom complet du titulaire du document. Sa valeur extraite est comparée au nom complet de l'identité vérifiée (le nom issu de l'OCR ou de la vérification d'identité, ou les détails attendus de la session), et `document_ai_name_mismatch_action` détermine ce qui se passe en cas de non-correspondance en dessous de `document_ai_name_match_score_threshold`. Cet indicateur est **facultatif** : un document peut ne marquer aucun champ, et **au plus un champ par document** peut le définir. Consultez [Configurations des fonctionnalités de workflow → Document AI](https://docs.didit.me/fr/management-api/workflows/feature-configs.md#document-ai) pour tous les champs de configuration Document AI.### Endpoints et cycle de vie du brouillonLa modification du graphe se fait par application et utilise les endpoints de la console et de gestion (Bearer utilisateur + `read:workflows`/`write:workflows`). Un graphe ne peut être modifié que sur une version **brouillon**, le cycle de vie est donc **créer le brouillon → définir le graphe → publier** :| Méthode | Chemin | Rôle |
| - | - | - |
| `GET` | `…/verification-settings/{settings_uuid}/workflow-graph/` | Graphe actuel + `status`/`is_editable` |
| `POST` | `…/application/{app}/workflow-graph/validate/` (body `{ "graph": … }`) | Validation en mode simulation |
| `GET` | `…/application/{app}/workflow-graph/field-definitions/` | Champs utilisables dans les branches et opérateurs valides |
| `POST` | `…/verification-settings/{settings_uuid}/create-draft/` | Brouillon modifiable à partir d'une version publiée |
| `PUT` | `…/verification-settings/{settings_uuid}/workflow-graph/` (body `{ "graph": … }`) | Enregistrer le graphe |
| `PATCH` | `…/verification-settings/{settings_uuid}/` (body `{ "status": "published" }`) | Publier le brouillon |#### Avertissements de validationL'endpoint de validation renvoie `{ "is_valid": true, "graph": …, "warnings": [] }`. Un avertissement est **consultatif** : le graphe est valide, il s'enregistre et il se publie même en présence d'avertissements. Chaque entrée indique le nœud de branchement et la branche concernés, afin qu'un créateur puisse repérer la condition exacte :```json theme={null}
{
  "type": "branch_outcome_contradiction",
  "node_id": "after_ocr",
  "branch_id": "b1",
  "severity": "warning",
  "message": "Branch 1 only matches sessions where ID Verification Status is Approved or In Review, but every path from it ends in the session outcome \"Declined\". Check the condition or the outcome.",
  "params": { "branchNumber": 1, "field": "ID Verification Status", "allowed": "Approved or In Review", "outcome": "Declined" }
}
```| `type` | Signification |
| - | - |
| `branch_outcome_contradiction` | La condition de la branche et le résultat vers lequel mènent tous les chemins qui en partent se contredisent, par exemple `kyc.status NOT EQUALS Declined` orienté vers un résultat `Declined`. Le résultat peut se trouver plusieurs étapes plus loin dans le chemin. |
| `branch_condition_unreachable` | La branche ne peut jamais correspondre, car tous les chemins menant à cette branche limitent déjà ce champ de statut à des valeurs que la condition exclut. |
| `else_branch_unreachable` | La branche else ne peut jamais s'exécuter : les autres branches couvrent déjà toutes les valeurs que le champ peut prendre à cet endroit. |Un graphe invalide renvoie `400` avec `errors` **et** une liste `warnings` renvoyée dans la mesure du possible. Seules les erreurs (`errors`) empêchent l'enregistrement.### Exemple pratique : orienter les ingénieurs logiciels vers le justificatif de fonds> Après la vérification d'identité : si elle a été **refusée**, refuser la session ; si la **profession ≈ "Software Engineer"** du titulaire présente une similarité ≥80%, demander un **justificatif de fonds** ; puis prendre une décision finale.```json theme={null}
{
  "start_node": "ocr_1",
  "nodes": {
    "ocr_1": { "node_type": "feature", "feature": "OCR", "label": "ID Verification", "next": "after_ocr" },
    "after_ocr": {
      "node_type": "branch",
      "label": "Route after ID verification",
      "branches": [
        { "id": "declined", "logic": "or", "rules": [ { "field": "kyc.status", "operator": "equals", "value": "Declined" } ], "goto": "decline" },
        { "id": "software_engineer", "logic": "and", "rules": [ { "field": "kyc.extra_fields.profession", "operator": "fuzzy_match", "value": "Software Engineer", "score": 80 } ], "goto": "proof_of_funds" },
        { "id": "else", "logic": "and", "rules": [], "goto": "final_decision" }
      ]
    },
    "proof_of_funds": {
      "node_type": "feature",
      "feature": "DOCUMENT_AI",
      "label": "Proof of Funds",
      "config": {
        "document_ai_documents": [
          { "document_key": "proof_of_funds", "title": "Proof of Funds", "description": "Upload a recent bank statement.",
            "fields": [
              { "key": "account_holder", "name": "Account holder", "type": "text", "required": true },
              { "key": "closing_balance", "name": "Closing balance", "type": "number", "required": true }
            ] }
        ],
        "document_ai_missing_required_fields_action": "REVIEW"
      },
      "next": "final_decision"
    },
    "final_decision": { "node_type": "status", "session_status": "Determine", "label": "Final decision" },
    "decline": { "node_type": "status", "session_status": "Declined", "label": "Declined" }
  }
}
```Le nœud final utilise `session_status: "Determine"`, qui **décide automatiquement** (approuver / refuser / en cours d'examen) à partir des fonctionnalités réellement exécutées sur la session. Ainsi, le chemin du justificatif de fonds et le chemin else aboutissent tous deux correctement, sans qu'aucune branche ne lise `document_ai.status` (qui n'existe pas sur le chemin où Document AI n'est pas exécuté). Rappel : une branche ne peut lire qu'un champ dont la fonctionnalité a été exécutée sur *tous* les chemins qui y mènent.Envoyez-le sous la forme `{ "graph": <above> }` à l'endpoint `…/workflow-graph/` (validez-le d'abord). Pour créer un **nouveau** workflow à partir de zéro, utilisez `didit_workflow_set_graph` du MCP ; pour modifier un workflow existant, utilisez `didit_workflow_edit_graph` (petites opérations, qui conserve les nœuds existants et les listes d'autorisation). Consultez le [MCP Didit](https://docs.didit.me/fr/integration/mcp/tools.md).


## OpenAPI

````yaml fr/openapi-25.json POST /v3/workflows/
openapi: 3.0.0
info:
  version: 3.0.0
  title: Didit Verification API
  description: API de vérification d'identité. Authentifiez-vous avec l'en-tête x-api-key.
servers:
  - url: https://verification.didit.me
security: []
tags: []
paths:
  /v3/workflows/:
    post:
      tags:
        - Workflows
      summary: Créer un workflow
      description: "Créez un workflow simple. Envoyez `features` dans l'ordre d'exécution (des règles d'ordonnancement s'appliquent)\_; défini par défaut sur `published` v1. Le corps accepte UNIQUEMENT les champs répertoriés dans `SimpleWorkflowRequest` — toute autre clé (y compris `workflow_type`) est rejetée avec une erreur 400. Non idempotent — chaque appel crée un nouveau workflow. Un maximum de 50 workflows par application."
      operationId: create_workflow
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SimpleWorkflowRequest'
            examples:
              Simple KYC:
                summary: >-
                  Workflow linéaire de vérification d'identité, de détection du
                  vivant et de correspondance faciale
                value:
                  workflow_label: Standard KYC
                  features:
                    - feature: OCR
                      config:
                        duplicated_user_action: review
                    - feature: LIVENESS
                      config:
                        face_liveness_method: passive
                    - feature: FACE_MATCH
                      config:
                        face_match_score_decline_threshold: 40
                        face_match_score_review_threshold: 60
              KYC + AML:
                summary: Exécuter le filtrage AML après la vérification d'identité
                value:
                  workflow_label: KYC with AML
                  features:
                    - feature: OCR
                    - feature: AML
                      config:
                        aml_score_review_threshold: 70
                        aml_score_approve_threshold: 30
              Questionnaire workflow:
                summary: Exécuter un questionnaire créé avec POST /v3/questionnaires/
                value:
                  workflow_label: Source of Funds
                  features:
                    - feature: QUESTIONNAIRE
                      config:
                        questionnaire_uuid: 11111111-2222-3333-4444-555555555555
                        review_questionnaire_manually: true
      responses:
        '201':
          description: >-
            Workflow créé. Le corps de la réponse contient la configuration
            COMPLÈTE du workflow — la même sérialisation brute
            `VerificationSettings` renvoyée par `GET
            /v3/workflows/{settings_uuid}/` (~190 champs : chaque activation de
            fonctionnalité, seuil, action et le `workflow_graph` généré), et non
            l'élément de liste compact. Enregistrez l'`uuid` (également exposé
            sous le nom `workflow_id`) afin de pouvoir le référencer lors de
            futurs appels de création de session. Remarque : `features` est une
            chaîne jointe par `" + "` et les prix sont des nombres JSON.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowDetail'
              examples:
                Created (abbreviated):
                  summary: Champs clés de l'objet de configuration complet
                  value:
                    uuid: c3d4e5f6-7890-12cd-ef34-333333333333
                    workflow_id: c3d4e5f6-7890-12cd-ef34-333333333333
                    workflow_label: Standard KYC
                    workflow_type: null
                    is_default: false
                    is_archived: false
                    total_price: 0.15
                    min_price: 0.15
                    max_price: 0.15
                    features: OCR + LIVENESS + FACE_MATCH
                    is_simple_workflow: false
                    is_editable: false
                    workflow_url: https://verify.didit.me/u/w9TkPOx8RWG3UAz5ZRQGVQ
                    workflow_graph:
                      start_node: ocr
                      nodes: ... one node per feature plus a final status node ...
                    max_retry_attempts: 3
                    retry_window_days: 7
                    session_expiration_time: 604800
                    version: 1
                    status: published
                    published_at: '2026-05-17T10:30:00Z'
                    created_at: '2026-05-17T10:30:00Z'
                    updated_at: '2026-05-17T10:30:00Z'
        '400':
          description: >-
            Erreur de validation. Causes courantes : une clé de premier niveau
            en dehors de la liste autorisée (par exemple `workflow_type`), une
            valeur de `feature` inconnue, une fonctionnalité placée avant sa
            dépendance (par exemple `FACE_MATCH` avant `OCR`), une
            fonctionnalité passive (`AML`, `IP_ANALYSIS`, `DATABASE_VALIDATION`,
            `KYB_KEY_PEOPLE`) utilisée comme première étape, une fonctionnalité
            `QUESTIONNAIRE` dont le `config.questionnaire_uuid` n'existe pas
            pour cette application, une fonctionnalité `SIGNATURE` qui passerait
            en production sans document à signer, une fonctionnalité `SIGNATURE`
            avec `signature_level: "aes"` alors que le niveau avancé (AES) n'est
            pas disponible (`aes_not_available`, voir `signature_availability`
            sur le workflow), ou un seuil hors limites. Le corps est un objet
            d'erreur de validation DRF — les clés sont des noms de champs, les
            valeurs sont des tableaux de messages lisibles par l'humain.
          content:
            application/json:
              examples:
                Advanced (AES) not available:
                  value:
                    features:
                      signature_level:
                        - >-
                          Node 'signature': Advanced (AES) is not available yet
                          (aes_not_available).
                Signature step with no document:
                  value:
                    features:
                      non_field_errors:
                        - >-
                          Node 'signature': Add a document to sign, or let each
                          session bring its own.
                Missing feature:
                  value:
                    features:
                      - At least one feature is required.
                Unknown top-level field:
                  value:
                    workflow_type:
                      - >-
                        The v3 workflow API only accepts `workflow_label`,
                        `is_default`, `status`, `features`,
                        `is_white_label_enabled`, `is_desktop_allowed`,
                        `max_retry_attempts`, `retry_window_days`,
                        `session_expiration_time`, `face_liveness_max_attempts`,
                        `face_match_max_attempts`.
                Unknown feature:
                  value:
                    features:
                      - >-
                        Invalid feature 'FOO'. Allowed values: AGE_ESTIMATION,
                        AML, DATABASE_VALIDATION, EMAIL_VERIFICATION,
                        FACE_MATCH, IP_ANALYSIS, KYB_DOCUMENTS, KYB_KEY_PEOPLE,
                        KYB_REGISTRY, LIVENESS, NFC, OCR, PHONE_VERIFICATION,
                        PROOF_OF_ADDRESS, QUESTIONNAIRE.
                Ordering:
                  value:
                    features:
                      - 'Feature ''FACE_MATCH'' must appear after one of: OCR.'
                Questionnaire not found:
                  value:
                    features: >-
                      Questionnaire '11111111-2222-3333-4444-555555555555' was
                      not found for this application. Create the questionnaire
                      first, then use its questionnaire_id.
        '403':
          description: >-
            La clé API est manquante, malformée, expirée ou restreinte à une
            autre application. Actualisez via l'[API
            Auth](/fr/auth-api/get-credentials) et réessayez.
          content:
            application/json:
              examples:
                Invalid token:
                  value:
                    detail: You do not have permission to perform this action.
        '415':
          description: >-
            Type de média non pris en charge. Définissez `Content-Type:
            application/json` et envoyez un corps JSON.
        '429':
          description: >-
            Limite de débit dépassée. Réessayez avec une temporisation
            exponentielle.
        '500':
          description: >-
            Erreur serveur inattendue lors de la création du workflow. Peut être
            réessayé en toute sécurité.
      security:
        - ApiKeyAuth: []
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |-
            curl -X POST 'https://verification.didit.me/v3/workflows/' \
              -H 'x-api-key: YOUR_API_KEY' \
              -H 'Content-Type: application/json' \
              -d '{
                "workflow_label": "Standard KYC",
                "features": [
                  {"feature": "OCR"},
                  {"feature": "LIVENESS", "config": {"face_liveness_method": "passive"}},
                  {"feature": "FACE_MATCH", "config": {"face_match_score_decline_threshold": 40, "face_match_score_review_threshold": 60}}
                ]
              }'
        - lang: python
          label: Python
          source: |-
            from didit import Didit

            # Reads DIDIT_API_KEY.
            client = Didit()

            result = client.workflows.create(
                workflow_label="Standard KYC",
                features=[{
                    "feature": "OCR",
                    "config": {"duplicated_user_action": "review"},
                }, {
                    "feature": "LIVENESS",
                    "config": {"face_liveness_method": "passive"},
                }, {
                    "feature": "FACE_MATCH",
                    "config": {"face_match_score_decline_threshold": 40, "face_match_score_review_threshold": 60},
                }],
            )
            print(result)
        - lang: javascript
          label: Node.js
          source: |-
            import Didit from "@didit-protocol/sdk-node";

            // Reads DIDIT_API_KEY.
            const didit = new Didit();

            const result = await didit.workflows.create({
              workflow_label: "Standard KYC",
              features: [{
                feature: "OCR",
                config: { duplicated_user_action: "review" },
              }, {
                feature: "LIVENESS",
                config: { face_liveness_method: "passive" },
              }, {
                feature: "FACE_MATCH",
                config: { face_match_score_decline_threshold: 40, face_match_score_review_threshold: 60 },
              }],
            });
            console.log(result);
        - lang: go
          label: Go
          source: "ctx := context.Background()\n// Reads DIDIT_API_KEY.\nclient := didit.NewClient()\n\nresult, err := client.Workflows.Create(ctx, didit.SimpleWorkflowRequest{\n\tWorkflowLabel: didit.String(\"Standard KYC\"),\n\tFeatures: []didit.SimpleWorkflowFeature{{\n\t\tFeature: \"OCR\",\n\t\tConfig:  didit.Some(map[string]any{\"duplicated_user_action\": \"review\"}),\n\t}, {\n\t\tFeature: \"LIVENESS\",\n\t\tConfig:  didit.Some(map[string]any{\"face_liveness_method\": \"passive\"}),\n\t}, {\n\t\tFeature: \"FACE_MATCH\",\n\t\tConfig:  didit.Some(map[string]any{\"face_match_score_decline_threshold\": 40, \"face_match_score_review_threshold\": 60}),\n\t}},\n})\nif err != nil {\n\tlog.Fatal(err)\n}\nfmt.Printf(\"%+v\\n\", result)"
        - lang: java
          label: Java
          source: >-
            import java.util.List;

            import java.util.Map;

            import me.didit.Didit;

            import me.didit.types.SimpleWorkflowFeature;

            import me.didit.types.SimpleWorkflowRequest;


            // Reads DIDIT_API_KEY.

            Didit client = new Didit();


            var result =
            client.workflows().create(SimpleWorkflowRequest.builder()
                    .workflowLabel("Standard KYC")
                    .features(List.of(
                            SimpleWorkflowFeature.builder()
                                    .feature(SimpleWorkflowFeature.Feature.of("OCR"))
                                    .config(Map.of("duplicated_user_action", "review"))
                                    .build(),
                            SimpleWorkflowFeature.builder()
                                    .feature(SimpleWorkflowFeature.Feature.of("LIVENESS"))
                                    .config(Map.of("face_liveness_method", "passive"))
                                    .build(),
                            SimpleWorkflowFeature.builder()
                                    .feature(SimpleWorkflowFeature.Feature.of("FACE_MATCH"))
                                    .config(Map.of("face_match_score_decline_threshold", 40, "face_match_score_review_threshold", 60))
                                    .build()))
                    .build());
            System.out.println(result);
        - lang: php
          label: PHP
          source: |-
            <?php

            require 'vendor/autoload.php';

            use Didit\DiditClient;

            // Reads DIDIT_API_KEY.
            $didit = new DiditClient();

            $result = $didit->workflows->create([
                'workflow_label' => 'Standard KYC',
                'features' => [[
                    'feature' => 'OCR',
                    'config' => ['duplicated_user_action' => 'review'],
                ], [
                    'feature' => 'LIVENESS',
                    'config' => ['face_liveness_method' => 'passive'],
                ], [
                    'feature' => 'FACE_MATCH',
                    'config' => ['face_match_score_decline_threshold' => 40, 'face_match_score_review_threshold' => 60],
                ]],
            ]);
            print_r($result->toArray());
        - lang: ruby
          label: Ruby
          source: |-
            require "didit"

            # Reads DIDIT_API_KEY.
            didit = Didit::Client.new

            result = didit.workflows.create(
              workflow_label: "Standard KYC",
              features: [{
                feature: "OCR",
                config: { duplicated_user_action: "review" },
              }, {
                feature: "LIVENESS",
                config: { face_liveness_method: "passive" },
              }, {
                feature: "FACE_MATCH",
                config: { face_match_score_decline_threshold: 40, face_match_score_review_threshold: 60 },
              }]
            )
            puts result.to_h
        - lang: csharp
          label: .NET
          source: >-
            using System;

            using System.Collections.Generic;

            using System.Text.Json;

            using Didit;


            // Reads DIDIT_API_KEY.

            using var client = new DiditClient();


            var result = await client.Workflows.CreateAsync(new
            SimpleWorkflowRequest

            {
                WorkflowLabel = "Standard KYC",
                Features = new SimpleWorkflowFeature[]
                {
                    new()
                    {
                        Feature = "OCR",
                        Config = new Dictionary<string, object> { ["duplicated_user_action"] = "review" },
                    },
                    new()
                    {
                        Feature = "LIVENESS",
                        Config = new Dictionary<string, object> { ["face_liveness_method"] = "passive" },
                    },
                    new()
                    {
                        Feature = "FACE_MATCH",
                        Config = new Dictionary<string, object> { ["face_match_score_decline_threshold"] = 40, ["face_match_score_review_threshold"] = 60 },
                    },
                },
            });

            Console.WriteLine(JsonSerializer.Serialize(result));
components:
  schemas:
    SimpleWorkflowRequest:
      type: object
      description: "Créez un workflow linéaire simple. L'API convertit en interne cette liste de fonctionnalités en un graphe de workflow basé sur des nœuds. Liste blanche de champs STRICTE\_: toute clé non répertoriée ici (y compris `workflow_type`) est rejetée avec une erreur 400."
      required:
        - features
      properties:
        workflow_label:
          type: string
          maxLength: 50
          default: ID Verification
          description: >-
            Nom d'affichage du workflow (50 caractères maximum). La valeur par
            défaut est `"ID Verification"` lorsqu'il est omis — définissez-le
            toujours pour que les workflows restent faciles à distinguer.
          example: Standard KYC
        is_default:
          type: boolean
          description: >-
            Définir ce workflow comme celui par défaut pour les nouvelles
            sessions.
        status:
          type: string
          enum:
            - draft
            - published
          description: >-
            Omettez ce champ pour créer un workflow publié et prêt pour les
            sessions. Utilisez `draft` uniquement si vous souhaitez enregistrer
            sans publier.
        features:
          type: array
          minItems: 1
          description: >-
            Fonctionnalités de vérification dans l'ordre d'exécution. L'API
            relie automatiquement chaque élément au suivant et ajoute un nœud de
            statut final.
          items:
            $ref: '#/components/schemas/SimpleWorkflowFeature'
        is_white_label_enabled:
          type: boolean
          description: >-
            Activer la personnalisation en marque blanche pour les sessions
            créées avec ce workflow.
          default: false
        is_desktop_allowed:
          type: boolean
          description: >-
            Autoriser l'exécution du flux de vérification sur les navigateurs
            sur ordinateur.
          default: false
        max_retry_attempts:
          type: integer
          minimum: 0
          maximum: 10
          default: 3
          description: >-
            Nombre maximal de tentatives de réessai autorisées après une
            vérification refusée. `0` bloque l'utilisateur après le premier
            refus.
        retry_window_days:
          type: integer
          minimum: 1
          maximum: 365
          nullable: true
          default: 7
          description: >-
            Fenêtre glissante en jours utilisée pour compter les nouvelles
            tentatives. `null` applique une limite globale depuis le début.
        face_liveness_max_attempts:
          type: integer
          minimum: 1
          maximum: 3
          default: 3
          description: "Nombre maximal de soumissions de détection du vivant par session. Par défaut 3\_: une tentative initiale plus deux nouvelles tentatives. Cette valeur est également copiée dans les nœuds de workflow de détection du vivant et d'estimation de l'âge générés, sauf si la configuration au niveau de la fonctionnalité la remplace."
        face_match_max_attempts:
          type: integer
          minimum: 1
          maximum: 3
          default: 3
          description: "Nombre maximal de soumissions de correspondance faciale par session. Par défaut 3\_: une tentative initiale plus deux nouvelles tentatives. Cette valeur est également copiée dans les nœuds de workflow de correspondance faciale générés, sauf si la configuration au niveau de la fonctionnalité la remplace."
        session_expiration_time:
          type: integer
          minimum: 3600
          maximum: 2419200
          default: 604800
          description: >-
            Secondes avant l'expiration d'une session inachevée. Minimum 1 heure
            (3600), maximum 4 semaines (2419200).
    WorkflowDetail:
      type: object
      description: >-
        Les champs ci-dessous sont typés ; l'objet contient également toutes les
        autres colonnes de la configuration du workflow (activation des
        fonctionnalités, seuils, actions d'avertissement, documents autorisés,
        rappels) sous forme de propriétés supplémentaires. Les champs qui ne
        s'appliquent pas au `workflow_type` sont `null`.
      properties:
        uuid:
          type: string
          format: uuid
          description: >-
            Identifiant par version (`settings_uuid`). Chaque version d'un
            workflow possède le sien.
        workflow_id:
          type: string
          format: uuid
          description: >-
            Identifiant stable partagé par toutes les versions du workflow. Les
            sessions créées avec celui-ci utilisent la dernière version publiée.
        workflow_label:
          type: string
          description: Nom d'affichage du workflow.
        workflow_type:
          type: string
          nullable: true
          enum:
            - kyc
            - kyb
            - adaptive_age_verification
            - biometric_authentication
            - address_verification
            - questionnaire_verification
            - email_verification
            - phone_verification
            - null
          description: >-
            Modèle de workflow. `null` pour les workflows sous forme de graphe
            créés sans modèle.
        is_default:
          type: boolean
          description: Indique s'il s'agit du workflow par défaut de l'application.
        is_archived:
          type: boolean
          description: >-
            Les workflows archivés ne peuvent pas démarrer de nouvelles
            sessions.
        version:
          type: integer
          description: Numéro de version au sein du workflow.
        status:
          type: string
          enum:
            - draft
            - published
          description: "Les versions de brouillon sont modifiables\_; les versions publiées sont immuables et utilisées pour les nouvelles sessions."
        published_at:
          type: string
          format: date-time
          nullable: true
          description: >-
            Date et heure auxquelles cette version a été publiée. `null` pour
            les brouillons.
        is_simple_workflow:
          type: boolean
          description: >-
            `true` lorsque le workflow ne comporte aucun `workflow_graph`
            personnalisé.
        is_editable:
          type: boolean
          description: '`true` uniquement pour les versions `draft`.'
        workflow_url:
          type: string
          format: uri
          description: URL publique qui démarre une session avec ce workflow.
        workflow_graph:
          type: object
          nullable: true
          additionalProperties: true
          description: >-
            Structure en graphe du workflow (nœuds et arêtes). `null` pour les
            workflows simples.
        features:
          type: string
          description: >-
            Fonctionnalités activées jointes par `" + "`, par exemple `"OCR +
            LIVENESS"`.
        total_price:
          type: number
          nullable: true
          description: >-
            Prix par session ; pour les workflows de graphe, le parcours le plus
            cher.
        min_price:
          type: number
          nullable: true
          description: Prix du parcours le moins cher dans le workflow.
        max_price:
          type: number
          nullable: true
          description: Prix du parcours le plus cher dans le workflow.
        special_add_on_prices:
          type: object
          nullable: true
          additionalProperties: true
          description: >-
            Prix des modules complémentaires spécifiques à l'organisation, ou
            `null` si aucun n'est défini.
        max_retry_attempts:
          type: integer
          description: Réessais autorisés après une vérification refusée.
        retry_window_days:
          type: integer
          nullable: true
          description: >-
            Nombre de jours pendant lesquels les nouvelles tentatives sont
            autorisées.
        session_expiration_time:
          type: integer
          description: Secondes avant l'expiration d'une session inachevée.
        questionnaire:
          type: object
          nullable: true
          additionalProperties: true
          description: Le questionnaire associé au workflow, ou `null`.
        kyb_role_settings:
          type: array
          items:
            type: object
            additionalProperties: true
          description: Paramètres de vérification par rôle pour les personnes clés KYB.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        signature_availability:
          type: object
          description: >-
            Quels niveaux de Signature électronique peuvent signer actuellement.
            Vérifiez-le avant de définir `signature_level`. Renvoyé sur les
            workflows `kyc`, `kyb` et de graphe.
          properties:
            simple:
              type: boolean
              description: Simple est toujours disponible.
            aes:
              type: boolean
              description: >-
                Indique si Advanced (AES) peut signer actuellement. Tant que
                cette valeur est `false`, l'enregistrement d'une nouvelle étape
                avec `signature_level: "aes"` est refusé avec
                `aes_not_available`.
            qes:
              type: boolean
              description: >-
                Indique si la signature qualifiée (QES) peut signer
                actuellement. La valeur est `false` jusqu'à ce qu'un fournisseur
                qualifié soit connecté.
        is_bank_verification_enabled:
          type: boolean
          description: >-
            Indique si un workflow KYC ou KYB simple exécute la vérification
            bancaire. Elle s'exécute après les étapes de contact (et la
            vérification de localisation) et avant la validation en base de
            données, le filtrage AML et l'analyse IP. La vérification bancaire
            n'est pas encore disponible pour tous : son activation est refusée
            partout où elle n'est pas activée.
        bank_verification_config:
          type: object
          additionalProperties: true
          description: >-
            Paramètres de vérification bancaire d'un workflow simple, avec les
            mêmes clés qu'un nœud `BANK_VERIFICATION` (par exemple
            `bank_countries` et `bank_method_order`). Les clés inconnues sont
            ignorées. Chaque clé est optionnelle et une clé absente conserve sa
            valeur par défaut, `{}` désigne donc toutes les valeurs par défaut.
            Voir [Paramètres de vérification
            bancaire](/fr/management-api/workflows/feature-configs#vérification-bancaire).
          example: {}
        is_geolocation_enabled:
          type: boolean
          description: >-
            Indique si un workflow KYC simple exécute la vérification de
            localisation. Elle s'exécute après les étapes de contact et avant la
            vérification bancaire et la validation en base de données. Les
            workflows d'entreprise (KYB) ne peuvent pas l'exécuter.
        geolocation_config:
          type: object
          additionalProperties: true
          description: >-
            Paramètres de vérification de localisation d'un workflow simple,
            avec les mêmes clés qu'un nœud `GEOLOCATION` (par exemple
            `is_geolocation_geofencing_enabled` et `geolocation_max_attempts`).
            Les clés inconnues sont ignorées. Chaque clé est facultative et une
            clé absente conserve sa valeur par défaut, `{}` correspond donc à
            toutes les valeurs par défaut.
          example: {}
      additionalProperties: true
    SimpleWorkflowFeature:
      type: object
      required:
        - feature
      properties:
        feature:
          type: string
          description: >-
            Fonctionnalité à exécuter. Utilisez les valeurs en majuscules
            indiquées ici.
          enum:
            - OCR
            - NFC
            - LIVENESS
            - FACE_MATCH
            - PROOF_OF_ADDRESS
            - QUESTIONNAIRE
            - DOCUMENT_AI
            - PHONE_VERIFICATION
            - EMAIL_VERIFICATION
            - DATABASE_VALIDATION
            - AML
            - IP_ANALYSIS
            - AGE_ESTIMATION
            - KYB_REGISTRY
            - KYB_DOCUMENTS
            - KYB_KEY_PEOPLE
            - SIGNATURE
        config:
          type: object
          description: >-
            Paramètres spécifiques à la fonctionnalité. Pour `QUESTIONNAIRE`,
            incluez `questionnaire_uuid` avec le `questionnaire_id` renvoyé par
            l'endpoint de création de questionnaire. Pour `SIGNATURE`, un
            workflow mis en production nécessite un document à signer :
            définissez `signature_documents_source` sur `session` ou `both` afin
            que chaque session puisse fournir le sien, ou envoyez `status:
            "draft"` et ajoutez les documents avant de publier.
            `signature_level: "aes"` est refusé avec `aes_not_available` tant
            que `signature_availability.aes` est `false` sur le workflow. Pour
            `OCR`, `methods` sélectionne les méthodes de vérification d'identité
            que chaque pays peut utiliser (capture de document, recherche sans
            document auprès d'une source gouvernementale ou d'une autre source
            faisant autorité, et portefeuilles d'identité numérique) ;
            omettez-le et chaque pays est configuré en capture de document
            uniquement. Pour `SIGNATURE`, consultez [Paramètres de signature
            électronique](/fr/management-api/workflows/feature-configs#signature-électronique)
            : `signature_level` accepte `simple` (par défaut, sans vérification
            d'identité requise ni e-mail ni téléphone requis) et `aes` pour le
            moment, et les documents de workflow ainsi que leurs champs sont
            gérés dans la console, donc via l'API, envoyez les documents et
            champs par session avec `signature_documents_source: "session"`.
          additionalProperties: true
        label:
          type: string
          description: Libellé interne optionnel pour ce nœud de workflow.
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
      description: >-
        La clé API de votre application, issue de Développeurs -> Clés API dans
        la Business Console. La clé principale dispose d'un accès complet. Une
        clé nommée peut être restreinte : aucun, lecture ou écriture par
        ressource, limitée à certains workflows ou aux sessions approuvées, à
        une liste d'adresses IP et à une date d'expiration. 401 signifie que la
        clé est incorrecte, révoquée ou expirée ; 403 signifie qu'aucune clé n'a
        été envoyée, que la clé n'a pas accès à cette ressource ou action, ou
        que la requête provient d'une adresse hors de sa liste d'IP ; 404 sur
        une route de session signifie que la session est hors des workflows ou
        des statuts de la clé. Une clé sans accès aux médias reçoit les URL
        d'images, de vidéos et de PDF à null, et une clé sans accès en écriture
        aux sessions reçoit les liens et tokens de session à null. Voir
        https://docs.didit.me/console/api-keys.

````

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