> ## 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 workflow: monte seu fluxo de KYC

> Crie via API um workflow de verificação da Didit com recursos em ordem: KYC, KYB, AML e biometria em um só fluxo. Pague por chamada, sem contratos.



Prompt da API de criação de workflow
        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 de verificação (workflows) pela API sem precisar montar você mesmo os nós do grafo do fluxo. Envie um array `features` na ordem em que os usuários devem concluir as verificações. A Didit o converte internamente em um fluxo baseado em nós e adiciona automaticamente a etapa final de status.Este endpoint `POST /v3/workflows/` cria apenas fluxos **simples e lineares**, ou seja, uma lista `features` plana. Para **ramificações, roteamento condicional, Document AI e nós de recusa/decisão**, use o **[formato de grafo](#ramificação-e-fluxos-baseados-em-nós-grafo)** de nós documentado abaixo (usado pelo editor visual no console e pelas ferramentas de fluxo do [Didit MCP](https://docs.didit.me/pt-br/integration/mcp/overview.md)).Consulte [Configurações de recursos do fluxo](https://docs.didit.me/pt-br/management-api/workflows/feature-configs.md) para ver todos os campos de `config` aceitos por cada recurso.## Fluxo de 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 com questionárioPrimeiro, crie o questionário com `POST /v3/questionnaires/`. Depois, use o `questionnaire_id` retornado 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 dos recursos é importante. Por exemplo, coloque `OCR` antes de `FACE_MATCH`, `NFC`, `DATABASE_VALIDATION` ou de verificações AML do usuário que dependam dos dados do documento.## Referência de configuração de recursosCada item de `features[]` tem este formato:```json theme={null}
{
  "feature": "OCR",
  "label": "ID Verification",
  "config": {}
}
```A estrutura exata do objeto `config` depende do recurso. Consulte a lista completa de campos, valores padrão, configurações obrigatórias, intervalos de valores e exemplos em [Configurações de recursos do fluxo](https://docs.didit.me/pt-br/management-api/workflows/feature-configs.md).Para `OCR`, omita `config.documents_allowed`, envie `null` ou envie `{}` para permitir todos os tipos de documento compatíveis. Inclua-o apenas quando quiser restringir o fluxo a países ou tipos de documento específicos. Para `QUESTIONNAIRE`, `config.questionnaire_uuid` é obrigatório.## Configurações opcionais do fluxoVocê pode incluir qualquer um destes campos de nível superior ao criar um fluxo para controlar o comportamento das sessões. Todos são opcionais e, quando omitidos, assumem os valores padrão do fluxo.| Campo | Tipo | Descrição |
| - | - | - |
| `is_white_label_enabled` | boolean | Ativa a personalização de marca própria para as sessões criadas com este fluxo. Padrão `false`. |
| `is_desktop_allowed` | boolean | Permite que o fluxo de verificação seja executado em navegadores de desktop. Padrão `false`. |
| `max_retry_attempts` | integer (0–10) | Número máximo de novas tentativas permitidas após uma verificação recusada. `0` bloqueia o usuário após a primeira recusa. Padrão `3`. |
| `retry_window_days` | integer (1–365) ou `null` | Janela móvel, em dias, usada para contar as novas tentativas (`null` = limite sem prazo). Padrão `7`. |
| `session_expiration_time` | integer (3600–2419200) | Segundos até uma sessão não concluída expirar. Mínimo de 1 hora, máximo de 4 semanas. Padrão `604800` (7 dias). |
| `response_attributes` | object ou `null` | Limita os campos retornados para cada recurso, por exemplo `{"id_verifications": ["full_name", "document_number"], "face_matches": null}`. `null` ou uma chave ausente retorna todos os campos. Uma lista vazia retorna apenas `status`, `warnings`, `node_id`, `is_nfc_skipped` e `skip_reason`, e os demais campos retornam `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 de `features` não consegue expressar "se X, então Y". Para **ramificação condicional** (recusar com base em um status, rotear com base em um campo extraído, condicionar uma etapa a uma correspondência), uma etapa de requisição de **Document AI** ou nós terminais de **recusa/decisão**, os fluxos de verificação (workflows) da Didit usam um **grafo de nós**, a mesma estrutura que o construtor visual do console gera.> [!NOTE]
>
> A forma mais simples de montar um fluxo em grafo é o **[Didit MCP](https://docs.didit.me/pt-br/integration/mcp/overview.md)**: peça a um agente em linguagem natural ("após a verificação de identidade, se a profissão for ≈ 'Software Engineer', adicione uma etapa de comprovante de fundos") e ele constrói, valida e salva o grafo para você. A estrutura abaixo é o que essas ferramentas (e o console) geram.### Formato 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 via `next` (incondicional) e/ou `branches[].goto` (condicional).| Tipo de nó | Formato |
| - | - |
| `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` deve ser um nó de recurso. As ramificações são avaliadas em ordem e **a primeira correspondência prevalece**; um `rules: []` vazio é o "else" que captura todo o resto (mantenha-o por último). `session_status: "Determine"` decide automaticamente com base nos resultados combinados dos recursos.> [!WARNING]
>
> **Todo nó de ramificação deve ter uma ramificação else explícita**, ou seja, uma ramificação com `rules: []` que roteia as sessões que não atendem a nenhuma das condições. Não dependa do `next` do nó de ramificação como alternativa: um `next` isolado aparece como uma aresta ambígua no construtor e é fácil de omitir. Quando você envia um nó de ramificação com um `next` e sem ramificação else, a API **converte esse `next` em uma ramificação else explícita** para você (assim, `{ …, "branches": [<conditions>], "next": "liveness" }` é salvo como `branches: [<conditions>, { "id": "else", "rules": [], "goto": "liveness" }]`). Crie o else você mesmo para ser explícito; a conversão é uma rede de segurança, não um substituto.### Regras e operadores de ramificaçãoCada regra compara um **campo** a um **valor** usando um **operador**:```json theme={null}
{ "field": "kyc.extra_fields.profession", "operator": "fuzzy_match", "value": "Software Engineer", "score": 80 }
```- **Campos** identificam um resultado de recurso (`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 cujo recurso tenha sido executado em todos os caminhos que chegam até 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, que é um limiar de similaridade, por exemplo, profissão ≥ 80% semelhante a "Software Engineer").### Ramificação com base no motivo de o NFC não ter sido executadoApós um nó `NFC`, use `nfc.skip_reason` com `equals` ou `in` para rotear as sessões de acordo com 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 pular o NFC](https://docs.didit.me/pt-br/core-technology/nfc-verification/warnings-nfc-verification.md#motivos-de-omissão) para saber o significado de cada valor. Quando o NFC é bem-sucedido, `nfc.skip_reason` é `null`, portanto nenhuma regra `equals` ou `in` para um motivo de pulo corresponde.O campo também é preenchido para nós NFC **pulados por uma condição de bloqueio**. Isso inclui casos em que não existe relatório nem aviso de NFC, e casos em que `allow_nfc_skip` é `false`, mas a condição do documento ou do dispositivo impede a execução do NFC. Assim, você pode rotear essas sessões sem depender de uma entrada em `nfc_verifications[]`.> [!NOTE]
>
> Sessões armazenadas antes de a capacidade de NFC passar a ter três estados podem ter `has_device_nfc_support: false`. Por isso, uma sessão web mais antiga pode ser resolvida como `DEVICE_WITHOUT_NFC` em vez de `INTEGRATION_WITHOUT_NFC_ACCESS`.Este grafo usa `equals` para um pulo feito pelo usuário, `in` para pulos por condição de bloqueio ou por capacidade, e um else explícito para o NFC bem-sucedido. A ramificação vem depois do nó `NFC`, e cada destino aponta para a frente até 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 realizado por nenhum motivo conhecido", use `in` com **todos os sete** valores. Mantenha uma ramificação else 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, comprovante de fundos)Um nó de recurso `DOCUMENT_AI` solicita 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 se torna um valor ramificável `document_ai.<key>`, e o resultado da etapa é `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 do OCR/da verificação de identidade ou os dados esperados da sessão), e `document_ai_name_mismatch_action` decide o que acontece quando há divergência abaixo de `document_ai_name_match_score_threshold`. A flag é **opcional** (um documento pode não marcar nenhum campo) e **no máximo um campo por documento** pode defini-la. Consulte [Configurações de recursos do fluxo → Document AI](https://docs.didit.me/pt-br/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 do console/de gerenciamento (Bearer de usuário + `read:workflows`/`write:workflows`). Um grafo só pode ser editado em uma versão de **rascunho**, então 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 de teste (dry-run) |
| `GET` | `…/application/{app}/workflow-graph/field-definitions/` | Campos que permitem 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": … }`) | Salvar o grafo |
| `PATCH` | `…/verification-settings/{settings_uuid}/` (corpo `{ "status": "published" }`) | Publicar o rascunho |#### Avisos de validaçãoO endpoint de validação responde `{ "is_valid": true, "graph": …, "warnings": [] }`. Um aviso é apenas **informativo**: o grafo é válido, é salvo e é publicado mesmo com avisos. Cada entrada indica o nó de ramificação e o ramo a que se refere, para que um construtor 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 alcançado por todos os caminhos a partir dele se contradizem - por exemplo, `kyc.status NOT EQUALS Declined` direcionado para um resultado `Declined`. O resultado pode estar várias etapas adiante no caminho. |
| `branch_condition_unreachable` | O ramo nunca pode corresponder, porque todos os caminhos que chegam a essa ramificação já limitam esse campo de status a valores que a condição exclui. |
| `else_branch_unreachable` | O ramo else nunca pode ser executado: os outros braços já cobrem todos os valores que o campo pode assumir nesse ponto. |Um grafo inválido responde `400` com `errors` **e** uma lista `warnings` gerada conforme possível. Apenas `errors` impedem que o grafo seja salvo.### Exemplo prático: direcionar engenheiros de software para comprovante de fundos> Após a verificação de identidade: se ela foi **recusada**, recuse a sessão; se a **profissão ≈ "Software Engineer"** do titular (≥80% de similaridade), solicite um documento de **comprovante de fundos**; em seguida, tome 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 / recusa / coloca em revisão) com base nos recursos que de fato foram executados na sessão — assim, tanto o caminho do comprovante de fundos quanto o caminho else são resolvidos corretamente sem que nenhuma ramificação leia `document_ai.status` (que não existe no caminho que pula o Document AI). Lembre-se: uma ramificação só pode ler um campo cujo recurso foi executado em *todos* os caminhos que chegam até ela.Envie-o como `{ "graph": <above> }` para o endpoint `…/workflow-graph/` (valide antes). Para criar um fluxo **novo** do zero, use o `didit_workflow_set_graph` do MCP; para **modificar um fluxo existente**, use `didit_workflow_edit_graph` (operações pequenas, preserva os nós e as listas de permissão existentes). Consulte o [Didit MCP](https://docs.didit.me/pt-br/integration/mcp/tools.md).


## OpenAPI

````yaml pt-br/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 de verificação
      description: >-
        Cria um fluxo de verificação (workflow) simples. Envie `features` na
        ordem de execução (aplicam-se regras de ordenação); o padrão é
        `published` v1. O corpo aceita SOMENTE os campos listados em
        `SimpleWorkflowRequest` — qualquer outra chave (incluindo
        `workflow_type`) é rejeitada com 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 documento de identidade, 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 de verificação (workflow) criado. O corpo da resposta é a
            configuração COMPLETA do fluxo, a mesma serialização bruta de
            `VerificationSettings` retornada por `GET
            /v3/workflows/{settings_uuid}/` (~190 campos: cada ativação de
            recurso, limiar, ação e o `workflow_graph` gerado), e não o item
            compacto da lista. Armazene o `uuid` (também exposto como
            `workflow_id`) para poder referenciá-lo em chamadas posteriores de
            criação de sessão. Observação: `features` é uma string 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 chaves aceitas (por exemplo, `workflow_type`), um valor
            de `feature` desconhecido, um recurso colocado antes da sua
            dependência (por exemplo, `FACE_MATCH` antes de `OCR`), um recurso
            passivo (`AML`, `IP_ANALYSIS`, `DATABASE_VALIDATION`,
            `KYB_KEY_PEOPLE`) usado como primeira etapa, um recurso
            `QUESTIONNAIRE` cujo `config.questionnaire_uuid` não existe para
            esta aplicação, um recurso `SIGNATURE` que entraria em produção sem
            nenhum documento para assinar, um recurso `SIGNATURE` com
            `signature_level: "aes"` enquanto Advanced (AES) não está disponível
            (`aes_not_available`, veja `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.
          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á ausente, malformada, expirada ou tem escopo para
            outra aplicação. Renove pela [Auth
            API](/pt-br/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 mídia não suportado. Defina `Content-Type: application/json`
            e envie um corpo JSON.
        '429':
          description: >-
            Limite de requisições excedido. Tente novamente com backoff
            exponencial.
        '500':
          description: >-
            Erro inesperado do servidor ao criar o fluxo. É seguro tentar
            novamente.
      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: >-
        Cria um fluxo de verificação (workflow) linear simples. A API converte
        internamente esta lista de recursos em um grafo de fluxo baseado em nós.
        Lista de campos permitidos RESTRITA: qualquer chave não listada aqui
        (incluindo `workflow_type`) é rejeitada com 400.
      required:
        - features
      properties:
        workflow_label:
          type: string
          maxLength: 50
          default: ID Verification
          description: >-
            Nome de exibição do fluxo de verificação (workflow) (máximo de 50
            caracteres). O padrão é `"ID Verification"` quando omitido; sempre
            defina-o para que os fluxos continuem distinguíveis.
          example: Standard KYC
        is_default:
          type: boolean
          description: Define este fluxo como 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.
            Use `draft` somente quando quiser salvar sem publicar.
        features:
          type: array
          minItems: 1
          description: >-
            Recursos de verificação em ordem de execução. A API vincula cada
            item ao seguinte e adiciona automaticamente um nó final de status.
          items:
            $ref: '#/components/schemas/SimpleWorkflowFeature'
        is_white_label_enabled:
          type: boolean
          description: >-
            Ativa a personalização de marca própria para as sessões criadas com
            este fluxo.
          default: false
        is_desktop_allowed:
          type: boolean
          description: >-
            Permite que o fluxo de verificação seja executado em navegadores
            desktop.
          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 usuário após a primeira recusa.
        retry_window_days:
          type: integer
          minimum: 1
          maximum: 365
          nullable: true
          default: 7
          description: >-
            Janela móvel em dias usada para contar as novas tentativas. `null`
            aplica um limite para todo o histórico.
        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 mais duas novas tentativas. Esse valor também é
            copiado para os nós de prova de vida e de estimativa de idade
            gerados no fluxo de verificação (workflow), a menos que a
            configuração no nível do recurso 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 mais duas novas tentativas. Esse valor
            também é copiado para os nós de correspondência facial gerados no
            fluxo de verificação (workflow), a menos que a configuração no nível
            do recurso o substitua.
        session_expiration_time:
          type: integer
          minimum: 3600
          maximum: 2419200
          default: 604800
          description: >-
            Segundos até a expiração de uma sessão não concluída. 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 traz todas as outras
        colunas da configuração do fluxo de verificação (ativação de recursos,
        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 da 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 compartilhado por todas as versões do fluxo.
            As sessões criadas com ele usam a versão publicada mais recente.
        workflow_label:
          type: string
          description: Nome de exibiçã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 do fluxo. `null` para fluxos em grafo criados sem modelo.
        is_default:
          type: boolean
          description: Se este é o fluxo de verificação (workflow) padrão da aplicação.
        is_archived:
          type: boolean
          description: 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 usadas 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ública que inicia uma sessão com este fluxo.
        workflow_graph:
          type: object
          nullable: true
          additionalProperties: true
          description: >-
            Estrutura de grafo do fluxo (nós e arestas). `null` para fluxos
            simples.
        features:
          type: string
          description: >-
            Recursos ativados unidos 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 barato do fluxo.
        max_price:
          type: number
          nullable: true
          description: Preço do caminho mais caro do fluxo.
        special_add_on_prices:
          type: object
          nullable: true
          additionalProperties: true
          description: >-
            Preços de complementos específicos da organização, ou `null` quando
            nenhum está definido.
        max_retry_attempts:
          type: integer
          description: Novas tentativas permitidas após uma verificação recusada.
        retry_window_days:
          type: integer
          nullable: true
          description: Dias durante os quais as novas tentativas são permitidas.
        session_expiration_time:
          type: integer
          description: Segundos até a expiração de uma sessão não concluída.
        questionnaire:
          type: object
          nullable: true
          additionalProperties: true
          description: O questionário vinculado ao fluxo, ou `null`.
        kyb_role_settings:
          type: array
          items:
            type: object
            additionalProperties: true
          description: >-
            Configurações de verificação por função para as pessoas-chave do
            KYB.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        signature_availability:
          type: object
          description: >-
            Quais níveis de Assinatura eletrônica podem assinar neste momento.
            Verifique antes de definir `signature_level`. Retornado em fluxos
            `kyc`, `kyb` e de grafo.
          properties:
            simple:
              type: boolean
              description: O Simple está sempre disponível.
            aes:
              type: boolean
              description: >-
                Indica se a assinatura Avançada (AES) pode assinar agora.
                Enquanto for `false`, salvar uma nova etapa com
                `signature_level: "aes"` é recusado com `aes_not_available`.
            qes:
              type: boolean
              description: >-
                Indica se a assinatura Qualificada (QES) pode assinar agora. É
                `false` até que um provedor qualificado seja conectado.
        is_bank_verification_enabled:
          type: boolean
          description: >-
            Indica se um fluxo simples de KYC ou KYB executa a Verificação
            bancária. Ela é executada após as etapas de contato (e a Verificação
            de localização) e antes da validação de banco de dados, da AML e da
            análise de IP. A Verificação bancária ainda não está disponível para
            todos: ativá-la é recusado onde quer que ela não esteja habilitada.
        bank_verification_config:
          type: object
          additionalProperties: true
          description: >-
            Configurações de verificação bancária de um fluxo simples, com as
            mesmas chaves aceitas por um nó `BANK_VERIFICATION` (por exemplo,
            `bank_countries` e `bank_method_order`). Chaves desconhecidas são
            descartadas. Todas as chaves são opcionais e uma chave ausente
            mantém seu valor padrão, então `{}` significa todos os valores
            padrão. Consulte [Configurações de verificação
            bancária](/pt-br/management-api/workflows/feature-configs#verificação-bancária).
          example: {}
        is_geolocation_enabled:
          type: boolean
          description: >-
            Indica se um fluxo simples de KYC executa a Verificação de
            localização. Ela é executada após as etapas de contato e antes da
            Verificação bancária e da validação de banco de dados. Fluxos de
            empresas (KYB) não podem executá-la.
        geolocation_config:
          type: object
          additionalProperties: true
          description: >-
            Configurações de verificação de localização de um fluxo de
            verificação (workflow) simples, com as mesmas chaves que um nó
            `GEOLOCATION` aceita (por exemplo,
            `is_geolocation_geofencing_enabled` e `geolocation_max_attempts`).
            Chaves desconhecidas são descartadas. Todas as chaves são opcionais
            e uma chave ausente mantém seu valor padrão, então `{}` significa
            todos os valores padrão.
          example: {}
      additionalProperties: true
    SimpleWorkflowFeature:
      type: object
      required:
        - feature
      properties:
        feature:
          type: string
          description: >-
            Recurso a ser executado. Use os valores em maiúsculas mostrados
            aqui.
          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: >-
            Configurações específicas do recurso. Para `QUESTIONNAIRE`, inclua
            `questionnaire_uuid` com o `questionnaire_id` retornado pelo
            endpoint de criação de questionário. Para `SIGNATURE`, um fluxo de
            verificação (workflow) que entra em produção precisa de um documento
            para assinar: defina `signature_documents_source` como `session` ou
            `both` para que cada sessão possa trazer o seu, 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 métodos de Verificação de identidade cada
            país pode usar (captura de documento, consulta sem documento a uma
            fonte governamental ou outra fonte oficial e carteiras de identidade
            digital); se omitido, todos os países usam somente captura de
            documento. Para `SIGNATURE`, consulte as [configurações de
            Assinatura
            eletrônica](/pt-br/management-api/workflows/feature-configs#assinatura-eletrônica):
            `signature_level` aceita, por enquanto, `simple` (o padrão, sem
            necessidade de verificação de identidade, e-mail ou telefone) e
            `aes`, e os documentos do fluxo e seus campos são gerenciados no
            console; por isso, pela API, envie documentos e campos por sessão
            com `signature_documents_source: "session"`.
          additionalProperties: true
        label:
          type: string
          description: Rótulo interno opcional para este nó do fluxo.
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
      description: >-
        Chave de API da sua aplicação, em Developers -> API keys no Business
        Console. A chave principal tem acesso total. Uma chave nomeada pode ter
        escopo restrito: nenhum acesso, 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á errada, 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 a requisição veio de um
        endereço fora da sua lista de IPs; 404 em uma rota de sessão significa
        que a sessão está fora dos fluxos ou status da chave. Uma chave sem
        acesso a mídia recebe URLs de imagem, vídeo e PDF como null, e uma chave
        sem permissão de escrita em sessões recebe links e tokens de sessão como
        null. Consulte https://docs.didit.me/console/api-keys.

````

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