> ## 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 para criar fluxo: construir fluxo KYC

> Crie por API um fluxo de verificação da Didit com funcionalidades ordenadas: KYC, KYB, AML e biometria. Pague por chamada, sem contratos.



Prompt da API para criar fluxo
        Copiar o prompt para a área de transferência



      
      

\<pre>

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

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

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

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

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

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

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

\</pre>



      
      

Colar em

Use este endpoint para criar fluxos através da API sem construir você mesmo os nós do grafo do fluxo. Envie um array `features` pela ordem em que os utilizadores devem concluir as verificações. A Didit converte-o internamente num fluxo baseado em nós e adiciona automaticamente o passo final de estado.Este endpoint `POST /v3/workflows/` cria apenas fluxos **simples e lineares**, com uma lista plana `features`. Para **ramificação, encaminhamento condicional, Document AI e nós de recusa ou decisão**, use o [formato de grafo](#ramificação-e-fluxos-baseados-em-nós-grafo) descrito abaixo, que é o que alimenta o construtor visual da consola e as ferramentas de fluxo do [Didit MCP](https://docs.didit.me/pt/integration/mcp/overview.md).Consulte as [Configurações de funcionalidades do fluxo](https://docs.didit.me/pt/management-api/workflows/feature-configs.md) para ver todos os campos `config` aceites por cada funcionalidade.## Fluxo KYC básico```json theme={null}
{
  "workflow_label": "Standard KYC",
  "features": [
    {
      "feature": "OCR",
      "config": {
        "documents_allowed": {
          "USA": {
            "DL": {
              "enabled": 1,
              "sides": 2
            }
          }
        },
        "duplicated_user_action": "REVIEW"
      }
    },
    {
      "feature": "LIVENESS",
      "config": {
        "face_liveness_method": "PASSIVE"
      }
    },
    {
      "feature": "FACE_MATCH"
    }
  ]
}
```## Fluxo de questionáriosCrie primeiro o questionário com `POST /v3/questionnaires/`. Depois, use o `questionnaire_id` devolvido como `questionnaire_uuid`.```json theme={null}
{
  "workflow_label": "Source of Funds",
  "features": [
    {
      "feature": "QUESTIONNAIRE",
      "config": {
        "questionnaire_uuid": "11111111-2222-3333-4444-555555555555",
        "review_questionnaire_manually": true
      }
    }
  ]
}
```A ordem das funcionalidades é importante. Por exemplo, coloque `OCR` antes de `FACE_MATCH`, `NFC`, `DATABASE_VALIDATION` ou das verificações AML de utilizadores que dependam dos dados do documento.## Referência de configuração das funcionalidadesCada item de `features[]` tem esta estrutura:```json theme={null}
{
  "feature": "OCR",
  "label": "ID Verification",
  "config": {}
}
```O objeto `config` exato depende da funcionalidade. Reveja a lista completa de campos, valores predefinidos, definições obrigatórias, intervalos de valores e exemplos em [Configurações de funcionalidades do fluxo](https://docs.didit.me/pt/management-api/workflows/feature-configs.md).Para `OCR`, omita `config.documents_allowed`, envie `null` ou envie `{}` para permitir todos os tipos de documentos suportados. Inclua-o apenas se quiser restringir o fluxo a países ou tipos de documento específicos. Para `QUESTIONNAIRE`, `config.questionnaire_uuid` é obrigatório.## Definições opcionais do fluxoPode incluir qualquer um destes campos de nível superior ao criar um fluxo para controlar o comportamento da sessão. Todos são opcionais e, quando são omitidos, assumem os valores predefinidos do fluxo.| Campo | Tipo | Descrição |
| - | - | - |
| `is_white_label_enabled` | booleano | Ativa a personalização em marca branca para as sessões criadas com este fluxo. Predefinido `false`. |
| `is_desktop_allowed` | booleano | Permite que o fluxo de verificação seja executado em navegadores de computador. Predefinido `false`. |
| `max_retry_attempts` | inteiro (0–10) | Número máximo de novas tentativas permitidas após uma verificação recusada. `0` bloqueia o utilizador após a primeira recusa. Predefinido `3`. |
| `retry_window_days` | inteiro (1–365) ou `null` | Janela deslizante, em dias, usada para contar as tentativas (`null` = limite para todo o período). Predefinido `7`. |
| `session_expiration_time` | inteiro (3600–2419200) | Segundos até uma sessão não concluída expirar. Mínimo de 1 hora, máximo de 4 semanas. Predefinido `604800` (7 dias). |
| `response_attributes` | objeto ou `null` | Limita os campos devolvidos para cada funcionalidade, por exemplo `{"id_verifications": ["full_name", "document_number"], "face_matches": null}`. `null` ou uma chave em falta devolve todos os campos. Uma lista vazia devolve apenas `status`, `warnings`, `node_id`, `is_nfc_skipped` e `skip_reason`, e os outros campos são devolvidos como `null`. |```json theme={null}
{
  "workflow_label": "Standard KYC",
  "is_white_label_enabled": true,
  "is_desktop_allowed": true,
  "max_retry_attempts": 5,
  "retry_window_days": 14,
  "session_expiration_time": 86400,
  "features": [
    { "feature": "OCR" },
    { "feature": "LIVENESS" },
    { "feature": "FACE_MATCH" }
  ]
}
```## Ramificação e fluxos baseados em nós (grafo)Uma lista linear `features` não consegue expressar "se X então Y". Para a **ramificação condicional** (recusar com base num estado, encaminhar com base num campo extraído, condicionar um passo a uma correspondência), um passo de pedido de **Document AI**, ou nós terminais de **recusa/decisão**, os fluxos da Didit usam um **grafo de nós**, a mesma estrutura que o construtor visual da consola produz.> [!NOTE]
>
> A forma mais simples de criar um fluxo em grafo é através do **[Didit MCP](https://docs.didit.me/pt/integration/mcp/overview.md)**: peça a um agente, em linguagem simples ("após a verificação de identidade, se a profissão for ≈ 'Software Engineer', adicione um passo de comprovativo de fundos"), e ele constrói, valida e guarda o grafo por si. A estrutura abaixo é a que essas ferramentas (e a consola) produzem.### Forma do grafoUm grafo é `{ start_node, nodes }`. `nodes` é um mapa de id do nó → nó. As arestas são implícitas: cada nó aponta para a frente através de `next` (incondicional) e/ou `branches[].goto` (condicional).| Tipo de nó | Forma |
| - | - |
| `feature` | `{ "node_type": "feature", "feature": "OCR", "config": {…}, "next": "<id>", "branches": [] }` |
| `branch` | `{ "node_type": "branch", "branches": [ { "id", "logic": "and"\|"or", "rules": [<rule>], "goto": "<id>" } ], "next": "<fallback id>" }` |
| `status` (terminal) | `{ "node_type": "status", "session_status": "Approved"\|"Declined"\|"In Review"\|"Determine" }` |
| `action` | `{ "node_type": "action", "actions": [ { "type": "add_tag"\|"add_review"\|… } ], "next": "<id>" }` |
| `webhook` | `{ "node_type": "webhook", "config": { "url", "method" }, "branches": [], "next": "<id>" }` |`start_node` tem de ser um nó de funcionalidade. As ramificações são avaliadas por ordem, **vence a primeira correspondência**; um `rules: []` vazio é o "senão" que captura todos os casos restantes (mantenha-o por último). `session_status: "Determine"` decide automaticamente com base nos resultados combinados das funcionalidades.> [!WARNING]
>
> **Cada nó de ramificação tem de ter uma ramificação "senão" explícita**: uma ramificação com `rules: []` que encaminha as sessões que não correspondem a nenhuma das condições. Não confie no `next` do nó de ramificação como fallback: um `next` isolado aparece como uma aresta ambígua no construtor e é fácil de omitir. Quando envia um nó de ramificação com `next` e sem ramificação "senão", a API **converte esse `next` numa ramificação "senão" explícita** (por isso, `{ …, "branches": [<conditions>], "next": "liveness" }` é guardado como `branches: [<conditions>, { "id": "else", "rules": [], "goto": "liveness" }]`). Crie o "senão" você mesmo para ser explícito; a conversão é uma rede de segurança, não um substituto.### Regras de ramificação e operadoresCada regra compara um **campo** com um **valor** através de um **operador**:```json theme={null}
{ "field": "kyc.extra_fields.profession", "operator": "fuzzy_match", "value": "Software Engineer", "score": 80 }
```- **Campos** indicam um resultado de funcionalidade (`kyc.status`, `aml.status`, `document_ai.status`, `face_match.score`, …) ou um valor extraído (`kyc.full_name`, `kyc.age`, `kyc.nationality`, `kyc.extra_fields.profession`, um campo do Document AI `document_ai.<key>`, `expected_details.*`). Uma ramificação só pode ler um campo cuja funcionalidade tenha sido executada em todos os caminhos que chegam a ela.
- **Operadores:** `equals`, `not_equals`, `greater_than(_or_equals)`, `less_than(_or_equals)`, `contains`, `not_contains`, `in`, `not_in`, `is_empty`, `is_not_empty`, `regex` e **`fuzzy_match`** (apenas campos de texto; exige um `score` de 0 a 100, um limiar de semelhança, por exemplo profissão com uma semelhança ≥ 80% com "Software Engineer").### Ramificar com base no motivo pelo qual o NFC não foi executadoDepois de um nó `NFC`, use `nfc.skip_reason` com `equals` ou `in` para encaminhar as sessões consoante o motivo pelo qual a leitura do chip não aconteceu. O campo tem estes sete valores estáveis:- `USER_SKIPPED`
- `DOCUMENT_WITHOUT_CHIP`
- `CHIP_CERTIFICATE_UNAVAILABLE`
- `DEVICE_WITHOUT_NFC`
- `INTEGRATION_WITHOUT_NFC_ACCESS`
- `MRZ_KEY_UNAVAILABLE`
- `JPKI_UNAVAILABLE`Consulte [Motivos para ignorar o NFC](https://docs.didit.me/pt/core-technology/nfc-verification/warnings-nfc-verification.md#motivos-para-ignorar) para saber o que significa cada valor. Quando o NFC é bem-sucedido, `nfc.skip_reason` é `null`, por isso nem uma regra `equals` nem uma regra `in` para um motivo de omissão correspondem.O campo também é resolvido para nós NFC ignorados devido a uma condição de controlo. Isto inclui casos em que não existe qualquer relatório ou aviso de NFC, e casos em que `allow_nfc_skip` é `false`, mas o controlo do documento ou do dispositivo impede a execução do NFC. Pode, por isso, encaminhar estas sessões sem depender de uma entrada em `nfc_verifications[]`.> [!NOTE]
>
> As sessões guardadas antes de a capacidade NFC passar a ser tri-estado podem ter `has_device_nfc_support: false`. Uma sessão web mais antiga pode, por isso, resolver para `DEVICE_WITHOUT_NFC` em vez de `INTEGRATION_WITHOUT_NFC_ACCESS`.Este grafo usa `equals` para uma omissão pelo utilizador, `in` para omissões por controlo de acesso ou capacidade, e um "senão" explícito para o NFC bem-sucedido. A ramificação vem depois do nó `NFC`, e todos os destinos apontam para a frente, para um nó terminal:```json theme={null}
{
  "start_node": "ocr",
  "nodes": {
    "ocr": {
      "node_type": "feature",
      "feature": "OCR",
      "next": "nfc"
    },
    "nfc": {
      "node_type": "feature",
      "feature": "NFC",
      "config": { "allow_nfc_skip": false },
      "next": "route_nfc"
    },
    "route_nfc": {
      "node_type": "branch",
      "branches": [
        {
          "id": "user_skipped",
          "logic": "and",
          "rules": [
            { "field": "nfc.skip_reason", "operator": "equals", "value": "USER_SKIPPED" }
          ],
          "goto": "manual_review"
        },
        {
          "id": "nfc_unavailable",
          "logic": "and",
          "rules": [
            {
              "field": "nfc.skip_reason",
              "operator": "in",
              "value": [
                "DOCUMENT_WITHOUT_CHIP",
                "CHIP_CERTIFICATE_UNAVAILABLE",
                "DEVICE_WITHOUT_NFC",
                "INTEGRATION_WITHOUT_NFC_ACCESS",
                "MRZ_KEY_UNAVAILABLE",
                "JPKI_UNAVAILABLE"
              ]
            }
          ],
          "goto": "manual_review"
        },
        {
          "id": "else",
          "logic": "and",
          "rules": [],
          "goto": "approved"
        }
      ]
    },
    "manual_review": { "node_type": "status", "session_status": "In Review" },
    "approved": { "node_type": "status", "session_status": "Approved" }
  }
}
```Para expressar "o NFC não foi executado por nenhum motivo conhecido", use `in` com **todos os sete** valores. Mantenha uma ramificação "senão" explícita para o NFC bem-sucedido (`nfc.skip_reason: null`) e para qualquer valor futuro que o seu grafo ainda não reconheça.### Document AI (por exemplo, comprovativo de fundos)Um nó de funcionalidade `DOCUMENT_AI` pede um ou mais documentos e extrai campos nomeados:```json theme={null}
{
  "node_type": "feature",
  "feature": "DOCUMENT_AI",
  "label": "Proof of Funds",
  "config": {
    "document_ai_documents": [
      {
        "document_key": "proof_of_funds",
        "title": "Proof of Funds",
        "description": "Upload a recent bank statement.",
        "fields": [
          {
            "key": "account_holder",
            "name": "Account holder",
            "instruction": "The full name of the account holder as printed on the statement.",
            "type": "text",
            "required": true,
            "is_full_name": true
          },
          {
            "key": "closing_balance",
            "name": "Closing balance",
            "instruction": "The closing balance amount.",
            "type": "number",
            "required": true
          }
        ]
      }
    ],
    "document_ai_missing_required_fields_action": "REVIEW",
    "document_ai_name_mismatch_action": "REVIEW"
  },
  "next": "final_decision"
}
```Cada campo configurado torna-se um valor que pode ser usado numa ramificação, `document_ai.<key>`, e o resultado do passo é `document_ai.status`.Defina `"is_full_name": true` no campo que contém o nome completo do titular do documento. O valor extraído é comparado com o nome completo da identidade verificada (o nome obtido por OCR/verificação de identidade, ou os detalhes esperados da sessão), e `document_ai_name_mismatch_action` decide o que acontece em caso de divergência abaixo de `document_ai_name_match_score_threshold`. A flag é **opcional**: um documento pode não definir nenhum campo, e **no máximo um campo por documento** pode defini-la. Consulte [Configurações de funcionalidades dos fluxos → Document AI](https://docs.didit.me/pt/management-api/workflows/feature-configs.md#document-ai) para ver todos os campos de configuração do Document AI.### Endpoints e ciclo de vida do rascunhoA edição do grafo é feita por aplicação e usa os endpoints da consola/gestão (Bearer do utilizador + `read:workflows`/`write:workflows`). Um grafo só pode ser editado numa versão em **rascunho**, por isso o ciclo de vida é **criar rascunho → definir grafo → publicar**:| Método | Caminho | Finalidade |
| - | - | - |
| `GET` | `…/verification-settings/{settings_uuid}/workflow-graph/` | Grafo atual + `status`/`is_editable` |
| `POST` | `…/application/{app}/workflow-graph/validate/` (corpo `{ "graph": … }`) | Validação em modo de teste |
| `GET` | `…/application/{app}/workflow-graph/field-definitions/` | Campos com ramificação + operadores válidos |
| `POST` | `…/verification-settings/{settings_uuid}/create-draft/` | Rascunho editável a partir de uma versão publicada |
| `PUT` | `…/verification-settings/{settings_uuid}/workflow-graph/` (corpo `{ "graph": … }`) | Guardar o grafo |
| `PATCH` | `…/verification-settings/{settings_uuid}/` (corpo `{ "status": "published" }`) | Publicar o rascunho |#### Avisos de validaçãoO endpoint de validação responde com `{ "is_valid": true, "graph": …, "warnings": [] }`. Um aviso é **consultivo**: o grafo é válido, é guardado e é publicado mesmo com avisos presentes. Cada entrada indica o nó de ramificação e o ramo a que diz respeito, para que quem constrói o fluxo possa apontar para a condição exata:```json theme={null}
{
  "type": "branch_outcome_contradiction",
  "node_id": "after_ocr",
  "branch_id": "b1",
  "severity": "warning",
  "message": "Branch 1 only matches sessions where ID Verification Status is Approved or In Review, but every path from it ends in the session outcome \"Declined\". Check the condition or the outcome.",
  "params": { "branchNumber": 1, "field": "ID Verification Status", "allowed": "Approved or In Review", "outcome": "Declined" }
}
```| `type` | O que significa |
| - | - |
| `branch_outcome_contradiction` | A condição do ramo e o resultado a que chega cada caminho a partir dele contradizem-se - por exemplo, `kyc.status NOT EQUALS Declined` encaminhado para um resultado `Declined`. O resultado pode estar vários passos mais abaixo no caminho. |
| `branch_condition_unreachable` | O ramo nunca pode corresponder, porque todos os caminhos que chegam a este nó de ramificação já limitam esse campo de estado a valores que a condição exclui. |
| `else_branch_unreachable` | O ramo else nunca pode ser executado: os outros ramos já cobrem todos os valores que o campo pode ter neste ponto. |Um grafo inválido responde com `400`, com `errors` **e** com uma lista `warnings` tanto quanto possível. Apenas `errors` bloqueiam a gravação.### Exemplo prático: encaminhar Engenheiros de software para comprovativo de fundos> Após a verificação de identidade: se foi **recusada**, recusar a sessão; se a **profissão do titular ≈ "Software Engineer"** (≥80% semelhante), pedir um comprovativo de fundos; depois tomar uma decisão final.```json theme={null}
{
  "start_node": "ocr_1",
  "nodes": {
    "ocr_1": { "node_type": "feature", "feature": "OCR", "label": "ID Verification", "next": "after_ocr" },
    "after_ocr": {
      "node_type": "branch",
      "label": "Route after ID verification",
      "branches": [
        { "id": "declined", "logic": "or", "rules": [ { "field": "kyc.status", "operator": "equals", "value": "Declined" } ], "goto": "decline" },
        { "id": "software_engineer", "logic": "and", "rules": [ { "field": "kyc.extra_fields.profession", "operator": "fuzzy_match", "value": "Software Engineer", "score": 80 } ], "goto": "proof_of_funds" },
        { "id": "else", "logic": "and", "rules": [], "goto": "final_decision" }
      ]
    },
    "proof_of_funds": {
      "node_type": "feature",
      "feature": "DOCUMENT_AI",
      "label": "Proof of Funds",
      "config": {
        "document_ai_documents": [
          { "document_key": "proof_of_funds", "title": "Proof of Funds", "description": "Upload a recent bank statement.",
            "fields": [
              { "key": "account_holder", "name": "Account holder", "type": "text", "required": true },
              { "key": "closing_balance", "name": "Closing balance", "type": "number", "required": true }
            ] }
        ],
        "document_ai_missing_required_fields_action": "REVIEW"
      },
      "next": "final_decision"
    },
    "final_decision": { "node_type": "status", "session_status": "Determine", "label": "Final decision" },
    "decline": { "node_type": "status", "session_status": "Declined", "label": "Declined" }
  }
}
```O nó final usa `session_status: "Determine"`, que **decide automaticamente** (aprovação / recusa / em análise) com base nas funcionalidades que foram efetivamente executadas na sessão - por isso, tanto o caminho do comprovativo de fundos como o caminho else são resolvidos corretamente, sem que nenhum ramo leia `document_ai.status` (que não existe no caminho que ignora o Document AI). Lembre-se: um ramo só pode ler um campo cuja funcionalidade tenha sido executada em *todos* os caminhos que chegam a ele.Envie-o como `{ "graph": <above> }` para o endpoint `…/workflow-graph/` (valide primeiro). Para criar um **novo** fluxo do zero, use `didit_workflow_set_graph` do MCP; para **modificar um fluxo existente**, use `didit_workflow_edit_graph` (pequenas operações que preservam os nós existentes e as listas de permissão). Consulte o [Didit MCP](https://docs.didit.me/pt/integration/mcp/tools.md).


## OpenAPI

````yaml pt/openapi-25.json POST /v3/workflows/
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/workflows/:
    post:
      tags:
        - Workflows
      summary: Criar fluxo
      description: >-
        Crie um fluxo simples. Envie `features` por ordem de execução
        (aplicam-se regras de ordenação); o valor predefinido é `published` v1.
        O corpo aceita APENAS os campos listados em `SimpleWorkflowRequest` —
        qualquer outra chave (incluindo `workflow_type`) é rejeitada com o
        código 400. Não idempotente — cada chamada cria um novo fluxo. Máximo de
        50 fluxos por aplicação.
      operationId: create_workflow
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SimpleWorkflowRequest'
            examples:
              Simple KYC:
                summary: Fluxo linear de ID, prova de vida e correspondência facial
                value:
                  workflow_label: Standard KYC
                  features:
                    - feature: OCR
                      config:
                        duplicated_user_action: review
                    - feature: LIVENESS
                      config:
                        face_liveness_method: passive
                    - feature: FACE_MATCH
                      config:
                        face_match_score_decline_threshold: 40
                        face_match_score_review_threshold: 60
              KYC + AML:
                summary: Executar AML após a verificação de identidade
                value:
                  workflow_label: KYC with AML
                  features:
                    - feature: OCR
                    - feature: AML
                      config:
                        aml_score_review_threshold: 70
                        aml_score_approve_threshold: 30
              Questionnaire workflow:
                summary: Executar um questionário criado com POST /v3/questionnaires/
                value:
                  workflow_label: Source of Funds
                  features:
                    - feature: QUESTIONNAIRE
                      config:
                        questionnaire_uuid: 11111111-2222-3333-4444-555555555555
                        review_questionnaire_manually: true
      responses:
        '201':
          description: >-
            Fluxo criado. O corpo da resposta é a configuração COMPLETA do fluxo
            — a mesma serialização bruta de `VerificationSettings` devolvida por
            `GET /v3/workflows/{settings_uuid}/` (~190 campos: todas as opções
            de funcionalidades, limiares, ações e o `workflow_graph` gerado), e
            não o elemento compacto da lista. Guarde o `uuid` (também exposto
            como `workflow_id`) para poder referenciá-lo em chamadas posteriores
            de criação de sessões. Nota: `features` é uma cadeia unida por `" +
            "` e os preços são números JSON.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowDetail'
              examples:
                Created (abbreviated):
                  summary: Campos principais do objeto de configuração completo
                  value:
                    uuid: c3d4e5f6-7890-12cd-ef34-333333333333
                    workflow_id: c3d4e5f6-7890-12cd-ef34-333333333333
                    workflow_label: Standard KYC
                    workflow_type: null
                    is_default: false
                    is_archived: false
                    total_price: 0.15
                    min_price: 0.15
                    max_price: 0.15
                    features: OCR + LIVENESS + FACE_MATCH
                    is_simple_workflow: false
                    is_editable: false
                    workflow_url: https://verify.didit.me/u/w9TkPOx8RWG3UAz5ZRQGVQ
                    workflow_graph:
                      start_node: ocr
                      nodes: ... one node per feature plus a final status node ...
                    max_retry_attempts: 3
                    retry_window_days: 7
                    session_expiration_time: 604800
                    version: 1
                    status: published
                    published_at: '2026-05-17T10:30:00Z'
                    created_at: '2026-05-17T10:30:00Z'
                    updated_at: '2026-05-17T10:30:00Z'
        '400':
          description: >-
            Erro de validação. Causas comuns: uma chave de nível superior fora
            da lista de permissões aceite (por ex., `workflow_type`), um valor
            de `feature` desconhecido, uma funcionalidade colocada antes da sua
            dependência (por ex., `FACE_MATCH` antes de `OCR`), uma
            funcionalidade passiva (`AML`, `IP_ANALYSIS`, `DATABASE_VALIDATION`,
            `KYB_KEY_PEOPLE`) utilizada como primeiro passo, uma funcionalidade
            `QUESTIONNAIRE` cujo `config.questionnaire_uuid` não existe para
            esta aplicação, uma funcionalidade `SIGNATURE` que entraria em
            produção sem nenhum documento para assinar, uma funcionalidade
            `SIGNATURE` com `signature_level: "aes"` quando o nível Avançado
            (AES) não está disponível (`aes_not_available`, consulte
            `signature_availability` no fluxo) ou um limiar fora do intervalo. O
            corpo é um objeto de erro de validação do DRF — as chaves são nomes
            de campos e os valores são arrays de mensagens legíveis por humanos.
          content:
            application/json:
              examples:
                Advanced (AES) not available:
                  value:
                    features:
                      signature_level:
                        - >-
                          Node 'signature': Advanced (AES) is not available yet
                          (aes_not_available).
                Signature step with no document:
                  value:
                    features:
                      non_field_errors:
                        - >-
                          Node 'signature': Add a document to sign, or let each
                          session bring its own.
                Missing feature:
                  value:
                    features:
                      - At least one feature is required.
                Unknown top-level field:
                  value:
                    workflow_type:
                      - >-
                        The v3 workflow API only accepts `workflow_label`,
                        `is_default`, `status`, `features`,
                        `is_white_label_enabled`, `is_desktop_allowed`,
                        `max_retry_attempts`, `retry_window_days`,
                        `session_expiration_time`, `face_liveness_max_attempts`,
                        `face_match_max_attempts`.
                Unknown feature:
                  value:
                    features:
                      - >-
                        Invalid feature 'FOO'. Allowed values: AGE_ESTIMATION,
                        AML, DATABASE_VALIDATION, EMAIL_VERIFICATION,
                        FACE_MATCH, IP_ANALYSIS, KYB_DOCUMENTS, KYB_KEY_PEOPLE,
                        KYB_REGISTRY, LIVENESS, NFC, OCR, PHONE_VERIFICATION,
                        PROOF_OF_ADDRESS, QUESTIONNAIRE.
                Ordering:
                  value:
                    features:
                      - 'Feature ''FACE_MATCH'' must appear after one of: OCR.'
                Questionnaire not found:
                  value:
                    features: >-
                      Questionnaire '11111111-2222-3333-4444-555555555555' was
                      not found for this application. Create the questionnaire
                      first, then use its questionnaire_id.
        '403':
          description: >-
            A chave de API está em falta, mal formatada, expirada ou tem como
            âmbito uma aplicação diferente. Atualize através da [Auth
            API](/pt/auth-api/get-credentials) e tente novamente.
          content:
            application/json:
              examples:
                Invalid token:
                  value:
                    detail: You do not have permission to perform this action.
        '415':
          description: >-
            Tipo de conteúdo não suportado. Defina `Content-Type:
            application/json` e envie um corpo JSON.
        '429':
          description: Limite de pedidos excedido. Tente novamente com recuo exponencial.
        '500':
          description: >-
            Erro de servidor inesperado ao criar o fluxo. Pode tentar novamente
            em segurança.
      security:
        - ApiKeyAuth: []
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |-
            curl -X POST 'https://verification.didit.me/v3/workflows/' \
              -H 'x-api-key: YOUR_API_KEY' \
              -H 'Content-Type: application/json' \
              -d '{
                "workflow_label": "Standard KYC",
                "features": [
                  {"feature": "OCR"},
                  {"feature": "LIVENESS", "config": {"face_liveness_method": "passive"}},
                  {"feature": "FACE_MATCH", "config": {"face_match_score_decline_threshold": 40, "face_match_score_review_threshold": 60}}
                ]
              }'
        - lang: python
          label: Python
          source: |-
            from didit import Didit

            # Reads DIDIT_API_KEY.
            client = Didit()

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

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

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

            import java.util.Map;

            import me.didit.Didit;

            import me.didit.types.SimpleWorkflowFeature;

            import me.didit.types.SimpleWorkflowRequest;


            // Reads DIDIT_API_KEY.

            Didit client = new Didit();


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

            require 'vendor/autoload.php';

            use Didit\DiditClient;

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

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

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

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

            using System.Collections.Generic;

            using System.Text.Json;

            using Didit;


            // Reads DIDIT_API_KEY.

            using var client = new DiditClient();


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

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

            Console.WriteLine(JsonSerializer.Serialize(result));
