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

# 문서 AI API: 맞춤형 문서 데이터 추출

> POST /v3/document-ai/로 PDF나 이미지에서 정의한 필드를 서버 간 방식으로 추출하며, 이름 대조와 위변조 검사를 지원합니다. Didit 기준 문서당 $0.20.

문서 한 개와 그 문서에서 읽어 올 필드 목록을 전송하십시오. Didit은 파일에 기본 추출 모델을 실행해 모든 필드를 타입과 함께 반환합니다(`text`, `number`, 또는 `YYYY-MM-DD` 형식의 `date`). 또한 지원되는 QR 코드와 바코드를 감지해 디코딩하고, 파일의 변조 여부를 확인하며, 원하는 경우 문서상의 이름을 기대하는 개인 또는 회사와 비교합니다. 급여명세서, 은행 거래내역서, 세무 서류, 청구서, 자금 증명, 또는 고정된 양식에 맞지 않는 모든 문서에 사용하십시오.

이 기능은 호스팅 [Document AI](https://docs.didit.me/ko/core-technology/document-ai/overview.md) 단계의 서버 간 버전입니다. 요청은 `multipart/form-data`이며, 파일은 `document`에, 필드 정의는 `fields`에 하나의 JSON 인코딩 배열 문자열로 담아 보냅니다.

## 추출할 필드 정의

`fields`는 하나의 문자열로 전송되는 1개에서 30개 사이의 JSON 배열입니다. 각 항목에는 `key`(소문자, 숫자, 밑줄만 사용하며 요청 내에서 고유해야 함), 사람이 읽을 수 있는 `name`, 모델이 무엇을 찾아야 하는지 알려 주는 `instruction`, `type`(`text`, `number`, `date` 중 하나), 그리고 두 개의 플래그인 `required`와 `is_full_name`이 있습니다.

```json theme={null}
[
  {
    "key": "account_holder",
    "name": "Account holder",
    "instruction": "Full name of the person or company the account belongs to",
    "type": "text",
    "required": true,
    "is_full_name": true
  },
  {
    "key": "balance",
    "name": "Closing balance",
    "instruction": "Closing balance shown on the statement, as a number without the currency symbol",
    "type": "number",
    "required": true
  },
  {
    "key": "currency",
    "name": "Currency",
    "instruction": "Three-letter currency code of the balance",
    "type": "text"
  },
  {
    "key": "statement_date",
    "name": "Statement date",
    "instruction": "Date the statement was issued",
    "type": "date",
    "required": true
  }
]
```

multipart 요청에서는 배열 전체가 하나의 폼 값입니다.

```bash theme={null}
curl -X POST https://verification.didit.me/v3/document-ai/ \
  -H "x-api-key: $DIDIT_API_KEY" \
  -F "document=@./bank-statement.pdf" \
  -F 'fields=[{"key":"account_holder","name":"Account holder","instruction":"Full name of the account holder","type":"text","required":true,"is_full_name":true},{"key":"balance","name":"Closing balance","instruction":"Closing balance as a number","type":"number","required":true}]' \
  -F "expected_first_name=Jane" \
  -F "expected_last_name=Doe"
```

응답은 정규화된 `fields`를 그대로 반환하고, `extracted_data`에 `key` 값을 기준으로 추출 결과를 담습니다. 모델이 찾지 못한 값은 `null`로 반환됩니다. 각 항목의 `name`, `instruction`, `type`은 선택 사항이며(`type`의 기본값은 `text`), `key`만 필수입니다. `fields`를 대괄호 형식의 폼 키(`fields[0][key]=...`)로 보내거나 JSON 배열이 아닌 형식으로 보내면, 추출이 시작되기 전에 `400`이 반환되며 요금은 청구되지 않습니다.

## QR 코드 및 바코드

성공한 모든 응답에는 `document_ai.detected_codes`가 포함됩니다. 각 항목에는 다음 필드가 있습니다.

| 필드 | 설명 |
| - | - |
| `type` | 감지된 심볼로지(`QR_CODE`, `CODE_128`, `EAN_13`, `DATA_MATRIX`, `PDF_417` 등) |
| `page_number` | 1부터 시작하는 페이지 번호 |
| `position` | 렌더링된 페이지 픽셀 기준으로 감지된 다각형 좌표이며, 신뢰할 수 있는 위치 정보가 없으면 `null` |
| `x`, `y`, `width`, `height` | 렌더링된 페이지 픽셀 기준의 경계 상자이며, 신뢰할 수 있는 위치 정보가 없으면 `null` |
| `page_width`, `page_height` | 좌표 계산에 사용된 렌더링 페이지의 크기 |
| `parsed_payload` | 디코딩된 문자열 또는 구조화된 값이며, 코드의 위치만 확인할 수 있는 경우 `null` |
| `raw_payload` | 가능한 경우 디코더의 원시 payload를 base64 문자열로 반환하며, 그렇지 않으면 빈 문자열 |

이 배열은 PDF의 처리된 모든 페이지를 포함하며, 지원되는 코드가 없으면 비어 있습니다. Didit은 payload를 추측하지 않습니다. 손상된 QR 코드는 위치 정보와 함께 `parsed_payload: null`로 반환될 수 있으며, 코드 감지에 실패하더라도 Document AI 요청은 실패하지 않습니다.

## 이름 대조

`is_full_name: true`는 최대 한 개의 필드에만 설정하고, 문서의 소유자로 예상하는 사람 또는 회사를 함께 전송합니다.

| 검증 대상 | 전송 항목 | 이름이 비교되는 대상 |
| - | - | - |
| 개인 | `expected_first_name` + `expected_last_name` | 개인의 전체 이름 |
| 회사 | `expected_company_name`(선택 사항: `expected_company_country`, 2자리 ISO 코드) | 회사명 |

두 그룹을 함께 보내지 말고 둘 중 하나만 보내야 합니다. 이름이 추출되고 예상 이름이 있으면, 응답에 `name_match_score`(0\~100)와 `name_on_document`가 포함됩니다. 점수가 `document_ai_name_match_score_threshold`(기본값 `80`)보다 낮으면 `DOCUMENT_AI_NAME_MISMATCH` 경고가 발생하고 `document_ai_name_mismatch_action`이 적용됩니다. `is_full_name`으로 표시된 필드가 없으면, Didit은 키가 `name`, `holder`, `full_name`, `account_holder`처럼 이름으로 보이는 첫 번째 텍스트 필드를 사용합니다. 비교할 값을 직접 지정하려면 해당 필드에 명시적으로 표시하십시오. 문서에서 이름 값을 찾을 수 없거나 예상 이름을 보내지 않으면 이름 검사는 건너뛰며, `name_match_score`는 `null`이 됩니다.

## 위험 및 작업

아래의 각 위험은 요청별로 설정할 수 있는 작업 파라미터에 대응하며, 값은 `DECLINE`, `REVIEW`, `NO_ACTION` 중 하나입니다. 발생한 작업 중 가장 엄격한 작업이 `document_ai.status`를 결정하며(`Declined` > `In Review` > `Approved`), 발생한 모든 위험은 `document_ai.warnings`에 나열됩니다. 기본값은 호스팅 워크플로 단계와 같습니다. 각 위험이 감지되는 방식은 [Document AI 규칙 및 경고](https://docs.didit.me/ko/core-technology/document-ai/warnings-document-ai.md)를 참조하십시오.

| 위험 | 파라미터 | 기본값 |
| - | - | - |
| `DOCUMENT_AI_UNREADABLE_DOCUMENT` | `document_ai_unreadable_document_action` | `REVIEW` |
| `DOCUMENT_AI_MISSING_REQUIRED_FIELDS` | `document_ai_missing_required_fields_action` | `REVIEW` |
| `DOCUMENT_AI_SUSPECTED_MANIPULATION` | `document_ai_document_tampering_action` | `DECLINE` |
| `DOCUMENT_AI_NAME_MISMATCH` | `document_ai_name_mismatch_action` | `NO_ACTION` |
| `DOCUMENT_AI_UNSUPPORTED_FILE` | `document_ai_unsupported_file_action` | `REVIEW` |

## 파일, 저장 및 요금 청구

* **지원 파일:** PDF(여러 페이지 지원), JPG, JPEG, PNG, TIFF, WebP, 또는 문서가 포함된 ZIP 파일이며, 최대 30MB입니다. 암호화된 PDF는 `400`을 반환합니다. 복호화된 PDF나 사진을 업로드하십시오.
* **`save_api_request`**(기본값 `true`): 요청, 추출된 필드, 감지된 코드가 **Manual Checks** 메뉴에서 Business Console에 표시되어 팀이 검토할 수 있습니다. 저장을 건너뛰려면 `false`로 설정하십시오.
* **`vendor_data`**와 **`metadata`**는 응답에 변경 없이 그대로 반환되므로, 결과를 자체 기록과 연결할 수 있습니다.
* **요금:** 문서당 \$0.20이며, 문서가 처리되고 상태가 반환될 때 청구됩니다. 요금제에 따른 할인이 적용됩니다. [요금](https://docs.didit.me/ko/getting-started/pricing.md#단독-api-요금-서버-간)을 참조하십시오.

> [!NOTE]
>
> 사용자가 직접 문서를 업로드하도록 하거나, 한 번의 흐름에서 문서를 최대 3개까지 수집하거나, 업로드 화면을 자동 번역하거나, 추출된 필드를 신분증 또는 설문지 데이터와 대조해야 합니까? 그렇다면 대신 호스팅 [Document AI 워크플로 단계](https://docs.didit.me/ko/core-technology/document-ai/overview.md)를 사용하십시오.


## OpenAPI

````yaml ko/openapi-25.json POST /v3/document-ai/
openapi: 3.0.0
info:
  version: 3.0.0
  title: Didit Verification API
  description: 신분증 인증 API입니다. x-api-key 헤더로 인증합니다.
servers:
  - url: https://verification.didit.me
security: []
tags: []
paths:
  /v3/document-ai/:
    post:
      tags:
        - Standalone APIs
      summary: Document AI(사용자 정의 필드 추출 + 위변조 검사)
      description: >-
        한 번의 호출로 급여 명세서, 자금 증명, 자산 출처 확인서, 세금 명세서, 계약서, 청구서 등 모든 증빙 문서에서 **직접**
        정의한 필드를 추출합니다. 파일과 함께 `fields` 정의(키, 이름, 추출 지침, 유형)를 전송하면 유형이 지정된
        값(`text`, `number`, `YYYY-MM-DD` 형식의 `date`), 대상 개인 또는 기업에 대한 선택적 성명 일치
        점수, PDF/EXIF 변조 신호, `Approved`/`Declined`/`In Review` `status`, 발견된 모든
        문제를 설명하는 `warnings` 목록을 반환받습니다. 이는 호스팅 Document AI 워크플로
        단계(`/core-technology/document-ai/overview` 참조)의 서버 간 대응 기능으로, Didit의 기본
        추출 모델을 사용하며 제공업체 선택 기능은 제공하지 않습니다.


        **지연 시간.** 추출은 LLM 기반으로 수행됩니다. 일반적인 호출은 **5–15초**, 여러 페이지로 구성된 PDF는 **최대
        약 30초**가 소요됩니다. 클라이언트 타임아웃을 **최소 45초**로 구성하고 호출이 완료되기 전에는 재시도하지 마십시오.


        **요청 형식.** `multipart/form-data`만 지원됩니다. `fields`는 **하나의 JSON 인코딩된 배열
        문자열**(1–30개 항목, 최대 16 KB)이어야 하며 대괄호 스타일의 폼 배열은 `400`으로 거부됩니다. 각 항목은
        `{key, name, instruction, type, required, is_full_name}` 형식입니다. `key`는
        반환받을 JSON 키(`^[a-z0-9_]{1,64}$`, 고유값), `instruction`은 모델이 읽어야 할 내용을 안내하는
        지침, `type`은 `text`(기본값) | `number` | `date`, `required`는 값을 읽을 수 없을 때
        `DOCUMENT_AI_MISSING_REQUIRED_FIELDS`를 발생시키는 플래그이며, 최대 하나의 필드만
        `is_full_name: true`로 설정하여 예상 성명과 비교할 값을 지정할 수 있습니다.


        **성명 일치.** `expected_first_name` + `expected_last_name`(개인) **또는**
        `expected_company_name`(+ 선택적 `expected_company_country`)을 전달해야 하며 둘 다
        전달할 수는 없습니다. `is_full_name` 필드의 값(또는 지정된 필드가 없는 경우 성명처럼 보이는 키를 가진 첫 번째
        텍스트 필드)을 퍼지 방식으로 비교합니다. 양쪽 값이 모두 존재하면 `name_match_score`(0–100)와
        `name_on_document`가 항상 반환되며, 점수가
        `document_ai_name_match_score_threshold`(기본값 80) 미만인 경우
        `document_ai_name_mismatch_action`의 설정을 따르는 `DOCUMENT_AI_NAME_MISMATCH`가
        추가됩니다.


        **결정 로직.** 각 위험은 구성 가능한 작업에 매핑됩니다. `DECLINE` → `status: "Declined"`,
        `REVIEW` → `status: "In Review"`, `NO_ACTION` → 정보 제공 전용. 기본값은 워크플로 단계를
        그대로 반영합니다. 읽을 수 없는 문서 `REVIEW`, 필수 필드 누락 `REVIEW`, 조작 의심 `DECLINE`, 성명
        불일치 `NO_ACTION`, 지원되지 않는 파일 `REVIEW`. 전혀 처리할 수 없는 문서는 `400`을 반환합니다. 읽을 수
        있지만 문제가 있는 문서는 `Declined`/`In Review`와 함께 `200`을 반환하므로 HTTP 코드뿐만 아니라 항상
        `document_ai.status`와 `document_ai.warnings`를 확인하십시오.


        **결제.** 각 `200` 응답은 하나의 Document AI API 크레딧을 소비합니다(정가 기준 **문서당 $0.20**).
        요금제 크레딧 또는 충전 잔액에서 차감됩니다. 조직의 잔액으로 호출 비용을 충당할 수 없는 경우 endpoint는 처리를 시작하기
        전에 `403`을 반환합니다. 샌드박스 API 키는 결정론적 모의 응답을 반환하며 비용이 청구되지 않습니다.


        **영속성.** `save_api_request=true`(기본값)를 사용하면 호출이 API 유형 세션으로 저장됩니다. 문서,
        추출된 값, 필드 정의 및 성명 일치 스냅샷이 보존되고 세션이 Business Console에 표시되며 `request_id`를
        `GET /v3/session/{sessionId}/decision/`과 함께 사용할 수 있습니다.
      operationId: post_v3document-ai
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
                - document
                - fields
              properties:
                document:
                  type: string
                  format: binary
                  description: >-
                    판독할 문서입니다. 허용되는 확장자: `tiff`, `jpg`, `jpeg`, `png`, `pdf`,
                    `webp`, `zip`. 최대 업로드 크기: **30 MB**(더 큰 파일은 `400`으로 거부됨). 다중
                    페이지 PDF가 지원됩니다(지연 시간이 길어질 수 있음). 암호화된 PDF는 `400`으로 거부됩니다.
                    이미지는 처리 전에 자동으로 압축됩니다.
                fields:
                  type: string
                  description: >-
                    **1–30**개의 필드 정의가 포함된 JSON 인코딩 배열 문자열(최대 16KB)입니다. 각 항목:
                    `key`(필수, `^[a-z0-9_]{1,64}$`, 고유값 — `extracted_data`에서 반환되는
                    키), `name`(100자 이하, 메시지에 표시됨), `instruction`(500자 이하, 모델이
                    읽어야 하는 내용), `type`(기본값 `text` | `number` | `date`),
                    `required`(부울, 기본값 `false`), `is_full_name`(부울, 기본값 `false`,
                    요청당 최대 1개). 대괄호 스타일 폼 배열, 잘못된 JSON, 중복된 키 또는 둘 이상의
                    `is_full_name`은 처리나 결제가 진행되기 전에 `400`을 반환합니다.
                  example: >-
                    [{"key": "employer_name", "name": "Employer name",
                    "instruction": "The employer or company issuing the
                    payslip.", "type": "text", "required": true, "is_full_name":
                    false}, {"key": "employee_name", "name": "Employee name",
                    "instruction": "The full name of the employee as printed on
                    the payslip.", "type": "text", "required": true,
                    "is_full_name": true}, {"key": "net_pay", "name": "Net pay",
                    "instruction": "The net amount paid to the employee for the
                    period.", "type": "number", "required": true,
                    "is_full_name": false}, {"key": "pay_date", "name": "Pay
                    date", "instruction": "The payment date shown on the
                    payslip.", "type": "date", "required": false,
                    "is_full_name": false}]
                expected_first_name:
                  type: string
                  description: >-
                    문서의 이름 필드와 대조하여 검증할 이름입니다(퍼지 일치, 음차 인식).
                    `expected_last_name`과 함께 사용해야 하며, `expected_company_name`과
                    결합할 수 없습니다.
                  example: Sophia
                expected_last_name:
                  type: string
                  description: >-
                    문서의 이름 필드와 대조하여 검증할 성으로, `name_match_score`를 위해
                    `expected_first_name`과 결합됩니다.
                  example: Martinez
                expected_company_name:
                  type: string
                  description: >-
                    문서의 이름 필드와 대조하여 확인할 기업명입니다(기업 문서용). `expected_first_name` /
                    `expected_last_name`과 함께 사용할 수 없습니다(예시 값은 의도적으로 제공되지 않음 —
                    생성된 요청 샘플에는 개인 필드가 사용되므로, 기업 문서를 확인할 때는 이 필드로 교체하십시오).
                expected_company_country:
                  type: string
                  description: >-
                    `expected_company_name`에 대한 기업명 정규화를 조정하는 선택적 ISO 3166-1
                    alpha-2 국가 코드(ASCII 2글자, 예: `FR`)입니다.
                    `expected_company_name`과 함께만 사용됩니다.
                document_ai_name_match_score_threshold:
                  type: integer
                  minimum: 0
                  maximum: 100
                  default: 80
                  description: >-
                    이름이 일치하는 것으로 간주하기 위한 최소 `name_match_score`(0–100)입니다. 점수가
                    이보다 낮으면 `DOCUMENT_AI_NAME_MISMATCH`가 추가되며,
                    `document_ai_name_mismatch_action`의 제어를 받습니다.
                  example: 80
                document_ai_unreadable_document_action:
                  type: string
                  enum:
                    - DECLINE
                    - REVIEW
                    - NO_ACTION
                  default: REVIEW
                  description: >-
                    문서에서 어떤 필드도 읽을 수 없을 때(`DOCUMENT_AI_UNREADABLE_DOCUMENT`) 수행할
                    작업입니다.
                document_ai_missing_required_fields_action:
                  type: string
                  enum:
                    - DECLINE
                    - REVIEW
                    - NO_ACTION
                  default: REVIEW
                  description: >-
                    `required` 필드 중 하나 이상을 읽을 수 없을
                    때(`DOCUMENT_AI_MISSING_REQUIRED_FIELDS`, 해당 필드는
                    `additional_data.missing_fields`에 나열됨) 수행할 작업입니다.
                document_ai_document_tampering_action:
                  type: string
                  enum:
                    - DECLINE
                    - REVIEW
                    - NO_ACTION
                  default: DECLINE
                  description: >-
                    PDF/EXIF 포렌식에서 전자 서명 후 수정, 알려진 PDF 편집기 사용, 오버레이 텍스트 조작,
                    의심스러운 재내보내기, 이미지 이상 현상 등 위변조가 의심될
                    때(`DOCUMENT_AI_SUSPECTED_MANIPULATION`) 수행할 작업입니다.
                document_ai_name_mismatch_action:
                  type: string
                  enum:
                    - DECLINE
                    - REVIEW
                    - NO_ACTION
                  default: NO_ACTION
                  description: >-
                    `name_match_score`가 임계값 미만일 때(`DOCUMENT_AI_NAME_MISMATCH`)
                    수행할 작업입니다.
                document_ai_unsupported_file_action:
                  type: string
                  enum:
                    - DECLINE
                    - REVIEW
                    - NO_ACTION
                  default: REVIEW
                  description: >-
                    업로드된 항목이 비어 있거나 처리할 수 없는 파일일 때 수행할 작업입니다
                    (`DOCUMENT_AI_UNSUPPORTED_FILE`).
                save_api_request:
                  type: boolean
                  default: true
                  description: >-
                    `true`(기본값)인 경우 호출을 API 유형 세션으로 저장합니다. Business Console에서
                    확인할 수 있고 반환된 `request_id`를 사용하여 `GET
                    /v3/session/{sessionId}/decision/`을 통해 조회할 수 있으며 업로드된 문서,
                    추출된 값, 필드 정의 및 이름 일치 스냅샷이 저장됩니다. `false`인 경우 아무것도 저장되지 않고
                    `request_id`는 응답 상관관계 식별 전용의 일시적인 UUID이지만 호출에 대한 요금은 여전히
                    청구됩니다.
                  example: true
                vendor_data:
                  type: string
                  description: >-
                    저장된 세션에 저장되고 응답에 다시 반환되는 선택적 불투명 문자열(내부 사용자 ID, 이메일, UUID
                    등)입니다. API 호출을 자체 기록과 연관 짓고 나중에 세션을 필터링하는 데 사용합니다.
                  example: user-123
                metadata:
                  type: object
                  additionalProperties: true
                  description: >-
                    세션과 함께 저장되고(`save_api_request=true`인 경우) 응답에 그대로 반환되는 선택적
                    JSON 객체입니다. multipart 요청에서는 JSON 인코딩된 문자열 필드(예:
                    `metadata={"flow":"onboarding"}`)로 전송하십시오. 객체로 파싱됩니다.
                  example:
                    flow: onboarding
      responses:
        '200':
          description: >-
            문서가 처리되었습니다. `document_ai.status`는 `Approved`, `Declined` 또는 `In
            Review`입니다. 감지된 모든 문제는 `document_ai.warnings`에 항목별로 나열됩니다. 문제가 있는
            문서라도 `200`을 반환하므로 HTTP 코드뿐만 아니라 본문도 검사해야 합니다.
            `save_api_request=true`인 경우 `request_id`는 저장된 세션 ID입니다.
          content:
            application/json:
              examples:
                Approved:
                  summary: 급여 명세서가 정상적으로 판독되었으며 성명이 일치함
                  value:
                    request_id: 9d6a3b1e-2f0c-4a8e-9b7d-1c2e3f4a5b6c
                    document_ai:
                      status: Approved
                      extracted_data:
                        employer_name: Acme Payments Ltd
                        employee_name: Sophia Martinez
                        net_pay: 3120.5
                        pay_date: '2026-07-31'
                      detected_codes:
                        - type: CODE_128
                          page_number: 1
                          position:
                            - - 96
                              - 456
                            - - 96
                              - 616
                            - - 1012
                              - 611
                            - - 1015
                              - 456
                          x: 96
                          'y': 456
                          width: 919
                          height: 160
                          page_width: 1200
                          page_height: 1700
                          raw_payload: SU5WLTIwMjYtMDA0Mg==
                          parsed_payload: INV-2026-0042
                      fields:
                        - key: employer_name
                          name: Employer name
                          instruction: The employer or company issuing the payslip.
                          type: text
                          required: true
                          is_full_name: false
                        - key: employee_name
                          name: Employee name
                          instruction: >-
                            The full name of the employee as printed on the
                            payslip.
                          type: text
                          required: true
                          is_full_name: true
                        - key: net_pay
                          name: Net pay
                          instruction: The net amount paid to the employee for the period.
                          type: number
                          required: true
                          is_full_name: false
                        - key: pay_date
                          name: Pay date
                          instruction: The payment date shown on the payslip.
                          type: date
                          required: false
                          is_full_name: false
                      name_match_score: 100
                      name_on_document: Sophia Martinez
                      warnings: []
                    vendor_data: user-123
                    metadata:
                      flow: onboarding
                    created_at: '2026-08-19T16:40:12.318Z'
                In Review — missing required field:
                  summary: '`net_pay`를 읽을 수 없습니다. 기본 작업 REVIEW'
                  value:
                    request_id: 3f2a9c7b-5d4e-4b1a-8c6f-0e9d8c7b6a5f
                    document_ai:
                      status: In Review
                      extracted_data:
                        employer_name: Acme Payments Ltd
                        employee_name: Sophia Martinez
                        net_pay: null
                        pay_date: '2026-07-31'
                      detected_codes: []
                      fields:
                        - key: employer_name
                          name: Employer name
                          instruction: The employer or company issuing the payslip.
                          type: text
                          required: true
                          is_full_name: false
                        - key: employee_name
                          name: Employee name
                          instruction: >-
                            The full name of the employee as printed on the
                            payslip.
                          type: text
                          required: true
                          is_full_name: true
                        - key: net_pay
                          name: Net pay
                          instruction: The net amount paid to the employee for the period.
                          type: number
                          required: true
                          is_full_name: false
                        - key: pay_date
                          name: Pay date
                          instruction: The payment date shown on the payslip.
                          type: date
                          required: false
                          is_full_name: false
                      name_match_score: 100
                      name_on_document: Sophia Martinez
                      warnings:
                        - risk: DOCUMENT_AI_MISSING_REQUIRED_FIELDS
                          feature: DOCUMENT_AI
                          additional_data:
                            missing_fields:
                              - Net pay
                          log_type: warning
                          short_description: Required fields could not be extracted
                          long_description: >-
                            One or more fields configured as required could not
                            be extracted from the uploaded document.
                    vendor_data: null
                    metadata: null
                    created_at: '2026-08-19T16:41:05.002Z'
                Declined — suspected manipulation:
                  summary: PDF 포렌식에서 알려진 편집기를 감지했습니다. 기본 작업은 거부(DECLINE)입니다.
                  value:
                    request_id: 7c1d2e3f-4a5b-4c6d-8e9f-0a1b2c3d4e5f
                    document_ai:
                      status: Declined
                      extracted_data:
                        employer_name: Acme Payments Ltd
                        employee_name: Sophia Martinez
                        net_pay: 3120.5
                        pay_date: '2026-07-31'
                      detected_codes: []
                      fields:
                        - key: employer_name
                          name: Employer name
                          instruction: The employer or company issuing the payslip.
                          type: text
                          required: true
                          is_full_name: false
                        - key: employee_name
                          name: Employee name
                          instruction: >-
                            The full name of the employee as printed on the
                            payslip.
                          type: text
                          required: true
                          is_full_name: true
                        - key: net_pay
                          name: Net pay
                          instruction: The net amount paid to the employee for the period.
                          type: number
                          required: true
                          is_full_name: false
                        - key: pay_date
                          name: Pay date
                          instruction: The payment date shown on the payslip.
                          type: date
                          required: false
                          is_full_name: false
                      name_match_score: null
                      name_on_document: Sophia Martinez
                      warnings:
                        - risk: DOCUMENT_AI_SUSPECTED_MANIPULATION
                          feature: DOCUMENT_AI
                          additional_data:
                            detection_method: known_editor
                            processed_by_known_editor: ilovepdf
                          log_type: error
                          short_description: Suspected document manipulation
                          long_description: >-
                            The system detected signs of potential manipulation
                            or editing in the uploaded document.
                    vendor_data: null
                    metadata: null
                    created_at: '2026-08-19T16:42:40.771Z'
              schema:
                type: object
                properties:
                  request_id:
                    type: string
                    format: uuid
                    description: >-
                      `save_api_request=true`일 때 유지된 세션 ID(`GET
                      /v3/session/{sessionId}/decision/`와 함께 사용 가능), 그렇지 않으면 임시
                      상관관계 UUID입니다.
                  document_ai:
                    type: object
                    properties:
                      status:
                        type: string
                        enum:
                          - Approved
                          - Declined
                          - In Review
                        description: >-
                          어떤 경고도 거부나 검토로 이어지지 않는 경우 `Approved`, 하나 이상의 위험 요소가
                          `DECLINE`에 매핑되는 경우 `Declined`, 그 외에 하나 이상의 위험 요소가
                          `REVIEW`에 매핑되는 경우 `In Review`가 됩니다. `warnings` 목록에 이유가
                          설명되어 있습니다.
                      extracted_data:
                        type: object
                        additionalProperties: true
                        description: >-
                          요청된 필드 키당 항목 1개입니다. 값은 필드 `type`에 따라 유형이 지정되며(`text`의
                          경우 문자열, `number`의 경우 숫자, `date`의 경우 `YYYY-MM-DD` 문자열),
                          값을 읽을 수 없는 경우 `null`입니다.
                        example:
                          employer_name: Acme Payments Ltd
                          employee_name: Sophia Martinez
                          net_pay: 3120.5
                          pay_date: '2026-07-31'
                      detected_codes:
                        type: array
                        description: >-
                          처리된 모든 페이지에서 감지된 QR 코드 및 바코드입니다. 발견되지 않으면 비어 있습니다. 위치는
                          식별되었으나 디코딩할 수 없는 QR 코드는 `parsed_payload: null`을 가집니다.
                        items:
                          $ref: '#/components/schemas/DocumentAIDetectedCode'
                      fields:
                        type: array
                        description: >-
                          이 추출에 사용된 정규화된 필드 정의이며, 그대로
                          반환됩니다(`save_api_request=true`인 경우 세션과 함께 저장되기도 함).
                        items:
                          type: object
                          properties:
                            key:
                              type: string
                            name:
                              type: string
                            instruction:
                              type: string
                            type:
                              type: string
                              enum:
                                - text
                                - number
                                - date
                            required:
                              type: boolean
                            is_full_name:
                              type: boolean
                      name_match_score:
                        type: number
                        nullable: true
                        minimum: 0
                        maximum: 100
                        description: >-
                          `name_on_document`와 예상되는 개인/기업 이름 간의 퍼지 유사도(0–100)입니다.
                          이름 값과 예상 이름이 모두 존재할 때 일치 **및** 불일치 시 항상 반환되며, 그렇지 않은
                          경우 `null`입니다.
                        example: 96
                      name_on_document:
                        type: string
                        nullable: true
                        description: >-
                          예상 이름과 비교된 문서에서 가져온 이름 값(`is_full_name` 필드 또는 이름과 유사한
                          첫 번째 텍스트 필드)입니다. 사용할 수 있는 이름 값이 없는 경우 `null`입니다.
                        example: Sophia Martinez
                      warnings:
                        type: array
                        description: >-
                          문제없이 승인된 경우 비어 있습니다. 각 항목은 감지된 하나의 위험을 설명합니다.
                          `log_type: "error"`인 항목은 `status`를 `Declined`로 설정하고,
                          `log_type: "warning"`인 항목은 `In Review`로 설정합니다.
                        items:
                          type: object
                          properties:
                            risk:
                              type: string
                              enum:
                                - DOCUMENT_AI_UNREADABLE_DOCUMENT
                                - DOCUMENT_AI_MISSING_REQUIRED_FIELDS
                                - DOCUMENT_AI_SUSPECTED_MANIPULATION
                                - DOCUMENT_AI_NAME_MISMATCH
                                - DOCUMENT_AI_UNSUPPORTED_FILE
                              description: >-
                                기계 판독 가능한 위험 코드입니다. 각각은 일치하는
                                `document_ai_*_action` 요청 옵션에 의해 제어됩니다.
                              example: DOCUMENT_AI_MISSING_REQUIRED_FIELDS
                            feature:
                              type: string
                              enum:
                                - DOCUMENT_AI
                              description: >-
                                경고를 발생시킨 기능입니다. 이 endpoint에서는 항상
                                `DOCUMENT_AI`입니다.
                            additional_data:
                              type: object
                              nullable: true
                              additionalProperties: true
                              description: >-
                                위험에 대한 추가 컨텍스트입니다. 필수 필드가 누락된 경우
                                `{"missing_fields": ["Net pay"]}`, 이름 불일치의 경우
                                `{"name_on_document", "score", "threshold"}`, 위조
                                의심의 경우 포렌식 결과(`detection_method`, 생성자/편집자 세부
                                정보)가 포함됩니다. 추가할 내용이 없는 경우 `null`입니다.
                              example:
                                missing_fields:
                                  - Net pay
                            log_type:
                              type: string
                              enum:
                                - error
                                - warning
                                - information
                              description: >-
                                구성된 작업에서 파생된 심각도: `error`(DECLINE),
                                `warning`(REVIEW), `information`(NO_ACTION).
                            short_description:
                              type: string
                              description: 사람이 읽을 수 있는 한 줄의 위험 요약입니다.
                              example: Required fields could not be extracted
                            long_description:
                              type: string
                              description: 해당하는 경우 `additional_data`가 삽입된 사람이 읽기 쉬운 설명입니다.
                  vendor_data:
                    type: string
                    nullable: true
                    description: 전송한 `vendor_data`의 에코 값 또는 `null`입니다.
                  metadata:
                    type: object
                    nullable: true
                    additionalProperties: true
                    description: 전송한 `metadata` 객체의 에코 값 또는 `null`입니다.
                  created_at:
                    type: string
                    format: date-time
                    description: 요청이 처리된 시점(UTC, ISO 8601).
        '400':
          description: >-
            검증 또는 처리 오류입니다. 필드 수준의 문제는 DRF의 표준 엔벨로프(문제가 발생한 필드당 하나의 메시지 배열)를
            반환합니다. 암호화되었거나 읽을 수 없는 문서 및 추출 실패는 `{"error": ...}`를 반환합니다.
          content:
            application/json:
              examples:
                Missing document:
                  summary: 폼 데이터에 `document`가 포함되지 않음
                  value:
                    document:
                      - No file was submitted.
                Invalid fields payload:
                  summary: '`fields`가 JSON 인코딩된 단일 배열 문자열이 아님'
                  value:
                    fields:
                      - fields must be a valid JSON array string.
                Duplicate field keys:
                  summary: 두 항목이 동일한 `key`를 공유합니다
                  value:
                    fields:
                      - Every fields item must have a unique key.
                Conflicting expectations:
                  summary: 개인 및 기업 기대치가 함께 전송됨
                  value:
                    non_field_errors:
                      - >-
                        Provide either person expected-name fields or
                        expected_company_name, not both.
                Invalid metadata:
                  summary: '`metadata`가 JSON 객체가 아니거나 NUL 문자를 포함합니다.'
                  value:
                    metadata:
                      - Metadata must be a JSON object.
                Oversized vendor_data:
                  summary: '`vendor_data`가 2,000바이트 한도를 초과했습니다.'
                  value:
                    vendor_data:
                      - vendor_data is too long (maximum 2000 bytes).
                Encrypted PDF:
                  summary: 비밀번호로 보호된 PDF
                  value:
                    error: >-
                      The PDF is encrypted. Please upload a decrypted PDF or a
                      photo instead.
                Extraction failed:
                  summary: 추출 파이프라인에서 문서를 처리할 수 없습니다
                  value:
                    error:
                      - Error extracting Document AI information
              schema:
                type: object
                additionalProperties: true
        '403':
          description: >-
            권한이 거부되었습니다. `x-api-key` 헤더가 누락되었거나 호출한 조직의 잔액이 호출 비용을 충당할 수 없을 때
            반환됩니다. 잘못되었거나 취소되었거나 만료된 키는 `401`을 반환합니다. 키가 누락된 경우 `{"detail":
            ...}`과 함께 `403`을 반환하고, 잘못되었거나 취소되었거나 만료된 키는 `401`을 반환합니다. 크레딧이 부족한
            경우, 그리고 요금표에 Document AI API 항목이 없는 계약 청구 조직의 경우 어떠한 문서 처리도 진행되기 전에
            `{"error": ...}`과 함께 `403`을 반환합니다.
          content:
            application/json:
              examples:
                Missing or invalid API key:
                  summary: '`x-api-key` 헤더가 없거나, 키가 유효하지 않거나 해지되었습니다'
                  value:
                    detail: You do not have permission to perform this action.
                Not enough credits:
                  summary: 조직 잔액이 호출 비용을 충당할 수 없습니다
                  value:
                    error: >-
                      You don't have enough credits to perform this request.
                      Please top up at https://business.didit.me
                Contract rate missing:
                  summary: 요금표에 Document AI API 항목이 없는 계약 결제 방식의 조직
                  value:
                    error: >-
                      Document AI API is not included in your contract rate
                      card. Contact support@didit.me to enable it.
              schema:
                type: object
                properties:
                  detail:
                    type: string
                  error:
                    type: string
        '429':
          description: >-
            속도 제한을 초과했습니다. POST/PATCH/DELETE endpoint는 API 키당 분당 300회의 쓰기 요청 한도를
            공유합니다(거래 생성 및 이벤트 수집은 별도로 600회 한도 적용). 응답에는 `X-RateLimit-Limit`,
            `X-RateLimit-Remaining`, `X-RateLimit-Reset`, `Retry-After` 헤더가
            포함됩니다.
          content:
            application/json:
              examples:
                Write rate limit exceeded:
                  summary: 현재 분 동안 300건을 초과하는 쓰기 요청 발생
                  value:
                    detail: >-
                      Write request rate limit exceeded. You can make up to 300
                      requests per minute.
              schema:
                type: object
                properties:
                  detail:
                    type: string
        '503':
          $ref: '#/components/responses/DocumentProcessingBusy'
      security:
        - ApiKeyAuth: []
      x-codeSamples:
        - lang: python
          label: Python
          source: |-
            from didit import Didit

            # Reads DIDIT_API_KEY.
            client = Didit()

            result = client.document_ai.analyze(
                document=open("document.pdf", "rb"),
                fields="[{\"key\": \"employer_name\", \"name\": \"Employer name\", \"instruction\": \"The employer or company issuing the payslip.\", \"type\": \"text\", \"required\": true, \"is_full_name\": false}, {\"key\": \"employee_name\", \"name\": \"Employee name\", \"instruction\": \"The full name of the employee as printed on the payslip.\", \"type\": \"text\", \"required\": true, \"is_full_name\": true}, {\"key\": \"net_pay\", \"name\": \"Net pay\", \"instruction\": \"The net amount paid to the employee for the period.\", \"type\": \"number\", \"required\": true, \"is_full_name\": false}, {\"key\": \"pay_date\", \"name\": \"Pay date\", \"instruction\": \"The payment date shown on the payslip.\", \"type\": \"date\", \"required\": false, \"is_full_name\": false}]",
            )
            print(result)
        - lang: javascript
          label: Node.js
          source: |-
            import Didit, { fileFromPath } from "@didit-protocol/sdk-node";

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

            const result = await didit.documentAi.analyze({
              document: await fileFromPath("document.pdf"),
              fields: "[{\"key\": \"employer_name\", \"name\": \"Employer name\", \"instruction\": \"The employer or company issuing the payslip.\", \"type\": \"text\", \"required\": true, \"is_full_name\": false}, {\"key\": \"employee_name\", \"name\": \"Employee name\", \"instruction\": \"The full name of the employee as printed on the payslip.\", \"type\": \"text\", \"required\": true, \"is_full_name\": true}, {\"key\": \"net_pay\", \"name\": \"Net pay\", \"instruction\": \"The net amount paid to the employee for the period.\", \"type\": \"number\", \"required\": true, \"is_full_name\": false}, {\"key\": \"pay_date\", \"name\": \"Pay date\", \"instruction\": \"The payment date shown on the payslip.\", \"type\": \"date\", \"required\": false, \"is_full_name\": false}]",
            });
            console.log(result);
        - lang: go
          label: Go
          source: "ctx := context.Background()\n// Reads DIDIT_API_KEY.\nclient := didit.NewClient()\n\nresult, err := client.DocumentAi.Analyze(ctx, didit.DocumentAiAnalyzeParams{\n\tDocument: didit.FileFromPath(\"document.pdf\"),\n\tFields:   \"[{\\\"key\\\": \\\"employer_name\\\", \\\"name\\\": \\\"Employer name\\\", \\\"instruction\\\": \\\"The employer or company issuing the payslip.\\\", \\\"type\\\": \\\"text\\\", \\\"required\\\": true, \\\"is_full_name\\\": false}, {\\\"key\\\": \\\"employee_name\\\", \\\"name\\\": \\\"Employee name\\\", \\\"instruction\\\": \\\"The full name of the employee as printed on the payslip.\\\", \\\"type\\\": \\\"text\\\", \\\"required\\\": true, \\\"is_full_name\\\": true}, {\\\"key\\\": \\\"net_pay\\\", \\\"name\\\": \\\"Net pay\\\", \\\"instruction\\\": \\\"The net amount paid to the employee for the period.\\\", \\\"type\\\": \\\"number\\\", \\\"required\\\": true, \\\"is_full_name\\\": false}, {\\\"key\\\": \\\"pay_date\\\", \\\"name\\\": \\\"Pay date\\\", \\\"instruction\\\": \\\"The payment date shown on the payslip.\\\", \\\"type\\\": \\\"date\\\", \\\"required\\\": false, \\\"is_full_name\\\": false}]\",\n})\nif err != nil {\n\tlog.Fatal(err)\n}\nfmt.Printf(\"%+v\\n\", result)"
        - lang: java
          label: Java
          source: >-
            import java.nio.file.Path;

            import me.didit.Didit;

            import me.didit.types.DocumentAiAnalyzeParams;


            // Reads DIDIT_API_KEY.

            Didit client = new Didit();


            var result =
            client.documentAi().analyze(DocumentAiAnalyzeParams.builder()
                    .document(Path.of("document.pdf"))
                    .fields("[{\"key\": \"employer_name\", \"name\": \"Employer name\", \"instruction\": \"The employer or company issuing the payslip.\", \"type\": \"text\", \"required\": true, \"is_full_name\": false}, {\"key\": \"employee_name\", \"name\": \"Employee name\", \"instruction\": \"The full name of the employee as printed on the payslip.\", \"type\": \"text\", \"required\": true, \"is_full_name\": true}, {\"key\": \"net_pay\", \"name\": \"Net pay\", \"instruction\": \"The net amount paid to the employee for the period.\", \"type\": \"number\", \"required\": true, \"is_full_name\": false}, {\"key\": \"pay_date\", \"name\": \"Pay date\", \"instruction\": \"The payment date shown on the payslip.\", \"type\": \"date\", \"required\": false, \"is_full_name\": false}]")
                    .build());
            System.out.println(result);
        - lang: php
          label: PHP
          source: |-
            <?php

            require 'vendor/autoload.php';

            use Didit\DiditClient;
            use Didit\File;

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

            $result = $didit->documentAi->analyze([
                'document' => File::fromPath('document.pdf'),
                'fields' => '[{"key": "employer_name", "name": "Employer name", "instruction": "The employer or company issuing the payslip.", "type": "text", "required": true, "is_full_name": false}, {"key": "employee_name", "name": "Employee name", "instruction": "The full name of the employee as printed on the payslip.", "type": "text", "required": true, "is_full_name": true}, {"key": "net_pay", "name": "Net pay", "instruction": "The net amount paid to the employee for the period.", "type": "number", "required": true, "is_full_name": false}, {"key": "pay_date", "name": "Pay date", "instruction": "The payment date shown on the payslip.", "type": "date", "required": false, "is_full_name": false}]',
            ]);
            print_r($result->toArray());
        - lang: ruby
          label: Ruby
          source: |-
            require "didit"

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

            result = didit.document_ai.analyze(
              document: Pathname("document.pdf"),
              fields: "[{\"key\": \"employer_name\", \"name\": \"Employer name\", \"instruction\": \"The employer or company issuing the payslip.\", \"type\": \"text\", \"required\": true, \"is_full_name\": false}, {\"key\": \"employee_name\", \"name\": \"Employee name\", \"instruction\": \"The full name of the employee as printed on the payslip.\", \"type\": \"text\", \"required\": true, \"is_full_name\": true}, {\"key\": \"net_pay\", \"name\": \"Net pay\", \"instruction\": \"The net amount paid to the employee for the period.\", \"type\": \"number\", \"required\": true, \"is_full_name\": false}, {\"key\": \"pay_date\", \"name\": \"Pay date\", \"instruction\": \"The payment date shown on the payslip.\", \"type\": \"date\", \"required\": false, \"is_full_name\": false}]"
            )
            puts result.to_h
        - lang: csharp
          label: .NET
          source: >-
            using System;

            using System.Text.Json;

            using Didit;


            // Reads DIDIT_API_KEY.

            using var client = new DiditClient();


            var result = await client.DocumentAi.AnalyzeAsync(new
            DocumentAiAnalyzeParams

            {
                Document = FileUpload.FromPath("document.pdf"),
                Fields = "[{\"key\": \"employer_name\", \"name\": \"Employer name\", \"instruction\": \"The employer or company issuing the payslip.\", \"type\": \"text\", \"required\": true, \"is_full_name\": false}, {\"key\": \"employee_name\", \"name\": \"Employee name\", \"instruction\": \"The full name of the employee as printed on the payslip.\", \"type\": \"text\", \"required\": true, \"is_full_name\": true}, {\"key\": \"net_pay\", \"name\": \"Net pay\", \"instruction\": \"The net amount paid to the employee for the period.\", \"type\": \"number\", \"required\": true, \"is_full_name\": false}, {\"key\": \"pay_date\", \"name\": \"Pay date\", \"instruction\": \"The payment date shown on the payslip.\", \"type\": \"date\", \"required\": false, \"is_full_name\": false}]",
            });

            Console.WriteLine(JsonSerializer.Serialize(result));
components:
  schemas:
    DocumentAIDetectedCode:
      type: object
      required:
        - type
        - page_number
        - position
        - x
        - 'y'
        - width
        - height
        - page_width
        - page_height
        - raw_payload
        - parsed_payload
      properties:
        type:
          type: string
          description: >-
            감지된 심볼로지(예: `QR_CODE`, `CODE_128`, `EAN_13`, `DATA_MATRIX` 또는
            `PDF_417`)입니다.
        page_number:
          type: integer
          minimum: 1
          description: 1부터 시작하는 페이지 번호입니다.
        position:
          type: array
          nullable: true
          description: >-
            렌더링된 페이지의 픽셀 좌표로 감지된 다각형(polygon)입니다. 신뢰할 수 있는 위치를 찾을 수 없는 경우
            `null`입니다.
          items:
            type: array
            minItems: 2
            maxItems: 2
            items:
              type: integer
        x:
          type: integer
          nullable: true
          description: 렌더링된 페이지 픽셀 단위의 경계 상자 왼쪽 가장자리입니다.
        'y':
          type: integer
          nullable: true
          description: 렌더링된 페이지 픽셀 기준 경계 상자의 상단 가장자리입니다.
        width:
          type: integer
          nullable: true
          description: 렌더링된 페이지 픽셀 단위의 경계 상자 너비입니다.
        height:
          type: integer
          nullable: true
          description: 렌더링된 페이지 픽셀 단위의 경계 상자 높이입니다.
        page_width:
          type: integer
          description: 렌더링된 페이지 너비(픽셀 단위)입니다.
        page_height:
          type: integer
          description: 렌더링된 페이지 높이(픽셀 단위)입니다.
        raw_payload:
          type: string
          description: 사용 가능한 경우 base64 문자열 형식의 원시 디코더 payload이며, 그렇지 않은 경우 빈 문자열입니다.
        parsed_payload:
          nullable: true
          description: 디코딩된 문자열 또는 구조화된 값입니다. 코드가 감지되었으나 디코딩할 수 없는 경우 `null`입니다.
  responses:
    DocumentProcessingBusy:
      description: >-
        현재 너무 많은 문서가 처리 중이므로 이 업로드가 처리되지 않았습니다. 아무것도 저장되지 않았으며 시도 횟수도 사용되지
        않았습니다. `Retry-After`의 시간(초)이 지난 후 동일한 파일을 다시 전송하세요. 메시지 텍스트가 아닌 `code`를
        기준으로 분기하세요.
      headers:
        Retry-After:
          description: 동일한 요청을 다시 전송하기 전에 대기해야 하는 시간(초)입니다.
          schema:
            type: integer
            example: 10
      content:
        application/json:
          schema:
            type: object
            required:
              - detail
              - code
            properties:
              detail:
                type: string
              code:
                type: string
                enum:
                  - document_processing_busy
          example:
            detail: We are busy processing documents. Please try again in a moment.
            code: document_processing_busy
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
      description: >-
        Business Console의 Developers -> API keys에서 발급받은 귀하 애플리케이션의 API 키입니다. 기본
        키는 전체 접근 권한을 갖습니다. 이름이 지정된 키는 리소스별 none, read, write 범위로 권한을 제한할 수 있으며
        특정 워크플로나 승인된 세션, IP 주소 목록, 만료일 등으로 제한할 수도 있습니다. 401은 키가 잘못되었거나 폐기되었거나
        만료되었음을 의미합니다. 403은 키가 전송되지 않았거나, 키에 해당 리소스나 작업에 대한 접근 권한이 없거나, 요청이 허용된
        IP 목록 외부 주소에서 발생했음을 의미합니다. 세션 경로에서의 404는 해당 세션이 키의 허용 워크플로 또는 상태 범위를
        벗어났음을 의미합니다. 미디어 접근 권한이 없는 키는 이미지, 비디오, PDF URL을 null로 수신하며, 세션 write
        권한이 없는 키는 세션 링크와 token을 null로 수신합니다.
        https://docs.didit.me/console/api-keys 를 참고하십시오.

````

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