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

# Document AI API: estrazione di dati personalizzata

> Didit Document AI: POST /v3/document-ai/ estrae campi personalizzati da PDF e immagini con controlli di manomissione. $0.20 per documento.

Invia un documento e l'elenco dei campi da leggere al suo interno. Didit esegue il modello di estrazione predefinito sul file, restituisce ogni campo con il proprio tipo (`text`, `number` oppure `date` nel formato `YYYY-MM-DD`), rileva e decodifica i codici QR e a barre supportati, controlla il file per individuare eventuali manipolazioni e, facoltativamente, confronta il nome presente sul documento con la persona o l'azienda che ti aspetti. Usalo per prova dei fondi, buste paga, estratti conto bancari, documenti fiscali, fatture o qualsiasi documento che non segue un modello fisso.

Questa è la versione server-to-server del passaggio [Document AI](https://docs.didit.me/it/core-technology/document-ai/overview.md) ospitato. La richiesta è `multipart/form-data`: il file va nel campo `document`, mentre le definizioni dei campi vanno nel campo `fields` come una singola stringa con un array JSON.

## Definire i campi da estrarre

`fields` è un array JSON di 1-30 elementi, inviato come una singola stringa. Ogni elemento ha una `key` (lettere minuscole, cifre e underscore, univoca all'interno della richiesta), un `name` leggibile, una `instruction` che indica al modello cosa cercare, un `type` (`text`, `number` oppure `date`) e due flag: `required` e `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
  }
]
```

In una richiesta multipart, l'intero array è un unico valore del form:

```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 risposta riporta i `fields` normalizzati e restituisce `extracted_data` indicizzato in base ai tuoi valori `key`, con `null` per tutto ciò che il modello non è riuscito a trovare. `name`, `instruction` e `type` sono facoltativi per ogni elemento (`type` ha come valore predefinito `text`); `key` è obbligatoria. Se invii `fields` come chiavi di form in stile parentesi (`fields[0][key]=...`) oppure come qualsiasi formato diverso da un array JSON, ricevi `400` prima che l'estrazione venga avviata, quindi nulla viene fatturato.

## Codici QR e codici a barre

Ogni risposta riuscita include `document_ai.detected_codes`. Ogni elemento contiene:

| Campo | Descrizione |
| - | - |
| `type` | Simbologia rilevata, come `QR_CODE`, `CODE_128`, `EAN_13`, `DATA_MATRIX` o `PDF_417` |
| `page_number` | Numero di pagina a partire da 1 |
| `position` | Poligono rilevato in pixel della pagina renderizzata, oppure `null` quando non è disponibile una posizione affidabile |
| `x`, `y`, `width`, `height` | Riquadro di delimitazione in pixel della pagina renderizzata, oppure `null` quando non è disponibile una posizione affidabile |
| `page_width`, `page_height` | Dimensioni della pagina renderizzata usate dalle coordinate |
| `parsed_payload` | Stringa decodificata o valore strutturato, oppure `null` quando il codice può essere solo individuato |
| `raw_payload` | Payload grezzo restituito dal decoder come stringa base64, quando disponibile; altrimenti una stringa vuota |

L'array copre tutte le pagine elaborate di un PDF ed è vuoto quando non viene trovato alcun codice supportato. Didit non indovina mai un payload: i codici QR danneggiati possono essere restituiti con la loro posizione e `parsed_payload: null`, mentre gli errori nel rilevamento dei codici non fanno fallire la richiesta Document AI.

## Confronto dei nomi

Contrassegna al massimo un campo con `is_full_name: true` e invia il nome della persona o dell'azienda a cui ti aspetti che appartenga il documento:

| Stai verificando | Invia | Il nome viene confrontato con |
| - | - | - |
| Una persona | `expected_first_name` + `expected_last_name` | il nome completo della persona |
| Un'azienda | `expected_company_name` (+ `expected_company_country`, facoltativo, un codice ISO a 2 lettere) | il nome dell'azienda |

