> ## 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 d'extraction de données de documents personnalisée

> Didit extrait les champs définis par vous de tout PDF ou image via POST /v3/document-ai/. Contrôle de falsification. $0.20 par document.

Envoyez un document et la liste des champs à lire dans ce document. Didit exécute sur le fichier son modèle d'extraction par défaut, renvoie tous les champs avec leur type (`text`, `number` ou `date` au format `YYYY-MM-DD`), détecte et décode les codes QR et codes-barres pris en charge, vérifie que le fichier n'a pas été manipulé et, si vous le souhaitez, compare le nom figurant sur le document à la personne ou à l'entreprise attendue. Utilisez-le pour les justificatifs de fonds, les fiches de paie, les relevés bancaires, les documents fiscaux, les factures ou tout document qui ne correspond pas à un modèle fixe.

Il s'agit de la version serveur à serveur de l'étape [Document AI](https://docs.didit.me/fr/core-technology/document-ai/overview.md) hébergée. La requête est au format `multipart/form-data` : le fichier est transmis dans `document`, et les définitions des champs dans `fields`, sous la forme d'une chaîne contenant un tableau JSON encodé.

## Définir les champs à extraire

`fields` est un tableau JSON de 1 à 30 éléments, envoyé sous la forme d'une seule chaîne. Chaque élément comporte une `key` (lettres minuscules, chiffres et tirets bas, unique dans la requête), un `name` lisible, une `instruction` qui indique au modèle ce qu'il doit rechercher, un `type` (`text`, `number` ou `date`) et deux indicateurs : `required` et `is_full_name`.

```json theme={null}
[
  {
    "key": "account_holder",
    "name": "Account holder",
    "instruction": "Full name of the person or company the account belongs to",
    "type": "text",
    "required": true,
    "is_full_name": true
  },
  {
    "key": "balance",
    "name": "Closing balance",
    "instruction": "Closing balance shown on the statement, as a number without the currency symbol",
    "type": "number",
    "required": true
  },
  {
    "key": "currency",
    "name": "Currency",
    "instruction": "Three-letter currency code of the balance",
    "type": "text"
  },
  {
    "key": "statement_date",
    "name": "Statement date",
    "instruction": "Date the statement was issued",
    "type": "date",
    "required": true
  }
]
```

Dans une requête multipart, le tableau entier constitue une seule valeur de formulaire :

```bash theme={null}
curl -X POST https://verification.didit.me/v3/document-ai/ \
  -H "x-api-key: $DIDIT_API_KEY" \
  -F "document=@./bank-statement.pdf" \
  -F 'fields=[{"key":"account_holder","name":"Account holder","instruction":"Full name of the account holder","type":"text","required":true,"is_full_name":true},{"key":"balance","name":"Closing balance","instruction":"Closing balance as a number","type":"number","required":true}]' \
  -F "expected_first_name=Jane" \
  -F "expected_last_name=Doe"
```

La réponse reprend les `fields` normalisés et renvoie `extracted_data`, indexé par vos valeurs `key`, avec `null` pour tout ce que le modèle n'a pas pu trouver. `name`, `instruction` et `type` sont facultatifs pour chaque élément (`type` vaut `text` par défaut) ; `key` est obligatoire. Envoyer `fields` sous forme de clés de formulaire de type crochets (`fields[0][key]=...`), ou sous une autre forme qu'un tableau JSON, renvoie `400` avant le lancement de toute extraction. Rien n'est donc facturé.

## Codes QR et codes-barres

Chaque réponse réussie inclut `document_ai.detected_codes`. Chaque élément contient :

| Champ | Description |
| - | - |
| `type` | Symbologie détectée, par exemple `QR_CODE`, `CODE_128`, `EAN_13`, `DATA_MATRIX` ou `PDF_417` |
| `page_number` | Numéro de la page, à partir de 1 |
| `position` | Polygone détecté, en pixels de la page rendue, ou `null` lorsqu'aucune localisation fiable n'est disponible |
| `x`, `y`, `width`, `height` | Cadre englobant, en pixels de la page rendue, ou `null` lorsqu'aucune localisation fiable n'est disponible |
| `page_width`, `page_height` | Dimensions de la page rendue utilisées par les coordonnées |
| `parsed_payload` | Chaîne décodée ou valeur structurée, ou `null` lorsque le code ne peut être que localisé |
| `raw_payload` | Payload brut du décodeur, sous forme de chaîne base64 lorsqu'il est disponible ; sinon, chaîne vide |

Le tableau couvre chaque page traitée d'un PDF et reste vide lorsqu'aucun code pris en charge n'est trouvé. Didit ne devine jamais un payload : des codes QR endommagés peuvent être renvoyés avec leur position et `parsed_payload: null`, tandis que les échecs de détection de codes ne font pas échouer la requête Document AI.

## Correspondance de nom

Marquez au plus un champ avec `is_full_name: true` et indiquez la personne ou l'entreprise à laquelle le document doit appartenir :

| Vous vérifiez | Envoyez | Le nom est comparé à |
| - | - | - |
| Une personne | `expected_first_name` + `expected_last_name` | le nom complet de la personne |
| Une entreprise | `expected_company_name` (+ `expected_company_country`, facultatif, code ISO à 2 lettres) | le nom de l'entreprise |

Envoyez l'un ou l'autre groupe, pas les deux. Lorsqu'un nom est extrait et qu'un nom attendu est présent, la réponse inclut `name_match_score` (de 0 à 100) et `name_on_document`. Un score inférieur à `document_ai_name_match_score_threshold` (par défaut `80`) déclenche l'avertissement `DOCUMENT_AI_NAME_MISMATCH` et applique `document_ai_name_mismatch_action`. Si aucun champ n'est marqué `is_full_name`, Didit utilise le premier champ de type texte dont la clé ressemble à un nom (`name`, `holder`, `full_name`, `account_holder`) ; marquez explicitement le champ lorsque vous voulez contrôler la valeur comparée. Lorsqu'aucune valeur de nom ne peut être trouvée sur le document, ou qu'aucun nom attendu n'est envoyé, la vérification du nom est ignorée et `name_match_score` vaut `null`.

## Risques et actions

Chaque risque ci-dessous correspond à un paramètre d'action que vous pouvez définir par requête : `DECLINE`, `REVIEW` ou `NO_ACTION`. L'action la plus stricte déclenchée détermine `document_ai.status` (`Declined` > `In Review` > `Approved`), et chaque risque déclenché est listé dans `document_ai.warnings`. Les valeurs par défaut correspondent à celles de l'étape de workflow hébergée ; consultez [Règles et avertissements Document AI](https://docs.didit.me/fr/core-technology/document-ai/warnings-document-ai.md) pour savoir comment chaque risque est détecté.

| Risque | Paramètre | Par défaut |
| - | - | - |
| `DOCUMENT_AI_UNREADABLE_DOCUMENT` | `document_ai_unreadable_document_action` | `REVIEW` |
| `DOCUMENT_AI_MISSING_REQUIRED_FIELDS` | `document_ai_missing_required_fields_action` | `REVIEW` |
| `DOCUMENT_AI_SUSPECTED_MANIPULATION` | `document_ai_document_tampering_action` | `DECLINE` |
| `DOCUMENT_AI_NAME_MISMATCH` | `document_ai_name_mismatch_action` | `NO_ACTION` |
| `DOCUMENT_AI_UNSUPPORTED_FILE` | `document_ai_unsupported_file_action` | `REVIEW` |

## Fichiers, sauvegarde et facturation

* **Fichiers acceptés :** PDF (plusieurs pages prises en charge), JPG, JPEG, PNG, TIFF, WebP, ou un ZIP contenant le document, jusqu'à 30 Mo. Les PDF chiffrés renvoient `400` ; importez à la place un PDF déchiffré ou une photo.
* **`save_api_request`** (par défaut `true`) : la requête, les champs extraits et les codes détectés apparaissent dans la Business Console, sous **Vérifications manuelles**, où votre équipe peut les examiner. Définissez-le sur `false` pour ne pas les sauvegarder.
* **`vendor_data`** et **`metadata`** sont renvoyés sans modification dans la réponse, afin que vous puissiez relier le résultat à vos propres enregistrements.
* **Prix :** \$0.20 par document, facturé lorsque le document est traité et qu'un statut est renvoyé. La remise de votre forfait s'applique. Voir [Tarifs](https://docs.didit.me/fr/getting-started/pricing.md#tarification-des-api-autonomes-serveur-à-serveur).

> [!NOTE]
>
> Vous avez besoin que l'utilisateur téléverse lui-même le document, que vous collectiez jusqu'à 3 documents dans un même parcours, que l'écran de téléversement soit traduit automatiquement, ou que vous croisiez les champs extraits avec des données d'identité ou de questionnaire ? Utilisez plutôt l'étape de workflow [Document AI](https://docs.didit.me/fr/core-technology/document-ai/overview.md) hébergée.


## OpenAPI

````yaml fr/openapi-25.json POST /v3/document-ai/
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/document-ai/:
    post:
      tags:
        - Standalone APIs
      summary: >-
        Document AI (extraction de champs personnalisés + vérifications de
        falsification)
      description: >-
        Extrayez les champs que **vous** définissez à partir de n'importe quel
        document justificatif — fiches de paie, justificatifs de fonds,
        attestations d'origine du patrimoine, avis d'imposition, contrats,
        factures — en un seul appel. Envoyez le fichier accompagné d'une
        définition `fields` (clé, nom, instruction d'extraction, type) et
        recevez des valeurs typées (`text`, `number`, `date` sous la forme
        `YYYY-MM-DD`), un score facultatif de correspondance de nom avec la
        personne ou l'entreprise attendue, des signaux d'altération PDF/EXIF, un
        `status` `Approved`/`Declined`/`In Review` et une liste `warnings`
        détaillant chaque problème détecté. Il s'agit de l'équivalent serveur à
        serveur de l'étape de workflow Document AI hébergée (voir
        `/core-technology/document-ai/overview`) ; il utilise le modèle
        d'extraction par défaut de Didit et ne permet pas de choisir le
        fournisseur.


        **Latence.** L'extraction repose sur un LLM : les appels classiques
        prennent **5 à 15 secondes**, les PDF multipages jusqu'à **~30
        secondes**. Configurez un délai d'expiration client d'**au moins 45
        secondes** et ne réessayez pas avant la fin de l'appel.


        **Format de la requête.** `multipart/form-data` uniquement. `fields` est
        **une chaîne de tableau encodée en JSON** (1 à 30 éléments, 16 Ko au
        maximum) ; les tableaux de formulaire au format crochets sont rejetés
        avec un code `400`. Chaque élément est `{key, name, instruction, type,
        required, is_full_name}` — `key` est la clé JSON retournée
        (`^[a-z0-9_]{1,64}$`, unique), `instruction` indique au modèle ce qu'il
        doit lire, `type` vaut `text` (par défaut) | `number` | `date`,
        `required` déclenche `DOCUMENT_AI_MISSING_REQUIRED_FIELDS` lorsque la
        valeur ne peut pas être lue, et au maximum un champ peut définir
        `is_full_name: true` pour indiquer la valeur comparée au nom attendu.


        **Correspondance de nom.** Transmettez `expected_first_name` +
        `expected_last_name` (personne physique) **ou** `expected_company_name`
        (+ facultatif `expected_company_country`) — mais pas les deux. La valeur
        du champ `is_full_name` (ou, si aucun n'est défini, le premier champ
        texte dont la clé ressemble à un nom) fait l'objet d'une comparaison
        approximative ; `name_match_score` (0–100) et `name_on_document` sont
        toujours retournés lorsque les deux côtés sont présents, et un score
        inférieur à `document_ai_name_match_score_threshold` (80 par défaut)
        ajoute `DOCUMENT_AI_NAME_MISMATCH`, régi par
        `document_ai_name_mismatch_action`.


        **Logique de décision.** Chaque risque est associé à une action
        configurable : `DECLINE` → `status: "Declined"`, `REVIEW` → `status: "In
        Review"`, `NO_ACTION` → informatif uniquement. Les valeurs par défaut
        reflètent l'étape de workflow : document illisible `REVIEW`, champs
        obligatoires manquants `REVIEW`, manipulation suspectée `DECLINE`,
        non-correspondance de nom `NO_ACTION`, fichier non pris en charge
        `REVIEW`. Un document qui ne peut pas du tout être traité renvoie `400`
        ; un document lisible mais problématique renvoie `200` avec
        `Declined`/`In Review` — vérifiez toujours `document_ai.status` et
        `document_ai.warnings`, et pas seulement le code HTTP.


        **Facturation.** Chaque réponse `200` consomme un crédit d'API Document
        AI — **$0.20** par document au tarif catalogue, déduit du crédit de
        votre forfait ou de votre solde de recharge. Lorsque le solde de
        l'organisation ne permet pas de couvrir l'appel, l'endpoint renvoie
        `403` avant tout traitement. Les clés API sandbox renvoient une réponse
        fictive déterministe et ne sont pas facturées.


        **Persistance.** Avec `save_api_request=true` (par défaut), l'appel est
        enregistré sous forme de session de type API : le document, les valeurs
        extraites, les définitions de champs et l'instantané de correspondance
        de nom sont conservés, la session apparaît dans la Business Console, et
        `request_id` fonctionne avec `GET /v3/session/{sessionId}/decision/`.
      operationId: post_v3document-ai
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
                - document
                - fields
              properties:
                document:
                  type: string
                  format: binary
                  description: >-
                    Le document à lire. Extensions autorisées : `tiff`, `jpg`,
                    `jpeg`, `png`, `pdf`, `webp`, `zip`. Taille maximale de
                    téléversement : **30 MB** (les fichiers plus volumineux sont
                    rejetés avec une erreur `400`). Les fichiers PDF multipages
                    sont pris en charge (prévoyez une latence plus élevée) ; les
                    PDF chiffrés sont rejetés avec une erreur `400`. Les images
                    sont automatiquement compressées avant le traitement.
                fields:
                  type: string
                  description: >-
                    Une chaîne de tableau encodée en JSON avec **1–30**
                    définitions de champs (maximum 16 KB). Chaque élément :
                    `key` (obligatoire, `^[a-z0-9_]{1,64}$`, unique — la clé
                    renvoyée dans `extracted_data`), `name` (≤ 100 caractères,
                    affiché dans les messages), `instruction` (≤ 500 caractères,
                    ce que le modèle doit lire), `type` (`text` par défaut |
                    `number` | `date`), `required` (booléen, par défaut
                    `false`), `is_full_name` (booléen, par défaut `false`, au
                    maximum un par requête). Les tableaux de formulaires de type
                    crochets, le JSON invalide, les clés dupliquées ou plus d'un
                    `is_full_name` renvoient `400` avant tout traitement ou
                    facturation.
                  example: >-
                    [{"key": "employer_name", "name": "Employer name",
                    "instruction": "The employer or company issuing the
                    payslip.", "type": "text", "required": true, "is_full_name":
                    false}, {"key": "employee_name", "name": "Employee name",
                    "instruction": "The full name of the employee as printed on
                    the payslip.", "type": "text", "required": true,
                    "is_full_name": true}, {"key": "net_pay", "name": "Net pay",
                    "instruction": "The net amount paid to the employee for the
                    period.", "type": "number", "required": true,
                    "is_full_name": false}, {"key": "pay_date", "name": "Pay
                    date", "instruction": "The payment date shown on the
                    payslip.", "type": "date", "required": false,
                    "is_full_name": false}]
                expected_first_name:
                  type: string
                  description: >-
                    Prénom à vérifier par rapport au champ du nom du document
                    (recherche floue, tenant compte de la translittération). À
                    utiliser conjointement avec `expected_last_name` ; ne peut
                    pas être combiné avec `expected_company_name`.
                  example: Sophia
                expected_last_name:
                  type: string
                  description: >-
                    Nom de famille à vérifier par rapport au champ du nom du
                    document, combiné avec `expected_first_name` pour
                    `name_match_score`.
                  example: Martinez
                expected_company_name:
                  type: string
                  description: >-
                    Nom de l'entreprise à vérifier par rapport au champ de nom
                    du document (pour les documents d'entreprise). Ne peut pas
                    être combiné avec `expected_first_name` /
                    `expected_last_name` (aucun exemple de valeur fourni
                    intentionnellement — l'exemple de requête généré utilise les
                    champs de personne physique ; remplacez-les par celui-ci
                    lors de la vérification d'un document d'entreprise).
                expected_company_country:
                  type: string
                  description: >-
                    Code pays ISO 3166-1 alpha-2 optionnel (deux lettres ASCII,
                    par ex. `FR`) qui ajuste la normalisation du nom de
                    l'entreprise pour `expected_company_name`. Utilisé
                    uniquement avec `expected_company_name`.
                document_ai_name_match_score_threshold:
                  type: integer
                  minimum: 0
                  maximum: 100
                  default: 80
                  description: >-
                    Valeur minimale de `name_match_score` (0–100) pour
                    considérer le nom comme correspondant. Un score inférieur
                    ajoute `DOCUMENT_AI_NAME_MISMATCH`, régi par
                    `document_ai_name_mismatch_action`.
                  example: 80
                document_ai_unreadable_document_action:
                  type: string
                  enum:
                    - DECLINE
                    - REVIEW
                    - NO_ACTION
                  default: REVIEW
                  description: >-
                    Ce qu'il faut faire lorsqu'aucun champ n'a pu être lu à
                    partir du document (`DOCUMENT_AI_UNREADABLE_DOCUMENT`).
                document_ai_missing_required_fields_action:
                  type: string
                  enum:
                    - DECLINE
                    - REVIEW
                    - NO_ACTION
                  default: REVIEW
                  description: >-
                    Ce qu'il faut faire lorsqu'au moins un champ `required` n'a
                    pas pu être lu (`DOCUMENT_AI_MISSING_REQUIRED_FIELDS` ;
                    `additional_data.missing_fields` les énumère).
                document_ai_document_tampering_action:
                  type: string
                  enum:
                    - DECLINE
                    - REVIEW
                    - NO_ACTION
                  default: DECLINE
                  description: >-
                    Ce qu'il faut faire lorsque l'analyse médico-légale PDF/EXIF
                    suspecte une manipulation — modification après signature
                    numérique, éditeur PDF connu, manipulation par texte
                    superposé, réexportation suspecte, anomalies d'image
                    (`DOCUMENT_AI_SUSPECTED_MANIPULATION`).
                document_ai_name_mismatch_action:
                  type: string
                  enum:
                    - DECLINE
                    - REVIEW
                    - NO_ACTION
                  default: NO_ACTION
                  description: >-
                    Ce qu'il faut faire lorsque `name_match_score` est inférieur
                    au seuil (`DOCUMENT_AI_NAME_MISMATCH`).
                document_ai_unsupported_file_action:
                  type: string
                  enum:
                    - DECLINE
                    - REVIEW
                    - NO_ACTION
                  default: REVIEW
                  description: >-
                    Que faire lorsque le fichier importé est vide ou n'est pas
                    un fichier traitable (`DOCUMENT_AI_UNSUPPORTED_FILE`).
                save_api_request:
                  type: boolean
                  default: true
                  description: >-
                    Lorsque la valeur est `true` (par défaut), conserve l'appel
                    sous la forme d'une session de type API — visible dans la
                    Business Console, récupérable via `GET
                    /v3/session/{sessionId}/decision/` à l'aide du `request_id`
                    renvoyé, avec le document importé, les valeurs extraites,
                    les définitions de champs et l'instantané de correspondance
                    de nom stockés. Lorsque la valeur est `false`, rien n'est
                    stocké et `request_id` est un UUID éphémère servant
                    uniquement à la corrélation des réponses ; l'appel reste
                    facturé.
                  example: true
                vendor_data:
                  type: string
                  description: >-
                    Chaîne opaque optionnelle (votre identifiant d'utilisateur
                    interne, e-mail, UUID…) stockée sur la session persistée et
                    renvoyée dans la réponse. Utilisez-la pour corréler les
                    appels d'API avec vos propres enregistrements et pour
                    filtrer les sessions ultérieurement.
                  example: user-123
                metadata:
                  type: object
                  additionalProperties: true
                  description: >-
                    Objet JSON optionnel stocké avec la session (lorsque
                    `save_api_request=true`) et renvoyé dans la réponse. Dans
                    les requêtes multipart, envoyez-le sous forme de champ
                    textuel encodé en JSON (par ex.
                    `metadata={"flow":"onboarding"}`) — il est analysé sous
                    forme d'objet.
                  example:
                    flow: onboarding
      responses:
        '200':
          description: >-
            Document traité. `document_ai.status` est `Approved`, `Declined` ou
            `In Review` ; chaque problème détecté est détaillé dans
            `document_ai.warnings`. Un document problématique renvoie tout de
            même `200` — inspectez le corps de la réponse, pas seulement le code
            HTTP. Lorsque `save_api_request=true`, `request_id` est
            l'identifiant de la session enregistrée.
          content:
            application/json:
              examples:
                Approved:
                  summary: Fiche de paie lue sans erreur, nom correspondant
                  value:
                    request_id: 9d6a3b1e-2f0c-4a8e-9b7d-1c2e3f4a5b6c
                    document_ai:
                      status: Approved
                      extracted_data:
                        employer_name: Acme Payments Ltd
                        employee_name: Sophia Martinez
                        net_pay: 3120.5
                        pay_date: '2026-07-31'
                      detected_codes:
                        - type: CODE_128
                          page_number: 1
                          position:
                            - - 96
                              - 456
                            - - 96
                              - 616
                            - - 1012
                              - 611
                            - - 1015
                              - 456
                          x: 96
                          'y': 456
                          width: 919
                          height: 160
                          page_width: 1200
                          page_height: 1700
                          raw_payload: SU5WLTIwMjYtMDA0Mg==
                          parsed_payload: INV-2026-0042
                      fields:
                        - key: employer_name
                          name: Employer name
                          instruction: The employer or company issuing the payslip.
                          type: text
                          required: true
                          is_full_name: false
                        - key: employee_name
                          name: Employee name
                          instruction: >-
                            The full name of the employee as printed on the
                            payslip.
                          type: text
                          required: true
                          is_full_name: true
                        - key: net_pay
                          name: Net pay
                          instruction: The net amount paid to the employee for the period.
                          type: number
                          required: true
                          is_full_name: false
                        - key: pay_date
                          name: Pay date
                          instruction: The payment date shown on the payslip.
                          type: date
                          required: false
                          is_full_name: false
                      name_match_score: 100
                      name_on_document: Sophia Martinez
                      warnings: []
                    vendor_data: user-123
                    metadata:
                      flow: onboarding
                    created_at: '2026-08-19T16:40:12.318Z'
                In Review — missing required field:
                  summary: '`net_pay` n''a pas pu être lu ; action par défaut REVIEW'
                  value:
                    request_id: 3f2a9c7b-5d4e-4b1a-8c6f-0e9d8c7b6a5f
                    document_ai:
                      status: In Review
                      extracted_data:
                        employer_name: Acme Payments Ltd
                        employee_name: Sophia Martinez
                        net_pay: null
                        pay_date: '2026-07-31'
                      detected_codes: []
                      fields:
                        - key: employer_name
                          name: Employer name
                          instruction: The employer or company issuing the payslip.
                          type: text
                          required: true
                          is_full_name: false
                        - key: employee_name
                          name: Employee name
                          instruction: >-
                            The full name of the employee as printed on the
                            payslip.
                          type: text
                          required: true
                          is_full_name: true
                        - key: net_pay
                          name: Net pay
                          instruction: The net amount paid to the employee for the period.
                          type: number
                          required: true
                          is_full_name: false
                        - key: pay_date
                          name: Pay date
                          instruction: The payment date shown on the payslip.
                          type: date
                          required: false
                          is_full_name: false
                      name_match_score: 100
                      name_on_document: Sophia Martinez
                      warnings:
                        - risk: DOCUMENT_AI_MISSING_REQUIRED_FIELDS
                          feature: DOCUMENT_AI
                          additional_data:
                            missing_fields:
                              - Net pay
                          log_type: warning
                          short_description: Required fields could not be extracted
                          long_description: >-
                            One or more fields configured as required could not
                            be extracted from the uploaded document.
                    vendor_data: null
                    metadata: null
                    created_at: '2026-08-19T16:41:05.002Z'
                Declined — suspected manipulation:
                  summary: >-
                    L'analyse forensique de PDF a signalé un éditeur connu ;
                    action par défaut DECLINE
                  value:
                    request_id: 7c1d2e3f-4a5b-4c6d-8e9f-0a1b2c3d4e5f
                    document_ai:
                      status: Declined
                      extracted_data:
                        employer_name: Acme Payments Ltd
                        employee_name: Sophia Martinez
                        net_pay: 3120.5
                        pay_date: '2026-07-31'
                      detected_codes: []
                      fields:
                        - key: employer_name
                          name: Employer name
                          instruction: The employer or company issuing the payslip.
                          type: text
                          required: true
                          is_full_name: false
                        - key: employee_name
                          name: Employee name
                          instruction: >-
                            The full name of the employee as printed on the
                            payslip.
                          type: text
                          required: true
                          is_full_name: true
                        - key: net_pay
                          name: Net pay
                          instruction: The net amount paid to the employee for the period.
                          type: number
                          required: true
                          is_full_name: false
                        - key: pay_date
                          name: Pay date
                          instruction: The payment date shown on the payslip.
                          type: date
                          required: false
                          is_full_name: false
                      name_match_score: null
                      name_on_document: Sophia Martinez
                      warnings:
                        - risk: DOCUMENT_AI_SUSPECTED_MANIPULATION
                          feature: DOCUMENT_AI
                          additional_data:
                            detection_method: known_editor
                            processed_by_known_editor: ilovepdf
                          log_type: error
                          short_description: Suspected document manipulation
                          long_description: >-
                            The system detected signs of potential manipulation
                            or editing in the uploaded document.
                    vendor_data: null
                    metadata: null
                    created_at: '2026-08-19T16:42:40.771Z'
              schema:
                type: object
                properties:
                  request_id:
                    type: string
                    format: uuid
                    description: "Identifiant de session persistante lorsque `save_api_request=true` (utilisable avec `GET /v3/session/{sessionId}/decision/`)\_; sinon un UUID de corrélation temporaire."
                  document_ai:
                    type: object
                    properties:
                      status:
                        type: string
                        enum:
                          - Approved
                          - Declined
                          - In Review
                        description: >-
                          `Approved` lorsqu'aucun avertissement n'aboutit à un
                          refus ou à un examen ; `Declined` lorsqu'au moins un
                          risque correspond à `DECLINE` ; sinon `In Review`
                          lorsqu'au moins un correspond à `REVIEW`. La liste
                          `warnings` en explique la raison.
                      extracted_data:
                        type: object
                        additionalProperties: true
                        description: >-
                          Une entrée par clé de champ demandée. Les valeurs sont
                          typées selon le `type` de champ — chaînes de
                          caractères pour `text`, nombres pour `number`, chaînes
                          `YYYY-MM-DD` pour `date` — ou `null` lorsque la valeur
                          n'a pas pu être lue.
                        example:
                          employer_name: Acme Payments Ltd
                          employee_name: Sophia Martinez
                          net_pay: 3120.5
                          pay_date: '2026-07-31'
                      detected_codes:
                        type: array
                        description: >-
                          Codes QR et codes-barres détectés sur chaque page
                          traitée. Vide si aucun n'est trouvé. Un code QR
                          localisé mais indécodable a `parsed_payload: null`.
                        items:
                          $ref: '#/components/schemas/DocumentAIDetectedCode'
                      fields:
                        type: array
                        description: >-
                          Les définitions de champs normalisées utilisées pour
                          cette extraction, renvoyées en écho (également
                          stockées avec la session lorsque
                          `save_api_request=true`).
                        items:
                          type: object
                          properties:
                            key:
                              type: string
                            name:
                              type: string
                            instruction:
                              type: string
                            type:
                              type: string
                              enum:
                                - text
                                - number
                                - date
                            required:
                              type: boolean
                            is_full_name:
                              type: boolean
                      name_match_score:
                        type: number
                        nullable: true
                        minimum: 0
                        maximum: 100
                        description: >-
                          Similarité approximative (0–100) entre
                          `name_on_document` et le nom attendu de la
                          personne/l'entreprise. Renvoyée dès lors qu'une valeur
                          de nom et un nom attendu existent tous deux — aussi
                          bien en cas de correspondance **qu'en cas** de
                          non-correspondance ; `null` sinon.
                        example: 96
                      name_on_document:
                        type: string
                        nullable: true
                        description: >-
                          La valeur de nom extraite du document (le champ
                          `is_full_name`, ou le premier champ de texte de type
                          nom) qui a été comparée au nom attendu. `null`
                          lorsqu'aucune valeur de nom n'était disponible.
                        example: Sophia Martinez
                      warnings:
                        type: array
                        description: >-
                          Vide en cas d'approbation sans réserve. Chaque entrée
                          explique un risque détecté ; les entrées avec
                          `log_type: "error"` définissent `status` sur
                          `Declined`, les entrées avec `log_type: "warning"` le
                          définissent sur `In Review`.
                        items:
                          type: object
                          properties:
                            risk:
                              type: string
                              enum:
                                - DOCUMENT_AI_UNREADABLE_DOCUMENT
                                - DOCUMENT_AI_MISSING_REQUIRED_FIELDS
                                - DOCUMENT_AI_SUSPECTED_MANIPULATION
                                - DOCUMENT_AI_NAME_MISMATCH
                                - DOCUMENT_AI_UNSUPPORTED_FILE
                              description: >-
                                Code de risque lisible par machine. Chacun est
                                régi par l'option de requête
                                `document_ai_*_action` correspondante.
                              example: DOCUMENT_AI_MISSING_REQUIRED_FIELDS
                            feature:
                              type: string
                              enum:
                                - DOCUMENT_AI
                              description: >-
                                Fonctionnalité ayant émis l'avertissement.
                                Toujours `DOCUMENT_AI` sur cet endpoint.
                            additional_data:
                              type: object
                              nullable: true
                              additionalProperties: true
                              description: >-
                                Contexte supplémentaire pour le risque :
                                `{"missing_fields": ["Net pay"]}` pour les
                                champs obligatoires manquants,
                                `{"name_on_document", "score", "threshold"}`
                                pour une discordance de nom, et le résultat
                                d'analyse médico-légale (`detection_method`,
                                détails sur le producteur/l'éditeur) pour une
                                suspicion de manipulation. `null` lorsqu'il n'y
                                a rien à ajouter.
                              example:
                                missing_fields:
                                  - Net pay
                            log_type:
                              type: string
                              enum:
                                - error
                                - warning
                                - information
                              description: >-
                                Gravité dérivée de l'action configurée : `error`
                                (DECLINE), `warning` (REVIEW), `information`
                                (NO_ACTION).
                            short_description:
                              type: string
                              description: >-
                                Description du risque en une ligne lisible par
                                l'utilisateur.
                              example: Required fields could not be extracted
                            long_description:
                              type: string
                              description: >-
                                Explication lisible par l'humain, interpolée
                                avec `additional_data` le cas échéant.
                  vendor_data:
                    type: string
                    nullable: true
                    description: Écho des `vendor_data` que vous avez envoyées, ou `null`.
                  metadata:
                    type: object
                    nullable: true
                    additionalProperties: true
                    description: >-
                      Écho de l'objet `metadata` que vous avez envoyé, ou
                      `null`.
                  created_at:
                    type: string
                    format: date-time
                    description: Moment où la requête a été traitée (UTC, ISO 8601).
        '400':
          description: >-
            Erreur de validation ou de traitement. Les problèmes au niveau des
            champs renvoient l'enveloppe standard de DRF (un tableau de messages
            par champ concerné). Les documents chiffrés ou illisibles et les
            échecs d'extraction renvoient `{"error": ...}`.
          content:
            application/json:
              examples:
                Missing document:
                  summary: '`document` n''est pas inclus dans les données du formulaire'
                  value:
                    document:
                      - No file was submitted.
                Invalid fields payload:
                  summary: >-
                    `fields` n'est pas une chaîne contenant un tableau encodé en
                    JSON
                  value:
                    fields:
                      - fields must be a valid JSON array string.
                Duplicate field keys:
                  summary: Deux éléments partagent la même `key`
                  value:
                    fields:
                      - Every fields item must have a unique key.
                Conflicting expectations:
                  summary: >-
                    Attentes concernant la personne et l'entreprise envoyées
                    conjointement
                  value:
                    non_field_errors:
                      - >-
                        Provide either person expected-name fields or
                        expected_company_name, not both.
                Invalid metadata:
                  summary: >-
                    `metadata` n'est pas un objet JSON (ou contient un caractère
                    NUL)
                  value:
                    metadata:
                      - Metadata must be a JSON object.
                Oversized vendor_data:
                  summary: "`vendor_data` dépasse la limite de 2\_000 octets"
                  value:
                    vendor_data:
                      - vendor_data is too long (maximum 2000 bytes).
                Encrypted PDF:
                  summary: PDF protégé par mot de passe
                  value:
                    error: >-
                      The PDF is encrypted. Please upload a decrypted PDF or a
                      photo instead.
                Extraction failed:
                  summary: Le pipeline d'extraction n'a pas pu traiter le document
                  value:
                    error:
                      - Error extracting Document AI information
              schema:
                type: object
                additionalProperties: true
        '403':
          description: "Permission refusée. Renvoyé lorsque l'en-tête `x-api-key` est manquant, ainsi que lorsque le solde de l'organisation appelante ne permet pas de couvrir l'appel\_; une clé incorrecte, révoquée ou expirée renvoie `401`. Une clé manquante renvoie `403` avec `{\"detail\": ...}`\_; une clé incorrecte, révoquée ou expirée renvoie `401`. Les crédits insuffisants (ainsi que les organisations facturées par contrat dont la grille tarifaire ne comporte aucune entrée pour l'API Document AI) renvoient `403` avec `{\"error\": ...}` avant tout traitement de document."
          content:
            application/json:
              examples:
                Missing or invalid API key:
                  summary: Aucun en-tête `x-api-key`, ou la clé est non valide/révoquée
                  value:
                    detail: You do not have permission to perform this action.
                Not enough credits:
                  summary: Le solde de l'organisation ne peut pas couvrir l'appel
                  value:
                    error: >-
                      You don't have enough credits to perform this request.
                      Please top up at https://business.didit.me
                Contract rate missing:
                  summary: >-
                    Organisation facturée sur contrat dont la grille tarifaire
                    ne comporte aucune entrée pour l'API Document AI
                  value:
                    error: >-
                      Document AI API is not included in your contract rate
                      card. Contact support@didit.me to enable it.
              schema:
                type: object
                properties:
                  detail:
                    type: string
                  error:
                    type: string
        '429':
          description: >-
            Limite de débit dépassée. Les endpoints POST/PATCH/DELETE partagent
            un budget de 300 requêtes d'écriture par minute par clé API (la
            création de transactions et l'ingestion d'événements ont un budget
            distinct de 600). La réponse comporte les en-têtes
            `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` et
            `Retry-After`.
          content:
            application/json:
              examples:
                Write rate limit exceeded:
                  summary: >-
                    Plus de 300 requêtes d'écriture au cours de la minute
                    actuelle
                  value:
                    detail: >-
                      Write request rate limit exceeded. You can make up to 300
                      requests per minute.
              schema:
                type: object
                properties:
                  detail:
                    type: string
        '503':
          $ref: '#/components/responses/DocumentProcessingBusy'
      security:
        - ApiKeyAuth: []
      x-codeSamples:
        - lang: python
          label: Python
          source: |-
            from didit import Didit

            # Reads DIDIT_API_KEY.
            client = Didit()

            result = client.document_ai.analyze(
                document=open("document.pdf", "rb"),
                fields="[{\"key\": \"employer_name\", \"name\": \"Employer name\", \"instruction\": \"The employer or company issuing the payslip.\", \"type\": \"text\", \"required\": true, \"is_full_name\": false}, {\"key\": \"employee_name\", \"name\": \"Employee name\", \"instruction\": \"The full name of the employee as printed on the payslip.\", \"type\": \"text\", \"required\": true, \"is_full_name\": true}, {\"key\": \"net_pay\", \"name\": \"Net pay\", \"instruction\": \"The net amount paid to the employee for the period.\", \"type\": \"number\", \"required\": true, \"is_full_name\": false}, {\"key\": \"pay_date\", \"name\": \"Pay date\", \"instruction\": \"The payment date shown on the payslip.\", \"type\": \"date\", \"required\": false, \"is_full_name\": false}]",
            )
            print(result)
        - lang: javascript
          label: Node.js
          source: |-
            import Didit, { fileFromPath } from "@didit-protocol/sdk-node";

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

            const result = await didit.documentAi.analyze({
              document: await fileFromPath("document.pdf"),
              fields: "[{\"key\": \"employer_name\", \"name\": \"Employer name\", \"instruction\": \"The employer or company issuing the payslip.\", \"type\": \"text\", \"required\": true, \"is_full_name\": false}, {\"key\": \"employee_name\", \"name\": \"Employee name\", \"instruction\": \"The full name of the employee as printed on the payslip.\", \"type\": \"text\", \"required\": true, \"is_full_name\": true}, {\"key\": \"net_pay\", \"name\": \"Net pay\", \"instruction\": \"The net amount paid to the employee for the period.\", \"type\": \"number\", \"required\": true, \"is_full_name\": false}, {\"key\": \"pay_date\", \"name\": \"Pay date\", \"instruction\": \"The payment date shown on the payslip.\", \"type\": \"date\", \"required\": false, \"is_full_name\": false}]",
            });
            console.log(result);
        - lang: go
          label: Go
          source: "ctx := context.Background()\n// Reads DIDIT_API_KEY.\nclient := didit.NewClient()\n\nresult, err := client.DocumentAi.Analyze(ctx, didit.DocumentAiAnalyzeParams{\n\tDocument: didit.FileFromPath(\"document.pdf\"),\n\tFields:   \"[{\\\"key\\\": \\\"employer_name\\\", \\\"name\\\": \\\"Employer name\\\", \\\"instruction\\\": \\\"The employer or company issuing the payslip.\\\", \\\"type\\\": \\\"text\\\", \\\"required\\\": true, \\\"is_full_name\\\": false}, {\\\"key\\\": \\\"employee_name\\\", \\\"name\\\": \\\"Employee name\\\", \\\"instruction\\\": \\\"The full name of the employee as printed on the payslip.\\\", \\\"type\\\": \\\"text\\\", \\\"required\\\": true, \\\"is_full_name\\\": true}, {\\\"key\\\": \\\"net_pay\\\", \\\"name\\\": \\\"Net pay\\\", \\\"instruction\\\": \\\"The net amount paid to the employee for the period.\\\", \\\"type\\\": \\\"number\\\", \\\"required\\\": true, \\\"is_full_name\\\": false}, {\\\"key\\\": \\\"pay_date\\\", \\\"name\\\": \\\"Pay date\\\", \\\"instruction\\\": \\\"The payment date shown on the payslip.\\\", \\\"type\\\": \\\"date\\\", \\\"required\\\": false, \\\"is_full_name\\\": false}]\",\n})\nif err != nil {\n\tlog.Fatal(err)\n}\nfmt.Printf(\"%+v\\n\", result)"
        - lang: java
          label: Java
          source: >-
            import java.nio.file.Path;

            import me.didit.Didit;

            import me.didit.types.DocumentAiAnalyzeParams;


            // Reads DIDIT_API_KEY.

            Didit client = new Didit();


            var result =
            client.documentAi().analyze(DocumentAiAnalyzeParams.builder()
                    .document(Path.of("document.pdf"))
                    .fields("[{\"key\": \"employer_name\", \"name\": \"Employer name\", \"instruction\": \"The employer or company issuing the payslip.\", \"type\": \"text\", \"required\": true, \"is_full_name\": false}, {\"key\": \"employee_name\", \"name\": \"Employee name\", \"instruction\": \"The full name of the employee as printed on the payslip.\", \"type\": \"text\", \"required\": true, \"is_full_name\": true}, {\"key\": \"net_pay\", \"name\": \"Net pay\", \"instruction\": \"The net amount paid to the employee for the period.\", \"type\": \"number\", \"required\": true, \"is_full_name\": false}, {\"key\": \"pay_date\", \"name\": \"Pay date\", \"instruction\": \"The payment date shown on the payslip.\", \"type\": \"date\", \"required\": false, \"is_full_name\": false}]")
                    .build());
            System.out.println(result);
        - lang: php
          label: PHP
          source: |-
            <?php

            require 'vendor/autoload.php';

            use Didit\DiditClient;
            use Didit\File;

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

            $result = $didit->documentAi->analyze([
                'document' => File::fromPath('document.pdf'),
                'fields' => '[{"key": "employer_name", "name": "Employer name", "instruction": "The employer or company issuing the payslip.", "type": "text", "required": true, "is_full_name": false}, {"key": "employee_name", "name": "Employee name", "instruction": "The full name of the employee as printed on the payslip.", "type": "text", "required": true, "is_full_name": true}, {"key": "net_pay", "name": "Net pay", "instruction": "The net amount paid to the employee for the period.", "type": "number", "required": true, "is_full_name": false}, {"key": "pay_date", "name": "Pay date", "instruction": "The payment date shown on the payslip.", "type": "date", "required": false, "is_full_name": false}]',
            ]);
            print_r($result->toArray());
        - lang: ruby
          label: Ruby
          source: |-
            require "didit"

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

            result = didit.document_ai.analyze(
              document: Pathname("document.pdf"),
              fields: "[{\"key\": \"employer_name\", \"name\": \"Employer name\", \"instruction\": \"The employer or company issuing the payslip.\", \"type\": \"text\", \"required\": true, \"is_full_name\": false}, {\"key\": \"employee_name\", \"name\": \"Employee name\", \"instruction\": \"The full name of the employee as printed on the payslip.\", \"type\": \"text\", \"required\": true, \"is_full_name\": true}, {\"key\": \"net_pay\", \"name\": \"Net pay\", \"instruction\": \"The net amount paid to the employee for the period.\", \"type\": \"number\", \"required\": true, \"is_full_name\": false}, {\"key\": \"pay_date\", \"name\": \"Pay date\", \"instruction\": \"The payment date shown on the payslip.\", \"type\": \"date\", \"required\": false, \"is_full_name\": false}]"
            )
            puts result.to_h
        - lang: csharp
          label: .NET
          source: >-
            using System;

            using System.Text.Json;

            using Didit;


            // Reads DIDIT_API_KEY.

            using var client = new DiditClient();


            var result = await client.DocumentAi.AnalyzeAsync(new
            DocumentAiAnalyzeParams

            {
                Document = FileUpload.FromPath("document.pdf"),
                Fields = "[{\"key\": \"employer_name\", \"name\": \"Employer name\", \"instruction\": \"The employer or company issuing the payslip.\", \"type\": \"text\", \"required\": true, \"is_full_name\": false}, {\"key\": \"employee_name\", \"name\": \"Employee name\", \"instruction\": \"The full name of the employee as printed on the payslip.\", \"type\": \"text\", \"required\": true, \"is_full_name\": true}, {\"key\": \"net_pay\", \"name\": \"Net pay\", \"instruction\": \"The net amount paid to the employee for the period.\", \"type\": \"number\", \"required\": true, \"is_full_name\": false}, {\"key\": \"pay_date\", \"name\": \"Pay date\", \"instruction\": \"The payment date shown on the payslip.\", \"type\": \"date\", \"required\": false, \"is_full_name\": false}]",
            });

            Console.WriteLine(JsonSerializer.Serialize(result));
