> For the complete documentation index, see [llms.txt](https://docs-kr.verifyvasp.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs-kr.verifyvasp.com/reference/vasp-api-reference/api.md).

# 사용자 검증 API

## 사용자 검증 API

<mark style="color:green;">`POST`</mark> `VEGA_VERIFICATION_API_PATH`

{% hint style="info" %}
**Enclave 환경 변수:**

<mark style="color:green;">**`VEGA_VERIFICATION_API_PATH`**</mark> 변수에, 사용자 검증 API 엔드포인트를 명시합니다.
{% endhint %}

> VASP는 Originating VASP, Beneficiary VASP 두 역할을 모두 수행하게 되는데, 이 API는 VASP가 Beneficiary VASP 역할을 할 때 호출되는 API입니다.
>
> Originating VASP에서 출금을 진행하기 전에 Beneficiary(수신자)에 대한 검증 요청 시 호출되는 API입니다.
>
> * API에서 처리해야 할 내용은 아래와 같습니다.
>   * Beneficiary의 이름을 확인합니다.
>   * Beneficiary의 address(계좌번호 또는 지갑 주소)를 확인합니다.
>   * KYC 인증 여부를 확인합니다.
>   * AML 인증 여부를 확인합니다.
>   * Originator(송신인)에 대한 STR monitoring이나 sanction screening을 수행합니다.
>   * 전달받은 송신인, 수신인의 개인 정보에 대해, VASP 자체적으로 추가적인 필터링이나 검증 작업을 진행하여도 됩니다.
>
> 사용자 검증이 정상적으로 끝났다면, Beneficiary에 대한 **추가 개인 정보**를 IVMS101 메시징 포맷으로 반환합니다.

{% hint style="info" %}
**응답 시간:**

이 사용자 검증 요청 API는 최대 5초 이내에 응답해야 합니다.
{% endhint %}

{% hint style="info" %}
**구현 정책:**

* 사용자 검증 API 구현 시, Beneficiary Name 검증에 대한 가이드입니다.
  * 전송할 가상 자산 금액이 법령에서 정한 금액보다 작을 경우 트래블룰 대상이 아니므로 VerifyVASP를 통한 검증은 필수 사항이 아닙니다.
  * 하지만, 모든 출금 요청에 VerifyVASP를 통해 처리하고자 한다면, 법령에서 정한 금액이 넘는 경우와 그렇지 않은 경우 이 모두를 동일한 수준으로 사용자 검증을 하기에는 무리가 있습니다.
  * Beneficiary VASP에서는, 법령이 정한 금액 보다 작은 경우(isExceedingThreshold 값이 false인 경우)에는 Beneficiary의 이름에 대한 매칭 검증을 수행하지 않도록 합니다.
* 사용자 검증 API 구현 시, Originating VASP가 요청한 정보(requiredBeneficiaryInfo)는 모두 기입해야 합니다.
  * Originating VASP가 요청한 정보가 없거나 정보를 제공하기를 원하지 않으면, verificationResult를 DENIED로, reason을 UNAVAILABLE-INFORMATION으로 반환합니다.
  * Originating VASP가 요청하지 않은 정보는 기입하지 않습니다. **오직 요청한(requiredBeneficiaryInfo에 있는) 정보만 기입**합니다.
  * Beneficiary account number는 수정하지 않고 그대로 반환해야 합니다. 지갑 주소를 변경하면 오입금이 발생할 수 있습니다. 만약 입금 주소가 잘못된 경우에는 DENIED 결과를 반환해야 합니다.
* 사용자 검증 API에서 Originator에 대한 검증 시, 필요한 개인 정보가 부족한 경우에는 다음과 같이 처리합니다.
  * verificationResult를 DENIED로 반환하고, reason에는 LACK-OF-INFORMATION로 반환합니다. 그리고, message 필드에 어떤 정보가 부족한지 개인 정보 코드 목록을 반환합니다.
    {% endhint %}

## Request Header

| Field Name    | Description                                        |
| ------------- | -------------------------------------------------- |
| Authorization | Bearer \<VEGA\_VERIFICATION\_AUTHORIZATION\_TOKEN> |

## Request Body

<details>

<summary>Request Body Examples</summary>

```json
  {
    "verificationUuid": "4bc03a8f-5679-4358-815c-906aae5557f4",
    "assetInfo": {
      "symbol": "ETH",
      "network": "Ethereum",
      "amount": "0.024",
      "isExceedingThreshold": true,
      "tradePrice": "2193200",
      "tradeCurrency": "KRW",
      "tradeISODatetime": "2022-02-06T23:13:43.513Z"
    },
    "requiredBeneficiaryInfo": "ACCOUNT_NUMBER,NATURAL_PERSON_NAME",
    "originatingVaspId": "123456789098765",
    "version": "1.0",
    "ivms101": {
      "originator": {
        "originatorPersons": [
          {
            "naturalPerson": {
              "name": {
                "nameIdentifier": [
                  {
                    "primaryIdentifier": "James",
                    "secondaryIdentifier": "Din",
                    "nameIdentifierType": "LEGL"
                  }
                ]
              }
            }
          }
        ],
        "accountNumber": [
            "0x5811001506550d8356a215be229c15b6ef371a9a"
        ]
      },
      "beneficiary": {
        "beneficiaryPersons": [{
          "naturalPerson": {
            "name": {
              "nameIdentifier"; [{
                "primaryIdentifier": "James",
                "nameIdentifierType": "LEGL"
              }]
            }
          }
        }],
        "accountNumber": ["0xb0bFf9721871e22653358956cf59a5FdBF3D752F"]
      }
    }
  }
```

</details>

| Field Name                             | Data Type | Required | Example                                         | Description                                                               |
| -------------------------------------- | --------- | -------- | ----------------------------------------------- | ------------------------------------------------------------------------- |
| verificationUuid                       | string    | true     | "4bc03a8f-5679-4358-815c-906aae5557f4"          | 해당 사용자 검증을 유일하게 구분하는 uuid 값                                               |
| assetInfo                              | object    | true     | -                                               | 전송할 가상 자산에 대한 정보                                                          |
| assetInfo.symbol                       | string    | true     | “ETH”                                           | VA. Virtual Asset. 전송할 가상 자산 심벌                                           |
| assetInfo.network                      | string    | false    | “Ethereum”                                      | 가상 자산이 배포되어 있는 블록체인 네트워크 이름 (자세한 사항은 '추가 정보' 메뉴 하단의 'network 필드 가이드'를 참조) |
| assetInfo.amount                       | string    | true     | “0.024”                                         | 실제 전송할 가상 자산의 양                                                           |
| assetInfo.isExceedingThreshold         | boolean   | true     | true or false                                   | 전송할 가상 자산의 법정 화폐 환산 금액이 법령에서 정한 금액 (1000달러 or 100만 원) 이상인지의 여부를 표시한 flag  |
| assetInfo.tradePrice                   | string    | true     | “2439800”                                       | 전송할 가상 자산을 법정 화폐로 환산한 금액                                                  |
| assetInfo.tradeCurrency                | string    | true     | “KRW”                                           | 전송할 가상 자산을 법정 화폐로 환산했을 때 사용한 화폐 코드                                        |
| assetInfo.tradeISODatetime             | string    | true     | “2022-02-06T23:13:43.513Z”                      | 전송할 가상 자산을 법정 화폐로 환산했을 때 시세 적용 기준 시간, ISO 포맷으로 입력                         |
| requiredBeneficiaryInfo                | string    | true     | "NATURAL\_PERSON\_NAME,ACCOUNT\_NUMBER"         | 송신 VASP가 필요로 하는 수신자의 개인 정보 필드 목록 (Links의 사용자 개인 정보 종류 코드 참조)              |
| originatingVaspId                      | string    | true     | "123456789098765"                               | 송신 VASP의 vasp ID                                                          |
| version                                | string    | true     | “1.0”                                           | IVMS101 포맷의 버전 정보 (현재는 1.0)                                               |
| ivms101                                | object    | true     | -                                               | 발신자와 수신자에 대한 정보를 IVMS101 형태로 전달. IVMS101 스펙 문서 및 IVMS101 가이드 문서 참조.       |
| ivms101.originator                     | object    | true     | -                                               | 발신자에 대한 정보                                                                |
| ivms101.beneficiary                    | object    | true     | -                                               | 수신인에 대한 정보                                                                |
| ivms101.beneficiary.beneficiaryPersons | array     | true     | -                                               | 수신인에 대한 개인 정보                                                             |
| ivms101.beneficiary.accountNumber      | array     | true     | \[”0x5811001506550d8356a215be229c15b6ef371a9a”] | 수신인에 대한 계좌 정보 (지갑 주소)                                                     |

* <mark style="color:blue;">**assetInfo.network**</mark> 필드는 필수가 아닙니다 (Optional). 여러 블록체인 네트워크에 배포된 가상 자산 (ex. USDT의 경우 이더리움, BSC, 솔라나 등 여러 네트워크에 배포)에 대해 상대 VASP가 network 필드 없이 나에게 호출하는 경우 다음과 같이 응답할 수 있습니다.
  * **Network 필드가 필수인 경우**: result는 'DENIED'로, reason은 'UNKNOWN-NETWORK'로 반환할 수 있습니다. 물론 상대 VASP에서 network 필드 지원을 구현하지 않았으면 거래 성공률이 낮아집니다.
  * **Network 필드가 옵션인 경우**: 상대 VASP가 생략한 network 필드를 내가 취급하는 network로 가정하고 검증한 결과를 반환할 수 있습니다. 다만, 상대 VASP와 네트워크가 다를 경우 가상 자산이 오전송 될 수 있습니다.
* 위 두 가지 중 어떤 응답을 반환할지는 각 VASP에서 스스로 결정할 수 있습니다. 예를 들어 상대 VASP 별로 다르게 정책을 가져갈 수도 있습니다. 각 VASP 별 상황을 고려하여 구현하시기 바랍니다.

## Response Body

<details>

<summary>Response Body Examples</summary>

* 200 OK

```json
{
  "result": "VERIFIED",
  "reason": "OK",
  "message": "",
  "ivms101": {
    "beneficiary": {
      "beneficiaryPersons": [
        {
          "naturalPerson": {
            "name": {
              "nameIdentifier": [
                {
                  "primaryIdentifier": "James",
                  "nameIdentifierType": "LEGL"
                }
              ],
              "localNameIdentifier": [
                {
                  "primaryIdentifier": "김재원",
                  "nameIdentifierType": "LEGL"
                }
              ]
            },
            "geographicAddress": [
              {
                "addressType": "GEOG",
                "townName": "Yeoksam-dong",
                "addressLine": ["14 Teheran-ro 4-gil, Gangnam-gu", "4th floor"],
                "country": "KR"
              }
            ],
            "nationalIdentification": {
              "nationalIdentifier": "12345-67890",
              "nationalIdentifierType": "IDCD"
            },
            "customerIdentification": "1234569999",
            "dateAndPlaceOfBirth": {
              "dateOfBirth": "1985-03-14",
              "placeOfBirth": "Nonsan"
            },
            "countryOfResidence": "KR"
          }
        }
      ],
      "accountNumber": ["0xb0bFf9721871e22653358956cf59a5FdBF3D752F"]
    }
  }
}
```

* 200 OK

```json
{
  "result": "DENIED",
  "reason": "UNVERIFIED-KYC"
}
```

</details>

: 사용자 검증 결과는 다음과 같은 형태로 반환해야 합니다.

| Field Name                             | Data Type | Required | Example    | Description                                                                         |
| -------------------------------------- | --------- | -------- | ---------- | ----------------------------------------------------------------------------------- |
| result                                 | string    | true     | “VERIFIED” | 사용자 검증 수행 결과. (하단 설명 참조)                                                            |
| reason                                 | string    | true     | “OK”       | 검증이 실패한 경우에 그 이유를 나타내는 에러 코드 (하단 설명 참조). 성공적인 경우에는 "OK"                             |
| message                                | string    | false    | -          | 각 에러 코드에 대한 추가적인 메시지 (하단 참조)                                                        |
| version                                | string    | false    | "1.0”      | IVMS101 포맷에 대한 버전 정보                                                                |
| ivms101                                | object    | true     | -          | IVMS101 포맷으로 정의된 수신자 정보. 송신 VASP에서 전송한 수신인 정보뿐만 아니라 수신인에 대한 추가 정보를 더 기입하여 반환할 수 있다. |
| ivms101.beneficiary                    | object    | true     | -          | 수신인에 대한 정보                                                                          |
| ivms101.beneficiary.beneficiaryPersons | array     | true     | -          | 수신인에 대한 개인 정보                                                                       |
| ivms101.beneficiary.accountNumber      | array     | true     | -          | 수신인에 대한 계좌 정보                                                                       |

* <mark style="color:blue;">**result**</mark> 필드에는 다음과 같은 값이 들어갈 수 있습니다.
  * “VERIFIED”, ”DENIED”, ”ERROR” 중 한 개의 값을 갖습니다.
  * **VERIFIED**: 검증이 성공적으로 끝난 경우, 사용자에게 아무런 문제가 없음을 의미합니다.
  * **DENIED**: 지갑 주소는 맞지만 사용자(송신인 포함)에게 문제가 발견된 경우 (e.g. lack of KYC credential)
  * **ERROR**: 기타 다른 에러가 발생한 경우
* <mark style="color:blue;">**reason**</mark> 필드에는 다음과 같은 값이 들어갈 수 있습니다.

  * 단, 사용자 검증 요청에 대한 결과 `result` 필드의 값이 **DENIED** 일 때에만 유효합니다.
  * `message` 필드에는, 아래 표에 설명된 값을 string으로 전달합니다. (하단 표 내용 참조)

  | <p>reason</p><p>(string)</p> | <p>result</p><p>(string)</p> | <p>message</p><p>(string)</p>    | Description                                                       |
  | ---------------------------- | ---------------------------- | -------------------------------- | ----------------------------------------------------------------- |
  | `UNKNOWN-SYMBOL`             | DENIED                       | 해당 VASP에서 취급하지 않는다고 판명된 심벌 이름    | 거래소에서 취급하지 않는 심벌일 경우 (VASP가 취급하지 않는 자산일 경우)                       |
  | `UNKNOWN-NETWORK`            | DENIED                       | 해당 VASP에서 취급하지 않는다고 판명된 네트워크 이름  | 거래소에서 취급하지 않는 네트워크이거나 네트워크 정보가 불충분한 경우 (심벌은 동일하지만 네트워크가 맞지 않는 경우) |
  | `UNKNOWN-ADDRESS`            | DENIED                       | 해당 VASP 소유의 주소가 아니라고 판명된 지갑 주소   | 가상 자산 주소가 해당 VASP의 주소가 아닌 경우                                      |
  | `LACK-OF-INFORMATION`        | DENIED                       | 부족한 개인 정보 필드 코드 목록 (쉼표로 구분)      | 상대방 사용자의 정보가 검증을 수행하기에 부족한 경우                                     |
  | `UNAVAILABLE-INFORMATION`    | DENIED                       | 넘겨줄 수 없는 개인 정보 필드 코드 목록 (쉼표로 구분) | 상대방이 요청한 개인 정보가 없거나 넘겨줄 수 없는 경우                                   |
  | `BLACKLISTED`                | DENIED                       | -                                | 상대방 사용자에 대한 sanction screening 결과 문제가 있는 경우                       |
  | `UNVERIFIED-KYC`             | DENIED                       | -                                | 자신의 VASP의 해당 사용자가 KYC 인증이 되어 있지 않은 경우                             |
  | `MISMATCHED-NAME`            | DENIED                       | -                                | 수신자의 이름이 송신 VASP에서 보내준 이름과 일치하지 않는 경우                             |
  | `NOT-ALLOWED`                | DENIED                       | 거부 사유                            | 수신 VASP에서 어떤 이유로든 해당 사용자 검증 요청을 거부한 경우                            |
  | `UNDEFINED-ERROR`            | DENIED                       | 에러 내용                            | 그 밖에, 따로 정의되어 있지 않은 에러가 발생한 경우                                    |

## Links

### IVMS101 포맷 정의

{% content-ref url="/pages/UfLBfOXnQHu4et5d3O5v" %}
[IVMS101 포맷 정의](/reference/ivms101/ivms101.md)
{% endcontent-ref %}

### IVMS101 정보 기입 가이드

{% content-ref url="/pages/XNorRrXsnc2rCgYjjT3G" %}
[IVMS101 정보 기입 가이드](/reference/ivms101/ivms101-1.md)
{% endcontent-ref %}

### 사용자 개인 정보 종류 코드

* `Request Body`의 **requiredBeneficiaryInfo** 필드의 값은 아래 링크 정보를 참조합니다.
* `Response Body`의 에러 코드 **reason** 필드의 값이 **LACK-OF-INFORMATION** 인 경우, **message** 필드의 값은 아래 링크 정보를 참조합니다.

{% content-ref url="/pages/7eieRI1CXQP64lZeluEk" %}
[IVMS101 사용자 개인 정보 종류 코드](/reference/ivms101/ivms101-personal-info-codes.md)
{% endcontent-ref %}