Invia un gruppo oppure l'altro, non entrambi. Quando un nome viene estratto e ne è presente uno atteso, la risposta include `name_match_score` (da 0 a 100) e `name_on_document`. Un punteggio inferiore a `document_ai_name_match_score_threshold` (predefinito `80`) genera l'avviso `DOCUMENT_AI_NAME_MISMATCH` e applica `document_ai_name_mismatch_action`. Se nessun campo è contrassegnato come `is_full_name`, Didit usa come ripiego il primo campo di testo la cui chiave sembra un nome (`name`, `holder`, `full_name`, `account_holder`); contrassegna esplicitamente il campo quando vuoi controllare quale valore viene confrontato. Quando non si trova alcun valore di nome sul documento, oppure non viene inviato alcun nome atteso, il controllo sul nome viene saltato e `name_match_score` è `null`.

## Rischi e azioni

Ogni rischio qui sotto corrisponde a un parametro di azione che puoi impostare per richiesta: `DECLINE`, `REVIEW` oppure `NO_ACTION`. L'azione più restrittiva tra quelle attivate determina `document_ai.status` (`Declined` > `In Review` > `Approved`), e ogni rischio attivato è elencato in `document_ai.warnings`. I valori predefiniti corrispondono a quelli del passaggio del workflow ospitato; consulta [Regole e avvisi di Document AI](https://docs.didit.me/it/core-technology/document-ai/warnings-document-ai.md) per sapere come viene rilevato ciascun rischio.

| Rischio | Parametro | Predefinito |
| - | - | - |
| `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` |

## File, salvataggio e fatturazione