components:
  schemas:
    DocumentAIDetectedCode:
      type: object
      required:
        - type
        - page_number
        - position
        - x
        - 'y'
        - width
        - height
        - page_width
        - page_height
        - raw_payload
        - parsed_payload
      properties:
        type:
          type: string
          description: >-
            Symbologie détectée, par exemple `QR_CODE`, `CODE_128`, `EAN_13`,
            `DATA_MATRIX` ou `PDF_417`.
        page_number:
          type: integer
          minimum: 1
          description: Numéro de page (à partir de 1).
        position:
          type: array
          nullable: true
          description: >-
            Polygone détecté en coordonnées de pixels de la page rendue. `null`
            lorsqu'aucun emplacement fiable n'est disponible.
          items:
            type: array
            minItems: 2
            maxItems: 2
            items:
              type: integer
        x:
          type: integer
          nullable: true
          description: Bord gauche de la boîte englobante en pixels de la page rendue.
        'y':
          type: integer
          nullable: true
          description: Bord supérieur de la boîte englobante en pixels de la page rendue.
        width:
          type: integer
          nullable: true
          description: Largeur de la boîte englobante en pixels de la page rendue.
        height:
          type: integer
          nullable: true
          description: Hauteur de la boîte englobante en pixels de la page rendue.
        page_width:
          type: integer
          description: Largeur de la page générée en pixels.
        page_height:
          type: integer
          description: Hauteur de la page générée en pixels.
        raw_payload:
          type: string
          description: >-
            Payload brut du décodeur sous forme de chaîne base64 lorsqu'il est
            disponible ; sinon chaîne vide.
        parsed_payload:
          nullable: true
          description: >-
            Chaîne décodée ou valeur structurée. `null` lorsque le code a été
            localisé mais n'a pas pu être décodé.
  responses:
    DocumentProcessingBusy:
      description: >-
        Un nombre trop important de documents est actuellement en cours de
        traitement, cet envoi n'a donc pas été traité : rien n'a été stocké et
        aucune tentative n'a été utilisée. Renvoyez le même fichier après le
        nombre de secondes indiqué dans `Retry-After`. Effectuez le branchement
        en fonction de `code`, et non du texte du message.
      headers:
        Retry-After:
          description: >-
            Nombre de secondes à attendre avant d'envoyer à nouveau la même
            requête.
          schema:
            type: integer
            example: 10
      content:
        application/json:
          schema:
            type: object
            required:
              - detail
              - code
            properties:
              detail:
                type: string
              code:
                type: string
                enum:
                  - document_processing_busy
          example:
            detail: We are busy processing documents. Please try again in a moment.
            code: document_processing_busy
  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.