> ## 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 Document AI: extração personalizada de documentos

> Didit: POST /v3/document-ai/ extrai campos definidos de qualquer PDF ou imagem, com verificação de nomes e adulteração. $0.20 por documento.

Envie um documento e a lista dos campos que pretende ler nele. A Didit aplica o seu modelo de extração predefinido ao ficheiro, devolve todos os campos tipificados (`text`, `number` ou `date` como `YYYY-MM-DD`), deteta e descodifica os códigos QR e de barras suportados, verifica se o ficheiro foi manipulado e, opcionalmente, compara o nome que consta do documento com a pessoa ou empresa que espera. Use-o para comprovativos de fundos, recibos de vencimento, extratos bancários, documentos fiscais, faturas ou qualquer documento que não se enquadre num modelo fixo.

Esta é a versão entre servidores do passo [Document AI](https://docs.didit.me/pt/core-technology/document-ai/overview.md) alojado. O pedido é `multipart/form-data`: o ficheiro vai em `document` e as definições dos campos vão em `fields`, como uma única string que contém um array JSON codificado.

## Definir os campos a extrair

`fields` é um array JSON de 1 a 30 itens, enviado como uma única string. Cada item tem uma `key` (letras minúsculas, dígitos e underscores, única dentro do pedido), um `name` legível para humanos, uma `instruction` que indica ao modelo o que procurar, um `type` (`text`, `number` ou `date`) e duas flags: `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
  }
]
```

Num pedido multipart, o array inteiro é um único valor de formulário:

```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"
```

A resposta repete os `fields` normalizados e devolve `extracted_data` com as suas chaves `key`, com `null` em qualquer campo que o modelo não tenha conseguido encontrar. `name`, `instruction` e `type` são opcionais em cada item (`type` assume `text` por predefinição); `key` é obrigatória. Enviar `fields` como chaves de formulário em notação de parênteses retos (`fields[0][key]=...`) ou em qualquer formato que não seja um array JSON devolve `400` antes de a extração ser executada, pelo que nada é faturado.

## Códigos QR e códigos de barras

Todas as respostas bem-sucedidas incluem `document_ai.detected_codes`. Cada item contém:

| Campo | Descrição |
| - | - |
| `type` | Simbologia detetada, como `QR_CODE`, `CODE_128`, `EAN_13`, `DATA_MATRIX` ou `PDF_417` |
| `page_number` | Número da página, começando em 1 |
| `position` | Polígono detetado em pixels da página renderizada, ou `null` quando não está disponível uma localização fiável |
| `x`, `y`, `width`, `height` | Caixa delimitadora em pixels da página renderizada, ou `null` quando não está disponível uma localização fiável |
| `page_width`, `page_height` | Dimensões da página renderizada usadas pelas coordenadas |
| `parsed_payload` | String descodificada ou valor estruturado, ou `null` quando o código só pode ser localizado |
| `raw_payload` | Payload bruto do descodificador em formato base64, quando disponível; caso contrário, uma string vazia |

O array abrange todas as páginas processadas de um PDF e fica vazio quando não é encontrado nenhum código suportado. A Didit nunca adivinha um payload: os códigos QR danificados podem ser devolvidos com a respetiva localização e `parsed_payload: null`, e as falhas na deteção de códigos não fazem falhar o pedido de Document AI.

## Correspondência de nomes

Marque no máximo um campo com `is_full_name: true` e envie a pessoa ou empresa que espera que seja a titular do documento:

| Está a verificar | Envie | O nome é comparado com |
| - | - | - |
| Uma pessoa | `expected_first_name` + `expected_last_name` | o nome completo da pessoa |
| Uma empresa | `expected_company_name` (+ opcionalmente `expected_company_country`, um código ISO de 2 letras) | o nome da empresa |

Envie um grupo ou outro, nunca ambos. Quando um nome é extraído e está presente um nome esperado, a resposta inclui `name_match_score` (0 a 100) e `name_on_document`. Uma pontuação abaixo de `document_ai_name_match_score_threshold` (predefinição `80`) gera o aviso `DOCUMENT_AI_NAME_MISMATCH` e aplica `document_ai_name_mismatch_action`. Se nenhum campo estiver marcado com `is_full_name`, a Didit recorre ao primeiro campo de texto cuja chave pareça um nome (`name`, `holder`, `full_name`, `account_holder`); marque o campo explicitamente se quiser controlar qual o valor comparado. Quando não é encontrado nenhum valor de nome no documento, ou não é enviado nenhum nome esperado, a verificação de nome é ignorada e `name_match_score` é `null`.

## Riscos e ações

Cada risco abaixo corresponde a um parâmetro de ação que pode definir por pedido: `DECLINE`, `REVIEW` ou `NO_ACTION`. A ação mais restritiva acionada determina `document_ai.status` (`Declined` > `In Review` > `Approved`), e todos os riscos acionados são listados em `document_ai.warnings`. As predefinições correspondem às do passo do fluxo alojado; consulte [Regras e avisos do Document AI](https://docs.didit.me/pt/core-technology/document-ai/warnings-document-ai.md) para saber como é detetado cada risco.

| Risco | Parâmetro | Predefinição |
| - | - | - |
| `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` |

## Ficheiros, gravação e faturação

* **Ficheiros aceites:** PDF (são suportados PDFs com várias páginas), JPG, JPEG, PNG, TIFF, WebP ou um ZIP que contenha o documento, até 30 MB. PDFs encriptados devolvem `400`; carregue um PDF desencriptado ou uma fotografia.
* **`save_api_request`** (predefinição `true`): o pedido, os campos extraídos e os códigos detetados aparecem na Business Console, em **Verificações manuais**, onde a sua equipa pode rever tudo. Defina-o como `false` para não gravar.
* **`vendor_data`** e **`metadata`** são devolvidos sem alterações na resposta, para que possa associar o resultado aos seus próprios registos.
* **Preço:** \$0.20 por documento, faturado quando o documento é processado e é devolvido um estado. O desconto do seu plano aplica-se. Consulte [Preços](https://docs.didit.me/pt/getting-started/pricing.md#preços-de-api-autónoma-servidor-a-servidor).

> [!NOTE]
>
> Precisa de que o utilizador carregue ele próprio o documento, de recolher até 3 documentos num único fluxo, de traduzir automaticamente o ecrã de carregamento ou de cruzar os campos extraídos com dados de identidade ou de questionários? Use, em vez disso, o passo de [fluxo Document AI](https://docs.didit.me/pt/core-technology/document-ai/overview.md) alojado.


## OpenAPI

````yaml pt/openapi-25.json POST /v3/document-ai/
openapi: 3.0.0
info:
  version: 3.0.0
  title: Didit Verification API
  description: API de Verificação de identidade. Autentique-se com o cabeçalho x-api-key.
servers:
  - url: https://verification.didit.me
security: []
tags: []
paths:
  /v3/document-ai/:
    post:
      tags:
        - Standalone APIs
      summary: >-
        Document AI (extração de campos personalizados + verificações de
        adulteração)
      description: >-
        Extraia os campos que **você** definir de qualquer documento
        comprovativo — recibos de vencimento, comprovativo de fundos, cartas
        sobre a origem do património, declarações fiscais, contratos, faturas —
        numa única chamada. Envie o ficheiro juntamente com uma definição de
        `fields` (chave, nome, instrução de extração, tipo) e receba valores
        tipados (`text`, `number`, `date` como `YYYY-MM-DD`), uma pontuação
        opcional de correspondência de nome com a pessoa ou empresa esperada,
        sinais de adulteração de PDF/EXIF, um `status` `Approved`/`Declined`/`In
        Review`, e uma lista de `warnings` a explicar cada problema encontrado.
        Esta é a contrapartida servidor a servidor do passo do fluxo Document AI
        alojado (consulte `/core-technology/document-ai/overview`); utiliza o
        modelo de extração predefinido da Didit e não expõe a seleção de
        fornecedor.


        **Latência.** A extração é baseada em LLM: as chamadas típicas demoram
        **5–15 segundos**, PDFs de várias páginas até **~30 segundos**.
        Configure um tempo limite do cliente de **pelo menos 45 segundos** e não
        tente novamente antes de a chamada ser concluída.


        **Formato do pedido.** Apenas `multipart/form-data`. `fields` é **uma
        string de array codificada em JSON** (1–30 itens, no máximo 16 KB);
        arrays de formulário no formato de parênteses retos são rejeitados com
        `400`. Cada item é `{key, name, instruction, type, required,
        is_full_name}` — `key` é a chave JSON devolvida (`^[a-z0-9_]{1,64}$`,
        única), `instruction` indica ao modelo o que ler, `type` é `text`
        (predefinição) | `number` | `date`, `required` gera
        `DOCUMENT_AI_MISSING_REQUIRED_FIELDS` quando o valor não pode ser lido,
        e no máximo um campo pode definir `is_full_name: true` para marcar o
        valor comparado com o nome esperado.


        **Correspondência de nomes.** Passe `expected_first_name` +
        `expected_last_name` (pessoa) **ou** `expected_company_name` (+
        opcionalmente `expected_company_country`) — não ambos. O valor do campo
        `is_full_name` (ou, quando nenhum estiver marcado, o primeiro campo de
        texto cuja chave se assemelhe a um nome) é comparado de forma
        aproximada; `name_match_score` (0–100) e `name_on_document` são sempre
        devolvidos quando ambos os lados existirem, e uma pontuação abaixo de
        `document_ai_name_match_score_threshold` (predefinição 80) adiciona
        `DOCUMENT_AI_NAME_MISMATCH`, controlado por
        `document_ai_name_mismatch_action`.


        **Lógica de decisão.** Cada risco mapeia para uma ação configurável:
        `DECLINE` → `status: "Declined"`, `REVIEW` → `status: "In Review"`,
        `NO_ACTION` → apenas informativo. As predefinições refletem o passo do
        fluxo: documento ilegível `REVIEW`, campos obrigatórios em falta
        `REVIEW`, suspeita de manipulação `DECLINE`, discrepância de nome
        `NO_ACTION`, ficheiro não suportado `REVIEW`. Um documento que não possa
        de todo ser processado devolve `400`; um documento legível mas com
        problemas devolve `200` com `Declined`/`In Review` — inspecione sempre
        `document_ai.status` e `document_ai.warnings`, e não apenas o código
        HTTP.


        **Faturação.** Cada resposta `200` consome um crédito da API de Document
        AI — **$0.20 por documento** ao preço de tabela, pagos a partir do
        crédito do seu plano ou do saldo de carregamento. Quando o saldo da
        organização não cobrir a chamada, o endpoint devolve `403` antes de
        qualquer processamento. As chaves de API da sandbox devolvem uma
        resposta simulada determinística e não são cobradas.


        **Persistência.** Com `save_api_request=true` (predefinição), a chamada
        é armazenada como uma sessão do tipo API: o documento, os valores
        extraídos, as definições de campos e o instantâneo da correspondência de
        nomes são guardados, a sessão aparece na Business Console e o
        `request_id` funciona com `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: >-
                    O documento a ler. Extensões permitidas: `tiff`, `jpg`,
                    `jpeg`, `png`, `pdf`, `webp`, `zip`. Tamanho máximo de
                    carregamento: **30 MB** (ficheiros maiores são rejeitados
                    com `400`). São suportados PDFs de várias páginas (espere
                    uma latência superior); PDFs encriptados são rejeitados com
                    `400`. As imagens são comprimidas automaticamente antes do
                    processamento.
                fields:
                  type: string
                  description: >-
                    Uma cadeia de caracteres que representa um array codificado
                    em JSON com **1–30** definições de campos (máximo de 16 KB).
                    Cada item: `key` (obrigatório, `^[a-z0-9_]{1,64}$`,
                    exclusivo — a chave devolvida em `extracted_data`), `name`
                    (≤100 caracteres, apresentado nas mensagens), `instruction`
                    (≤500 caracteres, o que o modelo deve ler), `type` (`text`
                    predefinido | `number` | `date`), `required` (booleano,
                    predefinição `false`), `is_full_name` (booleano,
                    predefinição `false`, no máximo um por pedido). Arrays de
                    formulário com estilo de parênteses retos, JSON inválido,
                    chaves duplicadas ou mais do que um `is_full_name` devolvem
                    `400` antes de qualquer processamento ou faturação.
                  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: >-
                    Primeiro nome a verificar contra o campo do nome do
                    documento (correspondência difusa, sensível a
                    transliteração). Utilize em conjunto com
                    `expected_last_name`; não pode ser combinado com
                    `expected_company_name`.
                  example: Sophia
                expected_last_name:
                  type: string
                  description: >-
                    Apelido a verificar face ao campo de nome do documento,
                    combinado com `expected_first_name` para `name_match_score`.
                  example: Martinez
                expected_company_name:
                  type: string
                  description: >-
                    Nome da empresa a verificar contra o campo de nome do
                    documento (para documentos de empresas). Não pode ser
                    combinado com `expected_first_name` / `expected_last_name`
                    (intencionalmente sem valor de exemplo — a amostra de pedido
                    gerada utiliza os campos de pessoa; substitua-os por este ao
                    verificar um documento de empresa).
                expected_company_country:
                  type: string
                  description: >-
                    Código de país ISO 3166-1 alpha-2 opcional (duas letras
                    ASCII, por exemplo, `FR`) que ajusta a normalização do nome
                    da empresa para `expected_company_name`. Utilizado apenas
                    com `expected_company_name`.
                document_ai_name_match_score_threshold:
                  type: integer
                  minimum: 0
                  maximum: 100
                  default: 80
                  description: >-
                    Valor mínimo de `name_match_score` (0–100) para considerar
                    que o nome coincide. Uma pontuação inferior adiciona
                    `DOCUMENT_AI_NAME_MISMATCH`, regulado por
                    `document_ai_name_mismatch_action`.
                  example: 80
                document_ai_unreadable_document_action:
                  type: string
                  enum:
                    - DECLINE
                    - REVIEW
                    - NO_ACTION
                  default: REVIEW
                  description: >-
                    O que fazer quando nenhum campo puder ser lido do documento
                    (`DOCUMENT_AI_UNREADABLE_DOCUMENT`).
                document_ai_missing_required_fields_action:
                  type: string
                  enum:
                    - DECLINE
                    - REVIEW
                    - NO_ACTION
                  default: REVIEW
                  description: >-
                    O que fazer quando não for possível ler pelo menos um campo
                    `required` (`DOCUMENT_AI_MISSING_REQUIRED_FIELDS`;
                    `additional_data.missing_fields` lista-os).
                document_ai_document_tampering_action:
                  type: string
                  enum:
                    - DECLINE
                    - REVIEW
                    - NO_ACTION
                  default: DECLINE
                  description: >-
                    O que fazer quando a análise forense de PDF/EXIF suspeitar
                    de manipulação — modificado após assinatura digital, editor
                    de PDF conhecido, manipulação de texto sobreposto,
                    reexportação suspeita, anomalias na imagem
                    (`DOCUMENT_AI_SUSPECTED_MANIPULATION`).
                document_ai_name_mismatch_action:
                  type: string
                  enum:
                    - DECLINE
                    - REVIEW
                    - NO_ACTION
                  default: NO_ACTION
                  description: >-
                    O que fazer quando `name_match_score` estiver abaixo do
                    limiar (`DOCUMENT_AI_NAME_MISMATCH`).
                document_ai_unsupported_file_action:
                  type: string
                  enum:
                    - DECLINE
                    - REVIEW
                    - NO_ACTION
                  default: REVIEW
                  description: >-
                    O que fazer quando o carregamento está vazio ou não é um
                    ficheiro processável (`DOCUMENT_AI_UNSUPPORTED_FILE`).
                save_api_request:
                  type: boolean
                  default: true
                  description: >-
                    Quando for `true` (predefinição), persiste a chamada como
                    uma sessão do tipo API — visível na Business Console,
                    recuperável através de `GET
                    /v3/session/{sessionId}/decision/` utilizando o `request_id`
                    devolvido, com o documento carregado, os valores extraídos,
                    as definições de campos e o instantâneo da correspondência
                    de nomes armazenados. Quando for `false`, nada é armazenado
                    e `request_id` é um UUID transitório apenas para correlação
                    de respostas; a chamada continua a ser faturada.
                  example: true
                vendor_data:
                  type: string
                  description: >-
                    String opaca opcional (o seu ID de utilizador interno,
                    e-mail, UUID...) guardada na sessão persistida e devolvida
                    na resposta. Utilize-a para correlacionar chamadas de API
                    com os seus próprios registos e para filtrar sessões mais
                    tarde.
                  example: user-123
                metadata:
                  type: object
                  additionalProperties: true
                  description: >-
                    Objeto JSON opcional armazenado com a sessão (quando
                    `save_api_request=true`) e devolvido na resposta. Em pedidos
                    multipart, envie-o como um campo de texto codificado em JSON
                    (por exemplo, `metadata={"flow":"onboarding"}`) — é
                    convertido num objeto.
                  example:
                    flow: onboarding
      responses:
        '200':
          description: >-
            Documento processado. `document_ai.status` é `Approved`, `Declined`
            ou `In Review`; cada problema detetado é discriminado em
            `document_ai.warnings`. Um documento com problemas continua a
            devolver `200` — inspecione o corpo, não apenas o código HTTP.
            Quando `save_api_request=true`, `request_id` é o id da sessão
            persistida.
          content:
            application/json:
              examples:
                Approved:
                  summary: Recibo de vencimento lido com sucesso, nome correspondente
                  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: não foi possível ler `net_pay`; ação predefinida 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: >-
                    A análise forense de PDF sinalizou um editor conhecido; ação
                    predefinida 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 da sessão persistida quando `save_api_request=true`
                      (utilizável com `GET /v3/session/{sessionId}/decision/`);
                      caso contrário, um UUID de correlação transitório.
                  document_ai:
                    type: object
                    properties:
                      status:
                        type: string
                        enum:
                          - Approved
                          - Declined
                          - In Review
                        description: >-
                          `Approved` quando nenhum aviso resultar numa recusa ou
                          revisão; `Declined` quando pelo menos um risco mapear
                          para `DECLINE`; caso contrário, `In Review` quando
                          pelo menos um mapear para `REVIEW`. A lista `warnings`
                          explica o motivo.
                      extracted_data:
                        type: object
                        additionalProperties: true
                        description: >-
                          Uma entrada por chave de campo solicitada. Os valores
                          são tipados de acordo com o `type` do campo — strings
                          para `text`, números para `number`, strings
                          `YYYY-MM-DD` para `date` — ou `null` quando o valor
                          não pôde ser lido.
                        example:
                          employer_name: Acme Payments Ltd
                          employee_name: Sophia Martinez
                          net_pay: 3120.5
                          pay_date: '2026-07-31'
                      detected_codes:
                        type: array
                        description: >-
                          Códigos QR e códigos de barras detetados em todas as
                          páginas processadas. Vazio quando nenhum é encontrado.
                          Um código QR localizado mas não descodificável tem
                          `parsed_payload: null`.
                        items:
                          $ref: '#/components/schemas/DocumentAIDetectedCode'
                      fields:
                        type: array
                        description: >-
                          As definições normalizadas dos campos utilizadas para
                          esta extração, devolvidas na resposta (também
                          armazenadas com a sessão 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: >-
                          Semelhança difusa (0–100) entre `name_on_document` e o
                          nome esperado da pessoa/empresa. Devolvido sempre que
                          existirem tanto um valor de nome como um nome esperado
                          — em correspondências **e** não correspondências;
                          `null` caso contrário.
                        example: 96
                      name_on_document:
                        type: string
                        nullable: true
                        description: >-
                          O valor do nome extraído do documento (o campo
                          `is_full_name`, ou o primeiro campo de texto
                          semelhante a um nome) que foi comparado com o nome
                          esperado. `null` quando nenhum valor de nome estava
                          disponível.
                        example: Sophia Martinez
                      warnings:
                        type: array
                        description: >-
                          Vazio numa aprovação limpa. Cada entrada explica um
                          risco detetado; entradas com `log_type: "error"`
                          definem `status` como `Declined`, entradas com
                          `log_type: "warning"` definem-no como `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: >-
                                Código de risco legível por máquina. Cada um é
                                determinado pela opção correspondente no pedido
                                `document_ai_*_action`.
                              example: DOCUMENT_AI_MISSING_REQUIRED_FIELDS
                            feature:
                              type: string
                              enum:
                                - DOCUMENT_AI
                              description: >-
                                Funcionalidade que gerou o aviso. Sempre
                                `DOCUMENT_AI` neste endpoint.
                            additional_data:
                              type: object
                              nullable: true
                              additionalProperties: true
                              description: >-
                                Contexto adicional para o risco:
                                `{"missing_fields": ["Net pay"]}` para campos
                                obrigatórios em falta, `{"name_on_document",
                                "score", "threshold"}` para uma discrepância de
                                nome e a conclusão forense (`detection_method`,
                                detalhes do produtor/editor) para suspeita de
                                manipulação. `null` quando não houver nada a
                                acrescentar.
                              example:
                                missing_fields:
                                  - Net pay
                            log_type:
                              type: string
                              enum:
                                - error
                                - warning
                                - information
                              description: >-
                                Gravidade derivada da ação configurada: `error`
                                (DECLINE), `warning` (REVIEW), `information`
                                (NO_ACTION).
                            short_description:
                              type: string
                              description: >-
                                Resumo de uma linha legível por humanos para o
                                risco.
                              example: Required fields could not be extracted
                            long_description:
                              type: string
                              description: >-
                                Explicação legível, interpolada com
                                `additional_data` quando relevante.
                  vendor_data:
                    type: string
                    nullable: true
                    description: Eco do `vendor_data` que enviou, ou `null`.
                  metadata:
                    type: object
                    nullable: true
                    additionalProperties: true
                    description: Eco do objeto `metadata` que enviou, ou `null`.
                  created_at:
                    type: string
                    format: date-time
                    description: Quando o pedido foi processado (UTC, ISO 8601).
        '400':
          description: >-
            Erro de validação ou de processamento. Problemas ao nível do campo
            devolvem o envelope padrão do DRF (um array de mensagens por campo
            afetado). Documentos encriptados ou ilegíveis e falhas de extração
            devolvem `{"error": ...}`.
          content:
            application/json:
              examples:
                Missing document:
                  summary: '`document` não incluído nos dados do formulário'
                  value:
                    document:
                      - No file was submitted.
                Invalid fields payload:
                  summary: '`fields` não é uma string de array codificada em JSON'
                  value:
                    fields:
                      - fields must be a valid JSON array string.
                Duplicate field keys:
                  summary: Dois itens partilham a mesma `key`
                  value:
                    fields:
                      - Every fields item must have a unique key.
                Conflicting expectations:
                  summary: Valores esperados de pessoa e empresa enviados em conjunto
                  value:
                    non_field_errors:
                      - >-
                        Provide either person expected-name fields or
                        expected_company_name, not both.
                Invalid metadata:
                  summary: '`metadata` não é um objeto JSON (ou contém um caráter NUL)'
                  value:
                    metadata:
                      - Metadata must be a JSON object.
                Oversized vendor_data:
                  summary: '`vendor_data` acima do limite de 2.000 bytes'
                  value:
                    vendor_data:
                      - vendor_data is too long (maximum 2000 bytes).
                Encrypted PDF:
                  summary: PDF protegido por palavra-passe
                  value:
                    error: >-
                      The PDF is encrypted. Please upload a decrypted PDF or a
                      photo instead.
                Extraction failed:
                  summary: O pipeline de extração não conseguiu processar o documento
                  value:
                    error:
                      - Error extracting Document AI information
              schema:
                type: object
                additionalProperties: true
        '403':
          description: >-
            Permissão negada. Devolvido quando o cabeçalho `x-api-key` está em
            falta e também quando o saldo da organização que efetua a chamada
            não consegue cobrir a chamada; uma chave incorreta, revogada ou
            expirada devolve `401`. Uma chave em falta devolve `403` com
            `{"detail": ...}`; uma chave incorreta, revogada ou expirada devolve
            `401`. A insuficiência de créditos — e organizações com faturação
            por contrato cuja tabela de preços não tenha uma entrada para a
            Document AI API — devolvem `403` com `{"error": ...}` antes de
            qualquer processamento de documentos ocorrer.
          content:
            application/json:
              examples:
                Missing or invalid API key:
                  summary: Sem cabeçalho `x-api-key`, ou a chave é inválida/revogada
                  value:
                    detail: You do not have permission to perform this action.
                Not enough credits:
                  summary: O saldo da organização não cobre a chamada
                  value:
                    error: >-
                      You don't have enough credits to perform this request.
                      Please top up at https://business.didit.me
                Contract rate missing:
                  summary: >-
                    Organização faturada por contrato cuja tabela de preços não
                    tem entrada para a API do 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 pedidos excedido. Os endpoints POST/PATCH/DELETE partilham
            um orçamento de 300 pedidos de escrita por minuto por chave de API
            (as criações de transações e a ingestão de eventos têm um orçamento
            separado de 600). A resposta inclui os cabeçalhos
            `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` e
            `Retry-After`.
          content:
            application/json:
              examples:
                Write rate limit exceeded:
                  summary: Mais de 300 pedidos de escrita no minuto atual
                  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 detetada, por exemplo `QR_CODE`, `CODE_128`, `EAN_13`,
            `DATA_MATRIX` ou `PDF_417`.
        page_number:
          type: integer
          minimum: 1
          description: Número de página de base 1.
        position:
          type: array
          nullable: true
          description: >-
            Polígono detetado em coordenadas de píxeis da página renderizada.
            `null` quando não está disponível nenhuma localização fiável.
          items:
            type: array
            minItems: 2
            maxItems: 2
            items:
              type: integer
        x:
          type: integer
          nullable: true
          description: >-
            Margem esquerda da caixa delimitadora em píxeis da página
            renderizada.
        'y':
          type: integer
          nullable: true
          description: >-
            Limite superior da caixa delimitadora em píxeis da página
            renderizada.
        width:
          type: integer
          nullable: true
          description: Largura da caixa delimitadora em píxeis da página renderizada.
        height:
          type: integer
          nullable: true
          description: Altura da caixa delimitadora em píxeis da página renderizada.
        page_width:
          type: integer
          description: Largura da página renderizada em píxeis.
        page_height:
          type: integer
          description: Altura da página renderizada em píxeis.
        raw_payload:
          type: string
          description: >-
            Payload do descodificador em bruto como string base64 quando
            disponível; caso contrário, uma string vazia.
        parsed_payload:
          nullable: true
          description: >-
            String descodificada ou valor estruturado. `null` quando o código
            foi localizado mas não pôde ser descodificado.
  responses:
    DocumentProcessingBusy:
      description: >-
        Estão a ser processados demasiados documentos neste momento, pelo que
        este carregamento não foi processado: nada foi armazenado e nenhuma
        tentativa foi utilizada. Envie o mesmo ficheiro novamente após o número
        de segundos em `Retry-After`. Ramifique com base no `code`, não no texto
        da mensagem.
      headers:
        Retry-After:
          description: Segundos a aguardar antes de enviar o mesmo pedido novamente.
          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: >-
        A chave de API da sua aplicação, a partir de Programadores -> Chaves de
        API na Business Console. A chave principal tem acesso total. Uma chave
        nomeada pode ter o âmbito definido: nenhum, leitura ou escrita por
        recurso, limitada a alguns fluxos ou a sessões aprovadas, a uma lista de
        endereços IP e a uma data de validade. 401 significa que a chave está
        incorreta, revogada ou expirada; 403 significa que nenhuma chave foi
        enviada, que a chave não tem acesso a este recurso ou ação, ou que o
        pedido veio de um endereço fora da sua lista de IP; 404 numa rota de
        sessão significa que a sessão está fora dos fluxos ou estados da chave.
        Uma chave sem acesso a multimédia recebe URLs de imagens, vídeos e PDFs
        como nulos, e uma chave sem permissão de escrita em sessões recebe
        ligações de sessão e tokens como nulos. Consulte
        https://docs.didit.me/console/api-keys.

````

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