components:
  schemas:
    SimpleWorkflowRequest:
      type: object
      description: >-
        Crie um fluxo linear simples. A API converte internamente esta lista de
        funcionalidades num grafo de fluxo baseado em nós. Lista de permissões
        ESTRITA de campos: qualquer chave não listada aqui (incluindo
        `workflow_type`) é rejeitada com o código 400.
      required:
        - features
      properties:
        workflow_label:
          type: string
          maxLength: 50
          default: ID Verification
          description: >-
            Nome de apresentação do fluxo (máx. 50 carateres). Assume por
            predefinição `"ID Verification"` quando omitido — configure-o sempre
            para que os fluxos permaneçam distinguíveis.
          example: Standard KYC
        is_default:
          type: boolean
          description: Defina este fluxo como o padrão para novas sessões.
        status:
          type: string
          enum:
            - draft
            - published
          description: >-
            Omita este campo para criar um fluxo publicado pronto para sessões.
            Utilize `draft` apenas quando pretender guardar sem publicar.
        features:
          type: array
          minItems: 1
          description: >-
            Funcionalidades de verificação por ordem de execução. A API liga
            cada item ao seguinte e adiciona automaticamente um nó final de
            estado.
          items:
            $ref: '#/components/schemas/SimpleWorkflowFeature'
        is_white_label_enabled:
          type: boolean
          description: >-
            Ativar a personalização em marca branca para sessões criadas com
            este fluxo.
          default: false
        is_desktop_allowed:
          type: boolean
          description: >-
            Permitir que o fluxo de verificação seja executado em navegadores de
            computador.
          default: false
        max_retry_attempts:
          type: integer
          minimum: 0
          maximum: 10
          default: 3
          description: >-
            Número máximo de novas tentativas permitidas após uma verificação
            recusada. `0` bloqueia o utilizador após a primeira recusa.
        retry_window_days:
          type: integer
          minimum: 1
          maximum: 365
          nullable: true
          default: 7
          description: >-
            Janela móvel em dias utilizada para contar tentativas. `null` aplica
            um limite histórico total.
        face_liveness_max_attempts:
          type: integer
          minimum: 1
          maximum: 3
          default: 3
          description: >-
            Número máximo de envios de prova de vida por sessão. Padrão: 3 (uma
            tentativa inicial e duas novas tentativas). Este valor também é
            copiado para os nós do fluxo de prova de vida e de estimativa de
            idade gerados, a menos que a configuração ao nível da funcionalidade
            o substitua.
        face_match_max_attempts:
          type: integer
          minimum: 1
          maximum: 3
          default: 3
          description: >-
            Número máximo de envios de correspondência facial por sessão.
            Padrão: 3 (uma tentativa inicial e duas novas tentativas). Este
            valor também é copiado para os nós do fluxo de correspondência
            facial gerados, a menos que a configuração ao nível da
            funcionalidade o substitua.
        session_expiration_time:
          type: integer
          minimum: 3600
          maximum: 2419200
          default: 604800
          description: >-
            Segundos até uma sessão inacabada expirar. Mínimo de 1 hora (3600),
            máximo de 4 semanas (2419200).
    WorkflowDetail:
      type: object
      description: >-
        Os campos abaixo são tipados; o objeto também contém todas as outras
        colunas da configuração do fluxo (comutadores de funcionalidades,
        limiares, ações de aviso, documentos permitidos, callbacks) como
        propriedades adicionais. Os campos que não se aplicam ao `workflow_type`
        são `null`.
      properties:
        uuid:
          type: string
          format: uuid
          description: >-
            Identificador por versão (`settings_uuid`). Cada versão de um fluxo
            tem o seu próprio.
        workflow_id:
          type: string
          format: uuid
          description: >-
            Identificador estável partilhado por todas as versões do fluxo. As
            sessões criadas com ele utilizam a versão publicada mais recente.
        workflow_label:
          type: string
          description: Nome de apresentação do fluxo.
        workflow_type:
          type: string
          nullable: true
          enum:
            - kyc
            - kyb
            - adaptive_age_verification
            - biometric_authentication
            - address_verification
            - questionnaire_verification
            - email_verification
            - phone_verification
            - null
          description: Modelo de fluxo. `null` para fluxos em grafo criados sem um modelo.
        is_default:
          type: boolean
          description: Se este é o fluxo predefinido da aplicação.
        is_archived:
          type: boolean
          description: Os fluxos arquivados não podem iniciar novas sessões.
        version:
          type: integer
          description: Número da versão dentro do fluxo.
        status:
          type: string
          enum:
            - draft
            - published
          description: >-
            As versões de rascunho são editáveis; as versões publicadas são
            imutáveis e utilizadas para novas sessões.
        published_at:
          type: string
          format: date-time
          nullable: true
          description: Quando esta versão foi publicada. `null` para rascunhos.
        is_simple_workflow:
          type: boolean
          description: '`true` quando o fluxo não tem um `workflow_graph` personalizado.'
        is_editable:
          type: boolean
          description: '`true` apenas para versões `draft`.'
        workflow_url:
          type: string
          format: uri
          description: URL público que inicia uma sessão com este fluxo.
        workflow_graph:
          type: object
          nullable: true
          additionalProperties: true
          description: >-
            Estrutura em grafo do fluxo (nós e arestas). `null` para fluxos
            simples.
        features:
          type: string
          description: >-
            Funcionalidades ativadas unidas por `" + "`, por exemplo `"OCR +
            LIVENESS"`.
        total_price:
          type: number
          nullable: true
          description: Preço por sessão; para fluxos em grafo, o caminho mais caro.
        min_price:
          type: number
          nullable: true
          description: Preço do caminho mais económico ao longo do fluxo.
        max_price:
          type: number
          nullable: true
          description: Preço do caminho mais caro ao longo do fluxo.
        special_add_on_prices:
          type: object
          nullable: true
          additionalProperties: true
          description: >-
            Preços de suplementos específicos da organização, ou `null` quando
            nenhum estiver definido.
        max_retry_attempts:
          type: integer
          description: Tentativas permitidas após uma verificação recusada.
        retry_window_days:
          type: integer
          nullable: true
          description: Dias durante os quais as tentativas adicionais são permitidas.
        session_expiration_time:
          type: integer
          description: Segundos até uma sessão inacabada expirar.
        questionnaire:
          type: object
          nullable: true
          additionalProperties: true
          description: O questionário associado ao fluxo, ou `null`.
        kyb_role_settings:
          type: array
          items:
            type: object
            additionalProperties: true
          description: Definições de verificação por função para pessoas-chave de KYB.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        signature_availability:
          type: object
          description: >-
            Quais os níveis de Assinatura eletrónica que podem assinar neste
            momento. Verifique antes de definir `signature_level`. Devolvido em
            fluxos de `kyc`, `kyb` e de grafo.
          properties:
            simple:
              type: boolean
              description: Simple está sempre disponível.
            aes:
              type: boolean
              description: >-
                Se o nível Avançado (AES) pode assinar agora. Enquanto for
                `false`, guardar um novo passo com `signature_level: "aes"` é
                recusado com `aes_not_available`.
            qes:
              type: boolean
              description: >-
                Se o nível Qualificado (QES) pode assinar agora. É `false` até
                que um fornecedor qualificado esteja associado.
        is_bank_verification_enabled:
          type: boolean
          description: >-
            Se um fluxo simples de KYC ou KYB executa a verificação bancária. É
            executada após os passos de contacto (e a verificação de
            localização) e antes da validação de base de dados, AML e análise de
            IP. A verificação bancária ainda não está disponível de forma geral:
            a sua ativação é recusada onde não estiver ativada.
        bank_verification_config:
          type: object
          additionalProperties: true
          description: >-
            Definições de verificação bancária de um fluxo simples, com as
            mesmas chaves que um nó `BANK_VERIFICATION` aceita (por exemplo,
            `bank_countries` e `bank_method_order`). Chaves desconhecidas são
            ignoradas. Todas as chaves são opcionais e uma chave ausente mantém
            o seu valor predefinido, pelo que `{}` significa todas as
            predefinições. Consulte [Definições de verificação
            bancária](/pt/management-api/workflows/feature-configs#verificação-bancária).
          example: {}
        is_geolocation_enabled:
          type: boolean
          description: >-
            Se um fluxo simples de KYC executa a verificação de localização. É
            executada após os passos de contacto e antes da verificação bancária
            e da validação de base de dados. Os fluxos de empresas (KYB) não a
            podem executar.
        geolocation_config:
          type: object
          additionalProperties: true
          description: >-
            Definições de verificação de localização de um fluxo simples, com as
            mesmas chaves que um nó `GEOLOCATION` aceita (por exemplo
            `is_geolocation_geofencing_enabled` e `geolocation_max_attempts`).
            As chaves desconhecidas são ignoradas. Todas as chaves são opcionais
            e uma chave ausente mantém o seu valor padrão, pelo que `{}`
            significa todos os valores padrão.
          example: {}
      additionalProperties: true
    SimpleWorkflowFeature:
      type: object
      required:
        - feature
      properties:
        feature:
          type: string
          description: >-
            Funcionalidade a executar. Utilize os valores em maiúsculas aqui
            apresentados.
          enum:
            - OCR
            - NFC
            - LIVENESS
            - FACE_MATCH
            - PROOF_OF_ADDRESS
            - QUESTIONNAIRE
            - DOCUMENT_AI
            - PHONE_VERIFICATION
            - EMAIL_VERIFICATION
            - DATABASE_VALIDATION
            - AML
            - IP_ANALYSIS
            - AGE_ESTIMATION
            - KYB_REGISTRY
            - KYB_DOCUMENTS
            - KYB_KEY_PEOPLE
            - SIGNATURE
        config:
          type: object
          description: >-
            Definições específicas da funcionalidade. Para `QUESTIONNAIRE`,
            inclua `questionnaire_uuid` com o `questionnaire_id` devolvido pelo
            endpoint de criação de questionários. Para `SIGNATURE`, um fluxo que
            passa a produção precisa de um documento para assinar: defina
            `signature_documents_source` como `session` ou `both` para que cada
            sessão possa fornecer o seu próprio, ou envie `status: "draft"` e
            adicione os documentos antes de publicar. `signature_level: "aes"` é
            recusado com `aes_not_available` enquanto
            `signature_availability.aes` for `false` no fluxo. Para `OCR`,
            `methods` seleciona quais os métodos de Verificação de identidade
            que cada país pode utilizar (captura de documentos, consulta sem
            documento junto de uma fonte governamental ou de outra autoridade, e
            carteiras de identidade digital); omita-o e todos os países
            utilizarão apenas a captura de documentos. Para `SIGNATURE`,
            consulte [Definições de Assinatura
            eletrónica](/pt/management-api/workflows/feature-configs#assinatura-eletrónica):
            `signature_level` aceita `simple` (a predefinição; não é necessária
            verificação de identidade, e-mail ou telefone) e `aes` de momento, e
            os documentos do fluxo e respetivos campos são geridos na consola,
            pelo que, através da API, envie documentos e campos por sessão com
            `signature_documents_source: "session"`.
          additionalProperties: true
        label:
          type: string
          description: Etiqueta interna opcional para este nó do fluxo.
  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.