* **File accettati:** PDF (sono supportati più pagine), JPG, JPEG, PNG, TIFF, WebP oppure un ZIP contenente il documento, fino a 30 MB. I PDF cifrati restituiscono `400`; carica un PDF decifrato oppure una foto.
* **`save_api_request`** (predefinito `true`): la richiesta, i campi estratti e i codici rilevati compaiono nella Business Console sotto **Controlli Manuali**, dove il tuo team può verificarli. Impostalo su `false` per non salvarli.
* **`vendor_data`** e **`metadata`** vengono restituiti invariati nella risposta, così puoi collegare il risultato ai tuoi record.
* **Prezzo:** \$0.20 per documento, fatturato quando il documento viene elaborato e viene restituito uno stato. Si applica lo sconto del tuo piano. Consulta [Prezzi](https://docs.didit.me/it/getting-started/pricing.md#prezzi-delle-api-autonome-server-to-server).

> [!NOTE]
>
> Devi far caricare il documento direttamente all'utente, raccogliere fino a 3 documenti in un unico flusso, tradurre automaticamente la schermata di caricamento oppure incrociare i campi estratti con i dati di un documento d'identità o di un questionario? Usa invece il [passaggio del workflow Document AI](https://docs.didit.me/it/core-technology/document-ai/overview.md) ospitato.


## OpenAPI

````yaml it/openapi-25.json POST /v3/document-ai/
openapi: 3.0.0
info:
  version: 3.0.0
  title: Didit Verification API
  description: API di verifica dell'identità. Autenticati con l'intestazione x-api-key.
servers:
  - url: https://verification.didit.me
security: []
tags: []
paths:
  /v3/document-ai/:
    post:
      tags:
        - Standalone APIs
      summary: >-
        Document AI (estrazione di campi personalizzati + verifiche di
        manomissione)
      description: >-
        Estrai i campi definiti da **te** da qualsiasi documento di supporto —
        buste paga, prova dei fondi, lettere sull'origine del patrimonio,
        dichiarazioni fiscali, contratti, fatture — in un'unica chiamata. Invia
        il file insieme a una definizione di `fields` (chiave, nome, istruzione
        di estrazione, tipo) e ricevi valori tipizzati (`text`, `number`, `date`
        nel formato `YYYY-MM-DD`), un punteggio opzionale di corrispondenza del
        nome rispetto alla persona o all'azienda prevista, segnali di
        manomissione PDF/EXIF, uno `status` pari a `Approved`/`Declined`/`In
        Review` e un elenco `warnings` che spiega ogni problema riscontrato.
        Questa è la controparte server-to-server del passaggio di workflow
        Document AI ospitato (vedi `/core-technology/document-ai/overview`);
        utilizza il modello di estrazione predefinito di Didit e non espone la
        selezione del provider.


        **Latenza.** L'estrazione è basata su LLM: le chiamate tipiche
        richiedono **5–15 secondi**, i PDF multipagina fino a **~30 secondi**.
        Configura un timeout del client di **almeno 45 secondi** e non
        effettuare un nuovo tentativo prima del completamento della chiamata.


        **Formato della richiesta.** Solo `multipart/form-data`. `fields` è
        **una singola stringa di array codificata in JSON** (1–30 elementi,
        massimo 16 KB); gli array di moduli in stile parentesi quadre vengono
        rifiutati con `400`. Ogni elemento è `{key, name, instruction, type,
        required, is_full_name}` — `key` è la chiave JSON restituita
        (`^[a-z0-9_]{1,64}$`, univoca), `instruction` indica al modello cosa
        leggere, `type` è `text` (predefinito) | `number` | `date`, `required`
        genera `DOCUMENT_AI_MISSING_REQUIRED_FIELDS` quando il valore non può
        essere letto e al massimo un campo può impostare `is_full_name: true`
        per contrassegnare il valore da confrontare con il nome previsto.


        **Corrispondenza del nome.** Passa `expected_first_name` +
        `expected_last_name` (persona) **oppure** `expected_company_name` (+
        l'opzionale `expected_company_country`) — non entrambi. Il valore del
        campo `is_full_name` (o, se nessuno è contrassegnato, il primo campo di
        testo la cui chiave sembra un nome) viene confrontato in modo
        approssimativo; `name_match_score` (0–100) e `name_on_document` vengono
        sempre restituiti quando entrambi i lati esistono, e un punteggio
        inferiore a `document_ai_name_match_score_threshold` (predefinito 80)
        aggiunge `DOCUMENT_AI_NAME_MISMATCH`, regolato da
        `document_ai_name_mismatch_action`.


        **Logica decisionale.** Ogni rischio è mappato a un'azione
        configurabile: `DECLINE` → `status: "Declined"`, `REVIEW` → `status: "In
        Review"`, `NO_ACTION` → solo informativo. I valori predefiniti
        rispecchiano il passaggio del workflow: documento illeggibile `REVIEW`,
        campi obbligatori mancanti `REVIEW`, sospetta manipolazione `DECLINE`,
        mancata corrispondenza del nome `NO_ACTION`, file non supportato
        `REVIEW`. Un documento che non può essere elaborato affatto restituisce
        `400`; un documento leggibile ma problematico restituisce `200` con
        `Declined`/`In Review` — verifica sempre `document_ai.status` e
        `document_ai.warnings`, non solo il codice HTTP.


        **Fatturazione.** Ogni risposta `200` consuma un credito API Document AI
        — **$0.20 per documento** al prezzo di listino, scalati dal credito del
        tuo piano o dal saldo ricaricato. Quando il saldo dell'organizzazione
        non può coprire la chiamata, l'endpoint restituisce `403` prima di
        qualsiasi elaborazione. Le chiavi API sandbox restituiscono una risposta
        simulata deterministica e non vengono addebitate.


        **Persistenza.** Con `save_api_request=true` (predefinito) la chiamata
        viene memorizzata come sessione di tipo API: il documento, i valori
        estratti, le definizioni dei campi e lo snapshot della corrispondenza
        del nome vengono conservati, la sessione compare nella Business Console
        e `request_id` funziona con `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: >-
                    Il documento da leggere. Estensioni consentite: `tiff`,
                    `jpg`, `jpeg`, `png`, `pdf`, `webp`, `zip`. Dimensione
                    massima di caricamento: **30 MB** (i file più grandi vengono
                    rifiutati con un `400`). I PDF multipagina sono supportati
                    (prevedere una latenza maggiore); i PDF crittografati
                    vengono rifiutati con un `400`. Le immagini vengono
                    compresse automaticamente prima dell'elaborazione.
                fields:
                  type: string
                  description: >-
                    Una stringa contenente un array codificato in JSON con
                    **1–30** definizioni di campo (massimo 16 KB). Ogni
                    elemento: `key` (obbligatorio, `^[a-z0-9_]{1,64}$`, univoco
                    — la chiave restituita in `extracted_data`), `name` (≤100
                    caratteri, mostrato nei messaggi), `instruction` (≤500
                    caratteri, ciò che il modello deve leggere), `type` (`text`
                    predefinito | `number` | `date`), `required` (booleano,
                    predefinito `false`), `is_full_name` (booleano, predefinito
                    `false`, al massimo uno per richiesta). Gli array di form in
                    stile parentesi quadre, JSON non valido, chiavi duplicate o
                    più di un `is_full_name` restituiscono `400` prima di
                    qualsiasi elaborazione o fatturazione.
                  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: >-
                    Nome da verificare rispetto al campo del nome del documento
                    (confronto fuzzy, tiene conto della traslitterazione). Da
                    utilizzare insieme a `expected_last_name`; non può essere
                    combinato con `expected_company_name`.
                  example: Sophia
                expected_last_name:
                  type: string
                  description: >-
                    Cognome da verificare rispetto al campo del nome del
                    documento, combinato con `expected_first_name` per
                    `name_match_score`.
                  example: Martinez
                expected_company_name:
                  type: string
                  description: >-
                    Ragione sociale da verificare rispetto al campo del nome del
                    documento (per documenti aziendali). Non può essere
                    combinata con `expected_first_name` / `expected_last_name`
                    (nessun valore di esempio intenzionalmente: l'esempio di
                    richiesta generato utilizza i campi della persona fisica;
                    sostituiscili con questo quando verifichi un documento
                    aziendale).
                expected_company_country:
                  type: string
                  description: >-
                    Codice paese opzionale ISO 3166-1 alpha-2 (due lettere
                    ASCII, ad es. `FR`) che regola la normalizzazione del nome
                    dell'azienda per `expected_company_name`. Utilizzato solo
                    con `expected_company_name`.
                document_ai_name_match_score_threshold:
                  type: integer
                  minimum: 0
                  maximum: 100
                  default: 80
                  description: >-
                    Valore minimo di `name_match_score` (0–100) affinché il nome
                    sia considerato corrispondente. Un punteggio inferiore
                    aggiunge `DOCUMENT_AI_NAME_MISMATCH`, regolato da
                    `document_ai_name_mismatch_action`.
                  example: 80
                document_ai_unreadable_document_action:
                  type: string
                  enum:
                    - DECLINE
                    - REVIEW
                    - NO_ACTION
                  default: REVIEW
                  description: >-
                    Cosa fare quando non è stato possibile leggere alcun campo
                    dal documento (`DOCUMENT_AI_UNREADABLE_DOCUMENT`).
                document_ai_missing_required_fields_action:
                  type: string
                  enum:
                    - DECLINE
                    - REVIEW
                    - NO_ACTION
                  default: REVIEW
                  description: >-
                    Cosa fare quando non è stato possibile leggere almeno un
                    campo `required` (`DOCUMENT_AI_MISSING_REQUIRED_FIELDS`;
                    `additional_data.missing_fields` li elenca).
                document_ai_document_tampering_action:
                  type: string
                  enum:
                    - DECLINE
                    - REVIEW
                    - NO_ACTION
                  default: DECLINE
                  description: >-
                    Cosa fare quando l'analisi forense di PDF/EXIF sospetta una
                    manipolazione — modificato dopo la firma digitale, editor
                    PDF noto, manipolazione del testo in sovrimpressione,
                    riesportazione sospetta, anomalie dell'immagine
                    (`DOCUMENT_AI_SUSPECTED_MANIPULATION`).
                document_ai_name_mismatch_action:
                  type: string
                  enum:
                    - DECLINE
                    - REVIEW
                    - NO_ACTION
                  default: NO_ACTION
                  description: >-
                    Cosa fare quando `name_match_score` è inferiore alla soglia
                    (`DOCUMENT_AI_NAME_MISMATCH`).
                document_ai_unsupported_file_action:
                  type: string
                  enum:
                    - DECLINE
                    - REVIEW
                    - NO_ACTION
                  default: REVIEW
                  description: >-
                    Cosa fare quando il caricamento è vuoto o non è un file
                    elaborabile (`DOCUMENT_AI_UNSUPPORTED_FILE`).
                save_api_request:
                  type: boolean
                  default: true
                  description: >-
                    Quando è `true` (predefinito), rende persistente la chiamata
                    come sessione di tipo API — visibile nella Business Console,
                    recuperabile tramite `GET /v3/session/{sessionId}/decision/`
                    utilizzando il `request_id` restituito, con il documento
                    caricato, i valori estratti, le definizioni dei campi e lo
                    snapshot di corrispondenza dei nomi archiviati. Quando è
                    `false`, non viene memorizzato nulla e `request_id` è un
                    UUID temporaneo utilizzabile solo per la correlazione della
                    risposta; la chiamata viene comunque fatturata.
                  example: true
                vendor_data:
                  type: string
                  description: >-
                    Stringa opaca opzionale (il tuo ID utente interno, email,
                    UUID...) memorizzata nella sessione persistita e restituita
                    nella risposta. Usala per correlare le chiamate API con i
                    tuoi record e per filtrare le sessioni in un secondo
                    momento.
                  example: user-123
                metadata:
                  type: object
                  additionalProperties: true
                  description: >-
                    Oggetto JSON opzionale memorizzato con la sessione (quando
                    `save_api_request=true`) e restituito nella risposta. Nelle
                    richieste multipart, invialo come campo stringa con codifica
                    JSON (ad es. `metadata={"flow":"onboarding"}`): viene
                    interpretato come oggetto.
                  example:
                    flow: onboarding
      responses:
        '200':
          description: >-
            Documento elaborato. `document_ai.status` è `Approved`, `Declined` o
            `In Review`; ogni problema rilevato è dettagliato in
            `document_ai.warnings`. Un documento problematico restituisce
            comunque `200` — esamina il corpo, non solo il codice HTTP. Quando
            `save_api_request=true`, `request_id` è l'ID di sessione persistito.
          content:
            application/json:
              examples:
                Approved:
                  summary: Busta paga letta correttamente, nome corrispondente
                  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: Impossibile leggere `net_pay`; azione predefinita 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'analisi forense del PDF ha rilevato un editor noto; azione
                    predefinita 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: >-
                      ID della sessione salvata quando `save_api_request=true`
                      (utilizzabile con `GET
                      /v3/session/{sessionId}/decision/`); altrimenti un UUID di
                      correlazione temporaneo.
                  document_ai:
                    type: object
                    properties:
                      status:
                        type: string
                        enum:
                          - Approved
                          - Declined
                          - In Review
                        description: >-
                          `Approved` quando nessun avviso si traduce in un
                          rifiuto o in una revisione; `Declined` quando almeno
                          un rischio corrisponde a `DECLINE`; altrimenti `In
                          Review` quando almeno uno corrisponde a `REVIEW`.
                          L'elenco `warnings` ne spiega il motivo.
                      extracted_data:
                        type: object
                        additionalProperties: true
                        description: >-
                          Una voce per ogni chiave di campo richiesta. I valori
                          sono tipizzati in base al `type` del campo — stringhe
                          per `text`, numeri per `number`, stringhe `YYYY-MM-DD`
                          per `date` — oppure `null` quando non è stato
                          possibile leggere il valore.
                        example:
                          employer_name: Acme Payments Ltd
                          employee_name: Sophia Martinez
                          net_pay: 3120.5
                          pay_date: '2026-07-31'
                      detected_codes:
                        type: array
                        description: >-
                          Codici QR e codici a barre rilevati in ogni pagina
                          elaborata. Vuoto se non ne viene trovato nessuno. Un
                          codice QR individuato ma non decodificabile ha
                          `parsed_payload: null`.
                        items:
                          $ref: '#/components/schemas/DocumentAIDetectedCode'
                      fields:
                        type: array
                        description: >-
                          Le definizioni dei campi normalizzate usate per questa
                          estrazione, restituite come eco (memorizzate anche con
                          la sessione quando `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: >-
                          Somiglianza approssimativa (0–100) tra
                          `name_on_document` e il nome atteso della
                          persona/azienda. Restituita ogni volta che esistono
                          sia un valore per il nome sia un nome atteso, sia per
                          le corrispondenze **sia** per le non corrispondenze;
                          `null` altrimenti.
                        example: 96
                      name_on_document:
                        type: string
                        nullable: true
                        description: >-
                          Il valore del nome estratto dal documento (il campo
                          `is_full_name` o il primo campo di testo simile a un
                          nome) confrontato con il nome previsto. `null` se
                          nessun valore del nome era disponibile.
                        example: Sophia Martinez
                      warnings:
                        type: array
                        description: >-
                          Vuoto in caso di approvazione senza anomalie. Ogni
                          voce illustra un rischio rilevato; le voci con
                          `log_type: "error"` impostano `status` su `Declined`,
                          le voci con `log_type: "warning"` lo impostano su `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: >-
                                Codice di rischio leggibile dalla macchina.
                                Ognuno è regolato dalla corrispondente opzione
                                di richiesta `document_ai_*_action`.
                              example: DOCUMENT_AI_MISSING_REQUIRED_FIELDS
                            feature:
                              type: string
                              enum:
                                - DOCUMENT_AI
                              description: >-
                                Funzionalità che ha generato l'avviso. Sempre
                                `DOCUMENT_AI` su questo endpoint.
                            additional_data:
                              type: object
                              nullable: true
                              additionalProperties: true
                              description: >-
                                Contesto aggiuntivo per il rischio:
                                `{"missing_fields": ["Net pay"]}` per i campi
                                obbligatori mancanti, `{"name_on_document",
                                "score", "threshold"}` per una mancata
                                corrispondenza del nome e il riscontro forense
                                (`detection_method`, dettagli del
                                produttore/editor) per sospetta manipolazione.
                                `null` quando non c'è nulla da aggiungere.
                              example:
                                missing_fields:
                                  - Net pay
                            log_type:
                              type: string
                              enum:
                                - error
                                - warning
                                - information
                              description: >-
                                Gravità derivata dall'azione configurata:
                                `error` (DECLINE), `warning` (REVIEW),
                                `information` (NO_ACTION).
                            short_description:
                              type: string
                              description: >-
                                Sintesi su una riga leggibile dall'utente per il
                                rischio.
                              example: Required fields could not be extracted
                            long_description:
                              type: string
                              description: >-
                                Spiegazione di facile lettura, interpolata con
                                `additional_data` ove pertinente.
                  vendor_data:
                    type: string
                    nullable: true
                    description: Eco del `vendor_data` inviato, o `null`.
                  metadata:
                    type: object
                    nullable: true
                    additionalProperties: true
                    description: Eco dell'oggetto `metadata` inviato, o `null`.
                  created_at:
                    type: string
                    format: date-time
                    description: Quando la richiesta è stata elaborata (UTC, ISO 8601).
        '400':
          description: >-
            Errore di validazione o di elaborazione. I problemi a livello di
            campo restituiscono l'envelope standard di DRF (un array di messaggi
            per ciascun campo interessato). I documenti crittografati o
            illeggibili e gli errori di estrazione restituiscono `{"error":
            ...}`.
          content:
            application/json:
              examples:
                Missing document:
                  summary: '`document` non incluso nei dati del modulo'
                  value:
                    document:
                      - No file was submitted.
                Invalid fields payload:
                  summary: >-
                    `fields` non è un'unica stringa contenente un array
                    codificato in JSON
                  value:
                    fields:
                      - fields must be a valid JSON array string.
                Duplicate field keys:
                  summary: Due elementi condividono la stessa `key`
                  value:
                    fields:
                      - Every fields item must have a unique key.
                Conflicting expectations:
                  summary: Dati attesi per persona e azienda inviati insieme
                  value:
                    non_field_errors:
                      - >-
                        Provide either person expected-name fields or
                        expected_company_name, not both.
                Invalid metadata:
                  summary: >-
                    `metadata` non è un oggetto JSON (o contiene un carattere
                    NUL)
                  value:
                    metadata:
                      - Metadata must be a JSON object.
                Oversized vendor_data:
                  summary: '`vendor_data` supera il limite di 2.000 byte'
                  value:
                    vendor_data:
                      - vendor_data is too long (maximum 2000 bytes).
                Encrypted PDF:
                  summary: PDF protetto da password
                  value:
                    error: >-
                      The PDF is encrypted. Please upload a decrypted PDF or a
                      photo instead.
                Extraction failed:
                  summary: >-
                    La pipeline di estrazione non ha potuto elaborare il
                    documento
                  value:
                    error:
                      - Error extracting Document AI information
              schema:
                type: object
                additionalProperties: true
        '403':
          description: >-
            Autorizzazione negata. Restituito quando l'intestazione `x-api-key`
            è mancante e anche quando il saldo dell'organizzazione chiamante non
            è sufficiente a coprire la chiamata; una chiave errata, revocata o
            scaduta restituisce `401`. Una chiave mancante restituisce `403` con
            `{"detail": ...}`; una chiave errata, revocata o scaduta restituisce
            `401`. Crediti insufficienti – e organizzazioni con fatturazione
            contrattuale il cui listino prezzi non include una voce per l'API
            Document AI – restituiscono `403` con `{"error": ...}` prima che
            avvenga qualsiasi elaborazione dei documenti.
          content:
            application/json:
              examples:
                Missing or invalid API key:
                  summary: >-
                    Nessuna intestazione `x-api-key`, oppure la chiave non è
                    valida/revocata
                  value:
                    detail: You do not have permission to perform this action.
                Not enough credits:
                  summary: >-
                    Il saldo dell'organizzazione non è sufficiente per coprire
                    la chiamata
                  value:
                    error: >-
                      You don't have enough credits to perform this request.
                      Please top up at https://business.didit.me
                Contract rate missing:
                  summary: >-
                    Organizzazione con fatturazione a contratto il cui listino
                    prezzi non include una voce per 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 di frequenza superato. Gli endpoint POST/PATCH/DELETE
            condividono un budget di 300 richieste di scrittura al minuto per
            chiave API (la creazione di transazioni e l'ingestione di eventi
            hanno un budget separato di 600). La risposta contiene le
            intestazioni `X-RateLimit-Limit`, `X-RateLimit-Remaining`,
            `X-RateLimit-Reset` e `Retry-After`.
          content:
            application/json:
              examples:
                Write rate limit exceeded:
                  summary: Più di 300 richieste di scrittura nel minuto corrente
                  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: >-
            Simbologia rilevata, ad esempio `QR_CODE`, `CODE_128`, `EAN_13`,
            `DATA_MATRIX` o `PDF_417`.
        page_number:
          type: integer
          minimum: 1
          description: Numero di pagina a partire da 1.
        position:
          type: array
          nullable: true
          description: >-
            Poligono rilevato nelle coordinate in pixel della pagina
            renderizzata. `null` quando non è disponibile alcuna posizione
            affidabile.
          items:
            type: array
            minItems: 2
            maxItems: 2
            items:
              type: integer
        x:
          type: integer
          nullable: true
          description: >-
            Bordo sinistro del riquadro di delimitazione in pixel della pagina
            renderizzata.
        'y':
          type: integer
          nullable: true
          description: >-
            Bordo superiore del riquadro di delimitazione in pixel della pagina
            renderizzata.
        width:
          type: integer
          nullable: true
          description: >-
            Larghezza del riquadro di delimitazione in pixel della pagina
            renderizzata.
        height:
          type: integer
          nullable: true
          description: >-
            Altezza del riquadro di delimitazione in pixel della pagina
            renderizzata.
        page_width:
          type: integer
          description: Larghezza della pagina renderizzata in pixel.
        page_height:
          type: integer
          description: Altezza della pagina renderizzata in pixel.
        raw_payload:
          type: string
          description: >-
            Payload non elaborato del decodificatore come stringa base64 quando
            disponibile; altrimenti una stringa vuota.
        parsed_payload:
          nullable: true
          description: >-
            Stringa decodificata o valore strutturato. `null` quando il codice è
            stato individuato ma non è stato possibile decodificarlo.
  responses:
    DocumentProcessingBusy:
      description: >-
        Al momento sono in fase di elaborazione troppi documenti, pertanto
        questo caricamento non è stato elaborato: non è stato memorizzato nulla
        e non è stato utilizzato alcun tentativo. Invia nuovamente lo stesso
        file dopo il numero di secondi indicato in `Retry-After`. Basa la logica
        su `code`, non sul testo del messaggio.
      headers:
        Retry-After:
          description: >-
            Secondi da attendere prima di inviare nuovamente la stessa
            richiesta.
          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 chiave API della tua applicazione, da Developers -> API keys nella
        Business Console. La chiave primaria ha accesso completo. Una chiave con
        nome può avere un ambito definito: nessuno, lettura o scrittura per
        risorsa, limitata ad alcuni workflow o a sessioni approvate, a un elenco
        di indirizzi IP e a una data di scadenza. 401 indica che la chiave è
        errata, revocata o scaduta; 403 indica che non è stata inviata alcuna
        chiave, che la chiave non ha accesso a questa risorsa o azione, oppure
        che la richiesta proviene da un indirizzo non incluso nel suo elenco IP;
        404 su una route di sessione indica che la sessione non rientra nei
        workflow o negli stati previsti dalla chiave. Una chiave senza accesso
        ai media riceve gli URL di immagini, video e PDF come null, e una chiave
        senza permessi di scrittura sulle sessioni riceve link e token di
        sessione come null. Consulta https://docs.didit.me/console/api-keys.

````

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