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

# Didit MCP: avançado, solução de problemas e self-hosting

> Guia avançado do MCP da Didit: funções e permissões, uso entre aplicações, catálogo hospedado, webhooks, solução de problemas e auto-hospedagem.

## Escopos e permissões por função

O conector opera com os escopos `didit:management` e `didit:verification` (um terceiro, `didit:staff`, é anunciado, mas só é relevante para contas da equipe da Didit). Os dois limites são aplicados em cada chamada: o token carrega os escopos que você aprovou e, **dentro deles, sua função no console decide o que você pode fazer**. O acesso efetivo é a interseção dos dois, então aprovar apenas `didit:verification` mantém a área de gerenciamento fechada até para um `OWNER`.

Cada ferramenta corresponde a uma permissão do backend (`read:sessions`, `write:workflows`, `delete:lists` e assim por diante; a [referência de ferramentas](https://docs.didit.me/pt-br/integration/mcp/tools.md) indica a permissão de cada ferramenta). O servidor verifica previamente essa permissão em relação às permissões da organização retornadas pela introspecção do token, de acordo com `MCP_PERMISSION_MODE`:

| Modo | Comportamento |
| - | - |
| `shadow` (padrão) | Todas as ferramentas são listadas; uma chamada que sua função não pode fazer vai para o backend, que responde `403`. O resultado é registrado no registro de auditoria do servidor. |
| `enforce` | As ferramentas que sua função não pode executar ficam de fora de `tools/list`; chamar uma delas retorna um erro `missing_scope` que indica a permissão. |
| `off` | Nenhuma verificação prévia. |

O MCP nunca vai além da sua função. Um `403` em uma ferramenta significa que sua função não tem essa permissão, não que a conexão inteira falhou.

***

## Resolução entre aplicações e organizações

A API de gerenciamento da Didit tem escopo por aplicação (`/organization/{org}/application/{app}/…`). O MCP resolve o escopo para você:

* **Uma única organização + aplicação** → resolvido automaticamente; você nunca precisa passar IDs.
* **Várias aplicações** → as ferramentas entre aplicações (`didit_session_search`, `didit_transaction_search`, `didit_case_search`, `didit_vendor_user_search`, `didit_vendor_business_search`, `didit_workflow_search`, `didit_analytics`) agregam dados de **todas** elas em uma única chamada, com cada linha marcada com seu `organization_id` / `application_id`.
* As ferramentas de listagem por aplicação (`didit_session_list`, `didit_transaction_list`, `didit_case_list`, `didit_vendor_user_list`, `didit_vendor_business_list`, `didit_workflow_list`) **abrangem automaticamente todas as aplicações** quando você não especifica uma, então um simples "liste minhas sessões" funciona mesmo com muitas aplicações. Passe `organization_id` + `application_id` para restringir a uma única aplicação.

Use `didit_context_get` para ver todas as suas organizações e aplicações (e seus IDs) em uma única chamada. Ela também informa o `mode` de cada aplicação (`live` ou `sandbox`), então nunca deduza o ambiente pelo nome de uma aplicação; `didit_session_create` aceita um `sandbox_scenario` em aplicações sandbox para execuções de teste sem cobrança.

***

## O que o catálogo hospedado contém

O conector hospedado oferece **169 ferramentas**. Ele também lista 17 ferramentas do Investigator, que só funcionam depois que o Investigator é habilitado para sua organização. Outras duas ferramentas, `didit_org_top_up` (checkout do Stripe) e `didit_org_reveal_application_api_key` (segredo em texto puro), ficam ocultas no `tools/list` de todo chamador autenticado via OAuth. As cinco ferramentas de configuração de conta (`didit_account_*`) são totalmente excluídas dos servidores remotos e se recusam a ser executadas neles, porque um conector remoto nunca deve receber credenciais como argumentos de ferramenta. Um servidor stdio local mantém todas elas, 193 no total.

Cada ferramenta listada traz anotações MCP (`readOnlyHint`, `destructiveHint`, `openWorldHint`, um `title`) e um `outputSchema`, além de um rótulo `_meta["anthropic/toolGroup"]` que indica sua área (Sessions, Workflows & Questionnaires, Transactions (AML) e assim por diante). Clientes como o Claude usam as anotações para dividir as ferramentas do conector em grupos de somente leitura, de escrita e de ações destrutivas.

***

## Fluxos com ramificações

Fluxos simples são uma lista plana de recursos. Para **lógica condicional** (recusar com base em um status, direcionar com base em um campo extraído, condicionar uma etapa a uma correspondência, adicionar uma requisição de Document AI), o MCP manipula o **grafo de nós** da Didit (a mesma estrutura do construtor visual do console). Um grafo é `{ start_node, nodes }`; cada nó é um `feature`, `branch`, `status` (terminal), `action` ou `webhook`. As regras de ramificação aceitam operadores, entre eles **`fuzzy_match`** (campos de texto, com um limiar de similaridade `score` de 0 a 100), em campos extraídos como `kyc.extra_fields.profession`, e um nó de recurso `DOCUMENT_AI` solicita documentos (por exemplo, comprovante de fundos).

### Editar um fluxo existente (recomendado)

Fluxos reais carregam configurações de recursos **grandes**: uma lista de permissões `documents_allowed` do OCR pode ter mais de 150 KB, além das listas de comprovante de endereço e das listas de países para telefone. Por isso, **não** leia o grafo inteiro para reenviá-lo. Em vez disso, edite com pequenas **operações**:

**1. Localizar e inspecionar**

`didit_workflow_search { workflow_id }` encontra o fluxo em todas as suas aplicações. `didit_workflow_get_graph` retorna a estrutura com as configurações grandes **resumidas** (nunca excede o limite de tamanho; passe `include_config: true` para obter os valores brutos); `didit_workflow_get_field_definitions` lista os campos que podem ser usados em ramificações e os operadores válidos.

**2. Editar com operações**

`didit_workflow_edit_graph` recebe algumas operações pequenas (`set_next`, `set_node`, `set_branches`, `merge_node_config`), e o MCP busca o **grafo completo no servidor**, aplica as operações, valida, cria automaticamente um **rascunho** e salva. Suas listas de permissões são preservadas byte a byte e nunca passam pela requisição. Uma falha de validação retorna `applied: false` (nada é salvo).

**3. Revisar e publicar**

O fluxo continua como um rascunho revisável até você executar `publish` (no console ou via `didit_workflow_publish`). A versão em produção nunca é alterada.

Por exemplo, para inserir uma ramificação após a Verificação de identidade que **retorna ao seu pipeline existente**, você só envia: redirecionar o `next` do nó de OCR para uma nova ramificação e adicionar a ramificação, um nó terminal `Declined` e uma etapa `DOCUMENT_AI` (cujo `next` aponta de volta para a próxima etapa original). Os outros 10 nós, com suas listas de permissões, permanecem intactos.

> *"Após a Verificação de identidade, se ela tiver sido recusada, recuse; se a profissão for ≈ 'Software Engineer' (≥80%), solicite comprovante de fundos; depois, continue."* → um nó `branch`: `kyc.status == Declined` → um nó de status `Declined`; `kyc.extra_fields.profession` `fuzzy_match` "Software Engineer" com score 80 → um nó `DOCUMENT_AI` de comprovante de fundos → e um caminho **senão** de volta ao pipeline.

> [!NOTE]
>
> **Uma ramificação sempre precisa de um senão explícito.** O caminho padrão (aquele seguido quando nenhuma condição corresponde) deve ser uma ramificação com `rules` vazio, por exemplo `{ "id": "else", "rules": [], "goto": "<liveness>" }`, e não o `next` simples do nó de ramificação. O MCP (e o backend) convertem automaticamente o `next` de um nó de ramificação em uma ramificação senão, de modo que uma ramificação nunca é exibida como uma conexão ambígua sem um senão visível.

Para criar um fluxo **novo** do zero, use `didit_workflow_validate_graph` e depois `didit_workflow_set_graph` (grafo completo), ou deixe que `didit_workflow_build_graph` monte um grafo completo a partir de uma especificação simples de recursos, sem salvar. Até um simples `didit_workflow_create` é salvo como esse mesmo grafo de nós: o MCP transforma sua lista `features` em nós de recurso encadeados que terminam em um status de decisão automática, para que os recursos realmente sejam executados (por padrão, ele publica o fluxo; passe `status: "draft"` para mantê-lo não publicado). Consulte [Criar fluxo → fluxos com ramificações e baseados em nós](https://docs.didit.me/pt-br/management-api/workflows/create.md) para ver o formato JSON completo.

### Ramificações por motivo pelo qual o NFC foi ignorado com o MCP

Para um grafo novo, peça:

> *"Crie um fluxo em rascunho com Verificação de identidade seguida de NFC. Se `nfc.skip_reason` for `DEVICE_WITHOUT_NFC` ou `DOCUMENT_WITHOUT_CHIP`, envie a sessão para Em revisão; caso contrário, aprove-a. Mantenha uma ramificação senão explícita e valide o grafo antes de salvar."*

O agente deve chamar `didit_workflow_get_field_definitions`, `didit_workflow_validate_graph` e depois `didit_workflow_set_graph`. Para um fluxo existente, peça:

> *"Edite o fluxo `26d33c04…`: após o nó de NFC, direcione qualquer um dos sete valores conhecidos de `nfc.skip_reason` para Em revisão e preserve o caminho existente de NFC bem-sucedido como o senão explícito. Salve um rascunho; não o publique."*

O agente deve chamar `didit_workflow_search`, `didit_workflow_get_graph`, `didit_workflow_get_field_definitions` e depois `didit_workflow_edit_graph`. A ferramenta de edição aplica pequenas operações no servidor, por isso preserva os nós não relacionados e as listas de permissões grandes. Consulte [Criar fluxo → Ramificar conforme o motivo de o NFC não ter sido executado](https://docs.didit.me/pt-br/management-api/workflows/create.md#ramificação-com-base-no-motivo-de-o-nfc-não-ter-sido-executado) para ver os sete valores, as regras `equals`/`in`, o comportamento com nulos e um grafo completo e ordenado.

### Configurações de recursos e listas de permissões

O `config` de um nó de recurso restringe o que ele aceita. `didit_workflow_get_feature_config_schema` retorna todas as chaves que cada recurso aceita, com tipos e valores padrão, e as duas ferramentas de catálogo (`didit_workflow_get_id_verification_methods_catalog`, `didit_workflow_get_kyb_registry_catalog`) fornecem as informações do servidor de que uma configuração depende. A lista de permissões canônica do OCR é por país → por documento → objeto: `{ "<ISO3>": { "<CODE>": { "enabled": 1 } } }` (por exemplo, `{ "ESP": { "P": { "enabled": 1 } } }`). O MCP **normaliza formatos mais simples** para você antes de validar, então você não precisa escrever essa estrutura à mão:

| Você pode passar | Normalizado para |
| - | - |
| Um array de tipos de documento: `["PASSPORT"]` | esse documento para **todos** os países (somente passaporte no mundo todo) |
| **Nomes** ou **códigos** de documentos: `PASSPORT`/`P`, `NATIONAL_ID`/`ID`, `DRIVER_LICENSE`/`DL`, `RESIDENCE_PERMIT`/`RP` (também `SSC`, `HIC`, `TC`) | o código canônico |
| Uma chave de país `"ALL"`: `{ "ALL": { "P": 1 } }` | aplicado a todos os países |
| Flags escalares: `{ "ESP": { "P": 1 } }` | `{ "ESP": { "P": { "enabled": 1 } } }` |
| Totalmente omitido | todos os documentos, todos os países |

O `poa_languages_allowed` de `PROOF_OF_ADDRESS` segue a mesma ideia: passe um array de idiomas (`["es"]`) ou um mapa (`{ "es": 1 }`). Essas conveniências se aplicam sempre que você fornece ao MCP uma configuração de recurso: nos recursos de `didit_workflow_create` e nos `config` dos nós em `set_graph` / `edit_graph` / `validate_graph`.

## Formato da resposta (contrato V3)

Toda ferramenta retorna o JSON literal do endpoint REST correspondente. Há dois pontos importantes:

* **Ferramentas síncronas** (fluxos, faturamento, `verify_*` avulsas, gestão de listas) retornam o resultado final diretamente na resposta: aja com base nele no mesmo turno.
* **A criação de sessão é assíncrona**: `didit_session_create` retorna uma `url` para o usuário acessar e concluir; a decisão chega por webhook ou por `didit_session_get_decision`.

As chaves da decisão são **sempre arrays no plural**, com um `node_id` por entrada (`id_verifications[]`, `liveness_checks[]`, `face_matches[]`, `aml_screenings[]`, `poa_verifications[]`, `phone_verifications[]`, `email_verifications[]`, `database_validations[]`, `reviews[]`, além das chaves de KYB `registry_checks[]`, `document_verifications[]`, `key_people_checks[]`). Consulte [Modelos de dados](https://docs.didit.me/pt-br/reference/data-models.md) e [Status de verificação](https://docs.didit.me/pt-br/integration/verification-statuses.md).

***

## Tratamento de webhooks

Deixe que um webhook entregue a decisão final ao seu backend em vez de o agente fazer polling:

1. O agente chama `didit_webhook_create` uma única vez com um `label`, a sua `url` HTTPS pública, `webhook_version: "v3"` e `subscribed_events` (normalmente `["status.updated", "data.updated"]`). A resposta oculta o segredo de assinatura, assim como `didit_webhook_get` e `didit_webhook_list`; copie-o em **Webhooks** no console e armazene-o como `DIDIT_WEBHOOK_SECRET`.
2. O seu backend verifica o cabeçalho canônico **X-Signature-V2** em cada entrega: `shortenFloats` → `sortKeys` → `JSON.stringify` (unicode sem escape) → HMAC-SHA256 → `timingSafeEqual`. Rejeite se `abs(now − X-Timestamp) > 300` segundos.
3. Elimine duplicatas com base em `event_id`. Retorne `2xx` em até **5 segundos**; transfira o processamento pesado para uma fila.
4. A Didit tenta novamente em caso de `5xx` / `404` até **2 vezes** (após \~1 min e depois após \~4 min) e então descarta a entrega. Reenvie as que falharam pela aba **Deliveries** no console.

O agente só precisa *configurar* o receptor; ele mesmo não fica escutando. Contrato completo: [Webhooks](https://docs.didit.me/pt-br/integration/webhooks.md).

***

## Transporte e endpoints

| Endpoint | Finalidade |
| - | - |
| `POST https://mcp.didit.me/mcp` | O endpoint MCP (Streamable HTTP). `GET` e `DELETE` respondem `405`: o servidor é stateless, cria uma nova instância de servidor por requisição e não mantém nenhum stream iniciado pelo servidor nem sessão a encerrar. |
| `GET https://mcp.didit.me/healthz` | Verificação de integridade: `{ "status": "ok", "service": "didit-mcp-server", "version": "5.1.1" }`. |
| `GET https://mcp.didit.me/.well-known/oauth-protected-resource/mcp` | Metadados de recurso protegido conforme a RFC 9728 (também servidos na raiz da origem). |

O endpoint atende às duas gerações do protocolo: clientes que fixam uma versão de protocolo de 2026-07-28 ou posterior em todas as mensagens dispensam o handshake `initialize` e podem chamar `server/discover`; todos os clientes anteriores (Claude Code, Cursor, clientes baseados em SDK) usam o handshake padrão do Streamable HTTP sem alterações. O CORS é aberto, então clientes baseados em navegador (o conector web do Claude, o MCP Inspector) conseguem acessar `/mcp` e os documentos de descoberta a partir de outra origem. O limite do corpo JSON é de 25 MB, suficiente para um arquivo de entrada de 15 MB em base64.

***

## Solução de problemas

> [accordion] O agente chama uma ferramenta e recebe 403 ou missing\_scope
>
> A sua **função** nessa organização não tem a permissão que a ferramenta exige (por exemplo, um `READER` tentando uma operação de escrita); o erro `missing_scope` indica qual é. Se todas as ferramentas falharem, reconecte ou execute novamente *Log in with Didit*. (O MCP aceita apenas Bearer/OAuth: um `x-api-key` é rejeitado por todas as ferramentas, então uma chave de API não resolve um 403.)

> [accordion] Uma operação de escrita ou exclusão não foi executada
>
> Ferramentas destrutivas exigem confirmação no seu cliente. Aprove a chamada ou verifique se o pedido de confirmação da ferramenta não está sendo negado automaticamente. `didit_session_delete`, `didit_session_batch_delete` e `didit_org_reveal_application_api_key` exigem, além disso, `confirm: true` nos argumentos da ferramenta; sem isso, a chamada é rejeitada antes de qualquer execução.

> [accordion] 'organization\_id is required' ou nenhum resultado
>
> Provavelmente você tem várias aplicações. Use uma ferramenta `*_search` (que abrange tudo) ou execute `didit_context_get` e passe o `organization_id` / `application_id` desejado.

> [accordion] O navegador não abriu para o login (hospedado)
>
> Alguns clientes (Windsurf, Zed) se conectam pela ponte `mcp-remote`: verifique se `npx -y mcp-remote@latest https://mcp.didit.me/mcp` está na configuração. Reabra o cliente para disparar novamente o handshake OAuth.

> [accordion] As ferramentas não aparecem no cliente
>
> Confirme que o servidor se conectou (a maioria dos clientes mostra um indicador de status) e que você concluiu *Log in with Didit*. Reinicie o host MCP depois de editar a configuração dele. `didit_org_top_up` e `didit_org_reveal_application_api_key` nunca são listadas no conector hospedado.

> [accordion] O VS Code (ou outro cliente) não consegue descobrir o OAuth ou nunca abre o navegador
>
> Verifique se você está apontando para `https://mcp.didit.me/mcp`, e não para a origem sem caminho `https://mcp.didit.me`: os metadados do recurso são servidos em `/.well-known/oauth-protected-resource/mcp`, restritos a esse caminho (com uma cópia na raiz da origem para os clientes que a consultam). Se você hospeda o servidor por conta própria, isso geralmente significa que `MCP_RESOURCE_URI` está definido com a origem em vez da URL de transporte completa; consulte [Auto-hospedagem](#auto-hospedagem).

> [accordion] Um upload de arquivo é rejeitado
>
> No endpoint hospedado, envie o arquivo como `*_base64` (base64 puro ou uma URL `data:`); `*_path` só funciona em um servidor stdio local. Os arquivos têm limite de 15 MB e precisam ser PNG, JPG, WebP, GIF, BMP, ICO ou PDF pelo conteúdo, e não apenas pela extensão.

***

## Segurança

O MCP age com as suas permissões reais e pode alterar recursos em produção. Mantenha a opção **"review tool calls before running"** ativada no seu cliente, principalmente para as ferramentas de **Escrita** e **Destructive**. O próprio MCP **não armazena nenhum segredo** na sua configuração (ele usa **Entrar com a Didit**). Ele também oculta chaves de API e segredos de assinatura de webhook na saída das ferramentas, esconde do catálogo hospedado as ferramentas de recarga e de chaves brutas e nunca aceita credenciais de conta como argumentos de ferramenta em um servidor remoto. Cada chamada de ferramenta é gravada no registro de auditoria do servidor com o autor da chamada, a ferramenta e a decisão de permissão.

### Injeção de prompt

As ferramentas da Didit retornam o que os seus usuários digitaram ou enviaram, como nomes, texto de documentos e respostas de questionários.
A pessoa que está sendo verificada controla esse texto. Por isso, ela pode escrever em um nome ou em um documento instruções direcionadas ao seu agente, e o servidor retorna esses campos exatamente como foram capturados.
Um agente não consegue distinguir com segurança esse texto das suas próprias instruções, então trate todo resultado de ferramenta como dado:

* Mantenha a aprovação de ferramentas ativada para as ferramentas de **Escrita** e **Destructive** e leia o que o agente está prestes a fazer antes de aprovar.
* Entre como um membro cuja função seja adequada à tarefa. Um agente que apenas lê sessões pode usar um membro `READER`, e o backend recusa suas operações de escrita, não importa o que o agente tente.
* Teste primeiro um novo agente ou prompt em uma [aplicação de sandbox](https://docs.didit.me/pt-br/integration/sandbox-testing.md).
* Nunca aprove nem recuse uma verificação porque um texto dentro dela manda fazer isso. Baseie-se na decisão, nos avisos dela e na sua própria revisão.

### Dados pessoais

Os resultados das ferramentas podem incluir dados pessoais, como nomes, datas de nascimento, números de documentos e links para imagens, e o seu cliente MCP os envia ao modelo que ele usa.
Antes de conectar uma aplicação em produção, confirme que o seu contrato com o provedor de IA cobre esses dados ou trabalhe em uma aplicação de sandbox.

***

## Auto-hospedagem

O servidor hospedado é construído a partir de [github.com/didit-protocol/mcp](https://github.com/didit-protocol/mcp) (MIT). Clone o repositório e execute com **Docker** ou Node. Ele sempre se autentica como um **usuário** da Didit (não existe modo com chave de API), em qualquer um dos transportes. As etapas de configuração estão em [Instalação → auto-hospedagem](https://docs.didit.me/pt-br/integration/mcp/installation.md#auto-hospedagem).

```bash theme={null}
git clone https://github.com/didit-protocol/mcp.git && cd mcp

# Docker (recommended) — serves /mcp + /healthz on port 3000
docker build -t didit-mcp . && docker run -p 3000:3000 --env-file .env didit-mcp

# …or with Node
npm install && npm run build
node dist/http.js                                          # hosted HTTP/OAuth
DIDIT_ACCESS_TOKEN=<user-access-token> node dist/index.js  # stdio (headless)
```

### Variáveis de ambiente

| Variável | Modo | Finalidade |
| - | - | - |
| `DIDIT_ACCESS_TOKEN` | stdio | O token de acesso Bearer do usuário para execuções sem interface. |
| `DIDIT_API_BASE_URL` | ambos | Base da API de verificação (padrão `https://verification.didit.me/v3`). |
| `DIDIT_AUTH_BASE_URL` | ambos | Base da API de autenticação (padrão `https://apx.didit.me/auth/v2`). |
| `MCP_PORT` | hospedado | Porta HTTP de escuta (padrão `3000`). |
| `MCP_RESOURCE_URI` | hospedado | URL pública do transporte, incluindo o caminho (por exemplo, `https://mcp.example.com/mcp`). A URL de metadados da RFC 9728 é derivada dela. |
| `MCP_AUTHORIZATION_SERVER_ORIGIN` | hospedado | Origem do servidor de autorização (padrão `https://business.didit.me`). |
| `MCP_TOKEN_VERIFY_MODE` | hospedado | `introspection` (padrão; tokens opacos) ou `jwks` (tokens de acesso JWT). |
| `MCP_OAUTH_INTROSPECT_URL` | hospedado | Endpoint de introspecção da RFC 7662. |
| `MCP_OAUTH_CLIENT_ID` / `MCP_OAUTH_CLIENT_SECRET` | hospedado | Credenciais de autenticação Basic que o servidor de recursos usa para chamar a introspecção. Forneça-as usando um gerenciador de segredos. |
| `MCP_SCOPES_SUPPORTED` | hospedado | Escopos anunciados nos metadados (padrão `didit:management didit:verification`). |
| `MCP_DEFAULT_ORG` / `MCP_DEFAULT_APP` | ambos | Preenchem automaticamente o escopo em implantações de locatário único. |
| `DIDIT_AUTH_ISSUER`, `DIDIT_OIDC_DISCOVERY_URL`, `DIDIT_OIDC_AUTHORIZE_URL`, `DIDIT_OIDC_TOKEN_URL`, `DIDIT_OIDC_REGISTRATION_URL` | hospedado | Substituições para os metadados do servidor de autorização; só são necessárias quando ele publica seus endpoints em um local diferente dos padrões da origem. |
| `DIDIT_JWKS_URL` | hospedado | JWKS usado quando `MCP_TOKEN_VERIFY_MODE=jwks`. |
| `MCP_PERMISSION_MODE` | ambos | Pré-verificação de permissão por ferramenta: `shadow` (padrão), `enforce` ou `off`. |
| `MCP_JSON_BODY_LIMIT` | hospedado | Limite do corpo JSON-RPC (padrão `25mb`). |

O `.env.example` no repositório é um modelo que pode ser copiado, e o `ARCHITECTURE.md` explica a separação entre servidor de recursos e servidor de autorização. Na inicialização, o servidor HTTP busca o documento de descoberta do servidor de autorização e, se ele estiver inacessível, recorre às URLs configuradas.

***

> [card] [Referência de ferramentas](https://docs.didit.me/pt-br/integration/mcp/tools.md)
>
> Todas as ferramentas, agrupadas por área.

> [card] [Exemplos](https://docs.didit.me/pt-br/integration/mcp/examples.md)
>
> Prompts e conversas de ponta a ponta.


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